diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS deleted file mode 100644 index d227113e2..000000000 --- a/.github/CODEOWNERS +++ /dev/null @@ -1 +0,0 @@ -* @lambdaclass/zk_research_and_development diff --git a/.github/SECURITY.md b/.github/SECURITY.md deleted file mode 100644 index bda332d31..000000000 --- a/.github/SECURITY.md +++ /dev/null @@ -1,36 +0,0 @@ -# Security Policy - -## Reporting a Vulnerability - -We take the security of our project seriously. If you discover a vulnerability, we encourage you to report it responsibly so we can address it promptly. - -### How to Report - -1. Navigate to the **Security** tab of this repository. -2. Click on **"Report a Vulnerability"** to open the GitHub Security Advisories form. -3. Fill out the form with as much detail as possible, including: - - A clear description of the issue. - - Steps to reproduce the vulnerability. - - The affected versions or components. - - Any potential impact or severity details. - -Alternatively, you can send an email to **[security@lambdaclass.com](mailto:security@lambdaclass.com)** with the same details. - -### Guidelines for Reporting - -- **Do not publicly disclose vulnerabilities** until we have confirmed and fixed the issue. -- Include any proof-of-concept code, if possible, to help us verify the vulnerability more efficiently. -- If applicable, specify if the vulnerability is already being exploited. - -### Our Response Process - -- We commit to handling reports with diligence. -- We will investigate all reported vulnerabilities thoroughly and transparently. -- Once the vulnerability has been fixed, we will disclose the details publicly to ensure awareness and understanding. - - -### Reward Program - -While we do not currently offer a formal bug bounty program, we value your contribution and will recognize your efforts in our changelog or release notes (if you consent). - -Thank you for helping us improve the security of our project! diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md deleted file mode 100644 index 15dc01b46..000000000 --- a/.github/pull_request_template.md +++ /dev/null @@ -1,21 +0,0 @@ -# TITLE - -## Description - -Description of the pull request changes and motivation. - -## Type of change - -Please delete options that are not relevant. - -- [ ] New feature -- [ ] Bug fix -- [ ] Optimization - -## Checklist -- [ ] Linked to Github Issue -- [ ] Unit tests added -- [ ] This change requires new documentation. - - [ ] Documentation has been added/updated. -- [ ] This change is an Optimization - - [ ] Benchmarks added/run diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml deleted file mode 100644 index 21dadfd10..000000000 --- a/.github/workflows/ci.yaml +++ /dev/null @@ -1,188 +0,0 @@ -name: CI -on: - merge_group: - push: - branches: [main] - pull_request: - branches: ["*"] - -concurrency: - group: ${{ github.head_ref || github.run_id }} - cancel-in-progress: true - -jobs: - compile: - name: Compile - runs-on: ubuntu-latest - steps: - - name: Checkout sources - uses: actions/checkout@v4 - - - name: Rustup toolchain install - uses: dtolnay/rust-toolchain@stable - with: - toolchain: stable - targets: wasm32-unknown-unknown - - - name: Set up cargo cache - uses: Swatinem/rust-cache@v2 - - - name: Run cargo check - run: cargo check - - - name: Run cargo check and stark instruments - run: | - cargo check --features instruments - - - name: Run cargo check cli, stark instruments and parallel - run: | - cargo check --features instruments,parallel - - - name: Run cargo check for math with no-std - run: cargo check --package lambdaworks-math --no-default-features - - - name: Run cargo check for math with wasm target - run: cargo check --package lambdaworks-math --no-default-features --target wasm32-unknown-unknown - - - name: Run cargo check for crypto with wasm target - run: cargo check --package lambdaworks-crypto --no-default-features --target wasm32-unknown-unknown - - - name: Run cargo build ensure-no_std crate - run: | - cd ensure-no_std - cargo build - - - name: Run cargo build for all workspace - run: | - cargo build --workspace - - - name: Run cargo build ensure-no_std crate for wasm - run: | - cd ensure-no_std - cargo build --target wasm32-unknown-unknown - - - name: Check benchmarks - run: cargo check --benches - - lint: - name: Lint - runs-on: ubuntu-latest - steps: - - name: Checkout sources - uses: actions/checkout@v3 - - - name: Install stable toolchain - uses: dtolnay/rust-toolchain@stable - with: - toolchain: stable - components: rustfmt, clippy - - - name: Run cargo fmt - run: cargo fmt --all -- --check - - - name: Run clippy - run: make clippy - - - name: Run clippy math and crypto no std - run: cargo clippy --package lambdaworks-math --package lambdaworks-crypto --no-default-features -- -D warnings - - - name: Run clippy math no std + alloc - run: cargo clippy --package lambdaworks-math --no-default-features --features=alloc,lambdaworks-serde-string,lambdaworks-serde-binary -- -D warnings - - test: - name: Test (Ubuntu) - runs-on: ubuntu-latest - env: - CARGO_TERM_COLOR: always - steps: - - uses: actions/checkout@v3 - - uses: dtolnay/rust-toolchain@stable - with: - toolchain: stable - components: clippy - - - name: Set up cargo cache - uses: Swatinem/rust-cache@v2 - - - name: Install testing tools - uses: taiki-e/install-action@v2 - with: - tool: cargo-nextest,cargo-llvm-cov - - - name: Run tests with no std - run: cargo test --package lambdaworks-math --package lambdaworks-crypto --no-default-features - - - name: Run tests for math with no std + alloc - run: cargo test --package lambdaworks-math --no-default-features --features=alloc,lambdaworks-serde-string,lambdaworks-serde-binary - - - name: Run tests and generate code coverage - run: make coverage - - - name: Cache coverage data - uses: actions/cache/save@v4 - with: - path: lcov.info - key: coverage-${{ github.sha }} - - coverage: - name: Upload Coverage to Codecov - runs-on: ubuntu-latest - needs: test - steps: - - uses: actions/checkout@v3 - - name: Fetch coverage data - uses: actions/cache/restore@v4 - with: - path: lcov.info - key: coverage-${{ github.sha }} - fail-on-cache-miss: true - - - name: Upload coverage to Codecov - uses: codecov/codecov-action@v3 - with: - token: ${{ secrets.CODECOV_TOKEN }} - files: lcov.info - fail_ci_if_error: true - - test_wasm_pack: - name: Test wasm-pack - runs-on: ubuntu-latest - env: - CARGO_TERM_COLOR: always - steps: - - uses: actions/checkout@v3 - - uses: dtolnay/rust-toolchain@stable - with: - toolchain: stable - components: clippy - - - name: Set up cargo cache - uses: Swatinem/rust-cache@v2 - - - name: Install wasm-pack tools for testing - run: curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh - - test_macos: - name: Test (macOS, Apple sillicon) - runs-on: macos-latest - env: - CARGO_TERM_COLOR: always - steps: - - uses: actions/checkout@v3 - - - name: install GCC/GMP - run: | - brew install gcc - brew install gmp - - - name: Rustup toolchain install - uses: dtolnay/rust-toolchain@stable - with: - toolchain: stable - components: clippy - - - name: Run clippy - run: make clippy - - - name: Run tests - run: make test diff --git a/.github/workflows/criterion_benchs.yml b/.github/workflows/criterion_benchs.yml deleted file mode 100644 index 32d5c7f2e..000000000 --- a/.github/workflows/criterion_benchs.yml +++ /dev/null @@ -1,67 +0,0 @@ -name: CI - -on: - push: - branches: [main] - -permissions: - # deployments permission to deploy GitHub pages website - deployments: write - # contents permission to update benchmark contents in gh-pages branch - contents: write - -jobs: - criterion_bench: - name: Criterion benches (Ubuntu) - runs-on: ubuntu-latest - env: - CARGO_TERM_COLOR: always - steps: - - name: Install stable toolchain - uses: dtolnay/rust-toolchain@stable - with: - toolchain: stable - - uses: actions/checkout@v3 - - name: Run benchmark - run: | - cargo bench --no-fail-fast --bench "criterion_fft" \ - --bench "criterion_polynomial" \ - -- --output-format bencher | tee output.txt - - name: Store benchmark result - uses: benchmark-action/github-action-benchmark@v1 - with: - tool: "cargo" - output-file-path: output.txt - benchmark-data-dir-path: "./bench" - github-token: ${{ secrets.GITHUB_TOKEN }} - # Push and deploy GitHub pages branch automatically - auto-push: ${{ github.event_name != 'pull_request' }} - criterion_bench_macos: - name: Criterion benches (macOS, Apple sillicon) - runs-on: macos-latest - env: - CARGO_TERM_COLOR: always - steps: - - name: Install stable toolchain - uses: dtolnay/rust-toolchain@stable - with: - toolchain: stable - - uses: actions/checkout@v3 - - name: install GCC/GMP - run: | - brew install gcc - brew install gmp - - - name: Run benchmark - run: | - cargo bench -F metal --no-fail-fast --bench "criterion_metal" \ - -- --output-format bencher | tee output.txt - - name: Store benchmark result - uses: benchmark-action/github-action-benchmark@v1 - with: - tool: "cargo" - output-file-path: output.txt - benchmark-data-dir-path: "./bench" - github-token: ${{ secrets.GITHUB_TOKEN }} - # Push and deploy GitHub pages branch automatically - auto-push: ${{ github.event_name != 'pull_request' }} diff --git a/.github/workflows/gh-pages.yml b/.github/workflows/gh-pages.yml deleted file mode 100644 index 9741f5982..000000000 --- a/.github/workflows/gh-pages.yml +++ /dev/null @@ -1,36 +0,0 @@ -name: Github Pages - -on: - push: - branches: - - main - paths: - - "docs/**" - pull_request: - paths: - - "docs/**" - -jobs: - deploy: - runs-on: ubuntu-20.04 - concurrency: - group: ${{ github.workflow }}-${{ github.ref }} - steps: - - uses: actions/checkout@v2 - - - name: Setup mdBook - uses: peaceiris/actions-mdbook@v1 - with: - mdbook-version: '0.4.10' - - - name: Install Katex - run: cargo install mdbook-katex - - - run: mdbook build docs - - - name: Deploy - uses: peaceiris/actions-gh-pages@v3 - if: ${{ github.ref == 'refs/heads/main' }} - with: - github_token: ${{ secrets.GITHUB_TOKEN }} - publish_dir: ./docs/book diff --git a/.github/workflows/iai_benchs_main.yml b/.github/workflows/iai_benchs_main.yml deleted file mode 100644 index 0845b7ce0..000000000 --- a/.github/workflows/iai_benchs_main.yml +++ /dev/null @@ -1,32 +0,0 @@ -name: CI - -on: - push: - branches: [ main ] - -jobs: - cache_iai_benchs: - name: Cache iai benchs of main - runs-on: ubuntu-latest - steps: - - name: Install valgrind - run: | - sudo apt update - sudo apt-get install -y valgrind - cargo install --version 0.3.1 iai-callgrind-runner - - name: Install stable toolchain - uses: dtolnay/rust-toolchain@stable - with: - toolchain: stable - - uses: actions/checkout@v3 - - uses: Swatinem/rust-cache@v2 - with: - shared-key: ${{ runner.os }}-benchmark-build-cache - - name: Run benchmarks - run: cargo bench --no-fail-fast --bench "iai_*" - - name: Save cache - uses: actions/cache/save@v3 - with: - path: | - */target - key: ${{ runner.os }}-iai-benchmark-cache-${{ github.sha }} diff --git a/.github/workflows/iai_benchs_pr.yml b/.github/workflows/iai_benchs_pr.yml deleted file mode 100644 index 1aee916aa..000000000 --- a/.github/workflows/iai_benchs_pr.yml +++ /dev/null @@ -1,80 +0,0 @@ -name: CI - -on: - pull_request: - branches: [ '*' ] - -concurrency: - group: ${{ github.workflow }}-${{ github.ref }} - cancel-in-progress: true - -jobs: - fetch_iai_benchs: - name: Fetch iai benchmarks - runs-on: ubuntu-latest - steps: - - name: Check cache - id: cache-iai-results - uses: actions/cache/restore@v3 - with: - lookup-only: true - path: | - */target - key: ${{ runner.os }}-iai-benchmark-cache-${{ github.event.pull_request.base.sha }} - - name: Install valgrind - if: ${{ steps.cache-iai-results.outputs.cache-hit != 'true' }} - run: | - sudo apt update - sudo apt-get install -y valgrind - cargo install --version 0.3.1 iai-callgrind-runner - - name: Install stable toolchain - if: ${{ steps.cache-iai-results.outputs.cache-hit != 'true' }} - uses: dtolnay/rust-toolchain@stable - with: - toolchain: stable - - uses: actions/checkout@v3 - if: ${{ steps.cache-iai-results.outputs.cache-hit != 'true' }} - with: - ref: ${{ github.event.pull_request.base.sha }} - - uses: Swatinem/rust-cache@v2 - if: ${{ steps.cache-iai-results.outputs.cache-hit != 'true' }} - with: - shared-key: ${{ runner.os }}-benchmark-build-cache - - name: Run benchmarks - if: ${{ steps.cache-iai-results.outputs.cache-hit != 'true' }} - run: cargo bench --no-fail-fast --bench "iai_*" - - name: Save cache - if: ${{ steps.cache-iai-results.outputs.cache-hit != 'true' }} - uses: actions/cache/save@v3 - with: - path: | - */target - key: ${{ runner.os }}-iai-benchmark-cache-${{ github.event.pull_request.base.sha }} - run_iai_benchs: - name: Run iai benchmarks - needs: fetch_iai_benchs - runs-on: ubuntu-latest - steps: - - name: Install valgrind - run: | - sudo apt-get update - sudo apt-get install -y valgrind - cargo install --version 0.3.1 iai-callgrind-runner - - name: Install stable toolchain - uses: dtolnay/rust-toolchain@stable - with: - toolchain: stable - - uses: actions/checkout@v3 - - uses: Swatinem/rust-cache@v2 - with: - shared-key: ${{ runner.os }}-iai-benchmark-cache - - name: Restore cache - id: cache-iai-results - uses: actions/cache/restore@v3 - with: - path: | - */target - key: ${{ runner.os }}-iai-benchmark-cache-${{ github.event.pull_request.base.sha }} - fail-on-cache-miss: true - - name: Run benchmarks - run: cargo bench --no-fail-fast --bench "iai_*" diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml deleted file mode 100644 index fbf7926a2..000000000 --- a/.github/workflows/publish.yml +++ /dev/null @@ -1,22 +0,0 @@ -name: publish - -on: - push: - tags: - - "*" - -jobs: - publish: - name: Publish - runs-on: ubuntu-latest - steps: - - name: Checkout sources - uses: actions/checkout@v4 - - name: Install stable toolchain - uses: dtolnay/rust-toolchain@1.90.0 - - name: Publish crate lambdaworks-gpu - run: cargo publish -p lambdaworks-gpu --token ${{ secrets.CARGO_REGISTRY_TOKEN }} - - name: Publish crate lambdaworks-math - run: cargo publish -p lambdaworks-math --token ${{ secrets.CARGO_REGISTRY_TOKEN }} --features "parallel std alloc lambdaworks-serde-binary lambdaworks-serde-string proptest" - - name: Publish crate lambdaworks-crypto - run: cargo publish -p lambdaworks-crypto --token ${{ secrets.CARGO_REGISTRY_TOKEN }} --features "asm std serde parallel alloc" diff --git a/.gitignore b/.gitignore deleted file mode 100644 index e691dd6c4..000000000 --- a/.gitignore +++ /dev/null @@ -1,28 +0,0 @@ -# Generated by Cargo -# will have compiled files and executables -**/target/** - -# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries -# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html -Cargo.lock - -# These are backup files generated by rustfmt -**/*.rs.bk - -**/proptest-regressions -/output.txt -flamegraph.svg -proving_system/stark/src/cairo_run/program.memory -proving_system/stark/src/cairo_run/program.trace - -**/*.metallib -**/*.ptx - -**/.DS_Store - -ensure-no_std/target -# Files from fuzzers are inside a corpus folder -**/corpus/** -**/artifacts/** -/.idea/ - diff --git a/.gitmodules b/.gitmodules deleted file mode 100644 index e69de29bb..000000000 diff --git a/.nojekyll b/.nojekyll new file mode 100644 index 000000000..f17311098 --- /dev/null +++ b/.nojekyll @@ -0,0 +1 @@ +This file makes sure that Github Pages doesn't process mdBook's output. diff --git a/.rusty-hook.toml b/.rusty-hook.toml deleted file mode 100644 index 79862c083..000000000 --- a/.rusty-hook.toml +++ /dev/null @@ -1,5 +0,0 @@ -[hooks] -pre-commit = "cargo test && cargo clippy --all-targets -- -D warnings && cargo fmt --all -- --check" - -[logging] -verbose = true diff --git a/404.html b/404.html new file mode 100644 index 000000000..cc7b5bb66 --- /dev/null +++ b/404.html @@ -0,0 +1,187 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + + + +
+
+

Document not found (404)

+

This URL is invalid, sorry. Please use the navigation bar or search to continue.

+ +
+ + +
+
+ + + +
+ + + + + + + + + + + + + + + + + + + + diff --git a/Cargo.toml b/Cargo.toml deleted file mode 100644 index 0e9a4acc2..000000000 --- a/Cargo.toml +++ /dev/null @@ -1,62 +0,0 @@ -[workspace] - -members = [ - "benches", - "crates/crypto", - "crates/gpu", - "crates/math", - "crates/provers/groth16", - "crates/provers/groth16/arkworks-adapter", - "crates/provers/groth16/circom-adapter", - "crates/provers/plonk", - "crates/provers/stark", - "crates/provers/sumcheck", - "crates/provers/gkr", - "crates/provers/winterfell_adapter", - "examples/merkle-tree-cli", - "examples/prove-miden", - "examples/shamir_secret_sharing", - "examples/pinocchio", - "examples/prove-verify-circom", - "examples/baby-snark", - "examples/pohlig-hellman-attack", - "examples/schnorr-signature", - "examples/rsa", -] -exclude = ["ensure-no_std"] -resolver = "2" - -[workspace.package] -version = "0.13.0" -edition = "2021" -license = "Apache-2.0" -repository = "https://github.com/lambdaclass/lambdaworks" - -[workspace.dependencies] -lambdaworks-crypto = { path = "./crates/crypto", version = "0.13.0", default-features = false } -lambdaworks-gpu = { path = "./crates/gpu", version = "0.13.0" } -lambdaworks-math = { path = "./crates/math", version = "0.13.0", default-features = false } -lambdaworks-groth16 = { path = "./crates/provers/groth16" } -lambdaworks-circom-adapter = { path = "./crates/provers/groth16/circom-adapter" } -lambdaworks-sumcheck = { path = "./crates/provers/sumcheck" } -lambdaworks-sumcheck-gkr = { path = "./crates/provers/gkr" } -lambdaworks-winterfell-adapter = { path = "./crates/provers/winterfell_adapter" } -stark-platinum-prover = { path = "./crates/provers/stark" } -iai-callgrind = "0.3.1" - -[patch.crates-io] -miden-air = { git = "https://github.com/lambdaclass/miden-vm" } -miden-assembly = { git = "https://github.com/lambdaclass/miden-vm" } -miden-core = { git = "https://github.com/lambdaclass/miden-vm" } -miden-processor = { git = "https://github.com/lambdaclass/miden-vm" } -winter-air = { git = "https://github.com/lambdaclass/winterfell-for-lambdaworks.git", branch = "derive-clone-v6.4" } -winter-prover = { git = "https://github.com/lambdaclass/winterfell-for-lambdaworks.git", branch = "derive-clone-v6.4" } -winter-math = { git = "https://github.com/lambdaclass/winterfell-for-lambdaworks.git", branch = "derive-clone-v6.4" } -winter-utils = { git = "https://github.com/lambdaclass/winterfell-for-lambdaworks.git", branch = "derive-clone-v6.4" } -winter-crypto = { git = "https://github.com/lambdaclass/winterfell-for-lambdaworks.git", branch = "derive-clone-v6.4" } - - -[profile.bench] -lto = true -codegen-units = 1 -opt-level = 3 diff --git a/Dockerfile b/Dockerfile deleted file mode 100644 index 5ff2181be..000000000 --- a/Dockerfile +++ /dev/null @@ -1,6 +0,0 @@ -FROM rust:1.66 - -WORKDIR /usr/src/elliptic-curves -COPY . . - -CMD cargo test diff --git a/FontAwesome/css/font-awesome.css b/FontAwesome/css/font-awesome.css new file mode 100644 index 000000000..540440ce8 --- /dev/null +++ b/FontAwesome/css/font-awesome.css @@ -0,0 +1,4 @@ +/*! + * Font Awesome 4.7.0 by @davegandy - http://fontawesome.io - @fontawesome + * License - http://fontawesome.io/license (Font: SIL OFL 1.1, CSS: MIT License) + */@font-face{font-family:'FontAwesome';src:url('../fonts/fontawesome-webfont.eot?v=4.7.0');src:url('../fonts/fontawesome-webfont.eot?#iefix&v=4.7.0') format('embedded-opentype'),url('../fonts/fontawesome-webfont.woff2?v=4.7.0') format('woff2'),url('../fonts/fontawesome-webfont.woff?v=4.7.0') format('woff'),url('../fonts/fontawesome-webfont.ttf?v=4.7.0') format('truetype'),url('../fonts/fontawesome-webfont.svg?v=4.7.0#fontawesomeregular') format('svg');font-weight:normal;font-style:normal}.fa{display:inline-block;font:normal normal normal 14px/1 FontAwesome;font-size:inherit;text-rendering:auto;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.fa-lg{font-size:1.33333333em;line-height:.75em;vertical-align:-15%}.fa-2x{font-size:2em}.fa-3x{font-size:3em}.fa-4x{font-size:4em}.fa-5x{font-size:5em}.fa-fw{width:1.28571429em;text-align:center}.fa-ul{padding-left:0;margin-left:2.14285714em;list-style-type:none}.fa-ul>li{position:relative}.fa-li{position:absolute;left:-2.14285714em;width:2.14285714em;top:.14285714em;text-align:center}.fa-li.fa-lg{left:-1.85714286em}.fa-border{padding:.2em .25em .15em;border:solid .08em #eee;border-radius:.1em}.fa-pull-left{float:left}.fa-pull-right{float:right}.fa.fa-pull-left{margin-right:.3em}.fa.fa-pull-right{margin-left:.3em}.pull-right{float:right}.pull-left{float:left}.fa.pull-left{margin-right:.3em}.fa.pull-right{margin-left:.3em}.fa-spin{-webkit-animation:fa-spin 2s infinite linear;animation:fa-spin 2s infinite linear}.fa-pulse{-webkit-animation:fa-spin 1s infinite steps(8);animation:fa-spin 1s infinite steps(8)}@-webkit-keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}@keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}.fa-rotate-90{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=1)";-webkit-transform:rotate(90deg);-ms-transform:rotate(90deg);transform:rotate(90deg)}.fa-rotate-180{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=2)";-webkit-transform:rotate(180deg);-ms-transform:rotate(180deg);transform:rotate(180deg)}.fa-rotate-270{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=3)";-webkit-transform:rotate(270deg);-ms-transform:rotate(270deg);transform:rotate(270deg)}.fa-flip-horizontal{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=0, mirror=1)";-webkit-transform:scale(-1, 1);-ms-transform:scale(-1, 1);transform:scale(-1, 1)}.fa-flip-vertical{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=2, mirror=1)";-webkit-transform:scale(1, -1);-ms-transform:scale(1, -1);transform:scale(1, -1)}:root .fa-rotate-90,:root .fa-rotate-180,:root .fa-rotate-270,:root .fa-flip-horizontal,:root .fa-flip-vertical{filter:none}.fa-stack{position:relative;display:inline-block;width:2em;height:2em;line-height:2em;vertical-align:middle}.fa-stack-1x,.fa-stack-2x{position:absolute;left:0;width:100%;text-align:center}.fa-stack-1x{line-height:inherit}.fa-stack-2x{font-size:2em}.fa-inverse{color:#fff}.fa-glass:before{content:"\f000"}.fa-music:before{content:"\f001"}.fa-search:before{content:"\f002"}.fa-envelope-o:before{content:"\f003"}.fa-heart:before{content:"\f004"}.fa-star:before{content:"\f005"}.fa-star-o:before{content:"\f006"}.fa-user:before{content:"\f007"}.fa-film:before{content:"\f008"}.fa-th-large:before{content:"\f009"}.fa-th:before{content:"\f00a"}.fa-th-list:before{content:"\f00b"}.fa-check:before{content:"\f00c"}.fa-remove:before,.fa-close:before,.fa-times:before{content:"\f00d"}.fa-search-plus:before{content:"\f00e"}.fa-search-minus:before{content:"\f010"}.fa-power-off:before{content:"\f011"}.fa-signal:before{content:"\f012"}.fa-gear:before,.fa-cog:before{content:"\f013"}.fa-trash-o:before{content:"\f014"}.fa-home:before{content:"\f015"}.fa-file-o:before{content:"\f016"}.fa-clock-o:before{content:"\f017"}.fa-road:before{content:"\f018"}.fa-download:before{content:"\f019"}.fa-arrow-circle-o-down:before{content:"\f01a"}.fa-arrow-circle-o-up:before{content:"\f01b"}.fa-inbox:before{content:"\f01c"}.fa-play-circle-o:before{content:"\f01d"}.fa-rotate-right:before,.fa-repeat:before{content:"\f01e"}.fa-refresh:before{content:"\f021"}.fa-list-alt:before{content:"\f022"}.fa-lock:before{content:"\f023"}.fa-flag:before{content:"\f024"}.fa-headphones:before{content:"\f025"}.fa-volume-off:before{content:"\f026"}.fa-volume-down:before{content:"\f027"}.fa-volume-up:before{content:"\f028"}.fa-qrcode:before{content:"\f029"}.fa-barcode:before{content:"\f02a"}.fa-tag:before{content:"\f02b"}.fa-tags:before{content:"\f02c"}.fa-book:before{content:"\f02d"}.fa-bookmark:before{content:"\f02e"}.fa-print:before{content:"\f02f"}.fa-camera:before{content:"\f030"}.fa-font:before{content:"\f031"}.fa-bold:before{content:"\f032"}.fa-italic:before{content:"\f033"}.fa-text-height:before{content:"\f034"}.fa-text-width:before{content:"\f035"}.fa-align-left:before{content:"\f036"}.fa-align-center:before{content:"\f037"}.fa-align-right:before{content:"\f038"}.fa-align-justify:before{content:"\f039"}.fa-list:before{content:"\f03a"}.fa-dedent:before,.fa-outdent:before{content:"\f03b"}.fa-indent:before{content:"\f03c"}.fa-video-camera:before{content:"\f03d"}.fa-photo:before,.fa-image:before,.fa-picture-o:before{content:"\f03e"}.fa-pencil:before{content:"\f040"}.fa-map-marker:before{content:"\f041"}.fa-adjust:before{content:"\f042"}.fa-tint:before{content:"\f043"}.fa-edit:before,.fa-pencil-square-o:before{content:"\f044"}.fa-share-square-o:before{content:"\f045"}.fa-check-square-o:before{content:"\f046"}.fa-arrows:before{content:"\f047"}.fa-step-backward:before{content:"\f048"}.fa-fast-backward:before{content:"\f049"}.fa-backward:before{content:"\f04a"}.fa-play:before{content:"\f04b"}.fa-pause:before{content:"\f04c"}.fa-stop:before{content:"\f04d"}.fa-forward:before{content:"\f04e"}.fa-fast-forward:before{content:"\f050"}.fa-step-forward:before{content:"\f051"}.fa-eject:before{content:"\f052"}.fa-chevron-left:before{content:"\f053"}.fa-chevron-right:before{content:"\f054"}.fa-plus-circle:before{content:"\f055"}.fa-minus-circle:before{content:"\f056"}.fa-times-circle:before{content:"\f057"}.fa-check-circle:before{content:"\f058"}.fa-question-circle:before{content:"\f059"}.fa-info-circle:before{content:"\f05a"}.fa-crosshairs:before{content:"\f05b"}.fa-times-circle-o:before{content:"\f05c"}.fa-check-circle-o:before{content:"\f05d"}.fa-ban:before{content:"\f05e"}.fa-arrow-left:before{content:"\f060"}.fa-arrow-right:before{content:"\f061"}.fa-arrow-up:before{content:"\f062"}.fa-arrow-down:before{content:"\f063"}.fa-mail-forward:before,.fa-share:before{content:"\f064"}.fa-expand:before{content:"\f065"}.fa-compress:before{content:"\f066"}.fa-plus:before{content:"\f067"}.fa-minus:before{content:"\f068"}.fa-asterisk:before{content:"\f069"}.fa-exclamation-circle:before{content:"\f06a"}.fa-gift:before{content:"\f06b"}.fa-leaf:before{content:"\f06c"}.fa-fire:before{content:"\f06d"}.fa-eye:before{content:"\f06e"}.fa-eye-slash:before{content:"\f070"}.fa-warning:before,.fa-exclamation-triangle:before{content:"\f071"}.fa-plane:before{content:"\f072"}.fa-calendar:before{content:"\f073"}.fa-random:before{content:"\f074"}.fa-comment:before{content:"\f075"}.fa-magnet:before{content:"\f076"}.fa-chevron-up:before{content:"\f077"}.fa-chevron-down:before{content:"\f078"}.fa-retweet:before{content:"\f079"}.fa-shopping-cart:before{content:"\f07a"}.fa-folder:before{content:"\f07b"}.fa-folder-open:before{content:"\f07c"}.fa-arrows-v:before{content:"\f07d"}.fa-arrows-h:before{content:"\f07e"}.fa-bar-chart-o:before,.fa-bar-chart:before{content:"\f080"}.fa-twitter-square:before{content:"\f081"}.fa-facebook-square:before{content:"\f082"}.fa-camera-retro:before{content:"\f083"}.fa-key:before{content:"\f084"}.fa-gears:before,.fa-cogs:before{content:"\f085"}.fa-comments:before{content:"\f086"}.fa-thumbs-o-up:before{content:"\f087"}.fa-thumbs-o-down:before{content:"\f088"}.fa-star-half:before{content:"\f089"}.fa-heart-o:before{content:"\f08a"}.fa-sign-out:before{content:"\f08b"}.fa-linkedin-square:before{content:"\f08c"}.fa-thumb-tack:before{content:"\f08d"}.fa-external-link:before{content:"\f08e"}.fa-sign-in:before{content:"\f090"}.fa-trophy:before{content:"\f091"}.fa-github-square:before{content:"\f092"}.fa-upload:before{content:"\f093"}.fa-lemon-o:before{content:"\f094"}.fa-phone:before{content:"\f095"}.fa-square-o:before{content:"\f096"}.fa-bookmark-o:before{content:"\f097"}.fa-phone-square:before{content:"\f098"}.fa-twitter:before{content:"\f099"}.fa-facebook-f:before,.fa-facebook:before{content:"\f09a"}.fa-github:before{content:"\f09b"}.fa-unlock:before{content:"\f09c"}.fa-credit-card:before{content:"\f09d"}.fa-feed:before,.fa-rss:before{content:"\f09e"}.fa-hdd-o:before{content:"\f0a0"}.fa-bullhorn:before{content:"\f0a1"}.fa-bell:before{content:"\f0f3"}.fa-certificate:before{content:"\f0a3"}.fa-hand-o-right:before{content:"\f0a4"}.fa-hand-o-left:before{content:"\f0a5"}.fa-hand-o-up:before{content:"\f0a6"}.fa-hand-o-down:before{content:"\f0a7"}.fa-arrow-circle-left:before{content:"\f0a8"}.fa-arrow-circle-right:before{content:"\f0a9"}.fa-arrow-circle-up:before{content:"\f0aa"}.fa-arrow-circle-down:before{content:"\f0ab"}.fa-globe:before{content:"\f0ac"}.fa-wrench:before{content:"\f0ad"}.fa-tasks:before{content:"\f0ae"}.fa-filter:before{content:"\f0b0"}.fa-briefcase:before{content:"\f0b1"}.fa-arrows-alt:before{content:"\f0b2"}.fa-group:before,.fa-users:before{content:"\f0c0"}.fa-chain:before,.fa-link:before{content:"\f0c1"}.fa-cloud:before{content:"\f0c2"}.fa-flask:before{content:"\f0c3"}.fa-cut:before,.fa-scissors:before{content:"\f0c4"}.fa-copy:before,.fa-files-o:before{content:"\f0c5"}.fa-paperclip:before{content:"\f0c6"}.fa-save:before,.fa-floppy-o:before{content:"\f0c7"}.fa-square:before{content:"\f0c8"}.fa-navicon:before,.fa-reorder:before,.fa-bars:before{content:"\f0c9"}.fa-list-ul:before{content:"\f0ca"}.fa-list-ol:before{content:"\f0cb"}.fa-strikethrough:before{content:"\f0cc"}.fa-underline:before{content:"\f0cd"}.fa-table:before{content:"\f0ce"}.fa-magic:before{content:"\f0d0"}.fa-truck:before{content:"\f0d1"}.fa-pinterest:before{content:"\f0d2"}.fa-pinterest-square:before{content:"\f0d3"}.fa-google-plus-square:before{content:"\f0d4"}.fa-google-plus:before{content:"\f0d5"}.fa-money:before{content:"\f0d6"}.fa-caret-down:before{content:"\f0d7"}.fa-caret-up:before{content:"\f0d8"}.fa-caret-left:before{content:"\f0d9"}.fa-caret-right:before{content:"\f0da"}.fa-columns:before{content:"\f0db"}.fa-unsorted:before,.fa-sort:before{content:"\f0dc"}.fa-sort-down:before,.fa-sort-desc:before{content:"\f0dd"}.fa-sort-up:before,.fa-sort-asc:before{content:"\f0de"}.fa-envelope:before{content:"\f0e0"}.fa-linkedin:before{content:"\f0e1"}.fa-rotate-left:before,.fa-undo:before{content:"\f0e2"}.fa-legal:before,.fa-gavel:before{content:"\f0e3"}.fa-dashboard:before,.fa-tachometer:before{content:"\f0e4"}.fa-comment-o:before{content:"\f0e5"}.fa-comments-o:before{content:"\f0e6"}.fa-flash:before,.fa-bolt:before{content:"\f0e7"}.fa-sitemap:before{content:"\f0e8"}.fa-umbrella:before{content:"\f0e9"}.fa-paste:before,.fa-clipboard:before{content:"\f0ea"}.fa-lightbulb-o:before{content:"\f0eb"}.fa-exchange:before{content:"\f0ec"}.fa-cloud-download:before{content:"\f0ed"}.fa-cloud-upload:before{content:"\f0ee"}.fa-user-md:before{content:"\f0f0"}.fa-stethoscope:before{content:"\f0f1"}.fa-suitcase:before{content:"\f0f2"}.fa-bell-o:before{content:"\f0a2"}.fa-coffee:before{content:"\f0f4"}.fa-cutlery:before{content:"\f0f5"}.fa-file-text-o:before{content:"\f0f6"}.fa-building-o:before{content:"\f0f7"}.fa-hospital-o:before{content:"\f0f8"}.fa-ambulance:before{content:"\f0f9"}.fa-medkit:before{content:"\f0fa"}.fa-fighter-jet:before{content:"\f0fb"}.fa-beer:before{content:"\f0fc"}.fa-h-square:before{content:"\f0fd"}.fa-plus-square:before{content:"\f0fe"}.fa-angle-double-left:before{content:"\f100"}.fa-angle-double-right:before{content:"\f101"}.fa-angle-double-up:before{content:"\f102"}.fa-angle-double-down:before{content:"\f103"}.fa-angle-left:before{content:"\f104"}.fa-angle-right:before{content:"\f105"}.fa-angle-up:before{content:"\f106"}.fa-angle-down:before{content:"\f107"}.fa-desktop:before{content:"\f108"}.fa-laptop:before{content:"\f109"}.fa-tablet:before{content:"\f10a"}.fa-mobile-phone:before,.fa-mobile:before{content:"\f10b"}.fa-circle-o:before{content:"\f10c"}.fa-quote-left:before{content:"\f10d"}.fa-quote-right:before{content:"\f10e"}.fa-spinner:before{content:"\f110"}.fa-circle:before{content:"\f111"}.fa-mail-reply:before,.fa-reply:before{content:"\f112"}.fa-github-alt:before{content:"\f113"}.fa-folder-o:before{content:"\f114"}.fa-folder-open-o:before{content:"\f115"}.fa-smile-o:before{content:"\f118"}.fa-frown-o:before{content:"\f119"}.fa-meh-o:before{content:"\f11a"}.fa-gamepad:before{content:"\f11b"}.fa-keyboard-o:before{content:"\f11c"}.fa-flag-o:before{content:"\f11d"}.fa-flag-checkered:before{content:"\f11e"}.fa-terminal:before{content:"\f120"}.fa-code:before{content:"\f121"}.fa-mail-reply-all:before,.fa-reply-all:before{content:"\f122"}.fa-star-half-empty:before,.fa-star-half-full:before,.fa-star-half-o:before{content:"\f123"}.fa-location-arrow:before{content:"\f124"}.fa-crop:before{content:"\f125"}.fa-code-fork:before{content:"\f126"}.fa-unlink:before,.fa-chain-broken:before{content:"\f127"}.fa-question:before{content:"\f128"}.fa-info:before{content:"\f129"}.fa-exclamation:before{content:"\f12a"}.fa-superscript:before{content:"\f12b"}.fa-subscript:before{content:"\f12c"}.fa-eraser:before{content:"\f12d"}.fa-puzzle-piece:before{content:"\f12e"}.fa-microphone:before{content:"\f130"}.fa-microphone-slash:before{content:"\f131"}.fa-shield:before{content:"\f132"}.fa-calendar-o:before{content:"\f133"}.fa-fire-extinguisher:before{content:"\f134"}.fa-rocket:before{content:"\f135"}.fa-maxcdn:before{content:"\f136"}.fa-chevron-circle-left:before{content:"\f137"}.fa-chevron-circle-right:before{content:"\f138"}.fa-chevron-circle-up:before{content:"\f139"}.fa-chevron-circle-down:before{content:"\f13a"}.fa-html5:before{content:"\f13b"}.fa-css3:before{content:"\f13c"}.fa-anchor:before{content:"\f13d"}.fa-unlock-alt:before{content:"\f13e"}.fa-bullseye:before{content:"\f140"}.fa-ellipsis-h:before{content:"\f141"}.fa-ellipsis-v:before{content:"\f142"}.fa-rss-square:before{content:"\f143"}.fa-play-circle:before{content:"\f144"}.fa-ticket:before{content:"\f145"}.fa-minus-square:before{content:"\f146"}.fa-minus-square-o:before{content:"\f147"}.fa-level-up:before{content:"\f148"}.fa-level-down:before{content:"\f149"}.fa-check-square:before{content:"\f14a"}.fa-pencil-square:before{content:"\f14b"}.fa-external-link-square:before{content:"\f14c"}.fa-share-square:before{content:"\f14d"}.fa-compass:before{content:"\f14e"}.fa-toggle-down:before,.fa-caret-square-o-down:before{content:"\f150"}.fa-toggle-up:before,.fa-caret-square-o-up:before{content:"\f151"}.fa-toggle-right:before,.fa-caret-square-o-right:before{content:"\f152"}.fa-euro:before,.fa-eur:before{content:"\f153"}.fa-gbp:before{content:"\f154"}.fa-dollar:before,.fa-usd:before{content:"\f155"}.fa-rupee:before,.fa-inr:before{content:"\f156"}.fa-cny:before,.fa-rmb:before,.fa-yen:before,.fa-jpy:before{content:"\f157"}.fa-ruble:before,.fa-rouble:before,.fa-rub:before{content:"\f158"}.fa-won:before,.fa-krw:before{content:"\f159"}.fa-bitcoin:before,.fa-btc:before{content:"\f15a"}.fa-file:before{content:"\f15b"}.fa-file-text:before{content:"\f15c"}.fa-sort-alpha-asc:before{content:"\f15d"}.fa-sort-alpha-desc:before{content:"\f15e"}.fa-sort-amount-asc:before{content:"\f160"}.fa-sort-amount-desc:before{content:"\f161"}.fa-sort-numeric-asc:before{content:"\f162"}.fa-sort-numeric-desc:before{content:"\f163"}.fa-thumbs-up:before{content:"\f164"}.fa-thumbs-down:before{content:"\f165"}.fa-youtube-square:before{content:"\f166"}.fa-youtube:before{content:"\f167"}.fa-xing:before{content:"\f168"}.fa-xing-square:before{content:"\f169"}.fa-youtube-play:before{content:"\f16a"}.fa-dropbox:before{content:"\f16b"}.fa-stack-overflow:before{content:"\f16c"}.fa-instagram:before{content:"\f16d"}.fa-flickr:before{content:"\f16e"}.fa-adn:before{content:"\f170"}.fa-bitbucket:before{content:"\f171"}.fa-bitbucket-square:before{content:"\f172"}.fa-tumblr:before{content:"\f173"}.fa-tumblr-square:before{content:"\f174"}.fa-long-arrow-down:before{content:"\f175"}.fa-long-arrow-up:before{content:"\f176"}.fa-long-arrow-left:before{content:"\f177"}.fa-long-arrow-right:before{content:"\f178"}.fa-apple:before{content:"\f179"}.fa-windows:before{content:"\f17a"}.fa-android:before{content:"\f17b"}.fa-linux:before{content:"\f17c"}.fa-dribbble:before{content:"\f17d"}.fa-skype:before{content:"\f17e"}.fa-foursquare:before{content:"\f180"}.fa-trello:before{content:"\f181"}.fa-female:before{content:"\f182"}.fa-male:before{content:"\f183"}.fa-gittip:before,.fa-gratipay:before{content:"\f184"}.fa-sun-o:before{content:"\f185"}.fa-moon-o:before{content:"\f186"}.fa-archive:before{content:"\f187"}.fa-bug:before{content:"\f188"}.fa-vk:before{content:"\f189"}.fa-weibo:before{content:"\f18a"}.fa-renren:before{content:"\f18b"}.fa-pagelines:before{content:"\f18c"}.fa-stack-exchange:before{content:"\f18d"}.fa-arrow-circle-o-right:before{content:"\f18e"}.fa-arrow-circle-o-left:before{content:"\f190"}.fa-toggle-left:before,.fa-caret-square-o-left:before{content:"\f191"}.fa-dot-circle-o:before{content:"\f192"}.fa-wheelchair:before{content:"\f193"}.fa-vimeo-square:before{content:"\f194"}.fa-turkish-lira:before,.fa-try:before{content:"\f195"}.fa-plus-square-o:before{content:"\f196"}.fa-space-shuttle:before{content:"\f197"}.fa-slack:before{content:"\f198"}.fa-envelope-square:before{content:"\f199"}.fa-wordpress:before{content:"\f19a"}.fa-openid:before{content:"\f19b"}.fa-institution:before,.fa-bank:before,.fa-university:before{content:"\f19c"}.fa-mortar-board:before,.fa-graduation-cap:before{content:"\f19d"}.fa-yahoo:before{content:"\f19e"}.fa-google:before{content:"\f1a0"}.fa-reddit:before{content:"\f1a1"}.fa-reddit-square:before{content:"\f1a2"}.fa-stumbleupon-circle:before{content:"\f1a3"}.fa-stumbleupon:before{content:"\f1a4"}.fa-delicious:before{content:"\f1a5"}.fa-digg:before{content:"\f1a6"}.fa-pied-piper-pp:before{content:"\f1a7"}.fa-pied-piper-alt:before{content:"\f1a8"}.fa-drupal:before{content:"\f1a9"}.fa-joomla:before{content:"\f1aa"}.fa-language:before{content:"\f1ab"}.fa-fax:before{content:"\f1ac"}.fa-building:before{content:"\f1ad"}.fa-child:before{content:"\f1ae"}.fa-paw:before{content:"\f1b0"}.fa-spoon:before{content:"\f1b1"}.fa-cube:before{content:"\f1b2"}.fa-cubes:before{content:"\f1b3"}.fa-behance:before{content:"\f1b4"}.fa-behance-square:before{content:"\f1b5"}.fa-steam:before{content:"\f1b6"}.fa-steam-square:before{content:"\f1b7"}.fa-recycle:before{content:"\f1b8"}.fa-automobile:before,.fa-car:before{content:"\f1b9"}.fa-cab:before,.fa-taxi:before{content:"\f1ba"}.fa-tree:before{content:"\f1bb"}.fa-spotify:before{content:"\f1bc"}.fa-deviantart:before{content:"\f1bd"}.fa-soundcloud:before{content:"\f1be"}.fa-database:before{content:"\f1c0"}.fa-file-pdf-o:before{content:"\f1c1"}.fa-file-word-o:before{content:"\f1c2"}.fa-file-excel-o:before{content:"\f1c3"}.fa-file-powerpoint-o:before{content:"\f1c4"}.fa-file-photo-o:before,.fa-file-picture-o:before,.fa-file-image-o:before{content:"\f1c5"}.fa-file-zip-o:before,.fa-file-archive-o:before{content:"\f1c6"}.fa-file-sound-o:before,.fa-file-audio-o:before{content:"\f1c7"}.fa-file-movie-o:before,.fa-file-video-o:before{content:"\f1c8"}.fa-file-code-o:before{content:"\f1c9"}.fa-vine:before{content:"\f1ca"}.fa-codepen:before{content:"\f1cb"}.fa-jsfiddle:before{content:"\f1cc"}.fa-life-bouy:before,.fa-life-buoy:before,.fa-life-saver:before,.fa-support:before,.fa-life-ring:before{content:"\f1cd"}.fa-circle-o-notch:before{content:"\f1ce"}.fa-ra:before,.fa-resistance:before,.fa-rebel:before{content:"\f1d0"}.fa-ge:before,.fa-empire:before{content:"\f1d1"}.fa-git-square:before{content:"\f1d2"}.fa-git:before{content:"\f1d3"}.fa-y-combinator-square:before,.fa-yc-square:before,.fa-hacker-news:before{content:"\f1d4"}.fa-tencent-weibo:before{content:"\f1d5"}.fa-qq:before{content:"\f1d6"}.fa-wechat:before,.fa-weixin:before{content:"\f1d7"}.fa-send:before,.fa-paper-plane:before{content:"\f1d8"}.fa-send-o:before,.fa-paper-plane-o:before{content:"\f1d9"}.fa-history:before{content:"\f1da"}.fa-circle-thin:before{content:"\f1db"}.fa-header:before{content:"\f1dc"}.fa-paragraph:before{content:"\f1dd"}.fa-sliders:before{content:"\f1de"}.fa-share-alt:before{content:"\f1e0"}.fa-share-alt-square:before{content:"\f1e1"}.fa-bomb:before{content:"\f1e2"}.fa-soccer-ball-o:before,.fa-futbol-o:before{content:"\f1e3"}.fa-tty:before{content:"\f1e4"}.fa-binoculars:before{content:"\f1e5"}.fa-plug:before{content:"\f1e6"}.fa-slideshare:before{content:"\f1e7"}.fa-twitch:before{content:"\f1e8"}.fa-yelp:before{content:"\f1e9"}.fa-newspaper-o:before{content:"\f1ea"}.fa-wifi:before{content:"\f1eb"}.fa-calculator:before{content:"\f1ec"}.fa-paypal:before{content:"\f1ed"}.fa-google-wallet:before{content:"\f1ee"}.fa-cc-visa:before{content:"\f1f0"}.fa-cc-mastercard:before{content:"\f1f1"}.fa-cc-discover:before{content:"\f1f2"}.fa-cc-amex:before{content:"\f1f3"}.fa-cc-paypal:before{content:"\f1f4"}.fa-cc-stripe:before{content:"\f1f5"}.fa-bell-slash:before{content:"\f1f6"}.fa-bell-slash-o:before{content:"\f1f7"}.fa-trash:before{content:"\f1f8"}.fa-copyright:before{content:"\f1f9"}.fa-at:before{content:"\f1fa"}.fa-eyedropper:before{content:"\f1fb"}.fa-paint-brush:before{content:"\f1fc"}.fa-birthday-cake:before{content:"\f1fd"}.fa-area-chart:before{content:"\f1fe"}.fa-pie-chart:before{content:"\f200"}.fa-line-chart:before{content:"\f201"}.fa-lastfm:before{content:"\f202"}.fa-lastfm-square:before{content:"\f203"}.fa-toggle-off:before{content:"\f204"}.fa-toggle-on:before{content:"\f205"}.fa-bicycle:before{content:"\f206"}.fa-bus:before{content:"\f207"}.fa-ioxhost:before{content:"\f208"}.fa-angellist:before{content:"\f209"}.fa-cc:before{content:"\f20a"}.fa-shekel:before,.fa-sheqel:before,.fa-ils:before{content:"\f20b"}.fa-meanpath:before{content:"\f20c"}.fa-buysellads:before{content:"\f20d"}.fa-connectdevelop:before{content:"\f20e"}.fa-dashcube:before{content:"\f210"}.fa-forumbee:before{content:"\f211"}.fa-leanpub:before{content:"\f212"}.fa-sellsy:before{content:"\f213"}.fa-shirtsinbulk:before{content:"\f214"}.fa-simplybuilt:before{content:"\f215"}.fa-skyatlas:before{content:"\f216"}.fa-cart-plus:before{content:"\f217"}.fa-cart-arrow-down:before{content:"\f218"}.fa-diamond:before{content:"\f219"}.fa-ship:before{content:"\f21a"}.fa-user-secret:before{content:"\f21b"}.fa-motorcycle:before{content:"\f21c"}.fa-street-view:before{content:"\f21d"}.fa-heartbeat:before{content:"\f21e"}.fa-venus:before{content:"\f221"}.fa-mars:before{content:"\f222"}.fa-mercury:before{content:"\f223"}.fa-intersex:before,.fa-transgender:before{content:"\f224"}.fa-transgender-alt:before{content:"\f225"}.fa-venus-double:before{content:"\f226"}.fa-mars-double:before{content:"\f227"}.fa-venus-mars:before{content:"\f228"}.fa-mars-stroke:before{content:"\f229"}.fa-mars-stroke-v:before{content:"\f22a"}.fa-mars-stroke-h:before{content:"\f22b"}.fa-neuter:before{content:"\f22c"}.fa-genderless:before{content:"\f22d"}.fa-facebook-official:before{content:"\f230"}.fa-pinterest-p:before{content:"\f231"}.fa-whatsapp:before{content:"\f232"}.fa-server:before{content:"\f233"}.fa-user-plus:before{content:"\f234"}.fa-user-times:before{content:"\f235"}.fa-hotel:before,.fa-bed:before{content:"\f236"}.fa-viacoin:before{content:"\f237"}.fa-train:before{content:"\f238"}.fa-subway:before{content:"\f239"}.fa-medium:before{content:"\f23a"}.fa-yc:before,.fa-y-combinator:before{content:"\f23b"}.fa-optin-monster:before{content:"\f23c"}.fa-opencart:before{content:"\f23d"}.fa-expeditedssl:before{content:"\f23e"}.fa-battery-4:before,.fa-battery:before,.fa-battery-full:before{content:"\f240"}.fa-battery-3:before,.fa-battery-three-quarters:before{content:"\f241"}.fa-battery-2:before,.fa-battery-half:before{content:"\f242"}.fa-battery-1:before,.fa-battery-quarter:before{content:"\f243"}.fa-battery-0:before,.fa-battery-empty:before{content:"\f244"}.fa-mouse-pointer:before{content:"\f245"}.fa-i-cursor:before{content:"\f246"}.fa-object-group:before{content:"\f247"}.fa-object-ungroup:before{content:"\f248"}.fa-sticky-note:before{content:"\f249"}.fa-sticky-note-o:before{content:"\f24a"}.fa-cc-jcb:before{content:"\f24b"}.fa-cc-diners-club:before{content:"\f24c"}.fa-clone:before{content:"\f24d"}.fa-balance-scale:before{content:"\f24e"}.fa-hourglass-o:before{content:"\f250"}.fa-hourglass-1:before,.fa-hourglass-start:before{content:"\f251"}.fa-hourglass-2:before,.fa-hourglass-half:before{content:"\f252"}.fa-hourglass-3:before,.fa-hourglass-end:before{content:"\f253"}.fa-hourglass:before{content:"\f254"}.fa-hand-grab-o:before,.fa-hand-rock-o:before{content:"\f255"}.fa-hand-stop-o:before,.fa-hand-paper-o:before{content:"\f256"}.fa-hand-scissors-o:before{content:"\f257"}.fa-hand-lizard-o:before{content:"\f258"}.fa-hand-spock-o:before{content:"\f259"}.fa-hand-pointer-o:before{content:"\f25a"}.fa-hand-peace-o:before{content:"\f25b"}.fa-trademark:before{content:"\f25c"}.fa-registered:before{content:"\f25d"}.fa-creative-commons:before{content:"\f25e"}.fa-gg:before{content:"\f260"}.fa-gg-circle:before{content:"\f261"}.fa-tripadvisor:before{content:"\f262"}.fa-odnoklassniki:before{content:"\f263"}.fa-odnoklassniki-square:before{content:"\f264"}.fa-get-pocket:before{content:"\f265"}.fa-wikipedia-w:before{content:"\f266"}.fa-safari:before{content:"\f267"}.fa-chrome:before{content:"\f268"}.fa-firefox:before{content:"\f269"}.fa-opera:before{content:"\f26a"}.fa-internet-explorer:before{content:"\f26b"}.fa-tv:before,.fa-television:before{content:"\f26c"}.fa-contao:before{content:"\f26d"}.fa-500px:before{content:"\f26e"}.fa-amazon:before{content:"\f270"}.fa-calendar-plus-o:before{content:"\f271"}.fa-calendar-minus-o:before{content:"\f272"}.fa-calendar-times-o:before{content:"\f273"}.fa-calendar-check-o:before{content:"\f274"}.fa-industry:before{content:"\f275"}.fa-map-pin:before{content:"\f276"}.fa-map-signs:before{content:"\f277"}.fa-map-o:before{content:"\f278"}.fa-map:before{content:"\f279"}.fa-commenting:before{content:"\f27a"}.fa-commenting-o:before{content:"\f27b"}.fa-houzz:before{content:"\f27c"}.fa-vimeo:before{content:"\f27d"}.fa-black-tie:before{content:"\f27e"}.fa-fonticons:before{content:"\f280"}.fa-reddit-alien:before{content:"\f281"}.fa-edge:before{content:"\f282"}.fa-credit-card-alt:before{content:"\f283"}.fa-codiepie:before{content:"\f284"}.fa-modx:before{content:"\f285"}.fa-fort-awesome:before{content:"\f286"}.fa-usb:before{content:"\f287"}.fa-product-hunt:before{content:"\f288"}.fa-mixcloud:before{content:"\f289"}.fa-scribd:before{content:"\f28a"}.fa-pause-circle:before{content:"\f28b"}.fa-pause-circle-o:before{content:"\f28c"}.fa-stop-circle:before{content:"\f28d"}.fa-stop-circle-o:before{content:"\f28e"}.fa-shopping-bag:before{content:"\f290"}.fa-shopping-basket:before{content:"\f291"}.fa-hashtag:before{content:"\f292"}.fa-bluetooth:before{content:"\f293"}.fa-bluetooth-b:before{content:"\f294"}.fa-percent:before{content:"\f295"}.fa-gitlab:before{content:"\f296"}.fa-wpbeginner:before{content:"\f297"}.fa-wpforms:before{content:"\f298"}.fa-envira:before{content:"\f299"}.fa-universal-access:before{content:"\f29a"}.fa-wheelchair-alt:before{content:"\f29b"}.fa-question-circle-o:before{content:"\f29c"}.fa-blind:before{content:"\f29d"}.fa-audio-description:before{content:"\f29e"}.fa-volume-control-phone:before{content:"\f2a0"}.fa-braille:before{content:"\f2a1"}.fa-assistive-listening-systems:before{content:"\f2a2"}.fa-asl-interpreting:before,.fa-american-sign-language-interpreting:before{content:"\f2a3"}.fa-deafness:before,.fa-hard-of-hearing:before,.fa-deaf:before{content:"\f2a4"}.fa-glide:before{content:"\f2a5"}.fa-glide-g:before{content:"\f2a6"}.fa-signing:before,.fa-sign-language:before{content:"\f2a7"}.fa-low-vision:before{content:"\f2a8"}.fa-viadeo:before{content:"\f2a9"}.fa-viadeo-square:before{content:"\f2aa"}.fa-snapchat:before{content:"\f2ab"}.fa-snapchat-ghost:before{content:"\f2ac"}.fa-snapchat-square:before{content:"\f2ad"}.fa-pied-piper:before{content:"\f2ae"}.fa-first-order:before{content:"\f2b0"}.fa-yoast:before{content:"\f2b1"}.fa-themeisle:before{content:"\f2b2"}.fa-google-plus-circle:before,.fa-google-plus-official:before{content:"\f2b3"}.fa-fa:before,.fa-font-awesome:before{content:"\f2b4"}.fa-handshake-o:before{content:"\f2b5"}.fa-envelope-open:before{content:"\f2b6"}.fa-envelope-open-o:before{content:"\f2b7"}.fa-linode:before{content:"\f2b8"}.fa-address-book:before{content:"\f2b9"}.fa-address-book-o:before{content:"\f2ba"}.fa-vcard:before,.fa-address-card:before{content:"\f2bb"}.fa-vcard-o:before,.fa-address-card-o:before{content:"\f2bc"}.fa-user-circle:before{content:"\f2bd"}.fa-user-circle-o:before{content:"\f2be"}.fa-user-o:before{content:"\f2c0"}.fa-id-badge:before{content:"\f2c1"}.fa-drivers-license:before,.fa-id-card:before{content:"\f2c2"}.fa-drivers-license-o:before,.fa-id-card-o:before{content:"\f2c3"}.fa-quora:before{content:"\f2c4"}.fa-free-code-camp:before{content:"\f2c5"}.fa-telegram:before{content:"\f2c6"}.fa-thermometer-4:before,.fa-thermometer:before,.fa-thermometer-full:before{content:"\f2c7"}.fa-thermometer-3:before,.fa-thermometer-three-quarters:before{content:"\f2c8"}.fa-thermometer-2:before,.fa-thermometer-half:before{content:"\f2c9"}.fa-thermometer-1:before,.fa-thermometer-quarter:before{content:"\f2ca"}.fa-thermometer-0:before,.fa-thermometer-empty:before{content:"\f2cb"}.fa-shower:before{content:"\f2cc"}.fa-bathtub:before,.fa-s15:before,.fa-bath:before{content:"\f2cd"}.fa-podcast:before{content:"\f2ce"}.fa-window-maximize:before{content:"\f2d0"}.fa-window-minimize:before{content:"\f2d1"}.fa-window-restore:before{content:"\f2d2"}.fa-times-rectangle:before,.fa-window-close:before{content:"\f2d3"}.fa-times-rectangle-o:before,.fa-window-close-o:before{content:"\f2d4"}.fa-bandcamp:before{content:"\f2d5"}.fa-grav:before{content:"\f2d6"}.fa-etsy:before{content:"\f2d7"}.fa-imdb:before{content:"\f2d8"}.fa-ravelry:before{content:"\f2d9"}.fa-eercast:before{content:"\f2da"}.fa-microchip:before{content:"\f2db"}.fa-snowflake-o:before{content:"\f2dc"}.fa-superpowers:before{content:"\f2dd"}.fa-wpexplorer:before{content:"\f2de"}.fa-meetup:before{content:"\f2e0"}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0, 0, 0, 0);border:0}.sr-only-focusable:active,.sr-only-focusable:focus{position:static;width:auto;height:auto;margin:0;overflow:visible;clip:auto} diff --git a/FontAwesome/fonts/FontAwesome.ttf b/FontAwesome/fonts/FontAwesome.ttf new file mode 100644 index 000000000..35acda2fa Binary files /dev/null and b/FontAwesome/fonts/FontAwesome.ttf differ diff --git a/FontAwesome/fonts/fontawesome-webfont.eot b/FontAwesome/fonts/fontawesome-webfont.eot new file mode 100644 index 000000000..e9f60ca95 Binary files /dev/null and b/FontAwesome/fonts/fontawesome-webfont.eot differ diff --git a/FontAwesome/fonts/fontawesome-webfont.svg b/FontAwesome/fonts/fontawesome-webfont.svg new file mode 100644 index 000000000..855c845e5 --- /dev/null +++ b/FontAwesome/fonts/fontawesome-webfont.svg @@ -0,0 +1,2671 @@ + + + + +Created by FontForge 20120731 at Mon Oct 24 17:37:40 2016 + By ,,, +Copyright Dave Gandy 2016. All rights reserved. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/FontAwesome/fonts/fontawesome-webfont.ttf b/FontAwesome/fonts/fontawesome-webfont.ttf new file mode 100644 index 000000000..35acda2fa Binary files /dev/null and b/FontAwesome/fonts/fontawesome-webfont.ttf differ diff --git a/FontAwesome/fonts/fontawesome-webfont.woff b/FontAwesome/fonts/fontawesome-webfont.woff new file mode 100644 index 000000000..400014a4b Binary files /dev/null and b/FontAwesome/fonts/fontawesome-webfont.woff differ diff --git a/FontAwesome/fonts/fontawesome-webfont.woff2 b/FontAwesome/fonts/fontawesome-webfont.woff2 new file mode 100644 index 000000000..4d13fc604 Binary files /dev/null and b/FontAwesome/fonts/fontawesome-webfont.woff2 differ diff --git a/Makefile b/Makefile deleted file mode 100644 index 9b2b4bf1b..000000000 --- a/Makefile +++ /dev/null @@ -1,65 +0,0 @@ -.PHONY: test clippy docker-shell nix-shell benchmarks benchmark docs build-cuda coverage clean - -FUZZ_DIR = fuzz/no_gpu_fuzz - -ROOT_DIR:=$(shell dirname $(realpath $(firstword $(MAKEFILE_LIST)))) - -test: - cargo test - -clippy: - cargo clippy --workspace --all-targets -- -D warnings - cargo clippy --workspace --all-targets --features wasm -- -D warnings - cargo clippy --workspace --all-targets --features parallel -- -D warnings - cargo clippy --tests - -clippy-cuda: - cargo clippy --workspace -F cuda -- -D warnings - -docker-shell: - docker build -t rust-curves . - docker run -it rust-curves bash - -nix-shell: - nix-shell - -benchmarks: - cargo criterion --workspace - -# BENCHMARK should be one of the [[bench]] names in Cargo.toml -benchmark: - cargo criterion --bench ${BENCH} - -flamegraph_stark: - CARGO_PROFILE_BENCH_DEBUG=true cargo flamegraph --root --bench stark_benchmarks -- --bench - -coverage: - cargo llvm-cov nextest --lcov --output-path lcov.info - -CUDA_DIR = math/src/gpu/cuda/shaders -CUDA_FILES:=$(wildcard $(CUDA_DIR)/**/*.cu) -CUDA_COMPILED:=$(patsubst $(CUDA_DIR)/%.cu, $(CUDA_DIR)/%.ptx, $(CUDA_FILES)) -CUDA_HEADERS:=$(wildcard $(CUDA_DIR)/**/*.cuh) - -$(CUDA_DIR)/%.ptx: $(CUDA_DIR)/%.cu $(CUDA_HEADERS) - nvcc -ptx $< -o $@ - -# This part compiles all .cu files in $(CUDA_DIR) -build-cuda: $(CUDA_COMPILED) - -CUDAPATH = math/src/gpu/cuda/shaders -build-cuda: - nvcc -ptx $(CUDAPATH)/field/stark256.cu -o $(CUDAPATH)/field/stark256.ptx - -docs: - cd docs && mdbook serve --open - -run-fuzzer: - cargo +nightly fuzz run --fuzz-dir $(FUZZ_DIR) $(FUZZER) - -proof-deserializer-fuzzer: - cargo +nightly fuzz run --fuzz-dir $(FUZZ_DIR) deserialize_stark_proof - -run-cuda-fuzzer: - cd fuzz/cuda_fuzz - cargo hfuzz run $(CUDAFUZZER) diff --git a/README.md b/README.md deleted file mode 100644 index fa78bed05..000000000 --- a/README.md +++ /dev/null @@ -1,233 +0,0 @@ -# lambdaworks - -> From the heights of these towers of fields, forty centuries of mathematics look down on us. - -This library provides efficient implementation of cryptographic primitives used to build proving systems. Along with it, many backends for proving systems are shipped, and compatibility with different frontends is supported. - -- [Transforming the Future with Zero-Knowledge Proofs, Fully Homomorphic Encryption and new Distributed Systems algorithms](https://blog.lambdaclass.com/transforming-the-future-with-zero-knowledge-proofs-fully-homomorphic-encryption-and-new-distributed-systems-algorithms/) -- [Lambda Crypto Doctrine](https://blog.lambdaclass.com/lambda-crypto-doctrine/) - -[![Telegram Chat][tg-badge]][tg-url] -[![codecov](https://img.shields.io/codecov/c/github/lambdaclass/lambdaworks)](https://codecov.io/gh/lambdaclass/lambdaworks) - -[tg-badge]: https://img.shields.io/endpoint?url=https%3A%2F%2Ftg.sumanjay.workers.dev%2Flambdaworks%2F&logo=telegram&label=chat&color=neon -[tg-url]: https://t.me/lambdaworks - - - -## Examples - mini apps - -Below is a list of examples to understand lambdaworks and learn what you can build with the tools provided. - -- [Merkle Tree CLI](./examples/merkle-tree-cli/) -- [Proving Miden](./examples/prove-miden/) -- [Shamir's secret sharing](./examples/shamir_secret_sharing/) -- [BabySNARK](./examples/baby-snark/) -- [Pinocchio](./examples/pinocchio/) -- [Pohlig-Hellman algorithm](./examples/pohlig-hellman-attack/) -- [Naive RSA](./examples/rsa/) -- [Naive Schnorr signatures](./examples/schnorr-signature/) -- [Using Circom with lambdaworks's Groth16](./examples/prove-verify-circom/circom_lambdaworks_tutorial.md) -- [Proving Fibonacci using Circom and lambdaworks](./examples/prove-verify-circom/circom_lambdaworks_tutorial.md) - -- You can use Circom to generate circuits and use lambdaworks's capabilities to prove the execution with [Groth16](./crates/provers/groth16/README.md). -- You can use the [Stark prover](./crates/provers/stark/README.md) to define an algebraic intermediate representation (AIR) and prove the execution of a program - -## Why we built lambdaworks - -Zero-Knowledge and Validity Proofs have gained a lot of attention over the last few years. We strongly believe in this potential and that is why we decided to start working in this challenging ecosystem, where math, cryptography and distributed systems meet. The main barrier in the beginning was not the cryptography or math but the lack of good libraries which are performant and developer friendly. There are some exceptions, though, like gnark or halo2. Some have nice APIs and are easy to work with, but they are not written in Rust, and some are written in Rust but have poor programming and engineering practices. Most of them don't have support for CUDA, Metal and WebGPU or distributed FFT calculation using schedulers like Dask. - -So, we decided to build our library, focusing on performance, with clear documentation and developer-focused. Our core team is a group of passionate people from different backgrounds and different strengths; we think that the whole is greater than just the addition of the parts. We don't want to be a compilation of every research result in the ZK space. We want this to be a library that can be used in production, not just in academic research. We want to offer developers the main building blocks and proof systems so that they can build their applications on top of this library. - -## [Documentation](https://lambdaclass.github.io/lambdaworks) - -## Main crates - -- [Math](./crates/math) -- [Crypto primitives](./crates/crypto/) -- [STARK Prover](./crates/provers/stark/) -- [Plonk Prover](./crates/provers/plonk/) -- [Groth 16](./crates/provers/groth16/) - -### Crypto - -- [Elliptic curves](./crates/math/src/elliptic_curve/) -- [Multiscalar multiplication](./crates/math/src/msm/) -- [Hashes](./crates/crypto/src/hash/) - -Most of math and crypto crates supports no-std without allocation with `no-default-features`. A few functions and modules require the `alloc` feature. - -Both Math and Crypto support wasm with target `wasm32-unknown-unknown`. To see an example of how to use this to deploy a verifier in a browser, check the Cairo Prover wasm-pack verifier. - -## Exercises and Challenges - -- [lambdaworks exercises and challenges](./exercises/) -- [Roadmap for Sparkling Water Bootcamp](https://github.com/lambdaclass/sparkling_water_bootcamp/blob/main/README.md) - -## Citing lambdaworks - -If you use ```lambdaworks``` libraries in your research projects, please cite them using the following template: - -``` bibtex -@software{lambdaworks, - author={lambdaworks contributors}, - title={lambdaworks}, - url={https://github.com/lambdaclass/lambdaworks}, - year={2023} -} -``` - -## List of features - -Disclaimer: This list contains cryptographic primitives and mathematical structures that we want to support in lambdaworks. It can be expanded later to include new primitives. If you find there is a mistake or there has been an update in another library, please let us know. - -List of symbols: -- :heavy_check_mark: means the feature is currently supported. -- 🏗️ means that the feature is partially implemented or is under active construction. -- :x: means that the feature is not currently supported. - -| Finite Fields | Lambdaworks | Arkworks | Halo2 | gnark | Constantine | -| -------------- | ------------------ | ------------------ | -------- | ------------------ | ----------- | -| StarkField 252 | :heavy_check_mark: | :heavy_check_mark: | :x: | :heavy_check_mark: | :x: | -| Mersenne 31 | :heavy_check_mark: | :x: | :x: | :x: | :x: | -| Baby Bear | :heavy_check_mark: | :x: | :x: | :x: | :x: | -| MiniGoldilocks | :heavy_check_mark: | :x: | :x: | :heavy_check_mark: | :x: | -| Binary fields | :heavy_check_mark: | :x: | :x: | :x: | :x: | -| **ZK friendly Hash function** | **Lambdaworks** | **Arkworks** | **Halo2** | **gnark** | **Constantine** | -| Poseidon | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :x: | :x: | -| Pedersen | 🏗️ | :heavy_check_mark: | :heavy_check_mark: | :x: | :x: | -| Rescue Prime XLIX | :x: | :x: | :x: | :x: | :x: | -| **Elliptic Curves** | **Lambdaworks** | **Arkworks** | **Halo2** | **gnark** | **Constantine** | -| BLS12-381 | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | -| BLS12-377 | :heavy_check_mark: | :heavy_check_mark: | :x: | :heavy_check_mark: | :heavy_check_mark: | -| BN-254 | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | -| Pallas | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :x: | :heavy_check_mark: | -| Vesta | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :x: | :heavy_check_mark: | -| Bandersnatch | 🏗️ | :heavy_check_mark: | :x: | :heavy_check_mark: | :heavy_check_mark: | -| secp256k1 | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | -| secq256k1 | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :x: | :x: | -| secq256r1 | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :x: | :x: | -| **STARKs** | **Lambdaworks** | **Arkworks** | **Halo2** | **gnark** | **Constantine** | -| STARK Prover | :heavy_check_mark: | :x: | :x: | :x: | :x: | -| Circle STARKs | :x: | :x: | :x: | :x: | :x: | -| **SNARKs** | **Lambdaworks** | **Arkworks** | **Halo2** | **gnark** | **Constantine** | -| Groth16 | :heavy_check_mark: | :heavy_check_mark: | :x: | :heavy_check_mark: | :x: | -| Plonk | 🏗️ | :heavy_check_mark: | ✔️ | :heavy_check_mark: | :x: | -| GKR | :heavy_check_mark: | :heavy_check_mark: | :x: | :heavy_check_mark: | :x: | -| **Polynomial Commitment Schemes** | **Lambdaworks** | **Arkworks** | **Halo2** | **gnark** | **Constantine** | -| KZG10 | :heavy_check_mark: | ✔️ | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | -| FRI | 🏗️ | :x: | :x: | :heavy_check_mark: | :x: | -| Binius | :x: | :x: | :x: | :x: | :x: | -| Circle FRI | :x: | :x: | :x: | :x: | :x: | - -Additionally, provers are compatible with the following frontends and VMs: - -| Backend | Frontend | Status | -|---------|----------|--------| -| Groth16 | Arkworks | :heavy_check_mark: | -| Groth16 | Gnark | :x: | -| Groth16 | Circom | 🏗️ | -| Plonk | Gnark | 🏗️ | -| Plonk | Noir | :x: | -| Stark | Winterfell | :heavy_check_mark: | -| Stark | Miden | :heavy_check_mark: | - -This can be used in a multi prover setting for extra security, or as a standalone to be used with Rust. - -## Additional tooling usage - -### Fuzzers - -Fuzzers are divided between the ones that use only the CPU, and the ones that use CUDA. - -To use them, make sure you have installed ```cargo fuzzer``` - -You can install it with: - -```cargo install cargo-fuzz``` - -CPU Fuzzers can be run with the command ```bash make run-fuzzer FUZZER=fuzzer_name``` - -For example: - -```bash -make run-fuzzer FUZZER=stark252 -``` - -The list of fuzzers can be found in `fuzz/no_gpu_fuzz` - -Fuzzers for FTT in Cuda can be run with `make run-cuda-fuzzer` - - -Run a specific fuzzer from the ones contained in **fuzz/fuzz_targets/** folder with`cargo`, for example to run the one for the target `field_from_hex`: - -```bash -make run-fuzzer FUZZER=field_from_hex -``` - -### Documentation building - -To serve the documentation locally, first install both [mdbook](https://rust-lang.github.io/mdBook/guide/installation.html) and the [Katex preprocessor](https://github.com/lzanini/mdbook-katex#getting-started) to render LaTeX, then run - -``` shell -make docs -``` - -## Learning resources - -If you want to learn about proof systems/cryptography, we have a list of resources available [here](./bootcamp/learning_resources.md) - -## 📚 References and acknowledgements - -The following links, repos, companies and projects have been important in the development of this library and we want to thank and acknowledge them. - -- [Starkware](https://starkware.co/) -- [Polygon](https://polygon.technology/) -- [Mina](https://minaprotocol.com/) -- [zcash](https://z.cash/) -- [Matter Labs](https://matter-labs.io/) -- [o1Labs](https://www.o1labs.org/) -- [zkSync](https://zksync.io/) -- [Aleo](https://aleo.org/) -- [Risc0](https://github.com/risc0/risc0) -- [Aztec](https://github.com/AztecProtocol) -- [Ingonyama](https://www.ingonyama.com/) -- [Neptune](https://github.com/Neptune-Crypto) -- [Winterfell](https://github.com/facebook/winterfell) -- [Anatomy of a Stark](https://aszepieniec.github.io/stark-anatomy/overview) -- [Giza](https://github.com/maxgillett/giza) -- [Ministark](https://github.com/andrewmilson/ministark) -- [Sandstorm](https://github.com/andrewmilson/sandstorm) -- [STARK-101](https://starkware.co/stark-101/) -- [starknet-rs](https://github.com/xJonathanLEI/starknet-rs/) -- [Summary on FRI low degree test](https://eprint.iacr.org/2022/1216) -- [STARKs paper](https://eprint.iacr.org/2018/046) -- [DEEP FRI](https://eprint.iacr.org/2019/336) -- [BrainSTARK](https://aszepieniec.github.io/stark-brainfuck/) -- [Plonky2](https://github.com/mir-protocol/plonky2) -- [Arkworks](https://github.com/arkworks-rs) -- [Thank goodness it's FRIday](https://vitalik.ca/general/2017/11/22/starks_part_2.html) -- [Diving DEEP FRI](https://blog.lambdaclass.com/diving-deep-fri/) -- [Periodic constraints](https://blog.lambdaclass.com/periodic-constraints-and-recursion-in-zk-starks/) -- [Chiplets Miden VM](https://wiki.polygon.technology/docs/miden/design/chiplets/main/) -- [Valida](https://github.com/valida-xyz/valida/tree/main) -- [Solidity Verifier](https://github.com/starkware-libs/starkex-contracts/tree/master/evm-verifier/solidity/contracts/cpu) -- [CAIRO verifier](https://github.com/starkware-libs/cairo-lang/tree/master/src/starkware/cairo/stark_verifier) -- [EthSTARK](https://github.com/starkware-libs/ethSTARK/tree/master) -- [CAIRO whitepaper](https://eprint.iacr.org/2021/1063.pdf) -- [Gnark](https://github.com/Consensys/gnark) -- [Constantine](https://github.com/mratsim/constantine) -- [Plonky3](https://github.com/Plonky3/Plonky3) -- [Stwo](https://github.com/starkware-libs/stwo/tree/dev) -- [Binius](https://gitlab.com/IrreducibleOSS/binius) -- [Zorch](https://github.com/vbuterin/zorch/tree/main) -- [Jolt](https://github.com/a16z/jolt) - -# Security - -We take security seriously. If you discover a vulnerability in this project, please report it responsibly. - -- You can report vulnerabilities directly via the **[GitHub "Report a Vulnerability" feature](../../security/advisories/new)**. -- Alternatively, send an email to **[security@lambdaclass.com](mailto:security@lambdaclass.com)**. - -For more details, please refer to our [Security Policy](./.github/SECURITY.md). diff --git a/References.md b/References.md deleted file mode 100644 index 4998301e4..000000000 --- a/References.md +++ /dev/null @@ -1,106 +0,0 @@ -# References - -The following links, repos and projects have been important in the development of this library and we want to thank and acknowledge them. -## Finite Fields -- [zkcrypto](https://github.com/zkcrypto/ff) -- [Nilfoundation's crypto 3 multiprecision](https://github.com/nilfoundation/crypto3-multiprecision) -- [Montgomery REDC](https://jeffhurchalla.com/2022/04/28/montgomery-redc-using-the-positive-inverse-mod-r/) -- [Domb's fast modular multiplication](http://ingonyama.com/s/modular_multiplication.pdf) -- [Rust crypto bigint](https://github.com/RustCrypto/crypto-bigint) -- [Cairo felt](https://github.com/lambdaclass/cairo-rs/blob/main/felt/src/lib.rs) -- [Various optimizations](https://github.com/mratsim/constantine/blob/master/docs/optimizations.md) - -## PLONK -- [Simplified Plonk](https://hackmd.io/vUGG8CO_Rk2iEjruBL_gGw?view#Note-A-Mind-Boggling-Issue-with-Ultra-Plonk) -- [Honest verifier fix in Plonk](https://hackmd.io/JiyexiqRQJW55TMRrBqp1g) -- [JellyFish library](https://github.com/EspressoSystems/jellyfish) -- [Bellman](https://github.com/matter-labs/bellman/tree/plonk_release/src/plonk) -- [Plonk Cafe](https://www.plonk.cafe/) -- [Awesome Plonk](https://github.com/fluidex/awesome-plonk) -- [Dusk Network](https://github.com/dusk-network/plonk/tree/master/src/fft) -- [gnark's Plonk](https://github.com/ConsenSys/gnark/tree/master/backend/plonk) -- [PlonkUp](https://eprint.iacr.org/2022/086.pdf) -- [PlonkByHand 1](https://research.metastate.dev/plonk-by-hand-part-1/) - -## Groth16 -- [Proof forgery](https://medium.com/ppio/how-to-generate-a-groth16-proof-for-forgery-9f857b0dcafd) - -## Elliptic curves -- [Pairing-friendly curves](https://members.loria.fr/AGuillevic/pairing-friendly-curves/) -- [Taxonomy of elliptic curves](https://eprint.iacr.org/2006/372) -- [Ristretto](https://ristretto.group/) -- [Double ODD](https://doubleodd.group/) -- [Banderwagon](https://hackmd.io/@6iQDuIePQjyYBqDChYw_jg/BJBNcv9fq) -- [Subgroup check via Tate](https://forum.zcashcommunity.com/t/subgroup-membership-testing-on-elliptic-curves-via-the-tate-pairing/42963) -- [Hashing to elliptic curves](https://datatracker.ietf.org/doc/id/draft-irtf-cfrg-hash-to-curve-06.html#name-hashing-to-a-finite-field) - -## Halo2 -- [Halo2's poly](https://github.com/zcash/halo2/tree/main/halo2_proofs/src/poly) - -## Pairings -- [Pairings or bilinear maps](https://alinush.github.io/2022/12/31/pairings-or-bilinear-maps.html) - -## Lookup Tables -- [Caulk Poc Implementation](https://github.com/caulk-crypto/caulk) -- [Caulk Paper](https://eprint.iacr.org/2022/621.pdf) -- [Plook Up Paper](https://eprint.iacr.org/2020/315.pdf) - -## Starks, FRI & Stark VMs -- [Anatomy of a Stark](https://aszepieniec.github.io/stark-anatomy/overview) -- [Anatomy of a Stark](https://neptune.cash/learn/stark-anatomy/fri/) -- [BrainSTARK](https://aszepieniec.github.io/stark-brainfuck/) -- [CAIRO verifier](https://github.com/starkware-libs/cairo-lang/tree/master/src/starkware/cairo/stark_verifier) -- [CAIRO whitepaper](https://eprint.iacr.org/2021/1063.pdf) -- [Chiplets Miden VM](https://wiki.polygon.technology/docs/miden/design/chiplets/main/) -- [DEEP FRI Video](https://www.youtube.com/watch?v=txo_kPSn59Y&list=PLcIyXLwiPilWvjvNkhMn283LV370Pk5CT&index=6) -- [DEEP FRI](https://eprint.iacr.org/2019/336) -- [DeepFRI](https://eprint.iacr.org/2019/336.pdf) -- [Diving DEEP FRI](https://blog.lambdaclass.com/diving-deep-fri/) -- [EthSTARK - Git](https://github.com/starkware-libs/ethSTARK/tree/master) -- [EthStark](https://eprint.iacr.org/2021/582.pdf) -- [FRI](https://eccc.weizmann.ac.il/report/2017/134/) -- [Giza](https://github.com/maxgillett/giza) -- [Gnark](https://github.com/Consensys/gnark) -- [Low-degree testing](https://medium.com/starkware/low-degree-testing-f7614f5172db) -- [Ministark](https://github.com/andrewmilson/ministark) -- [Periodic constraints](https://blog.lambdaclass.com/periodic-constraints-and-recursion-in-zk-starks/) -- [Plonky2](https://github.com/mir-protocol/plonky2) -- [Risc0](https://github.com/risc0/risc0) -- [STARKs paper](https://eprint.iacr.org/2018/046) -- [Sandstorm](https://github.com/andrewmilson/sandstorm) -- [Solidity Verifier](https://github.com/starkware-libs/starkex-contracts/tree/master/evm-verifier/solidity/contracts/cpu) -- [Starks arithmetization-1](https://medium.com/starkware/arithmetization-i-15c046390862) -- [Starks arithmetization-2](https://medium.com/starkware/arithmetization-ii-403c3b3f4355) -- [Starks-101-Github](https://github.com/starkware-industries/stark101) -- [Starks-101](https://starkware.co/stark-101/) -- [Starks](https://starkware.co/wp-content/uploads/2022/05/STARK-paper.pdf) -- [Summary on FRI low degree test](https://eprint.iacr.org/2022/1216) -- [Thank goodness it's FRIday](https://vitalik.ca/general/2017/11/22/starks_part_2.html) -- [Valida](https://github.com/valida-xyz/valida/tree/main) -- [Winterfell](https://github.com/facebook/winterfell) -## GPU -- [Yrrid and MatterLabs combined solution for MSM](https://github.com/matter-labs/z-prize-msm-gpu-combined) -- [FFT library](https://developer.nvidia.com/cufft) -- [snarks with GPU](https://github.com/MariusVanDerWijden/gpusnarks) -- [FFTW](https://fftw.org/) -- [FFT with hadoop and CUDA](https://arxiv.org/pdf/1407.6915.pdf) - -## Cairo prover -## FHE -- [Zama](https://github.com/zama-ai/concrete-core) -- [Sunscreen](https://github.com/Sunscreen-tech/Sunscreen) -- [Intro to FHE](https://blog.nucypher.com/an-engineers-guide-to-fully-homomorphic-encryption/) -- [Standard1.1](https://homomorphicencryption.org/wp-content/uploads/2018/11/HomomorphicEncryptionStandardv1.1.pdf) -- [SEAL](https://github.com/microsoft/SEAL) - -## Miscellanea -- [Arkworks](https://github.com/arkworks-rs) -- [Aztec](https://github.com/AztecProtocol) -- [BitwiseOperations](https://hackmd.io/vejHasuZSVWZOafBLNS_YQ) -- [Blake3](https://github.com/BLAKE3-team/BLAKE3-specs/blob/master/blake3.pdf) -- [FastCrypto-MystenLabs](https://github.com/MystenLabs/fastcrypto/tree/main/fastcrypto-zkp) -- [Neptune](https://github.com/Neptune-Crypto) -- [OlaVM](https://github.com/Sin7Y/olavm/blob/main/docs/olavm/olavm_sepc.pdf) -- [Wycheproof](https://github.com/google/wycheproof) -- [zeroize](https://docs.rs/zeroize/latest/zeroize/) -- [zkNotebook](https://github.com/hecmas/zkNotebook) diff --git a/ayu-highlight.css b/ayu-highlight.css new file mode 100644 index 000000000..0c45c6f14 --- /dev/null +++ b/ayu-highlight.css @@ -0,0 +1,79 @@ +/* +Based off of the Ayu theme +Original by Dempfi (https://github.com/dempfi/ayu) +*/ + +.hljs { + display: block; + overflow-x: auto; + background: #191f26; + color: #e6e1cf; + padding: 0.5em; +} + +.hljs-comment, +.hljs-quote { + color: #5c6773; + font-style: italic; +} + +.hljs-variable, +.hljs-template-variable, +.hljs-attribute, +.hljs-attr, +.hljs-regexp, +.hljs-link, +.hljs-selector-id, +.hljs-selector-class { + color: #ff7733; +} + +.hljs-number, +.hljs-meta, +.hljs-builtin-name, +.hljs-literal, +.hljs-type, +.hljs-params { + color: #ffee99; +} + +.hljs-string, +.hljs-bullet { + color: #b8cc52; +} + +.hljs-title, +.hljs-built_in, +.hljs-section { + color: #ffb454; +} + +.hljs-keyword, +.hljs-selector-tag, +.hljs-symbol { + color: #ff7733; +} + +.hljs-name { + color: #36a3d9; +} + +.hljs-tag { + color: #00568d; +} + +.hljs-emphasis { + font-style: italic; +} + +.hljs-strong { + font-weight: bold; +} + +.hljs-addition { + color: #91b362; +} + +.hljs-deletion { + color: #d96c75; +} diff --git a/bench/data.js b/bench/data.js new file mode 100644 index 000000000..db2fc98d2 --- /dev/null +++ b/bench/data.js @@ -0,0 +1,9992 @@ +window.BENCHMARK_DATA = { + "lastUpdate": 1759162232087, + "repoUrl": "https://github.com/lambdaclass/lambdaworks", + "entries": { + "Benchmark": [ + { + "commit": { + "author": { + "email": "56092489+ColoCarletti@users.noreply.github.com", + "name": "Joaquin Carletti", + "username": "ColoCarletti" + }, + "committer": { + "email": "noreply@github.com", + "name": "GitHub", + "username": "web-flow" + }, + "distinct": false, + "id": "3046f0e663d9182cd8798f8e9488ad300ffa4e46", + "message": "Remove Metal feature (#993)\n\n* rm all metal features\n\n* fix clippy\n\n* fix attribute\n\n* fix another attribute", + "timestamp": "2025-04-04T16:50:42Z", + "tree_id": "2aaf079d5f553baf9a3a9e9e5307664a03c827cc", + "url": "https://github.com/lambdaclass/lambdaworks/commit/3046f0e663d9182cd8798f8e9488ad300ffa4e46" + }, + "date": 1743787195774, + "tool": "cargo", + "benches": [ + { + "name": "Ordered FFT/Sequential from NR radix2", + "value": 320705388, + "range": "± 1229369", + "unit": "ns/iter" + }, + { + "name": "Ordered FFT/Sequential from RN radix2", + "value": 375578528, + "range": "± 1347354", + "unit": "ns/iter" + }, + { + "name": "Ordered FFT/Sequential from NR radix4", + "value": 279807033, + "range": "± 961256", + "unit": "ns/iter" + }, + { + "name": "Ordered FFT/Sequential from NR radix2 #2", + "value": 672838962, + "range": "± 1116880", + "unit": "ns/iter" + }, + { + "name": "Ordered FFT/Sequential from RN radix2 #2", + "value": 790967290, + "range": "± 2596968", + "unit": "ns/iter" + }, + { + "name": "Ordered FFT/Sequential from NR radix2 #3", + "value": 1410286987, + "range": "± 882187", + "unit": "ns/iter" + }, + { + "name": "Ordered FFT/Sequential from RN radix2 #3", + "value": 1657890232, + "range": "± 2605314", + "unit": "ns/iter" + }, + { + "name": "Ordered FFT/Sequential from NR radix4 #2", + "value": 1232780111, + "range": "± 1180582", + "unit": "ns/iter" + }, + { + "name": "Ordered FFT/Sequential from NR radix2 #4", + "value": 2949072229, + "range": "± 1321530", + "unit": "ns/iter" + }, + { + "name": "Ordered FFT/Sequential from RN radix2 #4", + "value": 3459300863, + "range": "± 8619321", + "unit": "ns/iter" + }, + { + "name": "Ordered FFT/Sequential from NR radix2 #5", + "value": 6179583205, + "range": "± 17065154", + "unit": "ns/iter" + }, + { + "name": "Ordered FFT/Sequential from RN radix2 #5", + "value": 7291301553, + "range": "± 247093445", + "unit": "ns/iter" + }, + { + "name": "Ordered FFT/Sequential from NR radix4 #3", + "value": 5414236330, + "range": "± 28395988", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/natural", + "value": 7468724, + "range": "± 1841", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/natural inversed", + "value": 7537959, + "range": "± 44546", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/bit-reversed", + "value": 9602368, + "range": "± 562231", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/bit-reversed inversed", + "value": 9865255, + "range": "± 143691", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/natural #2", + "value": 17834704, + "range": "± 163536", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/natural inversed #2", + "value": 17830367, + "range": "± 88037", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/bit-reversed #2", + "value": 26109838, + "range": "± 746716", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/bit-reversed inversed #2", + "value": 26475106, + "range": "± 1141531", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/natural #3", + "value": 35383803, + "range": "± 154065", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/natural inversed #3", + "value": 35704628, + "range": "± 2810906", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/bit-reversed #3", + "value": 64691879, + "range": "± 567840", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/bit-reversed inversed #3", + "value": 64827620, + "range": "± 805876", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/natural #4", + "value": 71253085, + "range": "± 124218", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/natural inversed #4", + "value": 71178972, + "range": "± 58657", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/bit-reversed #4", + "value": 136134606, + "range": "± 1075568", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/bit-reversed inversed #4", + "value": 137532919, + "range": "± 2683461", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/natural #5", + "value": 139752058, + "range": "± 466816", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/natural inversed #5", + "value": 139712869, + "range": "± 477014", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/bit-reversed #5", + "value": 270812589, + "range": "± 420818", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/bit-reversed inversed #5", + "value": 272761366, + "range": "± 1375428", + "unit": "ns/iter" + }, + { + "name": "Bit-reverse permutation/Sequential", + "value": 15817354, + "range": "± 146297", + "unit": "ns/iter" + }, + { + "name": "Bit-reverse permutation/Sequential #2", + "value": 34006462, + "range": "± 1676986", + "unit": "ns/iter" + }, + { + "name": "Bit-reverse permutation/Sequential #3", + "value": 66811860, + "range": "± 191893", + "unit": "ns/iter" + }, + { + "name": "Bit-reverse permutation/Sequential #4", + "value": 133633502, + "range": "± 282515", + "unit": "ns/iter" + }, + { + "name": "Bit-reverse permutation/Sequential #5", + "value": 353461137, + "range": "± 1402597", + "unit": "ns/iter" + }, + { + "name": "Polynomial evaluation/Sequential FFT", + "value": 351746130, + "range": "± 1375309", + "unit": "ns/iter" + }, + { + "name": "Polynomial evaluation/Sequential FFT #2", + "value": 753209559, + "range": "± 2292721", + "unit": "ns/iter" + }, + { + "name": "Polynomial evaluation/Sequential FFT #3", + "value": 1579028534, + "range": "± 2691542", + "unit": "ns/iter" + }, + { + "name": "Polynomial evaluation/Sequential FFT #4", + "value": 3285878471, + "range": "± 3601429", + "unit": "ns/iter" + }, + { + "name": "Polynomial evaluation/Sequential FFT #5", + "value": 6940267323, + "range": "± 4428984", + "unit": "ns/iter" + }, + { + "name": "Polynomial interpolation/Sequential FFT", + "value": 375302287, + "range": "± 298354", + "unit": "ns/iter" + }, + { + "name": "Polynomial interpolation/Sequential FFT #2", + "value": 788066352, + "range": "± 3896669", + "unit": "ns/iter" + }, + { + "name": "Polynomial interpolation/Sequential FFT #3", + "value": 1660458229, + "range": "± 855585", + "unit": "ns/iter" + }, + { + "name": "Polynomial interpolation/Sequential FFT #4", + "value": 3439714851, + "range": "± 4068957", + "unit": "ns/iter" + }, + { + "name": "Polynomial interpolation/Sequential FFT #5", + "value": 7242585864, + "range": "± 4345635", + "unit": "ns/iter" + }, + { + "name": "Polynomial/evaluate", + "value": 19, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/evaluate_slice", + "value": 90, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/add", + "value": 60, + "range": "± 5", + "unit": "ns/iter" + }, + { + "name": "Polynomial/neg", + "value": 26, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/sub", + "value": 88, + "range": "± 6", + "unit": "ns/iter" + }, + { + "name": "Polynomial/mul", + "value": 161, + "range": "± 7", + "unit": "ns/iter" + }, + { + "name": "Polynomial/div", + "value": 238, + "range": "± 40", + "unit": "ns/iter" + }, + { + "name": "Polynomial/div by 'x - b' with generic div", + "value": 605, + "range": "± 39", + "unit": "ns/iter" + }, + { + "name": "Polynomial/div by 'x - b' with Ruffini", + "value": 22, + "range": "± 18", + "unit": "ns/iter" + }, + { + "name": "Polynomial/evaluate #2", + "value": 18, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/evaluate_with", + "value": 5, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/merge", + "value": 153, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/add #2", + "value": 13668, + "range": "± 644", + "unit": "ns/iter" + }, + { + "name": "Polynomial/mul #2", + "value": 351, + "range": "± 1", + "unit": "ns/iter" + }, + { + "name": "Polynomial/evaluate 3", + "value": 1, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/evaluate 4", + "value": 1, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/evaluate 5", + "value": 1, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/evaluate 6", + "value": 1, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/evaluate 7", + "value": 1, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/evaluate 8", + "value": 1, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/evaluate 9", + "value": 1, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/evaluate 10", + "value": 1, + "range": "± 0", + "unit": "ns/iter" + } + ] + }, + { + "commit": { + "author": { + "email": "45471455+jotabulacios@users.noreply.github.com", + "name": "jotabulacios", + "username": "jotabulacios" + }, + "committer": { + "email": "noreply@github.com", + "name": "GitHub", + "username": "web-flow" + }, + "distinct": true, + "id": "d9385675aa91e7e99c8c023e74108fe316b243c7", + "message": "Update Readme (#987)\n\n* update kzg Readme\n\n* KZG: make sure example is ok\n\n* Add Readme for Merkle Tree\n\n* Add Readme for Circle\n\n* fix circle docs\n\n* fix circle readme\n\n* fix typo, remove unused test and fix markdown style\n\n* changes in circle readme\n\n* fix circle readme\n\n* fix clippy\n\n* fix clippy spaces\n\n---------\n\nCo-authored-by: Nicole \nCo-authored-by: Joaquin Carletti \nCo-authored-by: Diego K <43053772+diegokingston@users.noreply.github.com>", + "timestamp": "2025-04-04T17:10:25Z", + "tree_id": "74f03eeb691de0aa94763b5baf42b9f4ed23213b", + "url": "https://github.com/lambdaclass/lambdaworks/commit/d9385675aa91e7e99c8c023e74108fe316b243c7" + }, + "date": 1743788268386, + "tool": "cargo", + "benches": [ + { + "name": "Ordered FFT/Sequential from NR radix2", + "value": 319852266, + "range": "± 3805245", + "unit": "ns/iter" + }, + { + "name": "Ordered FFT/Sequential from RN radix2", + "value": 371104952, + "range": "± 1740701", + "unit": "ns/iter" + }, + { + "name": "Ordered FFT/Sequential from NR radix4", + "value": 280876259, + "range": "± 4949283", + "unit": "ns/iter" + }, + { + "name": "Ordered FFT/Sequential from NR radix2 #2", + "value": 675443325, + "range": "± 1105947", + "unit": "ns/iter" + }, + { + "name": "Ordered FFT/Sequential from RN radix2 #2", + "value": 791678995, + "range": "± 1828994", + "unit": "ns/iter" + }, + { + "name": "Ordered FFT/Sequential from NR radix2 #3", + "value": 1412436337, + "range": "± 1278948", + "unit": "ns/iter" + }, + { + "name": "Ordered FFT/Sequential from RN radix2 #3", + "value": 1652765386, + "range": "± 3284626", + "unit": "ns/iter" + }, + { + "name": "Ordered FFT/Sequential from NR radix4 #2", + "value": 1233422421, + "range": "± 10312045", + "unit": "ns/iter" + }, + { + "name": "Ordered FFT/Sequential from NR radix2 #4", + "value": 2952086639, + "range": "± 10618799", + "unit": "ns/iter" + }, + { + "name": "Ordered FFT/Sequential from RN radix2 #4", + "value": 3459442373, + "range": "± 4424472", + "unit": "ns/iter" + }, + { + "name": "Ordered FFT/Sequential from NR radix2 #5", + "value": 6182994347, + "range": "± 8128966", + "unit": "ns/iter" + }, + { + "name": "Ordered FFT/Sequential from RN radix2 #5", + "value": 7254550053, + "range": "± 17539210", + "unit": "ns/iter" + }, + { + "name": "Ordered FFT/Sequential from NR radix4 #3", + "value": 5409131663, + "range": "± 16148765", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/natural", + "value": 7487159, + "range": "± 5591", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/natural inversed", + "value": 7555693, + "range": "± 11890", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/bit-reversed", + "value": 9601082, + "range": "± 52661", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/bit-reversed inversed", + "value": 9656556, + "range": "± 12599", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/natural #2", + "value": 17727102, + "range": "± 20747", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/natural inversed #2", + "value": 17702983, + "range": "± 47819", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/bit-reversed #2", + "value": 26793247, + "range": "± 326104", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/bit-reversed inversed #2", + "value": 26627344, + "range": "± 326648", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/natural #3", + "value": 35393072, + "range": "± 61487", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/natural inversed #3", + "value": 35603696, + "range": "± 80770", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/bit-reversed #3", + "value": 65545766, + "range": "± 711173", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/bit-reversed inversed #3", + "value": 65906135, + "range": "± 173066", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/natural #4", + "value": 71450949, + "range": "± 123146", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/natural inversed #4", + "value": 70882820, + "range": "± 676315", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/bit-reversed #4", + "value": 139326643, + "range": "± 996994", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/bit-reversed inversed #4", + "value": 137803051, + "range": "± 1177147", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/natural #5", + "value": 140531442, + "range": "± 882189", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/natural inversed #5", + "value": 141094402, + "range": "± 489707", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/bit-reversed #5", + "value": 278696365, + "range": "± 2199133", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/bit-reversed inversed #5", + "value": 272579587, + "range": "± 526879", + "unit": "ns/iter" + }, + { + "name": "Bit-reverse permutation/Sequential", + "value": 15534075, + "range": "± 81717", + "unit": "ns/iter" + }, + { + "name": "Bit-reverse permutation/Sequential #2", + "value": 32852702, + "range": "± 232320", + "unit": "ns/iter" + }, + { + "name": "Bit-reverse permutation/Sequential #3", + "value": 66750776, + "range": "± 387176", + "unit": "ns/iter" + }, + { + "name": "Bit-reverse permutation/Sequential #4", + "value": 135963696, + "range": "± 2016853", + "unit": "ns/iter" + }, + { + "name": "Bit-reverse permutation/Sequential #5", + "value": 354161761, + "range": "± 1124515", + "unit": "ns/iter" + }, + { + "name": "Polynomial evaluation/Sequential FFT", + "value": 350899360, + "range": "± 1206074", + "unit": "ns/iter" + }, + { + "name": "Polynomial evaluation/Sequential FFT #2", + "value": 748718896, + "range": "± 7440503", + "unit": "ns/iter" + }, + { + "name": "Polynomial evaluation/Sequential FFT #3", + "value": 1577322363, + "range": "± 1531086", + "unit": "ns/iter" + }, + { + "name": "Polynomial evaluation/Sequential FFT #4", + "value": 3295011856, + "range": "± 13650702", + "unit": "ns/iter" + }, + { + "name": "Polynomial evaluation/Sequential FFT #5", + "value": 6931654664, + "range": "± 8755697", + "unit": "ns/iter" + }, + { + "name": "Polynomial interpolation/Sequential FFT", + "value": 379027620, + "range": "± 1035968", + "unit": "ns/iter" + }, + { + "name": "Polynomial interpolation/Sequential FFT #2", + "value": 791817068, + "range": "± 3980896", + "unit": "ns/iter" + }, + { + "name": "Polynomial interpolation/Sequential FFT #3", + "value": 1665178431, + "range": "± 1568354", + "unit": "ns/iter" + }, + { + "name": "Polynomial interpolation/Sequential FFT #4", + "value": 3448918670, + "range": "± 7856603", + "unit": "ns/iter" + }, + { + "name": "Polynomial interpolation/Sequential FFT #5", + "value": 7253263802, + "range": "± 9209284", + "unit": "ns/iter" + }, + { + "name": "Polynomial/evaluate", + "value": 1148, + "range": "± 6", + "unit": "ns/iter" + }, + { + "name": "Polynomial/evaluate_slice", + "value": 146687, + "range": "± 113", + "unit": "ns/iter" + }, + { + "name": "Polynomial/add", + "value": 770, + "range": "± 28", + "unit": "ns/iter" + }, + { + "name": "Polynomial/neg", + "value": 421, + "range": "± 2", + "unit": "ns/iter" + }, + { + "name": "Polynomial/sub", + "value": 1116, + "range": "± 17", + "unit": "ns/iter" + }, + { + "name": "Polynomial/mul", + "value": 82224, + "range": "± 172", + "unit": "ns/iter" + }, + { + "name": "Polynomial/div", + "value": 2705, + "range": "± 2645", + "unit": "ns/iter" + }, + { + "name": "Polynomial/div by 'x - b' with generic div", + "value": 198588, + "range": "± 6535", + "unit": "ns/iter" + }, + { + "name": "Polynomial/div by 'x - b' with Ruffini", + "value": 1167, + "range": "± 8", + "unit": "ns/iter" + }, + { + "name": "Polynomial/evaluate #2", + "value": 20, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/evaluate_with", + "value": 5, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/merge", + "value": 154, + "range": "± 6", + "unit": "ns/iter" + }, + { + "name": "Polynomial/add #2", + "value": 13738, + "range": "± 1295", + "unit": "ns/iter" + }, + { + "name": "Polynomial/mul #2", + "value": 355, + "range": "± 1", + "unit": "ns/iter" + }, + { + "name": "Polynomial/evaluate 3", + "value": 1, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/evaluate 4", + "value": 1, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/evaluate 5", + "value": 1, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/evaluate 6", + "value": 1, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/evaluate 7", + "value": 1, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/evaluate 8", + "value": 1, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/evaluate 9", + "value": 1, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/evaluate 10", + "value": 1, + "range": "± 0", + "unit": "ns/iter" + } + ] + }, + { + "commit": { + "author": { + "email": "45471455+jotabulacios@users.noreply.github.com", + "name": "jotabulacios", + "username": "jotabulacios" + }, + "committer": { + "email": "noreply@github.com", + "name": "GitHub", + "username": "web-flow" + }, + "distinct": true, + "id": "a7b22e18784dc80dc755644c0a6e310d544e1dee", + "message": "Add Binary Field (#984)\n\n* first commit\n\n* define add function\n\n* add more functions and refactor\n\n* save work\n\n* save work\n\n* fix new function,tests and add benches\n\n* change mul function\n\n* test\n\n* change comment\n\n* change mul algorithm. Mul in level 0 and 1 working\n\n* refactor some functions and fix tests\n\n* fix inverse function\n\n* add docs and update README\n\n* fix conflicts\n\n* fix clippy\n\n* fix no_std\n\n* fix tests no std\n\n* small fixex for benches\n\n* fix typo\n\n* remove num_bits from the struct. remove set_num_level. move mul\n\n* improve add_elements()\n\n* impl Eq instead of equalls function\n\n* derive default\n\n* fix prefix in test\n\n* add readme\n\n* omit mul lifetimes\n\n* use vector of random elements for benches\n\n* fix doc\n\n---------\n\nCo-authored-by: Nicole \nCo-authored-by: Nicole \nCo-authored-by: Nicole \nCo-authored-by: Diego K <43053772+diegokingston@users.noreply.github.com>", + "timestamp": "2025-04-04T22:04:46Z", + "tree_id": "c004e1007c6a9c30eae0542f5689f1dd6c537503", + "url": "https://github.com/lambdaclass/lambdaworks/commit/a7b22e18784dc80dc755644c0a6e310d544e1dee" + }, + "date": 1743805940142, + "tool": "cargo", + "benches": [ + { + "name": "Ordered FFT/Sequential from NR radix2", + "value": 322536981, + "range": "± 677708", + "unit": "ns/iter" + }, + { + "name": "Ordered FFT/Sequential from RN radix2", + "value": 381470548, + "range": "± 1184480", + "unit": "ns/iter" + }, + { + "name": "Ordered FFT/Sequential from NR radix4", + "value": 282006174, + "range": "± 5939037", + "unit": "ns/iter" + }, + { + "name": "Ordered FFT/Sequential from NR radix2 #2", + "value": 675795635, + "range": "± 659215", + "unit": "ns/iter" + }, + { + "name": "Ordered FFT/Sequential from RN radix2 #2", + "value": 795681046, + "range": "± 2425038", + "unit": "ns/iter" + }, + { + "name": "Ordered FFT/Sequential from NR radix2 #3", + "value": 1411782190, + "range": "± 1751973", + "unit": "ns/iter" + }, + { + "name": "Ordered FFT/Sequential from RN radix2 #3", + "value": 1651376887, + "range": "± 2140153", + "unit": "ns/iter" + }, + { + "name": "Ordered FFT/Sequential from NR radix4 #2", + "value": 1233122116, + "range": "± 3254844", + "unit": "ns/iter" + }, + { + "name": "Ordered FFT/Sequential from NR radix2 #4", + "value": 2946309310, + "range": "± 1941423", + "unit": "ns/iter" + }, + { + "name": "Ordered FFT/Sequential from RN radix2 #4", + "value": 3432717624, + "range": "± 11089516", + "unit": "ns/iter" + }, + { + "name": "Ordered FFT/Sequential from NR radix2 #5", + "value": 6173099230, + "range": "± 8414029", + "unit": "ns/iter" + }, + { + "name": "Ordered FFT/Sequential from RN radix2 #5", + "value": 7267160590, + "range": "± 13302343", + "unit": "ns/iter" + }, + { + "name": "Ordered FFT/Sequential from NR radix4 #3", + "value": 5403850651, + "range": "± 9004410", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/natural", + "value": 7477272, + "range": "± 7846", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/natural inversed", + "value": 7567425, + "range": "± 203683", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/bit-reversed", + "value": 9944972, + "range": "± 20392", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/bit-reversed inversed", + "value": 10014529, + "range": "± 13387", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/natural #2", + "value": 17618702, + "range": "± 30856", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/natural inversed #2", + "value": 17571476, + "range": "± 20902", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/bit-reversed #2", + "value": 24371876, + "range": "± 236123", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/bit-reversed inversed #2", + "value": 24059528, + "range": "± 206987", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/natural #3", + "value": 35327920, + "range": "± 386990", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/natural inversed #3", + "value": 35556992, + "range": "± 194748", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/bit-reversed #3", + "value": 65557303, + "range": "± 809469", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/bit-reversed inversed #3", + "value": 67348023, + "range": "± 923589", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/natural #4", + "value": 71434530, + "range": "± 133214", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/natural inversed #4", + "value": 71037702, + "range": "± 216806", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/bit-reversed #4", + "value": 136201709, + "range": "± 1189575", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/bit-reversed inversed #4", + "value": 135977087, + "range": "± 2000220", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/natural #5", + "value": 138991812, + "range": "± 1846913", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/natural inversed #5", + "value": 138849164, + "range": "± 100573", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/bit-reversed #5", + "value": 272816251, + "range": "± 1189382", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/bit-reversed inversed #5", + "value": 271332338, + "range": "± 557768", + "unit": "ns/iter" + }, + { + "name": "Bit-reverse permutation/Sequential", + "value": 15574852, + "range": "± 132202", + "unit": "ns/iter" + }, + { + "name": "Bit-reverse permutation/Sequential #2", + "value": 32855737, + "range": "± 144069", + "unit": "ns/iter" + }, + { + "name": "Bit-reverse permutation/Sequential #3", + "value": 65461659, + "range": "± 167480", + "unit": "ns/iter" + }, + { + "name": "Bit-reverse permutation/Sequential #4", + "value": 134686502, + "range": "± 1861292", + "unit": "ns/iter" + }, + { + "name": "Bit-reverse permutation/Sequential #5", + "value": 345398332, + "range": "± 3959000", + "unit": "ns/iter" + }, + { + "name": "Polynomial evaluation/Sequential FFT", + "value": 349246068, + "range": "± 882945", + "unit": "ns/iter" + }, + { + "name": "Polynomial evaluation/Sequential FFT #2", + "value": 746529539, + "range": "± 7013100", + "unit": "ns/iter" + }, + { + "name": "Polynomial evaluation/Sequential FFT #3", + "value": 1578407282, + "range": "± 3122986", + "unit": "ns/iter" + }, + { + "name": "Polynomial evaluation/Sequential FFT #4", + "value": 3290550733, + "range": "± 7700902", + "unit": "ns/iter" + }, + { + "name": "Polynomial evaluation/Sequential FFT #5", + "value": 6920528032, + "range": "± 9116519", + "unit": "ns/iter" + }, + { + "name": "Polynomial interpolation/Sequential FFT", + "value": 374258082, + "range": "± 2251004", + "unit": "ns/iter" + }, + { + "name": "Polynomial interpolation/Sequential FFT #2", + "value": 794782921, + "range": "± 4374850", + "unit": "ns/iter" + }, + { + "name": "Polynomial interpolation/Sequential FFT #3", + "value": 1654737887, + "range": "± 1901458", + "unit": "ns/iter" + }, + { + "name": "Polynomial interpolation/Sequential FFT #4", + "value": 3434244481, + "range": "± 10075935", + "unit": "ns/iter" + }, + { + "name": "Polynomial interpolation/Sequential FFT #5", + "value": 7233266618, + "range": "± 9545793", + "unit": "ns/iter" + }, + { + "name": "Polynomial/evaluate", + "value": 48, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/evaluate_slice", + "value": 443, + "range": "± 15", + "unit": "ns/iter" + }, + { + "name": "Polynomial/add", + "value": 104, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/neg", + "value": 59, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/sub", + "value": 173, + "range": "± 2", + "unit": "ns/iter" + }, + { + "name": "Polynomial/mul", + "value": 538, + "range": "± 41", + "unit": "ns/iter" + }, + { + "name": "Polynomial/div", + "value": 612, + "range": "± 94", + "unit": "ns/iter" + }, + { + "name": "Polynomial/div by 'x - b' with generic div", + "value": 2075, + "range": "± 159", + "unit": "ns/iter" + }, + { + "name": "Polynomial/div by 'x - b' with Ruffini", + "value": 51, + "range": "± 1", + "unit": "ns/iter" + }, + { + "name": "Polynomial/evaluate #2", + "value": 13, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/evaluate_with", + "value": 13, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/merge", + "value": 45, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/add #2", + "value": 6273, + "range": "± 110", + "unit": "ns/iter" + }, + { + "name": "Polynomial/mul #2", + "value": 32, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/evaluate 3", + "value": 1, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/evaluate 4", + "value": 1, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/evaluate 5", + "value": 1, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/evaluate 6", + "value": 1, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/evaluate 7", + "value": 1, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/evaluate 8", + "value": 9886, + "range": "± 449", + "unit": "ns/iter" + }, + { + "name": "Polynomial/evaluate 9", + "value": 1, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/evaluate 10", + "value": 1, + "range": "± 0", + "unit": "ns/iter" + } + ] + }, + { + "commit": { + "author": { + "email": "erhany96@gmail.com", + "name": "erhant", + "username": "erhant" + }, + "committer": { + "email": "noreply@github.com", + "name": "GitHub", + "username": "web-flow" + }, + "distinct": true, + "id": "7fe890e658ef17cf826c917ca7a71cf6a8e139f4", + "message": "fix: Circom Adapter (Groth16 over BLS12-381) (#991)\n\n* migrate fixes from other PR, todo tutorial guide fixes\n\n* update tutorial docs as well\n\n* update readme\n\n* fix lint\n\n* fix export witness.json doc\n\n* Remove Metal feature (#993)\n\n* rm all metal features\n\n* fix clippy\n\n* fix attribute\n\n* fix another attribute\n\n* Update Readme (#987)\n\n* update kzg Readme\n\n* KZG: make sure example is ok\n\n* Add Readme for Merkle Tree\n\n* Add Readme for Circle\n\n* fix circle docs\n\n* fix circle readme\n\n* fix typo, remove unused test and fix markdown style\n\n* changes in circle readme\n\n* fix circle readme\n\n* fix clippy\n\n* fix clippy spaces\n\n---------\n\nCo-authored-by: Nicole \nCo-authored-by: Joaquin Carletti \nCo-authored-by: Diego K <43053772+diegokingston@users.noreply.github.com>\n\n---------\n\nCo-authored-by: Joaquin Carletti <56092489+ColoCarletti@users.noreply.github.com>\nCo-authored-by: jotabulacios <45471455+jotabulacios@users.noreply.github.com>\nCo-authored-by: Nicole \nCo-authored-by: Joaquin Carletti \nCo-authored-by: Diego K <43053772+diegokingston@users.noreply.github.com>", + "timestamp": "2025-04-07T10:31:41-03:00", + "tree_id": "97f48d8c59fcf51b4577e7534ef45b2d59210387", + "url": "https://github.com/lambdaclass/lambdaworks/commit/7fe890e658ef17cf826c917ca7a71cf6a8e139f4" + }, + "date": 1744034142491, + "tool": "cargo", + "benches": [ + { + "name": "Ordered FFT/Sequential from NR radix2", + "value": 321437400, + "range": "± 348215", + "unit": "ns/iter" + }, + { + "name": "Ordered FFT/Sequential from RN radix2", + "value": 380145457, + "range": "± 1461575", + "unit": "ns/iter" + }, + { + "name": "Ordered FFT/Sequential from NR radix4", + "value": 280568088, + "range": "± 394822", + "unit": "ns/iter" + }, + { + "name": "Ordered FFT/Sequential from NR radix2 #2", + "value": 676218495, + "range": "± 347059", + "unit": "ns/iter" + }, + { + "name": "Ordered FFT/Sequential from RN radix2 #2", + "value": 802038191, + "range": "± 3380461", + "unit": "ns/iter" + }, + { + "name": "Ordered FFT/Sequential from NR radix2 #3", + "value": 1415170326, + "range": "± 1381686", + "unit": "ns/iter" + }, + { + "name": "Ordered FFT/Sequential from RN radix2 #3", + "value": 1667611437, + "range": "± 3930616", + "unit": "ns/iter" + }, + { + "name": "Ordered FFT/Sequential from NR radix4 #2", + "value": 1235252309, + "range": "± 1199095", + "unit": "ns/iter" + }, + { + "name": "Ordered FFT/Sequential from NR radix2 #4", + "value": 2957640592, + "range": "± 2451027", + "unit": "ns/iter" + }, + { + "name": "Ordered FFT/Sequential from RN radix2 #4", + "value": 3514601085, + "range": "± 13768918", + "unit": "ns/iter" + }, + { + "name": "Ordered FFT/Sequential from NR radix2 #5", + "value": 6195636016, + "range": "± 8128520", + "unit": "ns/iter" + }, + { + "name": "Ordered FFT/Sequential from RN radix2 #5", + "value": 7407546036, + "range": "± 28219330", + "unit": "ns/iter" + }, + { + "name": "Ordered FFT/Sequential from NR radix4 #3", + "value": 5419618752, + "range": "± 3876031", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/natural", + "value": 8222883, + "range": "± 8167", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/natural inversed", + "value": 8305733, + "range": "± 11553", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/bit-reversed", + "value": 11417889, + "range": "± 307054", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/bit-reversed inversed", + "value": 11524311, + "range": "± 176140", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/natural #2", + "value": 19698747, + "range": "± 71650", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/natural inversed #2", + "value": 19718337, + "range": "± 97455", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/bit-reversed #2", + "value": 34685528, + "range": "± 1095145", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/bit-reversed inversed #2", + "value": 33501042, + "range": "± 754484", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/natural #3", + "value": 38611651, + "range": "± 61391", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/natural inversed #3", + "value": 39391628, + "range": "± 104268", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/bit-reversed #3", + "value": 74704448, + "range": "± 701171", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/bit-reversed inversed #3", + "value": 75474289, + "range": "± 346220", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/natural #4", + "value": 78286128, + "range": "± 136024", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/natural inversed #4", + "value": 77947828, + "range": "± 329831", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/bit-reversed #4", + "value": 154740630, + "range": "± 2445437", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/bit-reversed inversed #4", + "value": 154354360, + "range": "± 963613", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/natural #5", + "value": 152519534, + "range": "± 363491", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/natural inversed #5", + "value": 152136820, + "range": "± 187903", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/bit-reversed #5", + "value": 310582411, + "range": "± 3168153", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/bit-reversed inversed #5", + "value": 303174745, + "range": "± 3898192", + "unit": "ns/iter" + }, + { + "name": "Bit-reverse permutation/Sequential", + "value": 17386870, + "range": "± 477977", + "unit": "ns/iter" + }, + { + "name": "Bit-reverse permutation/Sequential #2", + "value": 37379851, + "range": "± 726893", + "unit": "ns/iter" + }, + { + "name": "Bit-reverse permutation/Sequential #3", + "value": 73903717, + "range": "± 887313", + "unit": "ns/iter" + }, + { + "name": "Bit-reverse permutation/Sequential #4", + "value": 148636900, + "range": "± 1040597", + "unit": "ns/iter" + }, + { + "name": "Bit-reverse permutation/Sequential #5", + "value": 380034545, + "range": "± 3996013", + "unit": "ns/iter" + }, + { + "name": "Polynomial evaluation/Sequential FFT", + "value": 358053518, + "range": "± 1218181", + "unit": "ns/iter" + }, + { + "name": "Polynomial evaluation/Sequential FFT #2", + "value": 758901629, + "range": "± 1992783", + "unit": "ns/iter" + }, + { + "name": "Polynomial evaluation/Sequential FFT #3", + "value": 1589431581, + "range": "± 2117222", + "unit": "ns/iter" + }, + { + "name": "Polynomial evaluation/Sequential FFT #4", + "value": 3334508358, + "range": "± 7077874", + "unit": "ns/iter" + }, + { + "name": "Polynomial evaluation/Sequential FFT #5", + "value": 7026650519, + "range": "± 13257586", + "unit": "ns/iter" + }, + { + "name": "Polynomial interpolation/Sequential FFT", + "value": 383769972, + "range": "± 732942", + "unit": "ns/iter" + }, + { + "name": "Polynomial interpolation/Sequential FFT #2", + "value": 806184179, + "range": "± 3276873", + "unit": "ns/iter" + }, + { + "name": "Polynomial interpolation/Sequential FFT #3", + "value": 1679221092, + "range": "± 3563109", + "unit": "ns/iter" + }, + { + "name": "Polynomial interpolation/Sequential FFT #4", + "value": 3493952437, + "range": "± 7253656", + "unit": "ns/iter" + }, + { + "name": "Polynomial interpolation/Sequential FFT #5", + "value": 7350396887, + "range": "± 9856155", + "unit": "ns/iter" + }, + { + "name": "Polynomial/evaluate", + "value": 526, + "range": "± 4", + "unit": "ns/iter" + }, + { + "name": "Polynomial/evaluate_slice", + "value": 33821, + "range": "± 49", + "unit": "ns/iter" + }, + { + "name": "Polynomial/add", + "value": 412, + "range": "± 14", + "unit": "ns/iter" + }, + { + "name": "Polynomial/neg", + "value": 206, + "range": "± 1", + "unit": "ns/iter" + }, + { + "name": "Polynomial/sub", + "value": 714, + "range": "± 3", + "unit": "ns/iter" + }, + { + "name": "Polynomial/mul", + "value": 24807, + "range": "± 422", + "unit": "ns/iter" + }, + { + "name": "Polynomial/div", + "value": 1659, + "range": "± 1626", + "unit": "ns/iter" + }, + { + "name": "Polynomial/div by 'x - b' with generic div", + "value": 61620, + "range": "± 679", + "unit": "ns/iter" + }, + { + "name": "Polynomial/div by 'x - b' with Ruffini", + "value": 543, + "range": "± 4", + "unit": "ns/iter" + }, + { + "name": "Polynomial/evaluate #2", + "value": 12, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/evaluate_with", + "value": 68, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/merge", + "value": 83, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/add #2", + "value": 9480, + "range": "± 1249", + "unit": "ns/iter" + }, + { + "name": "Polynomial/mul #2", + "value": 43, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/evaluate 3", + "value": 1, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/evaluate 4", + "value": 1, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/evaluate 5", + "value": 1, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/evaluate 6", + "value": 1, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/evaluate 7", + "value": 1, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/evaluate 8", + "value": 1, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/evaluate 9", + "value": 1, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/evaluate 10", + "value": 1, + "range": "± 0", + "unit": "ns/iter" + } + ] + }, + { + "commit": { + "author": { + "email": "nicole.graus@lambdaclass.com", + "name": "Nicole Graus", + "username": "nicole-graus" + }, + "committer": { + "email": "noreply@github.com", + "name": "GitHub", + "username": "web-flow" + }, + "distinct": true, + "id": "ca84326212a16046c25764a04f09fbd25d4a5a5b", + "message": "Add RSA and Schnorr Examples (#989)\n\n* first commit. adds basic rsa\n\n* refactor and update README\n\n* finish schnorr signature. rand test not working\n\n* fix comments\n\n* Change random sampl in groth16. Schnorr worikng\n\n* change sample field elem\n\n* fix clippy\n\n* fix clippy\n\n* refactor rsa and update README\n\n* remove comments\n\n* refactor schnorr\n\n* fix clippy\n\n* update ubuntu version for ci\n\n* Update README.md\n\n* Update README.md\n\n* change p and q values in the test\n\n* Update README.md\n\n* Update examples/schnorr-signature/README.md\n\nCo-authored-by: Pablo Deymonnaz \n\n---------\n\nCo-authored-by: jotabulacios \nCo-authored-by: Diego K <43053772+diegokingston@users.noreply.github.com>\nCo-authored-by: Pablo Deymonnaz ", + "timestamp": "2025-04-07T20:32:33Z", + "tree_id": "bae795434cd5db4edc88ea2d19b5698ea4aa7e2c", + "url": "https://github.com/lambdaclass/lambdaworks/commit/ca84326212a16046c25764a04f09fbd25d4a5a5b" + }, + "date": 1744059616751, + "tool": "cargo", + "benches": [ + { + "name": "Ordered FFT/Sequential from NR radix2", + "value": 323302174, + "range": "± 310320", + "unit": "ns/iter" + }, + { + "name": "Ordered FFT/Sequential from RN radix2", + "value": 378198393, + "range": "± 1889551", + "unit": "ns/iter" + }, + { + "name": "Ordered FFT/Sequential from NR radix4", + "value": 282302187, + "range": "± 579703", + "unit": "ns/iter" + }, + { + "name": "Ordered FFT/Sequential from NR radix2 #2", + "value": 679977030, + "range": "± 555917", + "unit": "ns/iter" + }, + { + "name": "Ordered FFT/Sequential from RN radix2 #2", + "value": 800838249, + "range": "± 1952424", + "unit": "ns/iter" + }, + { + "name": "Ordered FFT/Sequential from NR radix2 #3", + "value": 1425839785, + "range": "± 2514275", + "unit": "ns/iter" + }, + { + "name": "Ordered FFT/Sequential from RN radix2 #3", + "value": 1669592523, + "range": "± 3375890", + "unit": "ns/iter" + }, + { + "name": "Ordered FFT/Sequential from NR radix4 #2", + "value": 1242617718, + "range": "± 1801842", + "unit": "ns/iter" + }, + { + "name": "Ordered FFT/Sequential from NR radix2 #4", + "value": 2979299365, + "range": "± 4949856", + "unit": "ns/iter" + }, + { + "name": "Ordered FFT/Sequential from RN radix2 #4", + "value": 3481898841, + "range": "± 6663047", + "unit": "ns/iter" + }, + { + "name": "Ordered FFT/Sequential from NR radix2 #5", + "value": 6242153472, + "range": "± 1656559", + "unit": "ns/iter" + }, + { + "name": "Ordered FFT/Sequential from RN radix2 #5", + "value": 7351475668, + "range": "± 4358052", + "unit": "ns/iter" + }, + { + "name": "Ordered FFT/Sequential from NR radix4 #3", + "value": 5533157933, + "range": "± 5560622", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/natural", + "value": 8291674, + "range": "± 8822", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/natural inversed", + "value": 8314536, + "range": "± 8702", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/bit-reversed", + "value": 10334249, + "range": "± 73145", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/bit-reversed inversed", + "value": 10462067, + "range": "± 41279", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/natural #2", + "value": 18895744, + "range": "± 72045", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/natural inversed #2", + "value": 18786245, + "range": "± 63210", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/bit-reversed #2", + "value": 28289380, + "range": "± 553212", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/bit-reversed inversed #2", + "value": 27904926, + "range": "± 689561", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/natural #3", + "value": 37860062, + "range": "± 113373", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/natural inversed #3", + "value": 38433200, + "range": "± 140895", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/bit-reversed #3", + "value": 69951362, + "range": "± 690983", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/bit-reversed inversed #3", + "value": 69917365, + "range": "± 452670", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/natural #4", + "value": 77564187, + "range": "± 33983", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/natural inversed #4", + "value": 77410617, + "range": "± 132595", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/bit-reversed #4", + "value": 144575783, + "range": "± 666787", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/bit-reversed inversed #4", + "value": 145391265, + "range": "± 509476", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/natural #5", + "value": 151334758, + "range": "± 230203", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/natural inversed #5", + "value": 152036489, + "range": "± 635478", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/bit-reversed #5", + "value": 289779727, + "range": "± 599516", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/bit-reversed inversed #5", + "value": 292670351, + "range": "± 685182", + "unit": "ns/iter" + }, + { + "name": "Bit-reverse permutation/Sequential", + "value": 15200285, + "range": "± 199663", + "unit": "ns/iter" + }, + { + "name": "Bit-reverse permutation/Sequential #2", + "value": 33801645, + "range": "± 123245", + "unit": "ns/iter" + }, + { + "name": "Bit-reverse permutation/Sequential #3", + "value": 69162244, + "range": "± 228661", + "unit": "ns/iter" + }, + { + "name": "Bit-reverse permutation/Sequential #4", + "value": 139832541, + "range": "± 472688", + "unit": "ns/iter" + }, + { + "name": "Bit-reverse permutation/Sequential #5", + "value": 363144425, + "range": "± 868273", + "unit": "ns/iter" + }, + { + "name": "Polynomial evaluation/Sequential FFT", + "value": 355590805, + "range": "± 1434883", + "unit": "ns/iter" + }, + { + "name": "Polynomial evaluation/Sequential FFT #2", + "value": 755813997, + "range": "± 1731425", + "unit": "ns/iter" + }, + { + "name": "Polynomial evaluation/Sequential FFT #3", + "value": 1594064923, + "range": "± 2063410", + "unit": "ns/iter" + }, + { + "name": "Polynomial evaluation/Sequential FFT #4", + "value": 3328267097, + "range": "± 3029485", + "unit": "ns/iter" + }, + { + "name": "Polynomial evaluation/Sequential FFT #5", + "value": 7024047657, + "range": "± 2048699", + "unit": "ns/iter" + }, + { + "name": "Polynomial interpolation/Sequential FFT", + "value": 379896032, + "range": "± 943930", + "unit": "ns/iter" + }, + { + "name": "Polynomial interpolation/Sequential FFT #2", + "value": 798572850, + "range": "± 1282477", + "unit": "ns/iter" + }, + { + "name": "Polynomial interpolation/Sequential FFT #3", + "value": 1677234138, + "range": "± 1460907", + "unit": "ns/iter" + }, + { + "name": "Polynomial interpolation/Sequential FFT #4", + "value": 3489203738, + "range": "± 1426865", + "unit": "ns/iter" + }, + { + "name": "Polynomial interpolation/Sequential FFT #5", + "value": 7333133452, + "range": "± 2950916", + "unit": "ns/iter" + }, + { + "name": "Polynomial/evaluate", + "value": 5, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/evaluate_slice", + "value": 14, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/add", + "value": 52, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/neg", + "value": 24, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/sub", + "value": 79, + "range": "± 6", + "unit": "ns/iter" + }, + { + "name": "Polynomial/mul", + "value": 32, + "range": "± 2", + "unit": "ns/iter" + }, + { + "name": "Polynomial/div", + "value": 242, + "range": "± 20", + "unit": "ns/iter" + }, + { + "name": "Polynomial/div by 'x - b' with generic div", + "value": 44, + "range": "± 2", + "unit": "ns/iter" + }, + { + "name": "Polynomial/div by 'x - b' with Ruffini", + "value": 5, + "range": "± 55", + "unit": "ns/iter" + }, + { + "name": "Polynomial/evaluate #2", + "value": 17, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/evaluate_with", + "value": 5, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/merge", + "value": 111, + "range": "± 1", + "unit": "ns/iter" + }, + { + "name": "Polynomial/add #2", + "value": 11799, + "range": "± 1536", + "unit": "ns/iter" + }, + { + "name": "Polynomial/mul #2", + "value": 209, + "range": "± 1", + "unit": "ns/iter" + }, + { + "name": "Polynomial/evaluate 3", + "value": 1, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/evaluate 4", + "value": 1, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/evaluate 5", + "value": 1, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/evaluate 6", + "value": 1, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/evaluate 7", + "value": 1, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/evaluate 8", + "value": 1, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/evaluate 9", + "value": 1, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/evaluate 10", + "value": 1, + "range": "± 0", + "unit": "ns/iter" + } + ] + }, + { + "commit": { + "author": { + "email": "70894690+0xLucqs@users.noreply.github.com", + "name": "0xLucqs", + "username": "0xLucqs" + }, + "committer": { + "email": "noreply@github.com", + "name": "GitHub", + "username": "web-flow" + }, + "distinct": false, + "id": "201f08d93652006d30cce0dbda83c68fa20f4213", + "message": "fix(coset): shl overflow when getting the points (#994)\n\n* fix(coset): shl overflow when getting the points\n\n* impl suggestion", + "timestamp": "2025-04-08T20:16:18Z", + "tree_id": "f8801b9b7fb0e19c401c46b50ae9b84edce6c97e", + "url": "https://github.com/lambdaclass/lambdaworks/commit/201f08d93652006d30cce0dbda83c68fa20f4213" + }, + "date": 1744145059078, + "tool": "cargo", + "benches": [ + { + "name": "Ordered FFT/Sequential from NR radix2", + "value": 322612516, + "range": "± 783501", + "unit": "ns/iter" + }, + { + "name": "Ordered FFT/Sequential from RN radix2", + "value": 375172800, + "range": "± 1755920", + "unit": "ns/iter" + }, + { + "name": "Ordered FFT/Sequential from NR radix4", + "value": 281936890, + "range": "± 254910", + "unit": "ns/iter" + }, + { + "name": "Ordered FFT/Sequential from NR radix2 #2", + "value": 677832008, + "range": "± 13655791", + "unit": "ns/iter" + }, + { + "name": "Ordered FFT/Sequential from RN radix2 #2", + "value": 794145062, + "range": "± 13243424", + "unit": "ns/iter" + }, + { + "name": "Ordered FFT/Sequential from NR radix2 #3", + "value": 1423013668, + "range": "± 2821673", + "unit": "ns/iter" + }, + { + "name": "Ordered FFT/Sequential from RN radix2 #3", + "value": 1669372326, + "range": "± 13723412", + "unit": "ns/iter" + }, + { + "name": "Ordered FFT/Sequential from NR radix4 #2", + "value": 1242990934, + "range": "± 673322", + "unit": "ns/iter" + }, + { + "name": "Ordered FFT/Sequential from NR radix2 #4", + "value": 2970303244, + "range": "± 2855584", + "unit": "ns/iter" + }, + { + "name": "Ordered FFT/Sequential from RN radix2 #4", + "value": 3466325400, + "range": "± 7093704", + "unit": "ns/iter" + }, + { + "name": "Ordered FFT/Sequential from NR radix2 #5", + "value": 6222552366, + "range": "± 14197329", + "unit": "ns/iter" + }, + { + "name": "Ordered FFT/Sequential from RN radix2 #5", + "value": 7317714054, + "range": "± 19869092", + "unit": "ns/iter" + }, + { + "name": "Ordered FFT/Sequential from NR radix4 #3", + "value": 5452381623, + "range": "± 1885502", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/natural", + "value": 7533338, + "range": "± 60821", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/natural inversed", + "value": 7599272, + "range": "± 18669", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/bit-reversed", + "value": 12299973, + "range": "± 1477743", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/bit-reversed inversed", + "value": 10896407, + "range": "± 297657", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/natural #2", + "value": 17683720, + "range": "± 71712", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/natural inversed #2", + "value": 17227183, + "range": "± 38677", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/bit-reversed #2", + "value": 28665738, + "range": "± 2466855", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/bit-reversed inversed #2", + "value": 26790666, + "range": "± 1521028", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/natural #3", + "value": 34946833, + "range": "± 94476", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/natural inversed #3", + "value": 35188797, + "range": "± 119149", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/bit-reversed #3", + "value": 66806435, + "range": "± 1623807", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/bit-reversed inversed #3", + "value": 66642918, + "range": "± 3325895", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/natural #4", + "value": 71874222, + "range": "± 164695", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/natural inversed #4", + "value": 71365370, + "range": "± 168413", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/bit-reversed #4", + "value": 139299207, + "range": "± 962149", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/bit-reversed inversed #4", + "value": 138070290, + "range": "± 836855", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/natural #5", + "value": 142398779, + "range": "± 457154", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/natural inversed #5", + "value": 142181162, + "range": "± 176661", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/bit-reversed #5", + "value": 280338474, + "range": "± 1033178", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/bit-reversed inversed #5", + "value": 278745190, + "range": "± 1571992", + "unit": "ns/iter" + }, + { + "name": "Bit-reverse permutation/Sequential", + "value": 15010369, + "range": "± 626637", + "unit": "ns/iter" + }, + { + "name": "Bit-reverse permutation/Sequential #2", + "value": 34196270, + "range": "± 241280", + "unit": "ns/iter" + }, + { + "name": "Bit-reverse permutation/Sequential #3", + "value": 67750478, + "range": "± 416911", + "unit": "ns/iter" + }, + { + "name": "Bit-reverse permutation/Sequential #4", + "value": 137221681, + "range": "± 639436", + "unit": "ns/iter" + }, + { + "name": "Bit-reverse permutation/Sequential #5", + "value": 361147163, + "range": "± 11347788", + "unit": "ns/iter" + }, + { + "name": "Polynomial evaluation/Sequential FFT", + "value": 359189887, + "range": "± 1607505", + "unit": "ns/iter" + }, + { + "name": "Polynomial evaluation/Sequential FFT #2", + "value": 759419329, + "range": "± 2488516", + "unit": "ns/iter" + }, + { + "name": "Polynomial evaluation/Sequential FFT #3", + "value": 1591326735, + "range": "± 3637246", + "unit": "ns/iter" + }, + { + "name": "Polynomial evaluation/Sequential FFT #4", + "value": 3310404885, + "range": "± 3380166", + "unit": "ns/iter" + }, + { + "name": "Polynomial evaluation/Sequential FFT #5", + "value": 6984313813, + "range": "± 15248299", + "unit": "ns/iter" + }, + { + "name": "Polynomial interpolation/Sequential FFT", + "value": 383772320, + "range": "± 2871366", + "unit": "ns/iter" + }, + { + "name": "Polynomial interpolation/Sequential FFT #2", + "value": 801865129, + "range": "± 2779209", + "unit": "ns/iter" + }, + { + "name": "Polynomial interpolation/Sequential FFT #3", + "value": 1672680649, + "range": "± 4350940", + "unit": "ns/iter" + }, + { + "name": "Polynomial interpolation/Sequential FFT #4", + "value": 3469673705, + "range": "± 12375245", + "unit": "ns/iter" + }, + { + "name": "Polynomial interpolation/Sequential FFT #5", + "value": 7318440621, + "range": "± 16088436", + "unit": "ns/iter" + }, + { + "name": "Polynomial/evaluate", + "value": 12, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/evaluate_slice", + "value": 30, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/add", + "value": 57, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/neg", + "value": 27, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/sub", + "value": 83, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/mul", + "value": 52, + "range": "± 1", + "unit": "ns/iter" + }, + { + "name": "Polynomial/div", + "value": 297, + "range": "± 5", + "unit": "ns/iter" + }, + { + "name": "Polynomial/div by 'x - b' with generic div", + "value": 285, + "range": "± 9", + "unit": "ns/iter" + }, + { + "name": "Polynomial/div by 'x - b' with Ruffini", + "value": 11, + "range": "± 33", + "unit": "ns/iter" + }, + { + "name": "Polynomial/evaluate #2", + "value": 13, + "range": "± 2", + "unit": "ns/iter" + }, + { + "name": "Polynomial/evaluate_with", + "value": 14, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/merge", + "value": 49, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/add #2", + "value": 7132, + "range": "± 1053", + "unit": "ns/iter" + }, + { + "name": "Polynomial/mul #2", + "value": 31, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/evaluate 3", + "value": 1, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/evaluate 4", + "value": 1, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/evaluate 5", + "value": 1, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/evaluate 6", + "value": 1, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/evaluate 7", + "value": 1, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/evaluate 8", + "value": 9922, + "range": "± 1124", + "unit": "ns/iter" + }, + { + "name": "Polynomial/evaluate 9", + "value": 1, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/evaluate 10", + "value": 1, + "range": "± 0", + "unit": "ns/iter" + } + ] + }, + { + "commit": { + "author": { + "email": "nicole.graus@lambdaclass.com", + "name": "Nicole Graus", + "username": "nicole-graus" + }, + "committer": { + "email": "noreply@github.com", + "name": "GitHub", + "username": "web-flow" + }, + "distinct": true, + "id": "83c041d722f6401d418f487ffc095046cef3ee33", + "message": "Pohlig Hellman Attack Example (#995)\n\n* save work. Add files\n\n* Save work\n\n* Curve 1 and curve 2 working just for small k\n\n* update ubuntu version for ci\n\n* save work\n\n* save work\n\n* new group over bls12-381\n\n* refactor\n\n* small refactor\n\n* Add Readme and remove comments\n\n* brute force test\n\n* print iterations brute force\n\n* add brute-force comment to readme\n\n* fix clippy\n\n* Update README.md\n\n* change return of function\n\n* fix clippy\n\n* change chinese theorem error\n\n* fix clippy\n\n---------\n\nCo-authored-by: jotabulacios \nCo-authored-by: Diego K <43053772+diegokingston@users.noreply.github.com>\nCo-authored-by: diegokingston ", + "timestamp": "2025-04-08T20:16:51Z", + "tree_id": "77bf8329b60320dd42d600228559cef253915074", + "url": "https://github.com/lambdaclass/lambdaworks/commit/83c041d722f6401d418f487ffc095046cef3ee33" + }, + "date": 1744145122407, + "tool": "cargo", + "benches": [ + { + "name": "Ordered FFT/Sequential from NR radix2", + "value": 337032598, + "range": "± 341044", + "unit": "ns/iter" + }, + { + "name": "Ordered FFT/Sequential from RN radix2", + "value": 381483433, + "range": "± 1256221", + "unit": "ns/iter" + }, + { + "name": "Ordered FFT/Sequential from NR radix4", + "value": 283310648, + "range": "± 2804886", + "unit": "ns/iter" + }, + { + "name": "Ordered FFT/Sequential from NR radix2 #2", + "value": 704873522, + "range": "± 1115648", + "unit": "ns/iter" + }, + { + "name": "Ordered FFT/Sequential from RN radix2 #2", + "value": 793288212, + "range": "± 3021807", + "unit": "ns/iter" + }, + { + "name": "Ordered FFT/Sequential from NR radix2 #3", + "value": 1475980310, + "range": "± 2772032", + "unit": "ns/iter" + }, + { + "name": "Ordered FFT/Sequential from RN radix2 #3", + "value": 1675442743, + "range": "± 2776426", + "unit": "ns/iter" + }, + { + "name": "Ordered FFT/Sequential from NR radix4 #2", + "value": 1243963546, + "range": "± 693660", + "unit": "ns/iter" + }, + { + "name": "Ordered FFT/Sequential from NR radix2 #4", + "value": 3085603525, + "range": "± 3928348", + "unit": "ns/iter" + }, + { + "name": "Ordered FFT/Sequential from RN radix2 #4", + "value": 3489619301, + "range": "± 5515004", + "unit": "ns/iter" + }, + { + "name": "Ordered FFT/Sequential from NR radix2 #5", + "value": 6469173555, + "range": "± 12242973", + "unit": "ns/iter" + }, + { + "name": "Ordered FFT/Sequential from RN radix2 #5", + "value": 7345325930, + "range": "± 14801476", + "unit": "ns/iter" + }, + { + "name": "Ordered FFT/Sequential from NR radix4 #3", + "value": 5458799022, + "range": "± 5324731", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/natural", + "value": 7535012, + "range": "± 4997", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/natural inversed", + "value": 7589493, + "range": "± 5072", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/bit-reversed", + "value": 9885412, + "range": "± 244086", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/bit-reversed inversed", + "value": 10617170, + "range": "± 756745", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/natural #2", + "value": 17681854, + "range": "± 95454", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/natural inversed #2", + "value": 17663075, + "range": "± 56199", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/bit-reversed #2", + "value": 33282154, + "range": "± 1042732", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/bit-reversed inversed #2", + "value": 32422792, + "range": "± 629797", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/natural #3", + "value": 35741599, + "range": "± 152664", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/natural inversed #3", + "value": 35891305, + "range": "± 124162", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/bit-reversed #3", + "value": 70867350, + "range": "± 1047052", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/bit-reversed inversed #3", + "value": 72034422, + "range": "± 814846", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/natural #4", + "value": 72028951, + "range": "± 217272", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/natural inversed #4", + "value": 72009037, + "range": "± 134463", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/bit-reversed #4", + "value": 146744415, + "range": "± 903954", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/bit-reversed inversed #4", + "value": 144872484, + "range": "± 1746349", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/natural #5", + "value": 143579239, + "range": "± 341996", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/natural inversed #5", + "value": 143657769, + "range": "± 213450", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/bit-reversed #5", + "value": 289237882, + "range": "± 1526873", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/bit-reversed inversed #5", + "value": 289097187, + "range": "± 2251628", + "unit": "ns/iter" + }, + { + "name": "Bit-reverse permutation/Sequential", + "value": 16802329, + "range": "± 176758", + "unit": "ns/iter" + }, + { + "name": "Bit-reverse permutation/Sequential #2", + "value": 36269099, + "range": "± 293885", + "unit": "ns/iter" + }, + { + "name": "Bit-reverse permutation/Sequential #3", + "value": 72344885, + "range": "± 455678", + "unit": "ns/iter" + }, + { + "name": "Bit-reverse permutation/Sequential #4", + "value": 144187979, + "range": "± 576753", + "unit": "ns/iter" + }, + { + "name": "Bit-reverse permutation/Sequential #5", + "value": 378376989, + "range": "± 3084053", + "unit": "ns/iter" + }, + { + "name": "Polynomial evaluation/Sequential FFT", + "value": 373914343, + "range": "± 819878", + "unit": "ns/iter" + }, + { + "name": "Polynomial evaluation/Sequential FFT #2", + "value": 792624341, + "range": "± 2955407", + "unit": "ns/iter" + }, + { + "name": "Polynomial evaluation/Sequential FFT #3", + "value": 1659470692, + "range": "± 2034017", + "unit": "ns/iter" + }, + { + "name": "Polynomial evaluation/Sequential FFT #4", + "value": 3444698418, + "range": "± 2100483", + "unit": "ns/iter" + }, + { + "name": "Polynomial evaluation/Sequential FFT #5", + "value": 7276915840, + "range": "± 8618775", + "unit": "ns/iter" + }, + { + "name": "Polynomial interpolation/Sequential FFT", + "value": 396680461, + "range": "± 1630153", + "unit": "ns/iter" + }, + { + "name": "Polynomial interpolation/Sequential FFT #2", + "value": 825378837, + "range": "± 2117927", + "unit": "ns/iter" + }, + { + "name": "Polynomial interpolation/Sequential FFT #3", + "value": 1728524137, + "range": "± 3164005", + "unit": "ns/iter" + }, + { + "name": "Polynomial interpolation/Sequential FFT #4", + "value": 3598299240, + "range": "± 6189967", + "unit": "ns/iter" + }, + { + "name": "Polynomial interpolation/Sequential FFT #5", + "value": 7580173498, + "range": "± 8160892", + "unit": "ns/iter" + }, + { + "name": "Polynomial/evaluate", + "value": 1118, + "range": "± 1", + "unit": "ns/iter" + }, + { + "name": "Polynomial/evaluate_slice", + "value": 143324, + "range": "± 93", + "unit": "ns/iter" + }, + { + "name": "Polynomial/add", + "value": 787, + "range": "± 13", + "unit": "ns/iter" + }, + { + "name": "Polynomial/neg", + "value": 395, + "range": "± 2", + "unit": "ns/iter" + }, + { + "name": "Polynomial/sub", + "value": 1223, + "range": "± 14", + "unit": "ns/iter" + }, + { + "name": "Polynomial/mul", + "value": 103113, + "range": "± 6851", + "unit": "ns/iter" + }, + { + "name": "Polynomial/div", + "value": 2733, + "range": "± 2190", + "unit": "ns/iter" + }, + { + "name": "Polynomial/div by 'x - b' with generic div", + "value": 198259, + "range": "± 2451", + "unit": "ns/iter" + }, + { + "name": "Polynomial/div by 'x - b' with Ruffini", + "value": 1140, + "range": "± 4", + "unit": "ns/iter" + }, + { + "name": "Polynomial/evaluate #2", + "value": 13, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/evaluate_with", + "value": 14, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/merge", + "value": 49, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/add #2", + "value": 8708, + "range": "± 1263", + "unit": "ns/iter" + }, + { + "name": "Polynomial/mul #2", + "value": 31, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/evaluate 3", + "value": 1, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/evaluate 4", + "value": 1, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/evaluate 5", + "value": 1, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/evaluate 6", + "value": 1, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/evaluate 7", + "value": 1, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/evaluate 8", + "value": 1, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/evaluate 9", + "value": 1, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/evaluate 10", + "value": 1, + "range": "± 0", + "unit": "ns/iter" + } + ] + }, + { + "commit": { + "author": { + "email": "43053772+diegokingston@users.noreply.github.com", + "name": "Diego K", + "username": "diegokingston" + }, + "committer": { + "email": "noreply@github.com", + "name": "GitHub", + "username": "web-flow" + }, + "distinct": false, + "id": "3e413875bb4f9b45883678cf05a6184c2fa344a7", + "message": "Update README.md (#996)", + "timestamp": "2025-04-09T15:03:41Z", + "tree_id": "d9561f498cfb89b926cd67b1b68143bc679e0227", + "url": "https://github.com/lambdaclass/lambdaworks/commit/3e413875bb4f9b45883678cf05a6184c2fa344a7" + }, + "date": 1744212724057, + "tool": "cargo", + "benches": [ + { + "name": "Ordered FFT/Sequential from NR radix2", + "value": 324920723, + "range": "± 714090", + "unit": "ns/iter" + }, + { + "name": "Ordered FFT/Sequential from RN radix2", + "value": 389038579, + "range": "± 2439642", + "unit": "ns/iter" + }, + { + "name": "Ordered FFT/Sequential from NR radix4", + "value": 280658658, + "range": "± 155014", + "unit": "ns/iter" + }, + { + "name": "Ordered FFT/Sequential from NR radix2 #2", + "value": 681160668, + "range": "± 248528", + "unit": "ns/iter" + }, + { + "name": "Ordered FFT/Sequential from RN radix2 #2", + "value": 812031122, + "range": "± 3655666", + "unit": "ns/iter" + }, + { + "name": "Ordered FFT/Sequential from NR radix2 #3", + "value": 1425623370, + "range": "± 2525518", + "unit": "ns/iter" + }, + { + "name": "Ordered FFT/Sequential from RN radix2 #3", + "value": 1690466477, + "range": "± 9088668", + "unit": "ns/iter" + }, + { + "name": "Ordered FFT/Sequential from NR radix4 #2", + "value": 1233969790, + "range": "± 2262847", + "unit": "ns/iter" + }, + { + "name": "Ordered FFT/Sequential from NR radix2 #4", + "value": 2979013463, + "range": "± 1909816", + "unit": "ns/iter" + }, + { + "name": "Ordered FFT/Sequential from RN radix2 #4", + "value": 3537440924, + "range": "± 16510532", + "unit": "ns/iter" + }, + { + "name": "Ordered FFT/Sequential from NR radix2 #5", + "value": 6248667893, + "range": "± 3209709", + "unit": "ns/iter" + }, + { + "name": "Ordered FFT/Sequential from RN radix2 #5", + "value": 7418915661, + "range": "± 21389826", + "unit": "ns/iter" + }, + { + "name": "Ordered FFT/Sequential from NR radix4 #3", + "value": 5414230850, + "range": "± 4142559", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/natural", + "value": 8224964, + "range": "± 3977", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/natural inversed", + "value": 8327296, + "range": "± 14214", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/bit-reversed", + "value": 15235837, + "range": "± 613764", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/bit-reversed inversed", + "value": 15942800, + "range": "± 1397765", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/natural #2", + "value": 19346030, + "range": "± 80631", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/natural inversed #2", + "value": 19246494, + "range": "± 158896", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/bit-reversed #2", + "value": 36265454, + "range": "± 861308", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/bit-reversed inversed #2", + "value": 36820588, + "range": "± 715920", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/natural #3", + "value": 38403885, + "range": "± 151439", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/natural inversed #3", + "value": 38858659, + "range": "± 101715", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/bit-reversed #3", + "value": 74550757, + "range": "± 756336", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/bit-reversed inversed #3", + "value": 73759444, + "range": "± 762117", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/natural #4", + "value": 78384524, + "range": "± 224747", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/natural inversed #4", + "value": 77856418, + "range": "± 220158", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/bit-reversed #4", + "value": 149600278, + "range": "± 687720", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/bit-reversed inversed #4", + "value": 149696164, + "range": "± 1468261", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/natural #5", + "value": 153582268, + "range": "± 474294", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/natural inversed #5", + "value": 154268345, + "range": "± 287543", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/bit-reversed #5", + "value": 294645286, + "range": "± 1943735", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/bit-reversed inversed #5", + "value": 295609179, + "range": "± 3411762", + "unit": "ns/iter" + }, + { + "name": "Bit-reverse permutation/Sequential", + "value": 18537299, + "range": "± 896767", + "unit": "ns/iter" + }, + { + "name": "Bit-reverse permutation/Sequential #2", + "value": 36569058, + "range": "± 1253456", + "unit": "ns/iter" + }, + { + "name": "Bit-reverse permutation/Sequential #3", + "value": 74686485, + "range": "± 3202358", + "unit": "ns/iter" + }, + { + "name": "Bit-reverse permutation/Sequential #4", + "value": 149105815, + "range": "± 8062333", + "unit": "ns/iter" + }, + { + "name": "Bit-reverse permutation/Sequential #5", + "value": 377799909, + "range": "± 7356768", + "unit": "ns/iter" + }, + { + "name": "Polynomial evaluation/Sequential FFT", + "value": 363425586, + "range": "± 2688926", + "unit": "ns/iter" + }, + { + "name": "Polynomial evaluation/Sequential FFT #2", + "value": 766702373, + "range": "± 1698843", + "unit": "ns/iter" + }, + { + "name": "Polynomial evaluation/Sequential FFT #3", + "value": 1600311370, + "range": "± 4699138", + "unit": "ns/iter" + }, + { + "name": "Polynomial evaluation/Sequential FFT #4", + "value": 3335901351, + "range": "± 8207817", + "unit": "ns/iter" + }, + { + "name": "Polynomial evaluation/Sequential FFT #5", + "value": 7067173707, + "range": "± 19563159", + "unit": "ns/iter" + }, + { + "name": "Polynomial interpolation/Sequential FFT", + "value": 390649595, + "range": "± 1307099", + "unit": "ns/iter" + }, + { + "name": "Polynomial interpolation/Sequential FFT #2", + "value": 808225955, + "range": "± 3076283", + "unit": "ns/iter" + }, + { + "name": "Polynomial interpolation/Sequential FFT #3", + "value": 1688784363, + "range": "± 3784791", + "unit": "ns/iter" + }, + { + "name": "Polynomial interpolation/Sequential FFT #4", + "value": 3507734586, + "range": "± 9986787", + "unit": "ns/iter" + }, + { + "name": "Polynomial interpolation/Sequential FFT #5", + "value": 7397674088, + "range": "± 14892021", + "unit": "ns/iter" + }, + { + "name": "Polynomial/evaluate", + "value": 11, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/evaluate_slice", + "value": 27, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/add", + "value": 57, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/neg", + "value": 27, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/sub", + "value": 83, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/mul", + "value": 50, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/div", + "value": 420, + "range": "± 21", + "unit": "ns/iter" + }, + { + "name": "Polynomial/div by 'x - b' with generic div", + "value": 396, + "range": "± 24", + "unit": "ns/iter" + }, + { + "name": "Polynomial/div by 'x - b' with Ruffini", + "value": 10, + "range": "± 33", + "unit": "ns/iter" + }, + { + "name": "Polynomial/evaluate #2", + "value": 16, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/evaluate_with", + "value": 5, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/merge", + "value": 114, + "range": "± 1", + "unit": "ns/iter" + }, + { + "name": "Polynomial/add #2", + "value": 11709, + "range": "± 141", + "unit": "ns/iter" + }, + { + "name": "Polynomial/mul #2", + "value": 181, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/evaluate 3", + "value": 1, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/evaluate 4", + "value": 1, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/evaluate 5", + "value": 1, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/evaluate 6", + "value": 1, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/evaluate 7", + "value": 1, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/evaluate 8", + "value": 1, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/evaluate 9", + "value": 1, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/evaluate 10", + "value": 1, + "range": "± 0", + "unit": "ns/iter" + } + ] + }, + { + "commit": { + "author": { + "email": "70894690+0xLucqs@users.noreply.github.com", + "name": "0xLucqs", + "username": "0xLucqs" + }, + "committer": { + "email": "noreply@github.com", + "name": "GitHub", + "username": "web-flow" + }, + "distinct": true, + "id": "28d98d12796324d4e5ac0bd65822b60044833f4f", + "message": "feat(fft): add fast mul for fft friendly fields (#997)\n\n* feat(fft): add fast multiplication\n\n* chore: add fast_mul benchmark\n\n* impl suggestion\n\n* chore: impl suggestion", + "timestamp": "2025-04-24T11:42:11-03:00", + "tree_id": "6ca8fe89487eb084ce66efe5ed933d8a0469ee79", + "url": "https://github.com/lambdaclass/lambdaworks/commit/28d98d12796324d4e5ac0bd65822b60044833f4f" + }, + "date": 1745507172482, + "tool": "cargo", + "benches": [ + { + "name": "Ordered FFT/Sequential from NR radix2", + "value": 323095920, + "range": "± 1261552", + "unit": "ns/iter" + }, + { + "name": "Ordered FFT/Sequential from RN radix2", + "value": 381310400, + "range": "± 872291", + "unit": "ns/iter" + }, + { + "name": "Ordered FFT/Sequential from NR radix4", + "value": 284164188, + "range": "± 4116159", + "unit": "ns/iter" + }, + { + "name": "Ordered FFT/Sequential from NR radix2 #2", + "value": 679369981, + "range": "± 810523", + "unit": "ns/iter" + }, + { + "name": "Ordered FFT/Sequential from RN radix2 #2", + "value": 797459457, + "range": "± 3780285", + "unit": "ns/iter" + }, + { + "name": "Ordered FFT/Sequential from NR radix2 #3", + "value": 1423521246, + "range": "± 5238649", + "unit": "ns/iter" + }, + { + "name": "Ordered FFT/Sequential from RN radix2 #3", + "value": 1679774753, + "range": "± 4278875", + "unit": "ns/iter" + }, + { + "name": "Ordered FFT/Sequential from NR radix4 #2", + "value": 1245748884, + "range": "± 700869", + "unit": "ns/iter" + }, + { + "name": "Ordered FFT/Sequential from NR radix2 #4", + "value": 2975875355, + "range": "± 2490922", + "unit": "ns/iter" + }, + { + "name": "Ordered FFT/Sequential from RN radix2 #4", + "value": 3489698546, + "range": "± 10616458", + "unit": "ns/iter" + }, + { + "name": "Ordered FFT/Sequential from NR radix2 #5", + "value": 6234688392, + "range": "± 10017355", + "unit": "ns/iter" + }, + { + "name": "Ordered FFT/Sequential from RN radix2 #5", + "value": 7338377516, + "range": "± 18712246", + "unit": "ns/iter" + }, + { + "name": "Ordered FFT/Sequential from NR radix4 #3", + "value": 5463540080, + "range": "± 5381404", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/natural", + "value": 7535564, + "range": "± 13506", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/natural inversed", + "value": 7610741, + "range": "± 9731", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/bit-reversed", + "value": 9874940, + "range": "± 38479", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/bit-reversed inversed", + "value": 9957001, + "range": "± 39111", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/natural #2", + "value": 17565090, + "range": "± 59018", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/natural inversed #2", + "value": 17596298, + "range": "± 106852", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/bit-reversed #2", + "value": 29561814, + "range": "± 785516", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/bit-reversed inversed #2", + "value": 28986776, + "range": "± 838003", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/natural #3", + "value": 35319624, + "range": "± 141279", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/natural inversed #3", + "value": 35863407, + "range": "± 150121", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/bit-reversed #3", + "value": 69515687, + "range": "± 1070546", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/bit-reversed inversed #3", + "value": 72622649, + "range": "± 919373", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/natural #4", + "value": 72790486, + "range": "± 380722", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/natural inversed #4", + "value": 72645348, + "range": "± 305096", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/bit-reversed #4", + "value": 147070723, + "range": "± 2338026", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/bit-reversed inversed #4", + "value": 147494705, + "range": "± 3491661", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/natural #5", + "value": 143530041, + "range": "± 332839", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/natural inversed #5", + "value": 142656895, + "range": "± 867997", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/bit-reversed #5", + "value": 292109333, + "range": "± 1138652", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/bit-reversed inversed #5", + "value": 291708799, + "range": "± 3143061", + "unit": "ns/iter" + }, + { + "name": "Bit-reverse permutation/Sequential", + "value": 17541361, + "range": "± 212414", + "unit": "ns/iter" + }, + { + "name": "Bit-reverse permutation/Sequential #2", + "value": 36253166, + "range": "± 1459691", + "unit": "ns/iter" + }, + { + "name": "Bit-reverse permutation/Sequential #3", + "value": 73590100, + "range": "± 2807304", + "unit": "ns/iter" + }, + { + "name": "Bit-reverse permutation/Sequential #4", + "value": 150340022, + "range": "± 2419909", + "unit": "ns/iter" + }, + { + "name": "Bit-reverse permutation/Sequential #5", + "value": 374286957, + "range": "± 2555151", + "unit": "ns/iter" + }, + { + "name": "Polynomial evaluation/Sequential FFT", + "value": 356441569, + "range": "± 1384057", + "unit": "ns/iter" + }, + { + "name": "Polynomial evaluation/Sequential FFT #2", + "value": 761023412, + "range": "± 1855627", + "unit": "ns/iter" + }, + { + "name": "Polynomial evaluation/Sequential FFT #3", + "value": 1603843893, + "range": "± 6280452", + "unit": "ns/iter" + }, + { + "name": "Polynomial evaluation/Sequential FFT #4", + "value": 3344561007, + "range": "± 7075151", + "unit": "ns/iter" + }, + { + "name": "Polynomial evaluation/Sequential FFT #5", + "value": 7036811867, + "range": "± 16790565", + "unit": "ns/iter" + }, + { + "name": "Polynomial interpolation/Sequential FFT", + "value": 382696252, + "range": "± 1571526", + "unit": "ns/iter" + }, + { + "name": "Polynomial interpolation/Sequential FFT #2", + "value": 806443815, + "range": "± 2875034", + "unit": "ns/iter" + }, + { + "name": "Polynomial interpolation/Sequential FFT #3", + "value": 1688585365, + "range": "± 3490387", + "unit": "ns/iter" + }, + { + "name": "Polynomial interpolation/Sequential FFT #4", + "value": 3502721466, + "range": "± 9651174", + "unit": "ns/iter" + }, + { + "name": "Polynomial interpolation/Sequential FFT #5", + "value": 7396696375, + "range": "± 19519533", + "unit": "ns/iter" + }, + { + "name": "Polynomial/evaluate", + "value": 541, + "range": "± 4", + "unit": "ns/iter" + }, + { + "name": "Polynomial/evaluate_slice", + "value": 34669, + "range": "± 86", + "unit": "ns/iter" + }, + { + "name": "Polynomial/add", + "value": 491, + "range": "± 16", + "unit": "ns/iter" + }, + { + "name": "Polynomial/neg", + "value": 225, + "range": "± 3", + "unit": "ns/iter" + }, + { + "name": "Polynomial/sub", + "value": 704, + "range": "± 5", + "unit": "ns/iter" + }, + { + "name": "Polynomial/mul", + "value": 22367, + "range": "± 102", + "unit": "ns/iter" + }, + { + "name": "Polynomial/fast_mul big poly", + "value": 143710, + "range": "± 982", + "unit": "ns/iter" + }, + { + "name": "Polynomial/slow mul big poly", + "value": 1640725, + "range": "± 10187", + "unit": "ns/iter" + }, + { + "name": "Polynomial/div", + "value": 1764, + "range": "± 1524", + "unit": "ns/iter" + }, + { + "name": "Polynomial/div by 'x - b' with generic div", + "value": 64755, + "range": "± 689", + "unit": "ns/iter" + }, + { + "name": "Polynomial/div by 'x - b' with Ruffini", + "value": 558, + "range": "± 4", + "unit": "ns/iter" + }, + { + "name": "Polynomial/evaluate #2", + "value": 13, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/evaluate_with", + "value": 10454, + "range": "± 136", + "unit": "ns/iter" + }, + { + "name": "Polynomial/merge", + "value": 90, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/add #2", + "value": 10177, + "range": "± 1624", + "unit": "ns/iter" + }, + { + "name": "Polynomial/mul #2", + "value": 68, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/evaluate 3", + "value": 1, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/evaluate 4", + "value": 1, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/evaluate 5", + "value": 1, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/evaluate 6", + "value": 1, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/evaluate 7", + "value": 1, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/evaluate 8", + "value": 1, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/evaluate 9", + "value": 1, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/evaluate 10", + "value": 1, + "range": "± 0", + "unit": "ns/iter" + } + ] + }, + { + "commit": { + "author": { + "email": "43053772+diegokingston@users.noreply.github.com", + "name": "Diego K", + "username": "diegokingston" + }, + "committer": { + "email": "noreply@github.com", + "name": "GitHub", + "username": "web-flow" + }, + "distinct": false, + "id": "7a336ca3ba8d64838b443bd8efca463c8f9c73b9", + "message": "Improve documentation (#999)\n\n* add explanation\n\n* fix format\n\n* add more comments\n\n* explain fft\n\n* continue docs\n\n* add more docs\n\n* Update README.md", + "timestamp": "2025-04-25T13:14:46Z", + "tree_id": "022c7aafee18c3201d20dc2f72866b2976de4463", + "url": "https://github.com/lambdaclass/lambdaworks/commit/7a336ca3ba8d64838b443bd8efca463c8f9c73b9" + }, + "date": 1745588590282, + "tool": "cargo", + "benches": [ + { + "name": "Ordered FFT/Sequential from NR radix2", + "value": 325167512, + "range": "± 357543", + "unit": "ns/iter" + }, + { + "name": "Ordered FFT/Sequential from RN radix2", + "value": 383158347, + "range": "± 1617891", + "unit": "ns/iter" + }, + { + "name": "Ordered FFT/Sequential from NR radix4", + "value": 283667452, + "range": "± 673043", + "unit": "ns/iter" + }, + { + "name": "Ordered FFT/Sequential from NR radix2 #2", + "value": 681873524, + "range": "± 709042", + "unit": "ns/iter" + }, + { + "name": "Ordered FFT/Sequential from RN radix2 #2", + "value": 800844884, + "range": "± 2440366", + "unit": "ns/iter" + }, + { + "name": "Ordered FFT/Sequential from NR radix2 #3", + "value": 1426885051, + "range": "± 2199439", + "unit": "ns/iter" + }, + { + "name": "Ordered FFT/Sequential from RN radix2 #3", + "value": 1685459138, + "range": "± 6215426", + "unit": "ns/iter" + }, + { + "name": "Ordered FFT/Sequential from NR radix4 #2", + "value": 1245741005, + "range": "± 1225876", + "unit": "ns/iter" + }, + { + "name": "Ordered FFT/Sequential from NR radix2 #4", + "value": 2979830104, + "range": "± 2748076", + "unit": "ns/iter" + }, + { + "name": "Ordered FFT/Sequential from RN radix2 #4", + "value": 3512155368, + "range": "± 11844782", + "unit": "ns/iter" + }, + { + "name": "Ordered FFT/Sequential from NR radix2 #5", + "value": 6243942783, + "range": "± 3071006", + "unit": "ns/iter" + }, + { + "name": "Ordered FFT/Sequential from RN radix2 #5", + "value": 7412213765, + "range": "± 12457020", + "unit": "ns/iter" + }, + { + "name": "Ordered FFT/Sequential from NR radix4 #3", + "value": 5459712657, + "range": "± 4943137", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/natural", + "value": 8290321, + "range": "± 4696", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/natural inversed", + "value": 8370760, + "range": "± 3293", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/bit-reversed", + "value": 14170538, + "range": "± 818168", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/bit-reversed inversed", + "value": 13933441, + "range": "± 1034893", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/natural #2", + "value": 19307683, + "range": "± 59690", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/natural inversed #2", + "value": 19274290, + "range": "± 88828", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/bit-reversed #2", + "value": 36390917, + "range": "± 581987", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/bit-reversed inversed #2", + "value": 35852131, + "range": "± 584930", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/natural #3", + "value": 38590353, + "range": "± 97914", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/natural inversed #3", + "value": 38887320, + "range": "± 154665", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/bit-reversed #3", + "value": 74765705, + "range": "± 999177", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/bit-reversed inversed #3", + "value": 75525460, + "range": "± 869899", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/natural #4", + "value": 78438497, + "range": "± 366840", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/natural inversed #4", + "value": 78001815, + "range": "± 258709", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/bit-reversed #4", + "value": 151883276, + "range": "± 708111", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/bit-reversed inversed #4", + "value": 151727353, + "range": "± 1254970", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/natural #5", + "value": 154930578, + "range": "± 417839", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/natural inversed #5", + "value": 154533924, + "range": "± 739885", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/bit-reversed #5", + "value": 300723530, + "range": "± 2506367", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/bit-reversed inversed #5", + "value": 301823566, + "range": "± 1642341", + "unit": "ns/iter" + }, + { + "name": "Bit-reverse permutation/Sequential", + "value": 18338046, + "range": "± 410092", + "unit": "ns/iter" + }, + { + "name": "Bit-reverse permutation/Sequential #2", + "value": 37397722, + "range": "± 478214", + "unit": "ns/iter" + }, + { + "name": "Bit-reverse permutation/Sequential #3", + "value": 74264302, + "range": "± 1059452", + "unit": "ns/iter" + }, + { + "name": "Bit-reverse permutation/Sequential #4", + "value": 150819057, + "range": "± 2524344", + "unit": "ns/iter" + }, + { + "name": "Bit-reverse permutation/Sequential #5", + "value": 381178755, + "range": "± 1610092", + "unit": "ns/iter" + }, + { + "name": "Polynomial evaluation/Sequential FFT", + "value": 367148607, + "range": "± 1102936", + "unit": "ns/iter" + }, + { + "name": "Polynomial evaluation/Sequential FFT #2", + "value": 774064122, + "range": "± 2943004", + "unit": "ns/iter" + }, + { + "name": "Polynomial evaluation/Sequential FFT #3", + "value": 1612194695, + "range": "± 2441785", + "unit": "ns/iter" + }, + { + "name": "Polynomial evaluation/Sequential FFT #4", + "value": 3354505499, + "range": "± 6630940", + "unit": "ns/iter" + }, + { + "name": "Polynomial evaluation/Sequential FFT #5", + "value": 7088948617, + "range": "± 8311963", + "unit": "ns/iter" + }, + { + "name": "Polynomial interpolation/Sequential FFT", + "value": 396038895, + "range": "± 1603365", + "unit": "ns/iter" + }, + { + "name": "Polynomial interpolation/Sequential FFT #2", + "value": 818248907, + "range": "± 2089020", + "unit": "ns/iter" + }, + { + "name": "Polynomial interpolation/Sequential FFT #3", + "value": 1700098707, + "range": "± 4653184", + "unit": "ns/iter" + }, + { + "name": "Polynomial interpolation/Sequential FFT #4", + "value": 3512063378, + "range": "± 10489721", + "unit": "ns/iter" + }, + { + "name": "Polynomial interpolation/Sequential FFT #5", + "value": 7399620535, + "range": "± 8445734", + "unit": "ns/iter" + }, + { + "name": "Polynomial/evaluate", + "value": 49, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/evaluate_slice", + "value": 411, + "range": "± 1", + "unit": "ns/iter" + }, + { + "name": "Polynomial/add", + "value": 106, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/neg", + "value": 62, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/sub", + "value": 178, + "range": "± 1", + "unit": "ns/iter" + }, + { + "name": "Polynomial/mul", + "value": 525, + "range": "± 21", + "unit": "ns/iter" + }, + { + "name": "Polynomial/fast_mul big poly", + "value": 143348, + "range": "± 925", + "unit": "ns/iter" + }, + { + "name": "Polynomial/slow mul big poly", + "value": 1634355, + "range": "± 19974", + "unit": "ns/iter" + }, + { + "name": "Polynomial/div", + "value": 539, + "range": "± 102", + "unit": "ns/iter" + }, + { + "name": "Polynomial/div by 'x - b' with generic div", + "value": 2030, + "range": "± 9", + "unit": "ns/iter" + }, + { + "name": "Polynomial/div by 'x - b' with Ruffini", + "value": 50, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/evaluate #2", + "value": 13, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/evaluate_with", + "value": 14, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/merge", + "value": 48, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/add #2", + "value": 7560, + "range": "± 423", + "unit": "ns/iter" + }, + { + "name": "Polynomial/mul #2", + "value": 31, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/evaluate 3", + "value": 1, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/evaluate 4", + "value": 1, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/evaluate 5", + "value": 1, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/evaluate 6", + "value": 1, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/evaluate 7", + "value": 1, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/evaluate 8", + "value": 1, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/evaluate 9", + "value": 1, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/evaluate 10", + "value": 1, + "range": "± 0", + "unit": "ns/iter" + } + ] + }, + { + "commit": { + "author": { + "email": "76252340+MarcosNicolau@users.noreply.github.com", + "name": "Marcos Nicolau", + "username": "MarcosNicolau" + }, + "committer": { + "email": "noreply@github.com", + "name": "GitHub", + "username": "web-flow" + }, + "distinct": true, + "id": "5f8f2cfcc8a1a22f77e8dff2d581f1166eefb80b", + "message": "refactor: remove default contraint in merkle tree trait (#1004)", + "timestamp": "2025-05-05T17:24:46Z", + "tree_id": "99bb8089f5ac9fb827806203d60745320423ba07", + "url": "https://github.com/lambdaclass/lambdaworks/commit/5f8f2cfcc8a1a22f77e8dff2d581f1166eefb80b" + }, + "date": 1746467642382, + "tool": "cargo", + "benches": [ + { + "name": "Ordered FFT/Sequential from NR radix2", + "value": 322695457, + "range": "± 611075", + "unit": "ns/iter" + }, + { + "name": "Ordered FFT/Sequential from RN radix2", + "value": 379666224, + "range": "± 1708063", + "unit": "ns/iter" + }, + { + "name": "Ordered FFT/Sequential from NR radix4", + "value": 283159377, + "range": "± 280436", + "unit": "ns/iter" + }, + { + "name": "Ordered FFT/Sequential from NR radix2 #2", + "value": 678621383, + "range": "± 1024919", + "unit": "ns/iter" + }, + { + "name": "Ordered FFT/Sequential from RN radix2 #2", + "value": 801010091, + "range": "± 3717688", + "unit": "ns/iter" + }, + { + "name": "Ordered FFT/Sequential from NR radix2 #3", + "value": 1422804968, + "range": "± 1623345", + "unit": "ns/iter" + }, + { + "name": "Ordered FFT/Sequential from RN radix2 #3", + "value": 1685157329, + "range": "± 4821781", + "unit": "ns/iter" + }, + { + "name": "Ordered FFT/Sequential from NR radix4 #2", + "value": 1248506328, + "range": "± 1446382", + "unit": "ns/iter" + }, + { + "name": "Ordered FFT/Sequential from NR radix2 #4", + "value": 2970836960, + "range": "± 4518248", + "unit": "ns/iter" + }, + { + "name": "Ordered FFT/Sequential from RN radix2 #4", + "value": 3511009894, + "range": "± 13818676", + "unit": "ns/iter" + }, + { + "name": "Ordered FFT/Sequential from NR radix2 #5", + "value": 6218981042, + "range": "± 2679661", + "unit": "ns/iter" + }, + { + "name": "Ordered FFT/Sequential from RN radix2 #5", + "value": 7348890928, + "range": "± 45362648", + "unit": "ns/iter" + }, + { + "name": "Ordered FFT/Sequential from NR radix4 #3", + "value": 5471409863, + "range": "± 5267401", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/natural", + "value": 7547554, + "range": "± 3567", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/natural inversed", + "value": 7613059, + "range": "± 17813", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/bit-reversed", + "value": 11110302, + "range": "± 986468", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/bit-reversed inversed", + "value": 10456885, + "range": "± 935224", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/natural #2", + "value": 17720959, + "range": "± 117143", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/natural inversed #2", + "value": 17509571, + "range": "± 60419", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/bit-reversed #2", + "value": 26884118, + "range": "± 3893736", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/bit-reversed inversed #2", + "value": 27320635, + "range": "± 1262458", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/natural #3", + "value": 35282223, + "range": "± 88192", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/natural inversed #3", + "value": 35364523, + "range": "± 263505", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/bit-reversed #3", + "value": 70209580, + "range": "± 2554368", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/bit-reversed inversed #3", + "value": 68429936, + "range": "± 3239328", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/natural #4", + "value": 71839950, + "range": "± 358314", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/natural inversed #4", + "value": 71613864, + "range": "± 166352", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/bit-reversed #4", + "value": 143328198, + "range": "± 2573163", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/bit-reversed inversed #4", + "value": 144476560, + "range": "± 2732008", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/natural #5", + "value": 142982342, + "range": "± 358916", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/natural inversed #5", + "value": 142746211, + "range": "± 204361", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/bit-reversed #5", + "value": 293947899, + "range": "± 5164140", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/bit-reversed inversed #5", + "value": 285371374, + "range": "± 2739342", + "unit": "ns/iter" + }, + { + "name": "Bit-reverse permutation/Sequential", + "value": 15455079, + "range": "± 1100988", + "unit": "ns/iter" + }, + { + "name": "Bit-reverse permutation/Sequential #2", + "value": 34623605, + "range": "± 876922", + "unit": "ns/iter" + }, + { + "name": "Bit-reverse permutation/Sequential #3", + "value": 79039795, + "range": "± 2884411", + "unit": "ns/iter" + }, + { + "name": "Bit-reverse permutation/Sequential #4", + "value": 155956312, + "range": "± 4109799", + "unit": "ns/iter" + }, + { + "name": "Bit-reverse permutation/Sequential #5", + "value": 386660504, + "range": "± 4930568", + "unit": "ns/iter" + }, + { + "name": "Polynomial evaluation/Sequential FFT", + "value": 367288520, + "range": "± 5024959", + "unit": "ns/iter" + }, + { + "name": "Polynomial evaluation/Sequential FFT #2", + "value": 761519817, + "range": "± 4583372", + "unit": "ns/iter" + }, + { + "name": "Polynomial evaluation/Sequential FFT #3", + "value": 1602598212, + "range": "± 5446278", + "unit": "ns/iter" + }, + { + "name": "Polynomial evaluation/Sequential FFT #4", + "value": 3339587740, + "range": "± 9652549", + "unit": "ns/iter" + }, + { + "name": "Polynomial evaluation/Sequential FFT #5", + "value": 7032887949, + "range": "± 15356491", + "unit": "ns/iter" + }, + { + "name": "Polynomial interpolation/Sequential FFT", + "value": 381480913, + "range": "± 1204371", + "unit": "ns/iter" + }, + { + "name": "Polynomial interpolation/Sequential FFT #2", + "value": 800032601, + "range": "± 4976345", + "unit": "ns/iter" + }, + { + "name": "Polynomial interpolation/Sequential FFT #3", + "value": 1679026410, + "range": "± 3301481", + "unit": "ns/iter" + }, + { + "name": "Polynomial interpolation/Sequential FFT #4", + "value": 3498004477, + "range": "± 10242867", + "unit": "ns/iter" + }, + { + "name": "Polynomial interpolation/Sequential FFT #5", + "value": 7363066169, + "range": "± 23772139", + "unit": "ns/iter" + }, + { + "name": "Polynomial/evaluate", + "value": 260, + "range": "± 2", + "unit": "ns/iter" + }, + { + "name": "Polynomial/evaluate_slice", + "value": 8397, + "range": "± 19", + "unit": "ns/iter" + }, + { + "name": "Polynomial/add", + "value": 288, + "range": "± 1", + "unit": "ns/iter" + }, + { + "name": "Polynomial/neg", + "value": 174, + "range": "± 1", + "unit": "ns/iter" + }, + { + "name": "Polynomial/sub", + "value": 410, + "range": "± 4", + "unit": "ns/iter" + }, + { + "name": "Polynomial/mul", + "value": 7117, + "range": "± 24", + "unit": "ns/iter" + }, + { + "name": "Polynomial/fast_mul big poly", + "value": 142690, + "range": "± 370", + "unit": "ns/iter" + }, + { + "name": "Polynomial/slow mul big poly", + "value": 1643673, + "range": "± 9220", + "unit": "ns/iter" + }, + { + "name": "Polynomial/div", + "value": 1297, + "range": "± 1151", + "unit": "ns/iter" + }, + { + "name": "Polynomial/div by 'x - b' with generic div", + "value": 22317, + "range": "± 451", + "unit": "ns/iter" + }, + { + "name": "Polynomial/div by 'x - b' with Ruffini", + "value": 266, + "range": "± 1", + "unit": "ns/iter" + }, + { + "name": "Polynomial/evaluate #2", + "value": 13, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/evaluate_with", + "value": 76, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/merge", + "value": 86, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/add #2", + "value": 10357, + "range": "± 1017", + "unit": "ns/iter" + }, + { + "name": "Polynomial/mul #2", + "value": 47, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/evaluate 3", + "value": 1, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/evaluate 4", + "value": 1, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/evaluate 5", + "value": 1, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/evaluate 6", + "value": 1, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/evaluate 7", + "value": 1, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/evaluate 8", + "value": 1, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/evaluate 9", + "value": 1, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/evaluate 10", + "value": 1, + "range": "± 0", + "unit": "ns/iter" + } + ] + }, + { + "commit": { + "author": { + "email": "45471455+jotabulacios@users.noreply.github.com", + "name": "jotabulacios", + "username": "jotabulacios" + }, + "committer": { + "email": "noreply@github.com", + "name": "GitHub", + "username": "web-flow" + }, + "distinct": true, + "id": "c9d02b585f9dafee732eb6f2e30b7028b546e79e", + "message": "Add big uint conversion (#1002)\n\n* first iteration to add BigUint conversion\n\n* remove comments\n\n* refactor\n\n* refactor\n\n* save work. tests working for different fields\n\n* small refactor\n\n* conversion not working for baby bear u32\n\n* all tests working for every field\n\n* remove comments in u64_prime_field file\n\n* remove commented code\n\n* fix cargo check no-default-features\n\n* remove changes in unsigned_integer\n\n* fix ensure-no-std\n\n* remove Ok and unwrap\n\n* fix ci set up job\n\n* fix compile error\n\n* add BN254 conversion test\n\n* change test for bn254\n\n---------\n\nCo-authored-by: Nicole \nCo-authored-by: Nicole \nCo-authored-by: Diego K <43053772+diegokingston@users.noreply.github.com>", + "timestamp": "2025-05-09T17:05:53-03:00", + "tree_id": "411624d8e8ce1278066bf25ab94b6ec368be30ab", + "url": "https://github.com/lambdaclass/lambdaworks/commit/c9d02b585f9dafee732eb6f2e30b7028b546e79e" + }, + "date": 1746822596607, + "tool": "cargo", + "benches": [ + { + "name": "Ordered FFT/Sequential from NR radix2", + "value": 323451059, + "range": "± 268299", + "unit": "ns/iter" + }, + { + "name": "Ordered FFT/Sequential from RN radix2", + "value": 376807613, + "range": "± 1313548", + "unit": "ns/iter" + }, + { + "name": "Ordered FFT/Sequential from NR radix4", + "value": 282823402, + "range": "± 4229959", + "unit": "ns/iter" + }, + { + "name": "Ordered FFT/Sequential from NR radix2 #2", + "value": 679629634, + "range": "± 388299", + "unit": "ns/iter" + }, + { + "name": "Ordered FFT/Sequential from RN radix2 #2", + "value": 793103466, + "range": "± 1689426", + "unit": "ns/iter" + }, + { + "name": "Ordered FFT/Sequential from NR radix2 #3", + "value": 1423896294, + "range": "± 4353360", + "unit": "ns/iter" + }, + { + "name": "Ordered FFT/Sequential from RN radix2 #3", + "value": 1667357554, + "range": "± 2933823", + "unit": "ns/iter" + }, + { + "name": "Ordered FFT/Sequential from NR radix4 #2", + "value": 1243888081, + "range": "± 1182913", + "unit": "ns/iter" + }, + { + "name": "Ordered FFT/Sequential from NR radix2 #4", + "value": 2981132872, + "range": "± 14664835", + "unit": "ns/iter" + }, + { + "name": "Ordered FFT/Sequential from RN radix2 #4", + "value": 3491020927, + "range": "± 12755257", + "unit": "ns/iter" + }, + { + "name": "Ordered FFT/Sequential from NR radix2 #5", + "value": 6237050535, + "range": "± 3669578", + "unit": "ns/iter" + }, + { + "name": "Ordered FFT/Sequential from RN radix2 #5", + "value": 7378530124, + "range": "± 8954215", + "unit": "ns/iter" + }, + { + "name": "Ordered FFT/Sequential from NR radix4 #3", + "value": 5455713687, + "range": "± 3948262", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/natural", + "value": 8283673, + "range": "± 12800", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/natural inversed", + "value": 8365016, + "range": "± 10269", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/bit-reversed", + "value": 10708471, + "range": "± 263766", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/bit-reversed inversed", + "value": 10559195, + "range": "± 84566", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/natural #2", + "value": 18851283, + "range": "± 78314", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/natural inversed #2", + "value": 18851605, + "range": "± 91511", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/bit-reversed #2", + "value": 29964823, + "range": "± 1297784", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/bit-reversed inversed #2", + "value": 29475991, + "range": "± 1098928", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/natural #3", + "value": 37847179, + "range": "± 78574", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/natural inversed #3", + "value": 38543573, + "range": "± 123975", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/bit-reversed #3", + "value": 70124996, + "range": "± 889107", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/bit-reversed inversed #3", + "value": 71117875, + "range": "± 1337717", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/natural #4", + "value": 78066603, + "range": "± 191719", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/natural inversed #4", + "value": 77446273, + "range": "± 103521", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/bit-reversed #4", + "value": 147490149, + "range": "± 1618457", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/bit-reversed inversed #4", + "value": 148394439, + "range": "± 867443", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/natural #5", + "value": 153641567, + "range": "± 404252", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/natural inversed #5", + "value": 152971383, + "range": "± 191232", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/bit-reversed #5", + "value": 294984697, + "range": "± 1790160", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/bit-reversed inversed #5", + "value": 295303295, + "range": "± 1052874", + "unit": "ns/iter" + }, + { + "name": "Bit-reverse permutation/Sequential", + "value": 16497744, + "range": "± 258775", + "unit": "ns/iter" + }, + { + "name": "Bit-reverse permutation/Sequential #2", + "value": 35447765, + "range": "± 817360", + "unit": "ns/iter" + }, + { + "name": "Bit-reverse permutation/Sequential #3", + "value": 71884891, + "range": "± 759761", + "unit": "ns/iter" + }, + { + "name": "Bit-reverse permutation/Sequential #4", + "value": 145665541, + "range": "± 2061222", + "unit": "ns/iter" + }, + { + "name": "Bit-reverse permutation/Sequential #5", + "value": 362358587, + "range": "± 3427299", + "unit": "ns/iter" + }, + { + "name": "Polynomial evaluation/Sequential FFT", + "value": 357871222, + "range": "± 1944202", + "unit": "ns/iter" + }, + { + "name": "Polynomial evaluation/Sequential FFT #2", + "value": 762395212, + "range": "± 1794992", + "unit": "ns/iter" + }, + { + "name": "Polynomial evaluation/Sequential FFT #3", + "value": 1598158132, + "range": "± 2698045", + "unit": "ns/iter" + }, + { + "name": "Polynomial evaluation/Sequential FFT #4", + "value": 3342636358, + "range": "± 6681481", + "unit": "ns/iter" + }, + { + "name": "Polynomial evaluation/Sequential FFT #5", + "value": 7032456476, + "range": "± 8214333", + "unit": "ns/iter" + }, + { + "name": "Polynomial interpolation/Sequential FFT", + "value": 384676977, + "range": "± 1797321", + "unit": "ns/iter" + }, + { + "name": "Polynomial interpolation/Sequential FFT #2", + "value": 800995608, + "range": "± 2764245", + "unit": "ns/iter" + }, + { + "name": "Polynomial interpolation/Sequential FFT #3", + "value": 1684298581, + "range": "± 3068358", + "unit": "ns/iter" + }, + { + "name": "Polynomial interpolation/Sequential FFT #4", + "value": 3480717808, + "range": "± 3908788", + "unit": "ns/iter" + }, + { + "name": "Polynomial interpolation/Sequential FFT #5", + "value": 7347177147, + "range": "± 15737995", + "unit": "ns/iter" + }, + { + "name": "Polynomial/evaluate", + "value": 114, + "range": "± 1", + "unit": "ns/iter" + }, + { + "name": "Polynomial/evaluate_slice", + "value": 1785, + "range": "± 3", + "unit": "ns/iter" + }, + { + "name": "Polynomial/add", + "value": 189, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/neg", + "value": 103, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/sub", + "value": 306, + "range": "± 3", + "unit": "ns/iter" + }, + { + "name": "Polynomial/mul", + "value": 1664, + "range": "± 9", + "unit": "ns/iter" + }, + { + "name": "Polynomial/fast_mul big poly", + "value": 143733, + "range": "± 348", + "unit": "ns/iter" + }, + { + "name": "Polynomial/slow mul big poly", + "value": 1634228, + "range": "± 13567", + "unit": "ns/iter" + }, + { + "name": "Polynomial/div", + "value": 838, + "range": "± 11", + "unit": "ns/iter" + }, + { + "name": "Polynomial/div by 'x - b' with generic div", + "value": 7191, + "range": "± 70", + "unit": "ns/iter" + }, + { + "name": "Polynomial/div by 'x - b' with Ruffini", + "value": 119, + "range": "± 1", + "unit": "ns/iter" + }, + { + "name": "Polynomial/evaluate #2", + "value": 16, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/evaluate_with", + "value": 5, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/merge", + "value": 133, + "range": "± 1", + "unit": "ns/iter" + }, + { + "name": "Polynomial/add #2", + "value": 11765, + "range": "± 1220", + "unit": "ns/iter" + }, + { + "name": "Polynomial/mul #2", + "value": 192, + "range": "± 3", + "unit": "ns/iter" + }, + { + "name": "Polynomial/evaluate 3", + "value": 1, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/evaluate 4", + "value": 1, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/evaluate 5", + "value": 1, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/evaluate 6", + "value": 1, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/evaluate 7", + "value": 1, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/evaluate 8", + "value": 1, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/evaluate 9", + "value": 1, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/evaluate 10", + "value": 1, + "range": "± 0", + "unit": "ns/iter" + } + ] + }, + { + "commit": { + "author": { + "email": "70894690+0xLucqs@users.noreply.github.com", + "name": "0xLucqs", + "username": "0xLucqs" + }, + "committer": { + "email": "noreply@github.com", + "name": "GitHub", + "username": "web-flow" + }, + "distinct": true, + "id": "3a04406abfa059bd5688dd0622188d516b9207bd", + "message": "Feat/fast division (#1001)\n\n* feat(fft): add fast multiplication\n\n* chore: add fast_mul benchmark\n\n* impl suggestion\n\n* chore: impl suggestion\n\n* chore: add fast_mul benchmark\n\n* feat(fft): fast division\n\n* refacto: proper error handling\n\n* refacto: rename inversion function\n\n* test: add tests for fast division helper functions\n\n* doc: explain where fast division is taken from\n\n---------\n\nCo-authored-by: Diego K <43053772+diegokingston@users.noreply.github.com>", + "timestamp": "2025-05-15T11:21:27-03:00", + "tree_id": "a401827c78813d6302976cb93c864f92ff017538", + "url": "https://github.com/lambdaclass/lambdaworks/commit/3a04406abfa059bd5688dd0622188d516b9207bd" + }, + "date": 1747320346474, + "tool": "cargo", + "benches": [ + { + "name": "Ordered FFT/Sequential from NR radix2", + "value": 323651543, + "range": "± 1347806", + "unit": "ns/iter" + }, + { + "name": "Ordered FFT/Sequential from RN radix2", + "value": 377760438, + "range": "± 5438045", + "unit": "ns/iter" + }, + { + "name": "Ordered FFT/Sequential from NR radix4", + "value": 281258420, + "range": "± 414580", + "unit": "ns/iter" + }, + { + "name": "Ordered FFT/Sequential from NR radix2 #2", + "value": 680871017, + "range": "± 1593472", + "unit": "ns/iter" + }, + { + "name": "Ordered FFT/Sequential from RN radix2 #2", + "value": 794346637, + "range": "± 3680509", + "unit": "ns/iter" + }, + { + "name": "Ordered FFT/Sequential from NR radix2 #3", + "value": 1426878837, + "range": "± 2642264", + "unit": "ns/iter" + }, + { + "name": "Ordered FFT/Sequential from RN radix2 #3", + "value": 1664871175, + "range": "± 7061273", + "unit": "ns/iter" + }, + { + "name": "Ordered FFT/Sequential from NR radix4 #2", + "value": 1234675450, + "range": "± 781125", + "unit": "ns/iter" + }, + { + "name": "Ordered FFT/Sequential from NR radix2 #4", + "value": 2980746943, + "range": "± 2749213", + "unit": "ns/iter" + }, + { + "name": "Ordered FFT/Sequential from RN radix2 #4", + "value": 3472252181, + "range": "± 15622923", + "unit": "ns/iter" + }, + { + "name": "Ordered FFT/Sequential from NR radix2 #5", + "value": 6241157860, + "range": "± 6483124", + "unit": "ns/iter" + }, + { + "name": "Ordered FFT/Sequential from RN radix2 #5", + "value": 7328442952, + "range": "± 21082462", + "unit": "ns/iter" + }, + { + "name": "Ordered FFT/Sequential from NR radix4 #3", + "value": 5418122047, + "range": "± 3827502", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/natural", + "value": 8294349, + "range": "± 10955", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/natural inversed", + "value": 8324635, + "range": "± 7575", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/bit-reversed", + "value": 10548718, + "range": "± 565785", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/bit-reversed inversed", + "value": 10565931, + "range": "± 85847", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/natural #2", + "value": 19237325, + "range": "± 99834", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/natural inversed #2", + "value": 19090906, + "range": "± 87802", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/bit-reversed #2", + "value": 30147093, + "range": "± 889764", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/bit-reversed inversed #2", + "value": 29569539, + "range": "± 698940", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/natural #3", + "value": 38045423, + "range": "± 412428", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/natural inversed #3", + "value": 38700087, + "range": "± 111030", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/bit-reversed #3", + "value": 70792684, + "range": "± 1858007", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/bit-reversed inversed #3", + "value": 71763221, + "range": "± 1104188", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/natural #4", + "value": 77901976, + "range": "± 149738", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/natural inversed #4", + "value": 77577288, + "range": "± 70800", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/bit-reversed #4", + "value": 151523400, + "range": "± 2341193", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/bit-reversed inversed #4", + "value": 151679560, + "range": "± 5131143", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/natural #5", + "value": 153066959, + "range": "± 328225", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/natural inversed #5", + "value": 152323824, + "range": "± 158579", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/bit-reversed #5", + "value": 298841401, + "range": "± 3845369", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/bit-reversed inversed #5", + "value": 297299902, + "range": "± 4046873", + "unit": "ns/iter" + }, + { + "name": "Bit-reverse permutation/Sequential", + "value": 16843391, + "range": "± 371024", + "unit": "ns/iter" + }, + { + "name": "Bit-reverse permutation/Sequential #2", + "value": 36288315, + "range": "± 661660", + "unit": "ns/iter" + }, + { + "name": "Bit-reverse permutation/Sequential #3", + "value": 73578024, + "range": "± 1583633", + "unit": "ns/iter" + }, + { + "name": "Bit-reverse permutation/Sequential #4", + "value": 151147337, + "range": "± 3069945", + "unit": "ns/iter" + }, + { + "name": "Bit-reverse permutation/Sequential #5", + "value": 384451913, + "range": "± 2802015", + "unit": "ns/iter" + }, + { + "name": "Polynomial evaluation/Sequential FFT", + "value": 360296155, + "range": "± 2186425", + "unit": "ns/iter" + }, + { + "name": "Polynomial evaluation/Sequential FFT #2", + "value": 764778711, + "range": "± 2073004", + "unit": "ns/iter" + }, + { + "name": "Polynomial evaluation/Sequential FFT #3", + "value": 1607611338, + "range": "± 3010162", + "unit": "ns/iter" + }, + { + "name": "Polynomial evaluation/Sequential FFT #4", + "value": 3352624023, + "range": "± 15148626", + "unit": "ns/iter" + }, + { + "name": "Polynomial evaluation/Sequential FFT #5", + "value": 7078324717, + "range": "± 15005930", + "unit": "ns/iter" + }, + { + "name": "Polynomial interpolation/Sequential FFT", + "value": 385570603, + "range": "± 1541553", + "unit": "ns/iter" + }, + { + "name": "Polynomial interpolation/Sequential FFT #2", + "value": 805398869, + "range": "± 7281180", + "unit": "ns/iter" + }, + { + "name": "Polynomial interpolation/Sequential FFT #3", + "value": 1693593662, + "range": "± 3103706", + "unit": "ns/iter" + }, + { + "name": "Polynomial interpolation/Sequential FFT #4", + "value": 3510503667, + "range": "± 14395872", + "unit": "ns/iter" + }, + { + "name": "Polynomial interpolation/Sequential FFT #5", + "value": 7377033939, + "range": "± 11796395", + "unit": "ns/iter" + }, + { + "name": "Polynomial/evaluate", + "value": 117, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/evaluate_slice", + "value": 1903, + "range": "± 4", + "unit": "ns/iter" + }, + { + "name": "Polynomial/add", + "value": 178, + "range": "± 3", + "unit": "ns/iter" + }, + { + "name": "Polynomial/neg", + "value": 116, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/sub", + "value": 301, + "range": "± 3", + "unit": "ns/iter" + }, + { + "name": "Polynomial/mul", + "value": 1642, + "range": "± 6", + "unit": "ns/iter" + }, + { + "name": "Polynomial/fast_mul big poly", + "value": 144007, + "range": "± 403", + "unit": "ns/iter" + }, + { + "name": "Polynomial/slow mul big poly", + "value": 1571401, + "range": "± 99375", + "unit": "ns/iter" + }, + { + "name": "Polynomial/fast div big poly", + "value": 810686, + "range": "± 5528", + "unit": "ns/iter" + }, + { + "name": "Polynomial/slow div big poly", + "value": 1639385, + "range": "± 24018", + "unit": "ns/iter" + }, + { + "name": "Polynomial/div", + "value": 872, + "range": "± 378", + "unit": "ns/iter" + }, + { + "name": "Polynomial/div by 'x - b' with generic div", + "value": 7335, + "range": "± 117", + "unit": "ns/iter" + }, + { + "name": "Polynomial/div by 'x - b' with Ruffini", + "value": 121, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/evaluate #2", + "value": 16, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/evaluate_with", + "value": 5, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/merge", + "value": 128, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/add #2", + "value": 11837, + "range": "± 1418", + "unit": "ns/iter" + }, + { + "name": "Polynomial/mul #2", + "value": 185, + "range": "± 3", + "unit": "ns/iter" + }, + { + "name": "Polynomial/evaluate 3", + "value": 1, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/evaluate 4", + "value": 1, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/evaluate 5", + "value": 1, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/evaluate 6", + "value": 1, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/evaluate 7", + "value": 1, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/evaluate 8", + "value": 1, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/evaluate 9", + "value": 1, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/evaluate 10", + "value": 1, + "range": "± 0", + "unit": "ns/iter" + } + ] + }, + { + "commit": { + "author": { + "email": "45471455+jotabulacios@users.noreply.github.com", + "name": "jotabulacios", + "username": "jotabulacios" + }, + "committer": { + "email": "noreply@github.com", + "name": "GitHub", + "username": "web-flow" + }, + "distinct": true, + "id": "055dc6f600786dd49f4ff49383fb94ff5f629992", + "message": "Add quadratic and cubic sumcheck (#1003)\n\n* first commit, add quadratic and cubic\n\n* fix quadratic and cubic sumcheck implementation\n\n* refactor code to use wrappers\n\n* save work\n\n* small refactor\n\n* implement suggestion\n\n* add number of factors and number of variables to transcript\n\n* fix clippy\n\n* fix clippy\n\n---------\n\nCo-authored-by: Diego K <43053772+diegokingston@users.noreply.github.com>", + "timestamp": "2025-05-23T21:05:22Z", + "tree_id": "d7ece2de75dabfc66391f1d65ae7ac0e5f588916", + "url": "https://github.com/lambdaclass/lambdaworks/commit/055dc6f600786dd49f4ff49383fb94ff5f629992" + }, + "date": 1748036131031, + "tool": "cargo", + "benches": [ + { + "name": "Ordered FFT/Sequential from NR radix2", + "value": 326496974, + "range": "± 438535", + "unit": "ns/iter" + }, + { + "name": "Ordered FFT/Sequential from RN radix2", + "value": 370473084, + "range": "± 2057600", + "unit": "ns/iter" + }, + { + "name": "Ordered FFT/Sequential from NR radix4", + "value": 279833211, + "range": "± 249176", + "unit": "ns/iter" + }, + { + "name": "Ordered FFT/Sequential from NR radix2 #2", + "value": 685084167, + "range": "± 1356756", + "unit": "ns/iter" + }, + { + "name": "Ordered FFT/Sequential from RN radix2 #2", + "value": 778619601, + "range": "± 1773755", + "unit": "ns/iter" + }, + { + "name": "Ordered FFT/Sequential from NR radix2 #3", + "value": 1436109821, + "range": "± 750294", + "unit": "ns/iter" + }, + { + "name": "Ordered FFT/Sequential from RN radix2 #3", + "value": 1642109086, + "range": "± 5425699", + "unit": "ns/iter" + }, + { + "name": "Ordered FFT/Sequential from NR radix4 #2", + "value": 1230921589, + "range": "± 6874242", + "unit": "ns/iter" + }, + { + "name": "Ordered FFT/Sequential from NR radix2 #4", + "value": 3003171093, + "range": "± 3646826", + "unit": "ns/iter" + }, + { + "name": "Ordered FFT/Sequential from RN radix2 #4", + "value": 3434759053, + "range": "± 11654593", + "unit": "ns/iter" + }, + { + "name": "Ordered FFT/Sequential from NR radix2 #5", + "value": 6281247560, + "range": "± 6053618", + "unit": "ns/iter" + }, + { + "name": "Ordered FFT/Sequential from RN radix2 #5", + "value": 7255101011, + "range": "± 31926338", + "unit": "ns/iter" + }, + { + "name": "Ordered FFT/Sequential from NR radix4 #3", + "value": 5391957695, + "range": "± 2193662", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/natural", + "value": 7982502, + "range": "± 9125", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/natural inversed", + "value": 7988857, + "range": "± 6945", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/bit-reversed", + "value": 10760900, + "range": "± 461432", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/bit-reversed inversed", + "value": 10239695, + "range": "± 62862", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/natural #2", + "value": 18148284, + "range": "± 77625", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/natural inversed #2", + "value": 18180202, + "range": "± 48963", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/bit-reversed #2", + "value": 27906144, + "range": "± 1798682", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/bit-reversed inversed #2", + "value": 27001470, + "range": "± 509727", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/natural #3", + "value": 37256775, + "range": "± 532440", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/natural inversed #3", + "value": 37008792, + "range": "± 143783", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/bit-reversed #3", + "value": 67117391, + "range": "± 544298", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/bit-reversed inversed #3", + "value": 67528725, + "range": "± 1092948", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/natural #4", + "value": 74617386, + "range": "± 140251", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/natural inversed #4", + "value": 74609511, + "range": "± 300154", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/bit-reversed #4", + "value": 140176752, + "range": "± 1681715", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/bit-reversed inversed #4", + "value": 142683585, + "range": "± 3404708", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/natural #5", + "value": 151740687, + "range": "± 159612", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/natural inversed #5", + "value": 151544508, + "range": "± 207066", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/bit-reversed #5", + "value": 293685774, + "range": "± 4258666", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/bit-reversed inversed #5", + "value": 289174893, + "range": "± 3285835", + "unit": "ns/iter" + }, + { + "name": "Bit-reverse permutation/Sequential", + "value": 15739747, + "range": "± 294617", + "unit": "ns/iter" + }, + { + "name": "Bit-reverse permutation/Sequential #2", + "value": 33651440, + "range": "± 643980", + "unit": "ns/iter" + }, + { + "name": "Bit-reverse permutation/Sequential #3", + "value": 68485500, + "range": "± 962095", + "unit": "ns/iter" + }, + { + "name": "Bit-reverse permutation/Sequential #4", + "value": 136081483, + "range": "± 1168278", + "unit": "ns/iter" + }, + { + "name": "Bit-reverse permutation/Sequential #5", + "value": 357297181, + "range": "± 4718925", + "unit": "ns/iter" + }, + { + "name": "Polynomial evaluation/Sequential FFT", + "value": 356095104, + "range": "± 926830", + "unit": "ns/iter" + }, + { + "name": "Polynomial evaluation/Sequential FFT #2", + "value": 763605224, + "range": "± 2668117", + "unit": "ns/iter" + }, + { + "name": "Polynomial evaluation/Sequential FFT #3", + "value": 1608294675, + "range": "± 2081607", + "unit": "ns/iter" + }, + { + "name": "Polynomial evaluation/Sequential FFT #4", + "value": 3349213751, + "range": "± 5320388", + "unit": "ns/iter" + }, + { + "name": "Polynomial evaluation/Sequential FFT #5", + "value": 7084137048, + "range": "± 8029157", + "unit": "ns/iter" + }, + { + "name": "Polynomial interpolation/Sequential FFT", + "value": 389087602, + "range": "± 1085388", + "unit": "ns/iter" + }, + { + "name": "Polynomial interpolation/Sequential FFT #2", + "value": 808083947, + "range": "± 1913828", + "unit": "ns/iter" + }, + { + "name": "Polynomial interpolation/Sequential FFT #3", + "value": 1697281784, + "range": "± 6104393", + "unit": "ns/iter" + }, + { + "name": "Polynomial interpolation/Sequential FFT #4", + "value": 3509655096, + "range": "± 6328631", + "unit": "ns/iter" + }, + { + "name": "Polynomial interpolation/Sequential FFT #5", + "value": 7393626168, + "range": "± 17853875", + "unit": "ns/iter" + }, + { + "name": "Polynomial/evaluate", + "value": 115, + "range": "± 1", + "unit": "ns/iter" + }, + { + "name": "Polynomial/evaluate_slice", + "value": 1840, + "range": "± 21", + "unit": "ns/iter" + }, + { + "name": "Polynomial/add", + "value": 173, + "range": "± 1", + "unit": "ns/iter" + }, + { + "name": "Polynomial/neg", + "value": 104, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/sub", + "value": 311, + "range": "± 2", + "unit": "ns/iter" + }, + { + "name": "Polynomial/mul", + "value": 1570, + "range": "± 192", + "unit": "ns/iter" + }, + { + "name": "Polynomial/fast_mul big poly", + "value": 144982, + "range": "± 1136", + "unit": "ns/iter" + }, + { + "name": "Polynomial/slow mul big poly", + "value": 1547321, + "range": "± 5389", + "unit": "ns/iter" + }, + { + "name": "Polynomial/fast div big poly", + "value": 815367, + "range": "± 1753", + "unit": "ns/iter" + }, + { + "name": "Polynomial/slow div big poly", + "value": 1672401, + "range": "± 8214", + "unit": "ns/iter" + }, + { + "name": "Polynomial/div", + "value": 807, + "range": "± 14", + "unit": "ns/iter" + }, + { + "name": "Polynomial/div by 'x - b' with generic div", + "value": 7399, + "range": "± 233", + "unit": "ns/iter" + }, + { + "name": "Polynomial/div by 'x - b' with Ruffini", + "value": 119, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/evaluate #2", + "value": 13, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/evaluate_with", + "value": 14, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/merge", + "value": 48, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/add #2", + "value": 6863, + "range": "± 695", + "unit": "ns/iter" + }, + { + "name": "Polynomial/mul #2", + "value": 31, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/evaluate 3", + "value": 1, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/evaluate 4", + "value": 1, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/evaluate 5", + "value": 1, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/evaluate 6", + "value": 1, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/evaluate 7", + "value": 1, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/evaluate 8", + "value": 1, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/evaluate 9", + "value": 1, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/evaluate 10", + "value": 1, + "range": "± 0", + "unit": "ns/iter" + } + ] + }, + { + "commit": { + "author": { + "email": "45471455+jotabulacios@users.noreply.github.com", + "name": "jotabulacios", + "username": "jotabulacios" + }, + "committer": { + "email": "noreply@github.com", + "name": "GitHub", + "username": "web-flow" + }, + "distinct": true, + "id": "ad2a380641758cfbbe2de6f4a2d8e27684f0fed7", + "message": "fix clippy for new rust version (#1013)", + "timestamp": "2025-08-01T21:05:21Z", + "tree_id": "f84f62fc85c1e1ea5772038679bacf93f88bdb68", + "url": "https://github.com/lambdaclass/lambdaworks/commit/ad2a380641758cfbbe2de6f4a2d8e27684f0fed7" + }, + "date": 1754084121312, + "tool": "cargo", + "benches": [ + { + "name": "Ordered FFT/Sequential from NR radix2", + "value": 326255928, + "range": "± 939803", + "unit": "ns/iter" + }, + { + "name": "Ordered FFT/Sequential from RN radix2", + "value": 369905715, + "range": "± 594165", + "unit": "ns/iter" + }, + { + "name": "Ordered FFT/Sequential from NR radix4", + "value": 279918677, + "range": "± 517863", + "unit": "ns/iter" + }, + { + "name": "Ordered FFT/Sequential from NR radix2 #2", + "value": 685061449, + "range": "± 401403", + "unit": "ns/iter" + }, + { + "name": "Ordered FFT/Sequential from RN radix2 #2", + "value": 784183788, + "range": "± 1889981", + "unit": "ns/iter" + }, + { + "name": "Ordered FFT/Sequential from NR radix2 #3", + "value": 1435415520, + "range": "± 471741", + "unit": "ns/iter" + }, + { + "name": "Ordered FFT/Sequential from RN radix2 #3", + "value": 1652110374, + "range": "± 10000424", + "unit": "ns/iter" + }, + { + "name": "Ordered FFT/Sequential from NR radix4 #2", + "value": 1229965637, + "range": "± 637220", + "unit": "ns/iter" + }, + { + "name": "Ordered FFT/Sequential from NR radix2 #4", + "value": 3000409988, + "range": "± 1215925", + "unit": "ns/iter" + }, + { + "name": "Ordered FFT/Sequential from RN radix2 #4", + "value": 3462072077, + "range": "± 23803410", + "unit": "ns/iter" + }, + { + "name": "Ordered FFT/Sequential from NR radix2 #5", + "value": 6282392621, + "range": "± 3316907", + "unit": "ns/iter" + }, + { + "name": "Ordered FFT/Sequential from RN radix2 #5", + "value": 7302940268, + "range": "± 51380774", + "unit": "ns/iter" + }, + { + "name": "Ordered FFT/Sequential from NR radix4 #3", + "value": 5401374650, + "range": "± 1015956", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/natural", + "value": 7984778, + "range": "± 5238", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/natural inversed", + "value": 8002408, + "range": "± 8754", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/bit-reversed", + "value": 10117812, + "range": "± 143285", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/bit-reversed inversed", + "value": 10105146, + "range": "± 171507", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/natural #2", + "value": 18284352, + "range": "± 43257", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/natural inversed #2", + "value": 18263368, + "range": "± 126630", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/bit-reversed #2", + "value": 28601307, + "range": "± 676882", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/bit-reversed inversed #2", + "value": 28027605, + "range": "± 549105", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/natural #3", + "value": 37646908, + "range": "± 104867", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/natural inversed #3", + "value": 37105755, + "range": "± 74237", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/bit-reversed #3", + "value": 73362296, + "range": "± 2625624", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/bit-reversed inversed #3", + "value": 70230220, + "range": "± 2546897", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/natural #4", + "value": 74917747, + "range": "± 221190", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/natural inversed #4", + "value": 74634070, + "range": "± 124648", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/bit-reversed #4", + "value": 144976371, + "range": "± 1219601", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/bit-reversed inversed #4", + "value": 144467825, + "range": "± 1143031", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/natural #5", + "value": 153115114, + "range": "± 660368", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/natural inversed #5", + "value": 153397623, + "range": "± 1002267", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/bit-reversed #5", + "value": 294342794, + "range": "± 1591177", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/bit-reversed inversed #5", + "value": 297526550, + "range": "± 3486689", + "unit": "ns/iter" + }, + { + "name": "Bit-reverse permutation/Sequential", + "value": 16776380, + "range": "± 143093", + "unit": "ns/iter" + }, + { + "name": "Bit-reverse permutation/Sequential #2", + "value": 35856901, + "range": "± 441807", + "unit": "ns/iter" + }, + { + "name": "Bit-reverse permutation/Sequential #3", + "value": 72059970, + "range": "± 1421281", + "unit": "ns/iter" + }, + { + "name": "Bit-reverse permutation/Sequential #4", + "value": 140824082, + "range": "± 890820", + "unit": "ns/iter" + }, + { + "name": "Bit-reverse permutation/Sequential #5", + "value": 368940861, + "range": "± 2559109", + "unit": "ns/iter" + }, + { + "name": "Polynomial evaluation/Sequential FFT", + "value": 359683099, + "range": "± 1662306", + "unit": "ns/iter" + }, + { + "name": "Polynomial evaluation/Sequential FFT #2", + "value": 765859213, + "range": "± 1812981", + "unit": "ns/iter" + }, + { + "name": "Polynomial evaluation/Sequential FFT #3", + "value": 1610462797, + "range": "± 2047693", + "unit": "ns/iter" + }, + { + "name": "Polynomial evaluation/Sequential FFT #4", + "value": 3353514823, + "range": "± 1999644", + "unit": "ns/iter" + }, + { + "name": "Polynomial evaluation/Sequential FFT #5", + "value": 7084306723, + "range": "± 4945241", + "unit": "ns/iter" + }, + { + "name": "Polynomial interpolation/Sequential FFT", + "value": 386847105, + "range": "± 4092238", + "unit": "ns/iter" + }, + { + "name": "Polynomial interpolation/Sequential FFT #2", + "value": 808214464, + "range": "± 2599220", + "unit": "ns/iter" + }, + { + "name": "Polynomial interpolation/Sequential FFT #3", + "value": 1700979431, + "range": "± 7115410", + "unit": "ns/iter" + }, + { + "name": "Polynomial interpolation/Sequential FFT #4", + "value": 3550286267, + "range": "± 10650763", + "unit": "ns/iter" + }, + { + "name": "Polynomial interpolation/Sequential FFT #5", + "value": 7463239684, + "range": "± 28967920", + "unit": "ns/iter" + }, + { + "name": "Polynomial/evaluate", + "value": 12, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/evaluate_slice", + "value": 31, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/add", + "value": 57, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/neg", + "value": 27, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/sub", + "value": 85, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/mul", + "value": 53, + "range": "± 1", + "unit": "ns/iter" + }, + { + "name": "Polynomial/fast_mul big poly", + "value": 145307, + "range": "± 729", + "unit": "ns/iter" + }, + { + "name": "Polynomial/slow mul big poly", + "value": 1545362, + "range": "± 7383", + "unit": "ns/iter" + }, + { + "name": "Polynomial/fast div big poly", + "value": 816282, + "range": "± 2472", + "unit": "ns/iter" + }, + { + "name": "Polynomial/slow div big poly", + "value": 1744462, + "range": "± 15641", + "unit": "ns/iter" + }, + { + "name": "Polynomial/div", + "value": 293, + "range": "± 22", + "unit": "ns/iter" + }, + { + "name": "Polynomial/div by 'x - b' with generic div", + "value": 291, + "range": "± 3", + "unit": "ns/iter" + }, + { + "name": "Polynomial/div by 'x - b' with Ruffini", + "value": 10, + "range": "± 36", + "unit": "ns/iter" + }, + { + "name": "Polynomial/evaluate #2", + "value": 13, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/evaluate_with", + "value": 10408, + "range": "± 30", + "unit": "ns/iter" + }, + { + "name": "Polynomial/merge", + "value": 90, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/add #2", + "value": 10176, + "range": "± 477", + "unit": "ns/iter" + }, + { + "name": "Polynomial/mul #2", + "value": 69, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/evaluate 3", + "value": 1, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/evaluate 4", + "value": 1, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/evaluate 5", + "value": 1, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/evaluate 6", + "value": 1, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/evaluate 7", + "value": 1, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/evaluate 8", + "value": 1, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/evaluate 9", + "value": 1, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/evaluate 10", + "value": 1, + "range": "± 0", + "unit": "ns/iter" + } + ] + }, + { + "commit": { + "author": { + "email": "pdeymon@fi.uba.ar", + "name": "Pablo Deymonnaz", + "username": "pablodeymo" + }, + "committer": { + "email": "noreply@github.com", + "name": "GitHub", + "username": "web-flow" + }, + "distinct": true, + "id": "4b018546d485d2183229af08050da6828e44727f", + "message": "Fix bincode repo change (#1023)\n\n* Fix bincode inclusion\n\n* Add feature serde\n\n* fix clippy\n\n* fix warnings in benches and solve bincode dependency\n\n* use crates io for bincode\n\n---------\n\nCo-authored-by: jotabulacios ", + "timestamp": "2025-09-04T10:39:44-03:00", + "tree_id": "92dc6ad2e4737096fc389a707999cd69ee64d351", + "url": "https://github.com/lambdaclass/lambdaworks/commit/4b018546d485d2183229af08050da6828e44727f" + }, + "date": 1756994647048, + "tool": "cargo", + "benches": [ + { + "name": "Ordered FFT/Sequential from NR radix2", + "value": 325991755, + "range": "± 681598", + "unit": "ns/iter" + }, + { + "name": "Ordered FFT/Sequential from RN radix2", + "value": 374448513, + "range": "± 3757645", + "unit": "ns/iter" + }, + { + "name": "Ordered FFT/Sequential from NR radix4", + "value": 277883420, + "range": "± 2144164", + "unit": "ns/iter" + }, + { + "name": "Ordered FFT/Sequential from NR radix2 #2", + "value": 685338373, + "range": "± 467234", + "unit": "ns/iter" + }, + { + "name": "Ordered FFT/Sequential from RN radix2 #2", + "value": 805259952, + "range": "± 4998892", + "unit": "ns/iter" + }, + { + "name": "Ordered FFT/Sequential from NR radix2 #3", + "value": 1436433999, + "range": "± 1251909", + "unit": "ns/iter" + }, + { + "name": "Ordered FFT/Sequential from RN radix2 #3", + "value": 1679420004, + "range": "± 12453102", + "unit": "ns/iter" + }, + { + "name": "Ordered FFT/Sequential from NR radix4 #2", + "value": 1223299184, + "range": "± 4274812", + "unit": "ns/iter" + }, + { + "name": "Ordered FFT/Sequential from NR radix2 #4", + "value": 3001160329, + "range": "± 2126221", + "unit": "ns/iter" + }, + { + "name": "Ordered FFT/Sequential from RN radix2 #4", + "value": 3496676362, + "range": "± 28102858", + "unit": "ns/iter" + }, + { + "name": "Ordered FFT/Sequential from NR radix2 #5", + "value": 6285745381, + "range": "± 3353500", + "unit": "ns/iter" + }, + { + "name": "Ordered FFT/Sequential from RN radix2 #5", + "value": 7399146270, + "range": "± 26025662", + "unit": "ns/iter" + }, + { + "name": "Ordered FFT/Sequential from NR radix4 #3", + "value": 5390031430, + "range": "± 5988611", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/natural", + "value": 7975984, + "range": "± 4911", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/natural inversed", + "value": 7995500, + "range": "± 5253", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/bit-reversed", + "value": 10100023, + "range": "± 36995", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/bit-reversed inversed", + "value": 10340819, + "range": "± 438263", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/natural #2", + "value": 18150525, + "range": "± 74325", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/natural inversed #2", + "value": 18224592, + "range": "± 107672", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/bit-reversed #2", + "value": 27015866, + "range": "± 1754348", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/bit-reversed inversed #2", + "value": 26961971, + "range": "± 945042", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/natural #3", + "value": 37349926, + "range": "± 69767", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/natural inversed #3", + "value": 37078828, + "range": "± 73917", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/bit-reversed #3", + "value": 71806229, + "range": "± 810077", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/bit-reversed inversed #3", + "value": 71588700, + "range": "± 963589", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/natural #4", + "value": 75228231, + "range": "± 309974", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/natural inversed #4", + "value": 75085703, + "range": "± 131621", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/bit-reversed #4", + "value": 153828378, + "range": "± 3316296", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/bit-reversed inversed #4", + "value": 151642432, + "range": "± 2680587", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/natural #5", + "value": 153118461, + "range": "± 366388", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/natural inversed #5", + "value": 153301852, + "range": "± 228070", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/bit-reversed #5", + "value": 309341257, + "range": "± 5463317", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/bit-reversed inversed #5", + "value": 314997063, + "range": "± 6437859", + "unit": "ns/iter" + }, + { + "name": "Bit-reverse permutation/Sequential", + "value": 18671865, + "range": "± 714154", + "unit": "ns/iter" + }, + { + "name": "Bit-reverse permutation/Sequential #2", + "value": 39585478, + "range": "± 1104319", + "unit": "ns/iter" + }, + { + "name": "Bit-reverse permutation/Sequential #3", + "value": 81110671, + "range": "± 3985246", + "unit": "ns/iter" + }, + { + "name": "Bit-reverse permutation/Sequential #4", + "value": 159675731, + "range": "± 4888886", + "unit": "ns/iter" + }, + { + "name": "Bit-reverse permutation/Sequential #5", + "value": 396307028, + "range": "± 4988328", + "unit": "ns/iter" + }, + { + "name": "Polynomial evaluation/Sequential FFT", + "value": 357561710, + "range": "± 1809150", + "unit": "ns/iter" + }, + { + "name": "Polynomial evaluation/Sequential FFT #2", + "value": 768588543, + "range": "± 3089943", + "unit": "ns/iter" + }, + { + "name": "Polynomial evaluation/Sequential FFT #3", + "value": 1623667653, + "range": "± 5221858", + "unit": "ns/iter" + }, + { + "name": "Polynomial evaluation/Sequential FFT #4", + "value": 3396026948, + "range": "± 5832406", + "unit": "ns/iter" + }, + { + "name": "Polynomial evaluation/Sequential FFT #5", + "value": 7144512983, + "range": "± 7067355", + "unit": "ns/iter" + }, + { + "name": "Polynomial interpolation/Sequential FFT", + "value": 385163105, + "range": "± 2373397", + "unit": "ns/iter" + }, + { + "name": "Polynomial interpolation/Sequential FFT #2", + "value": 811093379, + "range": "± 2634027", + "unit": "ns/iter" + }, + { + "name": "Polynomial interpolation/Sequential FFT #3", + "value": 1705919182, + "range": "± 4690839", + "unit": "ns/iter" + }, + { + "name": "Polynomial interpolation/Sequential FFT #4", + "value": 3540674157, + "range": "± 9615678", + "unit": "ns/iter" + }, + { + "name": "Polynomial interpolation/Sequential FFT #5", + "value": 7463924191, + "range": "± 17757604", + "unit": "ns/iter" + }, + { + "name": "Polynomial/evaluate", + "value": 5, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/evaluate_slice", + "value": 14, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/add", + "value": 51, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/neg", + "value": 24, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/sub", + "value": 75, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/mul", + "value": 32, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/fast_mul big poly", + "value": 145308, + "range": "± 298", + "unit": "ns/iter" + }, + { + "name": "Polynomial/slow mul big poly", + "value": 1543587, + "range": "± 14118", + "unit": "ns/iter" + }, + { + "name": "Polynomial/fast div big poly", + "value": 814917, + "range": "± 1820", + "unit": "ns/iter" + }, + { + "name": "Polynomial/slow div big poly", + "value": 1713758, + "range": "± 33265", + "unit": "ns/iter" + }, + { + "name": "Polynomial/div", + "value": 251, + "range": "± 14", + "unit": "ns/iter" + }, + { + "name": "Polynomial/div by 'x - b' with generic div", + "value": 44, + "range": "± 1", + "unit": "ns/iter" + }, + { + "name": "Polynomial/div by 'x - b' with Ruffini", + "value": 5, + "range": "± 60", + "unit": "ns/iter" + }, + { + "name": "Polynomial/evaluate #2", + "value": 13, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/evaluate_with", + "value": 10334, + "range": "± 25", + "unit": "ns/iter" + }, + { + "name": "Polynomial/merge", + "value": 88, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/add #2", + "value": 10157, + "range": "± 961", + "unit": "ns/iter" + }, + { + "name": "Polynomial/mul #2", + "value": 64, + "range": "± 1", + "unit": "ns/iter" + }, + { + "name": "Polynomial/evaluate 3", + "value": 1, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/evaluate 4", + "value": 1, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/evaluate 5", + "value": 1, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/evaluate 6", + "value": 1, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/evaluate 7", + "value": 1, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/evaluate 8", + "value": 9727, + "range": "± 1211", + "unit": "ns/iter" + }, + { + "name": "Polynomial/evaluate 9", + "value": 1, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/evaluate 10", + "value": 1, + "range": "± 0", + "unit": "ns/iter" + } + ] + }, + { + "commit": { + "author": { + "email": "nicole.graus@lambdaclass.com", + "name": "Nicole Graus", + "username": "nicole-graus" + }, + "committer": { + "email": "noreply@github.com", + "name": "GitHub", + "username": "web-flow" + }, + "distinct": false, + "id": "575c4a208e4182fa5998965808b953f512124cb7", + "message": "Add GKR Protocol (#1011)\n\n* save work, protocol structure now is following the post style\n\n* add prints to debug implementation\n\n* refactor to avoid unwraps\n\n* more refactor\n\n* more refactor\n\n* refactor\n\n* add readme\n\n* Fix readme. Add struct Prover\n\n* remove extra cargo.toml\n\n* remove claimed_sum from sumcheck_proof. Fix test. Add documentation\n\n* remove clones and fix clippy\n\n* add documentation\n\n* add check mark for gkr in readme\n\n* add degree check on g_j\n\n* add verifier checks: proof structure match circuit structure\n\n* fix clippy for new rust version (#1013)\n\n* fix clippy\n\n* fix readme\n\n* rename modulus\n\n* avoid repetitive computation\n\n* check terms has len 2\n\n* remove clone term_1 and term_2\n\n* add check number of inputs\n\n* fix type complexity\n\n---------\n\nCo-authored-by: jotabulacios \nCo-authored-by: jotabulacios <45471455+jotabulacios@users.noreply.github.com>\nCo-authored-by: Diego K <43053772+diegokingston@users.noreply.github.com>", + "timestamp": "2025-09-04T17:41:15Z", + "tree_id": "4a0dbcbe5f6bff84ac8a4a145fdbd56730252eb7", + "url": "https://github.com/lambdaclass/lambdaworks/commit/575c4a208e4182fa5998965808b953f512124cb7" + }, + "date": 1757009404290, + "tool": "cargo", + "benches": [ + { + "name": "Ordered FFT/Sequential from NR radix2", + "value": 327349357, + "range": "± 225229", + "unit": "ns/iter" + }, + { + "name": "Ordered FFT/Sequential from RN radix2", + "value": 369173127, + "range": "± 944363", + "unit": "ns/iter" + }, + { + "name": "Ordered FFT/Sequential from NR radix4", + "value": 278358638, + "range": "± 3996838", + "unit": "ns/iter" + }, + { + "name": "Ordered FFT/Sequential from NR radix2 #2", + "value": 686881448, + "range": "± 450374", + "unit": "ns/iter" + }, + { + "name": "Ordered FFT/Sequential from RN radix2 #2", + "value": 777632938, + "range": "± 1958864", + "unit": "ns/iter" + }, + { + "name": "Ordered FFT/Sequential from NR radix2 #3", + "value": 1439667581, + "range": "± 6619044", + "unit": "ns/iter" + }, + { + "name": "Ordered FFT/Sequential from RN radix2 #3", + "value": 1632517436, + "range": "± 6433470", + "unit": "ns/iter" + }, + { + "name": "Ordered FFT/Sequential from NR radix4 #2", + "value": 1221655289, + "range": "± 937573", + "unit": "ns/iter" + }, + { + "name": "Ordered FFT/Sequential from NR radix2 #4", + "value": 3007052303, + "range": "± 7970711", + "unit": "ns/iter" + }, + { + "name": "Ordered FFT/Sequential from RN radix2 #4", + "value": 3424299583, + "range": "± 4418343", + "unit": "ns/iter" + }, + { + "name": "Ordered FFT/Sequential from NR radix2 #5", + "value": 6293588909, + "range": "± 2019723", + "unit": "ns/iter" + }, + { + "name": "Ordered FFT/Sequential from RN radix2 #5", + "value": 7240206657, + "range": "± 21137611", + "unit": "ns/iter" + }, + { + "name": "Ordered FFT/Sequential from NR radix4 #3", + "value": 5357256651, + "range": "± 10105561", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/natural", + "value": 8005486, + "range": "± 4484", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/natural inversed", + "value": 8005962, + "range": "± 12041", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/bit-reversed", + "value": 10621194, + "range": "± 98679", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/bit-reversed inversed", + "value": 10575369, + "range": "± 67678", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/natural #2", + "value": 19365209, + "range": "± 125406", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/natural inversed #2", + "value": 19234876, + "range": "± 103713", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/bit-reversed #2", + "value": 32380989, + "range": "± 1786760", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/bit-reversed inversed #2", + "value": 30467225, + "range": "± 1327679", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/natural #3", + "value": 38214504, + "range": "± 49895", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/natural inversed #3", + "value": 38218622, + "range": "± 83004", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/bit-reversed #3", + "value": 71712421, + "range": "± 1102960", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/bit-reversed inversed #3", + "value": 69059180, + "range": "± 523308", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/natural #4", + "value": 75501772, + "range": "± 353384", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/natural inversed #4", + "value": 75331493, + "range": "± 94651", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/bit-reversed #4", + "value": 141133423, + "range": "± 1640193", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/bit-reversed inversed #4", + "value": 142705663, + "range": "± 566601", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/natural #5", + "value": 151238136, + "range": "± 228972", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/natural inversed #5", + "value": 151140655, + "range": "± 98154", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/bit-reversed #5", + "value": 285776897, + "range": "± 1171319", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/bit-reversed inversed #5", + "value": 289713741, + "range": "± 700536", + "unit": "ns/iter" + }, + { + "name": "Bit-reverse permutation/Sequential", + "value": 15281536, + "range": "± 358394", + "unit": "ns/iter" + }, + { + "name": "Bit-reverse permutation/Sequential #2", + "value": 33943059, + "range": "± 191313", + "unit": "ns/iter" + }, + { + "name": "Bit-reverse permutation/Sequential #3", + "value": 68260684, + "range": "± 330574", + "unit": "ns/iter" + }, + { + "name": "Bit-reverse permutation/Sequential #4", + "value": 137590252, + "range": "± 1099558", + "unit": "ns/iter" + }, + { + "name": "Bit-reverse permutation/Sequential #5", + "value": 350515501, + "range": "± 2421887", + "unit": "ns/iter" + }, + { + "name": "Polynomial evaluation/Sequential FFT", + "value": 362356571, + "range": "± 844229", + "unit": "ns/iter" + }, + { + "name": "Polynomial evaluation/Sequential FFT #2", + "value": 769775233, + "range": "± 2815799", + "unit": "ns/iter" + }, + { + "name": "Polynomial evaluation/Sequential FFT #3", + "value": 1612756789, + "range": "± 2208520", + "unit": "ns/iter" + }, + { + "name": "Polynomial evaluation/Sequential FFT #4", + "value": 3354180619, + "range": "± 7685594", + "unit": "ns/iter" + }, + { + "name": "Polynomial evaluation/Sequential FFT #5", + "value": 7081259770, + "range": "± 10563601", + "unit": "ns/iter" + }, + { + "name": "Polynomial interpolation/Sequential FFT", + "value": 389717837, + "range": "± 2412829", + "unit": "ns/iter" + }, + { + "name": "Polynomial interpolation/Sequential FFT #2", + "value": 810352827, + "range": "± 2141090", + "unit": "ns/iter" + }, + { + "name": "Polynomial interpolation/Sequential FFT #3", + "value": 1691467127, + "range": "± 4272275", + "unit": "ns/iter" + }, + { + "name": "Polynomial interpolation/Sequential FFT #4", + "value": 3509554173, + "range": "± 2748299", + "unit": "ns/iter" + }, + { + "name": "Polynomial interpolation/Sequential FFT #5", + "value": 7386491497, + "range": "± 5383268", + "unit": "ns/iter" + }, + { + "name": "Polynomial/evaluate", + "value": 242, + "range": "± 1", + "unit": "ns/iter" + }, + { + "name": "Polynomial/evaluate_slice", + "value": 7794, + "range": "± 7", + "unit": "ns/iter" + }, + { + "name": "Polynomial/add", + "value": 278, + "range": "± 1", + "unit": "ns/iter" + }, + { + "name": "Polynomial/neg", + "value": 195, + "range": "± 4", + "unit": "ns/iter" + }, + { + "name": "Polynomial/sub", + "value": 466, + "range": "± 5", + "unit": "ns/iter" + }, + { + "name": "Polynomial/mul", + "value": 6462, + "range": "± 72", + "unit": "ns/iter" + }, + { + "name": "Polynomial/fast_mul big poly", + "value": 145173, + "range": "± 193", + "unit": "ns/iter" + }, + { + "name": "Polynomial/slow mul big poly", + "value": 1578950, + "range": "± 5956", + "unit": "ns/iter" + }, + { + "name": "Polynomial/fast div big poly", + "value": 816826, + "range": "± 1654", + "unit": "ns/iter" + }, + { + "name": "Polynomial/slow div big poly", + "value": 1675230, + "range": "± 12407", + "unit": "ns/iter" + }, + { + "name": "Polynomial/div", + "value": 1245, + "range": "± 1077", + "unit": "ns/iter" + }, + { + "name": "Polynomial/div by 'x - b' with generic div", + "value": 22756, + "range": "± 313", + "unit": "ns/iter" + }, + { + "name": "Polynomial/div by 'x - b' with Ruffini", + "value": 249, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/evaluate #2", + "value": 12, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/evaluate_with", + "value": 14, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/merge", + "value": 48, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/add #2", + "value": 7236, + "range": "± 525", + "unit": "ns/iter" + }, + { + "name": "Polynomial/mul #2", + "value": 31, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/evaluate 3", + "value": 1, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/evaluate 4", + "value": 1, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/evaluate 5", + "value": 1, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/evaluate 6", + "value": 1, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/evaluate 7", + "value": 1, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/evaluate 8", + "value": 9669, + "range": "± 262", + "unit": "ns/iter" + }, + { + "name": "Polynomial/evaluate 9", + "value": 1, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/evaluate 10", + "value": 1, + "range": "± 0", + "unit": "ns/iter" + } + ] + }, + { + "commit": { + "author": { + "email": "56092489+ColoCarletti@users.noreply.github.com", + "name": "Joaquin Carletti", + "username": "ColoCarletti" + }, + "committer": { + "email": "noreply@github.com", + "name": "GitHub", + "username": "web-flow" + }, + "distinct": true, + "id": "160bb5ca435696a41387c8cd95ad1f451e497a73", + "message": "Fix clippy (#1029)\n\n* fix clippy\n\n* remove unused import\n\n* fix", + "timestamp": "2025-09-22T13:55:28Z", + "tree_id": "35c4006d18ca2209259c1fd0ec20c1ae7f75ef99", + "url": "https://github.com/lambdaclass/lambdaworks/commit/160bb5ca435696a41387c8cd95ad1f451e497a73" + }, + "date": 1758551006111, + "tool": "cargo", + "benches": [ + { + "name": "Ordered FFT/Sequential from NR radix2", + "value": 325335444, + "range": "± 125867", + "unit": "ns/iter" + }, + { + "name": "Ordered FFT/Sequential from RN radix2", + "value": 369983647, + "range": "± 985942", + "unit": "ns/iter" + }, + { + "name": "Ordered FFT/Sequential from NR radix4", + "value": 277303546, + "range": "± 165830", + "unit": "ns/iter" + }, + { + "name": "Ordered FFT/Sequential from NR radix2 #2", + "value": 684694098, + "range": "± 678642", + "unit": "ns/iter" + }, + { + "name": "Ordered FFT/Sequential from RN radix2 #2", + "value": 778176882, + "range": "± 1775762", + "unit": "ns/iter" + }, + { + "name": "Ordered FFT/Sequential from NR radix2 #3", + "value": 1434791074, + "range": "± 2940528", + "unit": "ns/iter" + }, + { + "name": "Ordered FFT/Sequential from RN radix2 #3", + "value": 1638901678, + "range": "± 4906497", + "unit": "ns/iter" + }, + { + "name": "Ordered FFT/Sequential from NR radix4 #2", + "value": 1220013815, + "range": "± 2853602", + "unit": "ns/iter" + }, + { + "name": "Ordered FFT/Sequential from NR radix2 #4", + "value": 2995143977, + "range": "± 3485241", + "unit": "ns/iter" + }, + { + "name": "Ordered FFT/Sequential from RN radix2 #4", + "value": 3421099090, + "range": "± 7458792", + "unit": "ns/iter" + }, + { + "name": "Ordered FFT/Sequential from NR radix2 #5", + "value": 6266438925, + "range": "± 3331942", + "unit": "ns/iter" + }, + { + "name": "Ordered FFT/Sequential from RN radix2 #5", + "value": 7254859249, + "range": "± 10861104", + "unit": "ns/iter" + }, + { + "name": "Ordered FFT/Sequential from NR radix4 #3", + "value": 5358648266, + "range": "± 7326848", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/natural", + "value": 8168692, + "range": "± 5196", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/natural inversed", + "value": 8179494, + "range": "± 5922", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/bit-reversed", + "value": 10447423, + "range": "± 332621", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/bit-reversed inversed", + "value": 11518927, + "range": "± 843071", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/natural #2", + "value": 18489389, + "range": "± 69462", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/natural inversed #2", + "value": 18383582, + "range": "± 84599", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/bit-reversed #2", + "value": 27534711, + "range": "± 250836", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/bit-reversed inversed #2", + "value": 27370098, + "range": "± 321637", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/natural #3", + "value": 37211194, + "range": "± 116104", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/natural inversed #3", + "value": 37496783, + "range": "± 174741", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/bit-reversed #3", + "value": 69841715, + "range": "± 414266", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/bit-reversed inversed #3", + "value": 69643673, + "range": "± 1306146", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/natural #4", + "value": 75099049, + "range": "± 132341", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/natural inversed #4", + "value": 75235898, + "range": "± 171668", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/bit-reversed #4", + "value": 146259185, + "range": "± 813673", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/bit-reversed inversed #4", + "value": 147469116, + "range": "± 992128", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/natural #5", + "value": 151743604, + "range": "± 173893", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/natural inversed #5", + "value": 151569894, + "range": "± 245275", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/bit-reversed #5", + "value": 295296106, + "range": "± 1103585", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/bit-reversed inversed #5", + "value": 294863150, + "range": "± 429504", + "unit": "ns/iter" + }, + { + "name": "Bit-reverse permutation/Sequential", + "value": 17265704, + "range": "± 263477", + "unit": "ns/iter" + }, + { + "name": "Bit-reverse permutation/Sequential #2", + "value": 36519140, + "range": "± 366020", + "unit": "ns/iter" + }, + { + "name": "Bit-reverse permutation/Sequential #3", + "value": 74608768, + "range": "± 5225600", + "unit": "ns/iter" + }, + { + "name": "Bit-reverse permutation/Sequential #4", + "value": 146583199, + "range": "± 751191", + "unit": "ns/iter" + }, + { + "name": "Bit-reverse permutation/Sequential #5", + "value": 380952927, + "range": "± 2779776", + "unit": "ns/iter" + }, + { + "name": "Polynomial evaluation/Sequential FFT", + "value": 363822408, + "range": "± 2322157", + "unit": "ns/iter" + }, + { + "name": "Polynomial evaluation/Sequential FFT #2", + "value": 774116277, + "range": "± 2971178", + "unit": "ns/iter" + }, + { + "name": "Polynomial evaluation/Sequential FFT #3", + "value": 1614958369, + "range": "± 6043447", + "unit": "ns/iter" + }, + { + "name": "Polynomial evaluation/Sequential FFT #4", + "value": 3360972770, + "range": "± 2582537", + "unit": "ns/iter" + }, + { + "name": "Polynomial evaluation/Sequential FFT #5", + "value": 7082677363, + "range": "± 8344820", + "unit": "ns/iter" + }, + { + "name": "Polynomial interpolation/Sequential FFT", + "value": 390025306, + "range": "± 1313770", + "unit": "ns/iter" + }, + { + "name": "Polynomial interpolation/Sequential FFT #2", + "value": 811466603, + "range": "± 2131396", + "unit": "ns/iter" + }, + { + "name": "Polynomial interpolation/Sequential FFT #3", + "value": 1697281333, + "range": "± 889175", + "unit": "ns/iter" + }, + { + "name": "Polynomial interpolation/Sequential FFT #4", + "value": 3513056901, + "range": "± 11513542", + "unit": "ns/iter" + }, + { + "name": "Polynomial interpolation/Sequential FFT #5", + "value": 7407719018, + "range": "± 5551583", + "unit": "ns/iter" + }, + { + "name": "Polynomial/evaluate", + "value": 541, + "range": "± 4", + "unit": "ns/iter" + }, + { + "name": "Polynomial/evaluate_slice", + "value": 34591, + "range": "± 379", + "unit": "ns/iter" + }, + { + "name": "Polynomial/add", + "value": 436, + "range": "± 4", + "unit": "ns/iter" + }, + { + "name": "Polynomial/neg", + "value": 237, + "range": "± 1", + "unit": "ns/iter" + }, + { + "name": "Polynomial/sub", + "value": 743, + "range": "± 8", + "unit": "ns/iter" + }, + { + "name": "Polynomial/mul", + "value": 27471, + "range": "± 202", + "unit": "ns/iter" + }, + { + "name": "Polynomial/fast_mul big poly", + "value": 149369, + "range": "± 332", + "unit": "ns/iter" + }, + { + "name": "Polynomial/slow mul big poly", + "value": 1568268, + "range": "± 7377", + "unit": "ns/iter" + }, + { + "name": "Polynomial/fast div big poly", + "value": 837200, + "range": "± 1554", + "unit": "ns/iter" + }, + { + "name": "Polynomial/slow div big poly", + "value": 1736615, + "range": "± 10807", + "unit": "ns/iter" + }, + { + "name": "Polynomial/div", + "value": 1677, + "range": "± 1405", + "unit": "ns/iter" + }, + { + "name": "Polynomial/div by 'x - b' with generic div", + "value": 68220, + "range": "± 825", + "unit": "ns/iter" + }, + { + "name": "Polynomial/div by 'x - b' with Ruffini", + "value": 556, + "range": "± 6", + "unit": "ns/iter" + }, + { + "name": "Polynomial/evaluate #2", + "value": 14, + "range": "± 0", + "unit": "ns/iter" + } + ] + }, + { + "commit": { + "author": { + "email": "45471455+jotabulacios@users.noreply.github.com", + "name": "jotabulacios", + "username": "jotabulacios" + }, + "committer": { + "email": "noreply@github.com", + "name": "GitHub", + "username": "web-flow" + }, + "distinct": true, + "id": "060dbf6af6e0c293e80f143fc84c01df47984304", + "message": "Add new unchecked (#1024)\n\n* add new_uncheckes for short weiestrass\n\n* remove unused files\n\n* use unchecked for stark curve generator\n\n* revert changes for vesta curve\n\n* add missing comment\n\n* make the point field private\n\n* fix clippy\n\n* add safety comments and rename function\n\n---------\n\nCo-authored-by: Nicole ", + "timestamp": "2025-09-26T12:41:53Z", + "tree_id": "58fde0f7f49b7bb1c20f6adb2b69cfd487ca2a32", + "url": "https://github.com/lambdaclass/lambdaworks/commit/060dbf6af6e0c293e80f143fc84c01df47984304" + }, + "date": 1758892260395, + "tool": "cargo", + "benches": [ + { + "name": "Ordered FFT/Sequential from NR radix2", + "value": 325215580, + "range": "± 847396", + "unit": "ns/iter" + }, + { + "name": "Ordered FFT/Sequential from RN radix2", + "value": 373657068, + "range": "± 2434340", + "unit": "ns/iter" + }, + { + "name": "Ordered FFT/Sequential from NR radix4", + "value": 278297585, + "range": "± 216465", + "unit": "ns/iter" + }, + { + "name": "Ordered FFT/Sequential from NR radix2 #2", + "value": 682379484, + "range": "± 1547866", + "unit": "ns/iter" + }, + { + "name": "Ordered FFT/Sequential from RN radix2 #2", + "value": 785330520, + "range": "± 5010239", + "unit": "ns/iter" + }, + { + "name": "Ordered FFT/Sequential from NR radix2 #3", + "value": 1430639896, + "range": "± 2018894", + "unit": "ns/iter" + }, + { + "name": "Ordered FFT/Sequential from RN radix2 #3", + "value": 1641789202, + "range": "± 4857329", + "unit": "ns/iter" + }, + { + "name": "Ordered FFT/Sequential from NR radix4 #2", + "value": 1223414376, + "range": "± 926179", + "unit": "ns/iter" + }, + { + "name": "Ordered FFT/Sequential from NR radix2 #4", + "value": 2990728001, + "range": "± 1673318", + "unit": "ns/iter" + }, + { + "name": "Ordered FFT/Sequential from RN radix2 #4", + "value": 3462773251, + "range": "± 27530512", + "unit": "ns/iter" + }, + { + "name": "Ordered FFT/Sequential from NR radix2 #5", + "value": 6254017292, + "range": "± 6648237", + "unit": "ns/iter" + }, + { + "name": "Ordered FFT/Sequential from RN radix2 #5", + "value": 7276449166, + "range": "± 28044908", + "unit": "ns/iter" + }, + { + "name": "Ordered FFT/Sequential from NR radix4 #3", + "value": 5363226478, + "range": "± 2129329", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/natural", + "value": 7802390, + "range": "± 7737", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/natural inversed", + "value": 7855133, + "range": "± 3591", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/bit-reversed", + "value": 12414636, + "range": "± 329671", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/bit-reversed inversed", + "value": 12445642, + "range": "± 406244", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/natural #2", + "value": 18406185, + "range": "± 25581", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/natural inversed #2", + "value": 18406602, + "range": "± 26824", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/bit-reversed #2", + "value": 28133954, + "range": "± 1611770", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/bit-reversed inversed #2", + "value": 25844952, + "range": "± 1778691", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/natural #3", + "value": 36405996, + "range": "± 273027", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/natural inversed #3", + "value": 36731186, + "range": "± 194837", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/bit-reversed #3", + "value": 71598287, + "range": "± 2491460", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/bit-reversed inversed #3", + "value": 73062654, + "range": "± 2116195", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/natural #4", + "value": 74007339, + "range": "± 182959", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/natural inversed #4", + "value": 73830442, + "range": "± 290963", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/bit-reversed #4", + "value": 145939467, + "range": "± 2058601", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/bit-reversed inversed #4", + "value": 144493665, + "range": "± 1143129", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/natural #5", + "value": 147382138, + "range": "± 149852", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/natural inversed #5", + "value": 147205967, + "range": "± 179827", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/bit-reversed #5", + "value": 289140208, + "range": "± 1155299", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/bit-reversed inversed #5", + "value": 296248833, + "range": "± 2802542", + "unit": "ns/iter" + }, + { + "name": "Bit-reverse permutation/Sequential", + "value": 16989761, + "range": "± 659266", + "unit": "ns/iter" + }, + { + "name": "Bit-reverse permutation/Sequential #2", + "value": 35842569, + "range": "± 462530", + "unit": "ns/iter" + }, + { + "name": "Bit-reverse permutation/Sequential #3", + "value": 69181791, + "range": "± 547365", + "unit": "ns/iter" + }, + { + "name": "Bit-reverse permutation/Sequential #4", + "value": 143995557, + "range": "± 1552718", + "unit": "ns/iter" + }, + { + "name": "Bit-reverse permutation/Sequential #5", + "value": 378716909, + "range": "± 5075004", + "unit": "ns/iter" + }, + { + "name": "Polynomial evaluation/Sequential FFT", + "value": 355185973, + "range": "± 4450696", + "unit": "ns/iter" + }, + { + "name": "Polynomial evaluation/Sequential FFT #2", + "value": 764245901, + "range": "± 5096431", + "unit": "ns/iter" + }, + { + "name": "Polynomial evaluation/Sequential FFT #3", + "value": 1612145494, + "range": "± 3757159", + "unit": "ns/iter" + }, + { + "name": "Polynomial evaluation/Sequential FFT #4", + "value": 3339393239, + "range": "± 3569650", + "unit": "ns/iter" + }, + { + "name": "Polynomial evaluation/Sequential FFT #5", + "value": 7105842097, + "range": "± 14807527", + "unit": "ns/iter" + }, + { + "name": "Polynomial interpolation/Sequential FFT", + "value": 382037081, + "range": "± 1024800", + "unit": "ns/iter" + }, + { + "name": "Polynomial interpolation/Sequential FFT #2", + "value": 804546863, + "range": "± 1461616", + "unit": "ns/iter" + }, + { + "name": "Polynomial interpolation/Sequential FFT #3", + "value": 1700177771, + "range": "± 3290557", + "unit": "ns/iter" + }, + { + "name": "Polynomial interpolation/Sequential FFT #4", + "value": 3544380419, + "range": "± 8550266", + "unit": "ns/iter" + }, + { + "name": "Polynomial interpolation/Sequential FFT #5", + "value": 7422766439, + "range": "± 23445052", + "unit": "ns/iter" + }, + { + "name": "Polynomial/evaluate", + "value": 20, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/evaluate_slice", + "value": 94, + "range": "± 1", + "unit": "ns/iter" + }, + { + "name": "Polynomial/add", + "value": 63, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/neg", + "value": 29, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/sub", + "value": 91, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/mul", + "value": 165, + "range": "± 5", + "unit": "ns/iter" + }, + { + "name": "Polynomial/fast_mul big poly", + "value": 149398, + "range": "± 235", + "unit": "ns/iter" + }, + { + "name": "Polynomial/slow mul big poly", + "value": 1577888, + "range": "± 15509", + "unit": "ns/iter" + }, + { + "name": "Polynomial/fast div big poly", + "value": 837095, + "range": "± 1089", + "unit": "ns/iter" + }, + { + "name": "Polynomial/slow div big poly", + "value": 1750395, + "range": "± 19949", + "unit": "ns/iter" + }, + { + "name": "Polynomial/div", + "value": 266, + "range": "± 52", + "unit": "ns/iter" + }, + { + "name": "Polynomial/div by 'x - b' with generic div", + "value": 646, + "range": "± 142", + "unit": "ns/iter" + }, + { + "name": "Polynomial/div by 'x - b' with Ruffini", + "value": 22, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/evaluate #2", + "value": 13, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/evaluate_with", + "value": 15, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/merge", + "value": 49, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/add #2", + "value": 7157, + "range": "± 647", + "unit": "ns/iter" + }, + { + "name": "Polynomial/mul #2", + "value": 32, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/evaluate 3", + "value": 1, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/evaluate 4", + "value": 1, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/evaluate 5", + "value": 1, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/evaluate 6", + "value": 1, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/evaluate 7", + "value": 1, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/evaluate 8", + "value": 1, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/evaluate 9", + "value": 1, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/evaluate 10", + "value": 1, + "range": "± 0", + "unit": "ns/iter" + } + ] + }, + { + "commit": { + "author": { + "email": "56092489+ColoCarletti@users.noreply.github.com", + "name": "Joaquin Carletti", + "username": "ColoCarletti" + }, + "committer": { + "email": "noreply@github.com", + "name": "GitHub", + "username": "web-flow" + }, + "distinct": false, + "id": "5c451b961aacd957e6d837f23550cfa3144241d0", + "message": "Release 0.13.0 (#1032)\n\n* update version\n\n* add publish workflow\n\n* update dependencies versions\n\n* fix name\n\n* add --workspace", + "timestamp": "2025-09-26T22:11:09Z", + "tree_id": "f6fa21ee6bc287b148793d5475cf7feb52221341", + "url": "https://github.com/lambdaclass/lambdaworks/commit/5c451b961aacd957e6d837f23550cfa3144241d0" + }, + "date": 1758926412276, + "tool": "cargo", + "benches": [ + { + "name": "Ordered FFT/Sequential from NR radix2", + "value": 325963016, + "range": "± 3399029", + "unit": "ns/iter" + }, + { + "name": "Ordered FFT/Sequential from RN radix2", + "value": 371738149, + "range": "± 1561326", + "unit": "ns/iter" + }, + { + "name": "Ordered FFT/Sequential from NR radix4", + "value": 278814470, + "range": "± 7929519", + "unit": "ns/iter" + }, + { + "name": "Ordered FFT/Sequential from NR radix2 #2", + "value": 683930195, + "range": "± 678225", + "unit": "ns/iter" + }, + { + "name": "Ordered FFT/Sequential from RN radix2 #2", + "value": 784875772, + "range": "± 4714139", + "unit": "ns/iter" + }, + { + "name": "Ordered FFT/Sequential from NR radix2 #3", + "value": 1431549352, + "range": "± 866932", + "unit": "ns/iter" + }, + { + "name": "Ordered FFT/Sequential from RN radix2 #3", + "value": 1640861027, + "range": "± 5503930", + "unit": "ns/iter" + }, + { + "name": "Ordered FFT/Sequential from NR radix4 #2", + "value": 1224447072, + "range": "± 1196037", + "unit": "ns/iter" + }, + { + "name": "Ordered FFT/Sequential from NR radix2 #4", + "value": 2993522190, + "range": "± 11282622", + "unit": "ns/iter" + }, + { + "name": "Ordered FFT/Sequential from RN radix2 #4", + "value": 3412001808, + "range": "± 15221984", + "unit": "ns/iter" + }, + { + "name": "Ordered FFT/Sequential from NR radix2 #5", + "value": 6259757545, + "range": "± 8681837", + "unit": "ns/iter" + }, + { + "name": "Ordered FFT/Sequential from RN radix2 #5", + "value": 7226452713, + "range": "± 8290036", + "unit": "ns/iter" + }, + { + "name": "Ordered FFT/Sequential from NR radix4 #3", + "value": 5365975769, + "range": "± 2925741", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/natural", + "value": 7806927, + "range": "± 5786", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/natural inversed", + "value": 7852937, + "range": "± 20188", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/bit-reversed", + "value": 11733822, + "range": "± 1335452", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/bit-reversed inversed", + "value": 11315285, + "range": "± 278017", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/natural #2", + "value": 18110453, + "range": "± 49818", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/natural inversed #2", + "value": 18093574, + "range": "± 40461", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/bit-reversed #2", + "value": 30587502, + "range": "± 1034200", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/bit-reversed inversed #2", + "value": 30614571, + "range": "± 767048", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/natural #3", + "value": 36459325, + "range": "± 155532", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/natural inversed #3", + "value": 36610808, + "range": "± 46152", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/bit-reversed #3", + "value": 70282343, + "range": "± 726581", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/bit-reversed inversed #3", + "value": 70748278, + "range": "± 952778", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/natural #4", + "value": 73801286, + "range": "± 184280", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/natural inversed #4", + "value": 73530496, + "range": "± 78973", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/bit-reversed #4", + "value": 143704585, + "range": "± 469241", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/bit-reversed inversed #4", + "value": 143309730, + "range": "± 405695", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/natural #5", + "value": 146940510, + "range": "± 221954", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/natural inversed #5", + "value": 146619131, + "range": "± 231523", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/bit-reversed #5", + "value": 286076963, + "range": "± 1299961", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/bit-reversed inversed #5", + "value": 286364706, + "range": "± 821430", + "unit": "ns/iter" + }, + { + "name": "Bit-reverse permutation/Sequential", + "value": 16264109, + "range": "± 257096", + "unit": "ns/iter" + }, + { + "name": "Bit-reverse permutation/Sequential #2", + "value": 35221517, + "range": "± 172517", + "unit": "ns/iter" + }, + { + "name": "Bit-reverse permutation/Sequential #3", + "value": 71058491, + "range": "± 338395", + "unit": "ns/iter" + }, + { + "name": "Bit-reverse permutation/Sequential #4", + "value": 140004925, + "range": "± 622099", + "unit": "ns/iter" + }, + { + "name": "Bit-reverse permutation/Sequential #5", + "value": 367674065, + "range": "± 818266", + "unit": "ns/iter" + }, + { + "name": "Polynomial evaluation/Sequential FFT", + "value": 361810685, + "range": "± 1080391", + "unit": "ns/iter" + }, + { + "name": "Polynomial evaluation/Sequential FFT #2", + "value": 769739185, + "range": "± 10838758", + "unit": "ns/iter" + }, + { + "name": "Polynomial evaluation/Sequential FFT #3", + "value": 1607312731, + "range": "± 1350613", + "unit": "ns/iter" + }, + { + "name": "Polynomial evaluation/Sequential FFT #4", + "value": 3343970614, + "range": "± 9667806", + "unit": "ns/iter" + }, + { + "name": "Polynomial evaluation/Sequential FFT #5", + "value": 7069251321, + "range": "± 8776698", + "unit": "ns/iter" + }, + { + "name": "Polynomial interpolation/Sequential FFT", + "value": 388927278, + "range": "± 1511521", + "unit": "ns/iter" + }, + { + "name": "Polynomial interpolation/Sequential FFT #2", + "value": 812653115, + "range": "± 2123301", + "unit": "ns/iter" + }, + { + "name": "Polynomial interpolation/Sequential FFT #3", + "value": 1699394986, + "range": "± 1993612", + "unit": "ns/iter" + }, + { + "name": "Polynomial interpolation/Sequential FFT #4", + "value": 3500682480, + "range": "± 3594168", + "unit": "ns/iter" + }, + { + "name": "Polynomial interpolation/Sequential FFT #5", + "value": 7373802937, + "range": "± 5009223", + "unit": "ns/iter" + }, + { + "name": "Polynomial/evaluate", + "value": 12, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/evaluate_slice", + "value": 30, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/add", + "value": 57, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/neg", + "value": 27, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/sub", + "value": 83, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/mul", + "value": 51, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/fast_mul big poly", + "value": 149089, + "range": "± 376", + "unit": "ns/iter" + }, + { + "name": "Polynomial/slow mul big poly", + "value": 1553940, + "range": "± 7253", + "unit": "ns/iter" + }, + { + "name": "Polynomial/fast div big poly", + "value": 834314, + "range": "± 1458", + "unit": "ns/iter" + }, + { + "name": "Polynomial/slow div big poly", + "value": 1688453, + "range": "± 17068", + "unit": "ns/iter" + }, + { + "name": "Polynomial/div", + "value": 269, + "range": "± 11", + "unit": "ns/iter" + }, + { + "name": "Polynomial/div by 'x - b' with generic div", + "value": 269, + "range": "± 4", + "unit": "ns/iter" + }, + { + "name": "Polynomial/div by 'x - b' with Ruffini", + "value": 10, + "range": "± 38", + "unit": "ns/iter" + }, + { + "name": "Polynomial/evaluate #2", + "value": 13, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/evaluate_with", + "value": 14, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/merge", + "value": 48, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/add #2", + "value": 7159, + "range": "± 985", + "unit": "ns/iter" + }, + { + "name": "Polynomial/mul #2", + "value": 32, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/evaluate 3", + "value": 1, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/evaluate 4", + "value": 1, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/evaluate 5", + "value": 1, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/evaluate 6", + "value": 1, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/evaluate 7", + "value": 1, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/evaluate 8", + "value": 9754, + "range": "± 121", + "unit": "ns/iter" + }, + { + "name": "Polynomial/evaluate 9", + "value": 1, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/evaluate 10", + "value": 1, + "range": "± 0", + "unit": "ns/iter" + } + ] + }, + { + "commit": { + "author": { + "email": "56092489+ColoCarletti@users.noreply.github.com", + "name": "Joaquin Carletti", + "username": "ColoCarletti" + }, + "committer": { + "email": "noreply@github.com", + "name": "GitHub", + "username": "web-flow" + }, + "distinct": true, + "id": "980313e6e5d3962aa15cbfbe9c73e534af2dfc82", + "message": "fix publish workflow (#1033)", + "timestamp": "2025-09-29T14:07:47Z", + "tree_id": "1715dc3b4f1804b05b1bdbb785bf06720cfd136f", + "url": "https://github.com/lambdaclass/lambdaworks/commit/980313e6e5d3962aa15cbfbe9c73e534af2dfc82" + }, + "date": 1759156644327, + "tool": "cargo", + "benches": [ + { + "name": "Ordered FFT/Sequential from NR radix2", + "value": 325276909, + "range": "± 299122", + "unit": "ns/iter" + }, + { + "name": "Ordered FFT/Sequential from RN radix2", + "value": 373799212, + "range": "± 1280792", + "unit": "ns/iter" + }, + { + "name": "Ordered FFT/Sequential from NR radix4", + "value": 276761830, + "range": "± 3240462", + "unit": "ns/iter" + }, + { + "name": "Ordered FFT/Sequential from NR radix2 #2", + "value": 684123108, + "range": "± 458835", + "unit": "ns/iter" + }, + { + "name": "Ordered FFT/Sequential from RN radix2 #2", + "value": 784548181, + "range": "± 860109", + "unit": "ns/iter" + }, + { + "name": "Ordered FFT/Sequential from NR radix2 #3", + "value": 1432390908, + "range": "± 2789501", + "unit": "ns/iter" + }, + { + "name": "Ordered FFT/Sequential from RN radix2 #3", + "value": 1648173519, + "range": "± 1888834", + "unit": "ns/iter" + }, + { + "name": "Ordered FFT/Sequential from NR radix4 #2", + "value": 1217575532, + "range": "± 3284841", + "unit": "ns/iter" + }, + { + "name": "Ordered FFT/Sequential from NR radix2 #4", + "value": 2993699711, + "range": "± 719515", + "unit": "ns/iter" + }, + { + "name": "Ordered FFT/Sequential from RN radix2 #4", + "value": 3440730954, + "range": "± 10184483", + "unit": "ns/iter" + }, + { + "name": "Ordered FFT/Sequential from NR radix2 #5", + "value": 6268181109, + "range": "± 3354598", + "unit": "ns/iter" + }, + { + "name": "Ordered FFT/Sequential from RN radix2 #5", + "value": 7305123303, + "range": "± 13595186", + "unit": "ns/iter" + }, + { + "name": "Ordered FFT/Sequential from NR radix4 #3", + "value": 5340979849, + "range": "± 11014483", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/natural", + "value": 7792357, + "range": "± 26787", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/natural inversed", + "value": 7845413, + "range": "± 4191", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/bit-reversed", + "value": 9905692, + "range": "± 54020", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/bit-reversed inversed", + "value": 9935119, + "range": "± 37029", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/natural #2", + "value": 17946159, + "range": "± 72651", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/natural inversed #2", + "value": 17854317, + "range": "± 29782", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/bit-reversed #2", + "value": 27077437, + "range": "± 1066053", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/bit-reversed inversed #2", + "value": 26842568, + "range": "± 504628", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/natural #3", + "value": 36099204, + "range": "± 25086", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/natural inversed #3", + "value": 36296376, + "range": "± 29384", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/bit-reversed #3", + "value": 67316601, + "range": "± 696305", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/bit-reversed inversed #3", + "value": 67900197, + "range": "± 610414", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/natural #4", + "value": 73460960, + "range": "± 125364", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/natural inversed #4", + "value": 73239280, + "range": "± 97542", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/bit-reversed #4", + "value": 142108141, + "range": "± 990596", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/bit-reversed inversed #4", + "value": 143959311, + "range": "± 2673369", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/natural #5", + "value": 146531409, + "range": "± 321114", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/natural inversed #5", + "value": 146469282, + "range": "± 105839", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/bit-reversed #5", + "value": 284632593, + "range": "± 945734", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/bit-reversed inversed #5", + "value": 283681688, + "range": "± 1164461", + "unit": "ns/iter" + }, + { + "name": "Bit-reverse permutation/Sequential", + "value": 15729115, + "range": "± 66530", + "unit": "ns/iter" + }, + { + "name": "Bit-reverse permutation/Sequential #2", + "value": 35615464, + "range": "± 669731", + "unit": "ns/iter" + }, + { + "name": "Bit-reverse permutation/Sequential #3", + "value": 70791379, + "range": "± 556050", + "unit": "ns/iter" + }, + { + "name": "Bit-reverse permutation/Sequential #4", + "value": 139936815, + "range": "± 910885", + "unit": "ns/iter" + }, + { + "name": "Bit-reverse permutation/Sequential #5", + "value": 365484553, + "range": "± 5490571", + "unit": "ns/iter" + }, + { + "name": "Polynomial evaluation/Sequential FFT", + "value": 361663058, + "range": "± 2990451", + "unit": "ns/iter" + }, + { + "name": "Polynomial evaluation/Sequential FFT #2", + "value": 762404414, + "range": "± 2192560", + "unit": "ns/iter" + }, + { + "name": "Polynomial evaluation/Sequential FFT #3", + "value": 1604253640, + "range": "± 3479460", + "unit": "ns/iter" + }, + { + "name": "Polynomial evaluation/Sequential FFT #4", + "value": 3343212740, + "range": "± 3525470", + "unit": "ns/iter" + }, + { + "name": "Polynomial evaluation/Sequential FFT #5", + "value": 7061624679, + "range": "± 9724140", + "unit": "ns/iter" + }, + { + "name": "Polynomial interpolation/Sequential FFT", + "value": 385896349, + "range": "± 2498005", + "unit": "ns/iter" + }, + { + "name": "Polynomial interpolation/Sequential FFT #2", + "value": 805836010, + "range": "± 2063810", + "unit": "ns/iter" + }, + { + "name": "Polynomial interpolation/Sequential FFT #3", + "value": 1693259461, + "range": "± 1272833", + "unit": "ns/iter" + }, + { + "name": "Polynomial interpolation/Sequential FFT #4", + "value": 3502638453, + "range": "± 9288862", + "unit": "ns/iter" + }, + { + "name": "Polynomial interpolation/Sequential FFT #5", + "value": 7379752116, + "range": "± 5243622", + "unit": "ns/iter" + }, + { + "name": "Polynomial/evaluate", + "value": 12, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/evaluate_slice", + "value": 27, + "range": "± 1", + "unit": "ns/iter" + }, + { + "name": "Polynomial/add", + "value": 56, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/neg", + "value": 27, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/sub", + "value": 84, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/mul", + "value": 50, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/fast_mul big poly", + "value": 149332, + "range": "± 202", + "unit": "ns/iter" + }, + { + "name": "Polynomial/slow mul big poly", + "value": 1576574, + "range": "± 14090", + "unit": "ns/iter" + }, + { + "name": "Polynomial/fast div big poly", + "value": 837195, + "range": "± 3444", + "unit": "ns/iter" + }, + { + "name": "Polynomial/slow div big poly", + "value": 1749773, + "range": "± 5302", + "unit": "ns/iter" + }, + { + "name": "Polynomial/div", + "value": 279, + "range": "± 20", + "unit": "ns/iter" + }, + { + "name": "Polynomial/div by 'x - b' with generic div", + "value": 278, + "range": "± 10", + "unit": "ns/iter" + }, + { + "name": "Polynomial/div by 'x - b' with Ruffini", + "value": 9, + "range": "± 37", + "unit": "ns/iter" + }, + { + "name": "Polynomial/evaluate #2", + "value": 14, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/evaluate_with", + "value": 10383, + "range": "± 26", + "unit": "ns/iter" + }, + { + "name": "Polynomial/merge", + "value": 92, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/add #2", + "value": 10130, + "range": "± 952", + "unit": "ns/iter" + }, + { + "name": "Polynomial/mul #2", + "value": 62, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/evaluate 3", + "value": 1, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/evaluate 4", + "value": 1, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/evaluate 5", + "value": 1, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/evaluate 6", + "value": 1, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/evaluate 7", + "value": 1, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/evaluate 8", + "value": 1, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/evaluate 9", + "value": 1, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/evaluate 10", + "value": 1, + "range": "± 0", + "unit": "ns/iter" + } + ] + }, + { + "commit": { + "author": { + "email": "56092489+ColoCarletti@users.noreply.github.com", + "name": "Joaquin Carletti", + "username": "ColoCarletti" + }, + "committer": { + "email": "noreply@github.com", + "name": "GitHub", + "username": "web-flow" + }, + "distinct": true, + "id": "6f03bf1ba13c4207faafb5b0c639b68f8b60d55f", + "message": "Remove all-features in publish (#1034)\n\n* remove all-features in publish\n\n* rm mac specific features", + "timestamp": "2025-09-29T15:41:05Z", + "tree_id": "3627df9a79145407673b45e92a794163a751866f", + "url": "https://github.com/lambdaclass/lambdaworks/commit/6f03bf1ba13c4207faafb5b0c639b68f8b60d55f" + }, + "date": 1759162230136, + "tool": "cargo", + "benches": [ + { + "name": "Ordered FFT/Sequential from NR radix2", + "value": 326052587, + "range": "± 327303", + "unit": "ns/iter" + }, + { + "name": "Ordered FFT/Sequential from RN radix2", + "value": 378794070, + "range": "± 1060771", + "unit": "ns/iter" + }, + { + "name": "Ordered FFT/Sequential from NR radix4", + "value": 279230377, + "range": "± 6382372", + "unit": "ns/iter" + }, + { + "name": "Ordered FFT/Sequential from NR radix2 #2", + "value": 684888426, + "range": "± 7314944", + "unit": "ns/iter" + }, + { + "name": "Ordered FFT/Sequential from RN radix2 #2", + "value": 797613594, + "range": "± 9744927", + "unit": "ns/iter" + }, + { + "name": "Ordered FFT/Sequential from NR radix2 #3", + "value": 1433431586, + "range": "± 645093", + "unit": "ns/iter" + }, + { + "name": "Ordered FFT/Sequential from RN radix2 #3", + "value": 1667154408, + "range": "± 11292086", + "unit": "ns/iter" + }, + { + "name": "Ordered FFT/Sequential from NR radix4 #2", + "value": 1221477059, + "range": "± 6207384", + "unit": "ns/iter" + }, + { + "name": "Ordered FFT/Sequential from NR radix2 #4", + "value": 2993403094, + "range": "± 1566266", + "unit": "ns/iter" + }, + { + "name": "Ordered FFT/Sequential from RN radix2 #4", + "value": 3480509202, + "range": "± 21420319", + "unit": "ns/iter" + }, + { + "name": "Ordered FFT/Sequential from NR radix2 #5", + "value": 6276792127, + "range": "± 11290789", + "unit": "ns/iter" + }, + { + "name": "Ordered FFT/Sequential from RN radix2 #5", + "value": 7341362876, + "range": "± 14007302", + "unit": "ns/iter" + }, + { + "name": "Ordered FFT/Sequential from NR radix4 #3", + "value": 5353857536, + "range": "± 12102265", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/natural", + "value": 7800649, + "range": "± 20792", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/natural inversed", + "value": 7867376, + "range": "± 97685", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/bit-reversed", + "value": 14969400, + "range": "± 510732", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/bit-reversed inversed", + "value": 15421656, + "range": "± 388852", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/natural #2", + "value": 18548995, + "range": "± 65264", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/natural inversed #2", + "value": 18477744, + "range": "± 49657", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/bit-reversed #2", + "value": 35407419, + "range": "± 369150", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/bit-reversed inversed #2", + "value": 35945733, + "range": "± 254154", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/natural #3", + "value": 37047255, + "range": "± 371682", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/natural inversed #3", + "value": 37149369, + "range": "± 905982", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/bit-reversed #3", + "value": 72676056, + "range": "± 785625", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/bit-reversed inversed #3", + "value": 72988400, + "range": "± 220160", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/natural #4", + "value": 74992312, + "range": "± 118634", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/natural inversed #4", + "value": 74678301, + "range": "± 76627", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/bit-reversed #4", + "value": 146134758, + "range": "± 419783", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/bit-reversed inversed #4", + "value": 144749031, + "range": "± 361141", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/natural #5", + "value": 148144466, + "range": "± 506503", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/natural inversed #5", + "value": 148525673, + "range": "± 175507", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/bit-reversed #5", + "value": 290099351, + "range": "± 1499140", + "unit": "ns/iter" + }, + { + "name": "FFT twiddles generation/bit-reversed inversed #5", + "value": 291142433, + "range": "± 1259193", + "unit": "ns/iter" + }, + { + "name": "Bit-reverse permutation/Sequential", + "value": 18126240, + "range": "± 529617", + "unit": "ns/iter" + }, + { + "name": "Bit-reverse permutation/Sequential #2", + "value": 36343143, + "range": "± 936185", + "unit": "ns/iter" + }, + { + "name": "Bit-reverse permutation/Sequential #3", + "value": 70890838, + "range": "± 746462", + "unit": "ns/iter" + }, + { + "name": "Bit-reverse permutation/Sequential #4", + "value": 144304341, + "range": "± 938313", + "unit": "ns/iter" + }, + { + "name": "Bit-reverse permutation/Sequential #5", + "value": 377452695, + "range": "± 7593715", + "unit": "ns/iter" + }, + { + "name": "Polynomial evaluation/Sequential FFT", + "value": 370578339, + "range": "± 875132", + "unit": "ns/iter" + }, + { + "name": "Polynomial evaluation/Sequential FFT #2", + "value": 776197736, + "range": "± 762739", + "unit": "ns/iter" + }, + { + "name": "Polynomial evaluation/Sequential FFT #3", + "value": 1613468970, + "range": "± 1173146", + "unit": "ns/iter" + }, + { + "name": "Polynomial evaluation/Sequential FFT #4", + "value": 3355807362, + "range": "± 2133974", + "unit": "ns/iter" + }, + { + "name": "Polynomial evaluation/Sequential FFT #5", + "value": 7078061283, + "range": "± 5411817", + "unit": "ns/iter" + }, + { + "name": "Polynomial interpolation/Sequential FFT", + "value": 397337274, + "range": "± 3375303", + "unit": "ns/iter" + }, + { + "name": "Polynomial interpolation/Sequential FFT #2", + "value": 814209726, + "range": "± 19613983", + "unit": "ns/iter" + }, + { + "name": "Polynomial interpolation/Sequential FFT #3", + "value": 1696964267, + "range": "± 17870997", + "unit": "ns/iter" + }, + { + "name": "Polynomial interpolation/Sequential FFT #4", + "value": 3508199492, + "range": "± 8024534", + "unit": "ns/iter" + }, + { + "name": "Polynomial interpolation/Sequential FFT #5", + "value": 7379264858, + "range": "± 23020004", + "unit": "ns/iter" + }, + { + "name": "Polynomial/evaluate", + "value": 257, + "range": "± 6", + "unit": "ns/iter" + }, + { + "name": "Polynomial/evaluate_slice", + "value": 8230, + "range": "± 192", + "unit": "ns/iter" + }, + { + "name": "Polynomial/add", + "value": 287, + "range": "± 1", + "unit": "ns/iter" + }, + { + "name": "Polynomial/neg", + "value": 184, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/sub", + "value": 439, + "range": "± 12", + "unit": "ns/iter" + }, + { + "name": "Polynomial/mul", + "value": 6177, + "range": "± 68", + "unit": "ns/iter" + }, + { + "name": "Polynomial/fast_mul big poly", + "value": 149213, + "range": "± 203", + "unit": "ns/iter" + }, + { + "name": "Polynomial/slow mul big poly", + "value": 1554409, + "range": "± 11862", + "unit": "ns/iter" + }, + { + "name": "Polynomial/fast div big poly", + "value": 836692, + "range": "± 1413", + "unit": "ns/iter" + }, + { + "name": "Polynomial/slow div big poly", + "value": 1658194, + "range": "± 9165", + "unit": "ns/iter" + }, + { + "name": "Polynomial/div", + "value": 1255, + "range": "± 1086", + "unit": "ns/iter" + }, + { + "name": "Polynomial/div by 'x - b' with generic div", + "value": 22172, + "range": "± 1776", + "unit": "ns/iter" + }, + { + "name": "Polynomial/div by 'x - b' with Ruffini", + "value": 266, + "range": "± 15", + "unit": "ns/iter" + }, + { + "name": "Polynomial/evaluate #2", + "value": 13, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/evaluate_with", + "value": 16, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/merge", + "value": 78, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/add #2", + "value": 10080, + "range": "± 197", + "unit": "ns/iter" + }, + { + "name": "Polynomial/mul #2", + "value": 38, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/evaluate 3", + "value": 1, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/evaluate 4", + "value": 1, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/evaluate 5", + "value": 1, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/evaluate 6", + "value": 1, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/evaluate 7", + "value": 1, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/evaluate 8", + "value": 1, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/evaluate 9", + "value": 1, + "range": "± 0", + "unit": "ns/iter" + }, + { + "name": "Polynomial/evaluate 10", + "value": 1, + "range": "± 0", + "unit": "ns/iter" + } + ] + } + ] + } +} \ No newline at end of file diff --git a/bench/index.html b/bench/index.html new file mode 100644 index 000000000..6c887805e --- /dev/null +++ b/bench/index.html @@ -0,0 +1,281 @@ + + + + + + + Benchmarks + + + + +
+ + + + + + + diff --git a/benches/Cargo.toml b/benches/Cargo.toml deleted file mode 100644 index 89f922d5e..000000000 --- a/benches/Cargo.toml +++ /dev/null @@ -1,54 +0,0 @@ -[package] -name = "lambdaworks-benches" -version.workspace = true -edition.workspace = true - -[dependencies] -ark-ff = { git = "https://github.com/arkworks-rs/algebra", rev = "ef8f758" } -ark-test-curves = { git = "https://github.com/arkworks-rs/algebra", rev = "ef8f758" } -ark-std = "0.4.0" -rand = "0.8.5" -rand_chacha = "0.3.1" -starknet-curve = { git = "https://github.com/xJonathanLEI/starknet-rs", tag = "starknet-curve/v0.4.2" } -starknet-ff = { git = "https://github.com/xJonathanLEI/starknet-rs", tag = "starknet-ff/v0.3.7" } -starknet-crypto = { git = "https://github.com/xJonathanLEI/starknet-rs", tag = "starknet-crypto/v0.6.2" } -pathfinder-crypto = { git = "https://github.com/eqlabs/pathfinder.git" } - -lambdaworks-math.workspace = true -lambdaworks-crypto.workspace = true - -[dev-dependencies] -criterion = { version = "0.5.1", default-features = false } -rand_chacha = "0.3.1" - -[[bench]] -name = "add" -harness = false - -[[bench]] -name = "mul" -harness = false - -[[bench]] -name = "sub" -harness = false - -[[bench]] -name = "invert" -harness = false - -[[bench]] -name = "sqrt" -harness = false - -[[bench]] -name = "pow" -harness = false - -[[bench]] -name = "point" -harness = false - -[[bench]] -name = "poseidon" -harness = false diff --git a/benches/benches/add.rs b/benches/benches/add.rs deleted file mode 100644 index 6a15f2eaa..000000000 --- a/benches/benches/add.rs +++ /dev/null @@ -1,63 +0,0 @@ -use std::{ops::Add, time::Duration}; - -use criterion::{black_box, criterion_group, criterion_main, Criterion}; -use utils::generate_random_elements; - -use crate::utils::to_lambdaworks_vec; - -pub mod utils; - -const BENCHMARK_NAME: &str = "add"; - -pub fn criterion_benchmark(c: &mut Criterion) { - let arkworks_vec = generate_random_elements(2000000); - - // arkworks-ff - { - c.bench_function( - &format!("{BENCHMARK_NAME} 1M elements | ark-ff - ef8f758"), - |b| { - b.iter(|| { - let mut iter = arkworks_vec.iter(); - - for _i in 0..1000000 { - let a = iter.next().unwrap(); - let b = iter.next().unwrap(); - black_box(black_box(&a).add(black_box(b))); - } - }); - }, - ); - } - - // lambdaworks-math - { - let lambdaworks_vec = to_lambdaworks_vec(&arkworks_vec); - - c.bench_function( - &format!("{BENCHMARK_NAME} 1M elements | lambdaworks",), - |b| { - b.iter(|| { - let mut iter = lambdaworks_vec.iter(); - - for _i in 0..1000000 { - let a = iter.next().unwrap(); - let b = iter.next().unwrap(); - black_box(black_box(&a).add(black_box(b))); - } - }); - }, - ); - } -} - -criterion_group! { - name = benches; - // This can be any expression that returns a `Criterion` object. - config = Criterion::default() - .significance_level(0.01) - .measurement_time(Duration::from_secs(15)) - .sample_size(300); - targets = criterion_benchmark -} -criterion_main!(benches); diff --git a/benches/benches/invert.rs b/benches/benches/invert.rs deleted file mode 100644 index 22bd212ad..000000000 --- a/benches/benches/invert.rs +++ /dev/null @@ -1,50 +0,0 @@ -use ark_ff::Field; -use criterion::{black_box, criterion_group, criterion_main, BatchSize, Criterion}; -use utils::generate_random_elements; - -use crate::utils::to_lambdaworks_vec; - -pub mod utils; - -const BENCHMARK_NAME: &str = "invert"; - -pub fn criterion_benchmark(c: &mut Criterion) { - let arkworks_vec = generate_random_elements(10000).to_vec(); - - // arkworks-ff - { - c.bench_function( - &format!("{BENCHMARK_NAME} 10000 elements| ark-ff - ef8f758"), - |b| { - b.iter_batched( - || arkworks_vec.clone(), - |mut v| { - for mut elem in v.iter_mut() { - black_box(black_box(&mut elem).inverse_in_place()); - } - }, - BatchSize::LargeInput, - ); - }, - ); - } - - // lambdaworks-math - { - let lambdaworks_vec = to_lambdaworks_vec(&arkworks_vec); - - c.bench_function( - &format!("{BENCHMARK_NAME} 10000 elements | lambdaworks",), - |b| { - b.iter(|| { - for elem in lambdaworks_vec.iter() { - black_box(black_box(&elem).inv().unwrap()); - } - }); - }, - ); - } -} - -criterion_group!(benches, criterion_benchmark); -criterion_main!(benches); diff --git a/benches/benches/mul.rs b/benches/benches/mul.rs deleted file mode 100644 index f7e59e3e7..000000000 --- a/benches/benches/mul.rs +++ /dev/null @@ -1,54 +0,0 @@ -use criterion::{black_box, criterion_group, criterion_main, Criterion}; -use std::ops::Mul; -use utils::generate_random_elements; - -use crate::utils::to_lambdaworks_vec; - -pub mod utils; - -const BENCHMARK_NAME: &str = "mul"; - -pub fn criterion_benchmark(c: &mut Criterion) { - let arkworks_vec = generate_random_elements(20000); - - // arkworks-ff - { - c.bench_function( - &format!("{BENCHMARK_NAME} 10K elements | ark-ff - commit: ef8f758 "), - |b| { - b.iter(|| { - let mut iter = arkworks_vec.iter(); - - for _i in 0..10000 { - let a = iter.next().unwrap(); - let b = iter.next().unwrap(); - black_box(black_box(a).mul(black_box(b))); - } - }); - }, - ); - } - - // lambdaworks-math - { - let lambdaworks_vec = to_lambdaworks_vec(&arkworks_vec); - - c.bench_function( - &format!("{BENCHMARK_NAME} 10K elements | lambdaworks",), - |b| { - b.iter(|| { - let mut iter = lambdaworks_vec.iter(); - - for _i in 0..10000 { - let a = iter.next().unwrap(); - let b = iter.next().unwrap(); - black_box(black_box(&a).mul(black_box(b))); - } - }); - }, - ); - } -} - -criterion_group!(benches, criterion_benchmark); -criterion_main!(benches); diff --git a/benches/benches/point.rs b/benches/benches/point.rs deleted file mode 100644 index e51a25567..000000000 --- a/benches/benches/point.rs +++ /dev/null @@ -1,265 +0,0 @@ -use criterion::{black_box, criterion_group, criterion_main, Criterion}; -use lambdaworks_math::{ - cyclic_group::IsGroup, - elliptic_curve::{short_weierstrass::curves::stark_curve::StarkCurve, traits::IsEllipticCurve}, -}; -use starknet_curve::{curve_params::GENERATOR, AffinePoint, ProjectivePoint}; -use std::ops::{Add, AddAssign}; - -const BENCHMARK_NAME: &str = "point"; - -pub fn point_double_projective(c: &mut Criterion) { - let starknet_rs_projective_generator = ProjectivePoint::from_affine_point(&GENERATOR); - - let mut initial_point = starknet_rs_projective_generator; - let copied_point = starknet_rs_projective_generator; - - { - c.bench_function( - &format!("{BENCHMARK_NAME} Projective Double | Starknet RS "), - |b| { - b.iter(|| { - let mut initial_point = starknet_rs_projective_generator; - initial_point.add_assign(&copied_point); - initial_point - }); - }, - ); - } - - initial_point.add_assign(&copied_point); - println!( - "Starknet RS result - X: {:#x}", - AffinePoint::from(&initial_point).x - ); - println!( - "Starknet RS result - Y: {:#x} \n", - AffinePoint::from(&initial_point).y - ); - - let lambdaworks_affine_generator = StarkCurve::generator(); - - { - c.bench_function( - &format!("{BENCHMARK_NAME} Projective Double | Lambdaworks"), - |b| { - b.iter(|| { - lambdaworks_affine_generator - .operate_with(black_box(&lambdaworks_affine_generator)) - }); - }, - ); - } - - let test_lambda_result = - lambdaworks_affine_generator.operate_with(&lambdaworks_affine_generator); - println!( - "Lambdaworks result - X: {}", - test_lambda_result.to_affine().x() - ); - println!( - "Lambdaworks result - Y: {}", - test_lambda_result.to_affine().y() - ); -} - -pub fn point_add_projective_affine(c: &mut Criterion) { - let starknet_rs_affine_generator = GENERATOR; - - let starknet_rs_initial_projective = - ProjectivePoint::from_affine_point(&GENERATOR.add(&GENERATOR)); - - { - c.bench_function( - &format!("{BENCHMARK_NAME} 10k Add Projective-Affine | Starknet RS "), - |b| { - b.iter(|| { - let mut projective_point_rs = starknet_rs_initial_projective; - for _i in 0..10000 { - projective_point_rs.add_assign(black_box(&starknet_rs_affine_generator)); - } - projective_point_rs - }); - }, - ); - } - - let mut projective_point_rs = starknet_rs_initial_projective; - for _i in 0..10000 { - projective_point_rs.add_assign(&starknet_rs_affine_generator); - } - - let starknet_rs_x = AffinePoint::from(&projective_point_rs).x; - println!("Starknet RS result - X: {starknet_rs_x:#x} "); - let starknet_rs_y = AffinePoint::from(&projective_point_rs).y; - println!("Starknet RS result - Y: {starknet_rs_y:#x} \n"); - - let lambdaworks_affine_generator = StarkCurve::generator(); - - // This is the code we are going to bench - // We test it once outside the bench to check the result matches with Starknet RS - let lambdaworks_rs_initial_projective = - StarkCurve::generator().operate_with(&StarkCurve::generator()); - - { - c.bench_function( - &format!("{BENCHMARK_NAME} 10k Add Projective-Affine | Lambdaworks"), - |b| { - b.iter(|| { - let mut projective_point = lambdaworks_rs_initial_projective.clone(); - for _i in 0..10000 { - projective_point = projective_point - .operate_with_affine(black_box(&lambdaworks_affine_generator)); - } - projective_point - }); - }, - ); - } - - let mut projective_point = lambdaworks_rs_initial_projective.clone(); - for _i in 0..10000 { - projective_point = - black_box(projective_point.operate_with(black_box(&lambdaworks_affine_generator))); - } - - let lambdaworks_x = projective_point.to_affine().x().to_string(); - let lambdaworks_y = projective_point.to_affine().y().to_string(); - println!("Lambdaworks result - X: {lambdaworks_x}"); - println!("Lambdaworks result - Y: {lambdaworks_y}"); -} - -pub fn point_add_projective_projective(c: &mut Criterion) { - let starknet_rs_projective_generator = ProjectivePoint::from_affine_point(&GENERATOR); - - let starknet_rs_initial_projective = - ProjectivePoint::from_affine_point(&GENERATOR.add(&GENERATOR)); - - { - c.bench_function( - &format!("{BENCHMARK_NAME} 10k Add Projective-Projective | Starknet RS "), - |b| { - b.iter(|| { - let mut projective_point_rs = starknet_rs_initial_projective; - for _i in 0..10000 { - projective_point_rs - .add_assign(black_box(&starknet_rs_projective_generator)); - } - projective_point_rs - }); - }, - ); - } - - let mut projective_point_rs = starknet_rs_initial_projective; - for _i in 0..10000 { - projective_point_rs.add_assign(&starknet_rs_projective_generator); - } - - let starknet_rs_x = AffinePoint::from(&projective_point_rs).x; - println!("Starknet RS result - X: {starknet_rs_x:#x} "); - let starknet_rs_y = AffinePoint::from(&projective_point_rs).y; - println!("Starknet RS result - Y: {starknet_rs_y:#x} \n"); - - let lambdaworks_affine_generator = StarkCurve::generator(); - - let lambdaworks_rs_initial_projective = - StarkCurve::generator().operate_with(&StarkCurve::generator()); - - { - c.bench_function( - &format!("{BENCHMARK_NAME} 10k Add Projective-Projective | Lambdaworks"), - |b| { - b.iter(|| { - let mut projective_point = lambdaworks_rs_initial_projective.clone(); - for _i in 0..10000 { - projective_point = - projective_point.operate_with(black_box(&lambdaworks_affine_generator)); - } - projective_point - }); - }, - ); - } - - let mut projective_point = lambdaworks_rs_initial_projective.clone(); - for _i in 0..10000 { - projective_point = - black_box(projective_point.operate_with(black_box(&lambdaworks_affine_generator))); - } - - let lambdaworks_x = projective_point.to_affine().x().to_string(); - let lambdaworks_y = projective_point.to_affine().y().to_string(); - println!("Lambdaworks result - X: {lambdaworks_x}"); - println!("Lambdaworks result - Y: {lambdaworks_y}"); -} - -pub fn point_add_affine_affine(c: &mut Criterion) { - let starknet_rs_affine_generator = GENERATOR; - - let starknet_rs_initial_point = &GENERATOR.add(&GENERATOR); - - { - c.bench_function( - &format!("{BENCHMARK_NAME} 10k Add Affine-Affine | Starknet RS "), - |b| { - b.iter(|| { - let mut point_rs = *starknet_rs_initial_point; - for _i in 0..10000 { - point_rs.add_assign(black_box(&starknet_rs_affine_generator)); - } - point_rs - }); - }, - ); - } - - let mut point_rs = *starknet_rs_initial_point; - for _i in 0..10000 { - point_rs.add_assign(&starknet_rs_affine_generator); - } - - let starknet_rs_x = point_rs.x; - println!("Starknet RS result - X: {starknet_rs_x:#x} "); - let starknet_rs_y = &point_rs.y; - println!("Starknet RS result - Y: {starknet_rs_y:#x} \n"); - - let lambdaworks_affine_generator = StarkCurve::generator(); - - let lambdaworks_rs_initial_projective = - StarkCurve::generator().operate_with(&StarkCurve::generator()); - { - c.bench_function( - &format!("{BENCHMARK_NAME} 10k Add Affine-Affine | Lambdaworks"), - |b| { - b.iter(|| { - let mut projective_point = lambdaworks_rs_initial_projective.clone(); - for _i in 0..10000 { - projective_point = - projective_point.operate_with(black_box(&lambdaworks_affine_generator)); - } - projective_point - }); - }, - ); - } - - let mut projective_point = lambdaworks_rs_initial_projective.clone(); - for _i in 0..10000 { - projective_point = - black_box(projective_point.operate_with(black_box(&lambdaworks_affine_generator))); - } - - let lambdaworks_x = projective_point.to_affine().x().to_string(); - let lambdaworks_y = projective_point.to_affine().y().to_string(); - println!("Lambdaworks result - X: {lambdaworks_x}"); - println!("Lambdaworks result - Y: {lambdaworks_y}"); -} - -criterion_group!( - benches, - point_add_projective_affine, - point_double_projective, - point_add_projective_projective -); -criterion_main!(benches); diff --git a/benches/benches/poseidon.rs b/benches/benches/poseidon.rs deleted file mode 100644 index d1ac86778..000000000 --- a/benches/benches/poseidon.rs +++ /dev/null @@ -1,47 +0,0 @@ -use criterion::{black_box, criterion_group, criterion_main, Criterion}; -use lambdaworks_crypto::hash::poseidon::starknet::PoseidonCairoStark252; -use lambdaworks_crypto::hash::poseidon::Poseidon; -use lambdaworks_math::field::element::FieldElement; -use lambdaworks_math::field::fields::fft_friendly::stark_252_prime_field::Stark252PrimeField; -use lambdaworks_math::traits::ByteConversion; -use pathfinder_crypto::MontFelt; -use rand::{RngCore, SeedableRng}; -use rand_chacha::ChaCha8Rng; - -fn poseidon_benchmarks(c: &mut Criterion) { - let mut group = c.benchmark_group("poseidon"); - - let mut rng = ChaCha8Rng::seed_from_u64(2); - let mut felt1: [u8; 32] = Default::default(); - rng.fill_bytes(&mut felt1); - let mut felt2: [u8; 32] = Default::default(); - rng.fill_bytes(&mut felt2); - - let lw_x = FieldElement::::from_bytes_be(&felt1).unwrap(); - let lw_y = FieldElement::::from_bytes_be(&felt2).unwrap(); - group.bench_function("lambdaworks", |bench| { - bench.iter(|| black_box(PoseidonCairoStark252::hash(&lw_x, &lw_y))) - }); - - let mut mont_x = lw_x.value().limbs; - let mut mont_y = lw_y.value().limbs; - - // In order use the same field elements for starknet-rs and pathfinder, we have to reverse - // the limbs order respect to the lambdaworks implementation. - mont_x.reverse(); - mont_y.reverse(); - - let sn_ff_x = starknet_crypto::FieldElement::from_mont(mont_x); - let sn_ff_y = starknet_crypto::FieldElement::from_mont(mont_y); - group.bench_function("starknet-rs", |bench| { - bench.iter(|| black_box(starknet_crypto::poseidon_hash(sn_ff_x, sn_ff_y))) - }); - - let pf_x = MontFelt(mont_x); - let pf_y = MontFelt(mont_y); - group.bench_function("pathfinder", |bench| { - bench.iter(|| black_box(pathfinder_crypto::hash::poseidon_hash(pf_x, pf_y))) - }); -} -criterion_group!(poseidon, poseidon_benchmarks); -criterion_main!(poseidon); diff --git a/benches/benches/pow.rs b/benches/benches/pow.rs deleted file mode 100644 index 85efac91e..000000000 --- a/benches/benches/pow.rs +++ /dev/null @@ -1,64 +0,0 @@ -use ark_ff::Field; -use criterion::{black_box, criterion_group, criterion_main, Criterion}; -use rand::{Rng, SeedableRng}; -use utils::generate_random_elements; - -use crate::utils::to_lambdaworks_vec; - -pub mod utils; - -const BENCHMARK_NAME: &str = "pow"; - -pub fn criterion_benchmark(c: &mut Criterion) { - let arkworks_vec = generate_random_elements(20000); - - let mut rng = rand_chacha::ChaCha20Rng::seed_from_u64(9001); - - let mut v_ints = Vec::new(); - for _i in 0..10000 { - v_ints.push(rng.gen::()); - } - - // arkworks-ff - { - c.bench_function( - &format!("{BENCHMARK_NAME} 10K elements | ark-ff - ef8f758"), - |b| { - b.iter(|| { - let mut iter = arkworks_vec.iter(); - let mut iter_ints = v_ints.iter(); - - for _i in 0..10000 { - let a = iter.next().unwrap(); - let exp = iter_ints.next().unwrap(); - black_box(black_box(&a).pow(black_box(&[*exp]))); - } - }); - }, - ); - } - - // lambdaworks-math - { - let lambdaworks_vec = to_lambdaworks_vec(&arkworks_vec); - - c.bench_function( - &format!("{BENCHMARK_NAME} 10K elements | lambdaworks",), - |b| { - b.iter(|| { - let mut iter = lambdaworks_vec.iter(); - let mut iter_ints = v_ints.iter(); - - for _i in 0..10000 { - let a = iter.next().unwrap(); - let exp = iter_ints.next().unwrap(); - black_box(black_box(&a).pow(black_box(*exp))); - } - }); - }, - ); - } -} - -criterion_group!(benches, criterion_benchmark); -criterion_main!(benches); diff --git a/benches/benches/sqrt.rs b/benches/benches/sqrt.rs deleted file mode 100644 index 50cdc1fdd..000000000 --- a/benches/benches/sqrt.rs +++ /dev/null @@ -1,60 +0,0 @@ -use ark_ff::Field; -use ark_std::UniformRand; -use ark_test_curves::starknet_fp::Fq as F; -use criterion::{black_box, criterion_group, criterion_main, Criterion}; - -use crate::utils::to_lambdaworks_vec; - -pub mod utils; - -const BENCHMARK_NAME: &str = "sqrt"; - -pub fn criterion_benchmark(c: &mut Criterion) { - let mut rng = ::seed_from_u64(9001); - - let mut arkworks_vec = Vec::new(); - for _i in 0..100 { - let a = F::rand(&mut rng); - let square = a * a; - arkworks_vec.push(square); - } - - // arkworks-ff - { - c.bench_function( - &format!("{BENCHMARK_NAME} 100 elements | ark-ff - ef8f758"), - |b| { - b.iter(|| { - let mut iter = arkworks_vec.iter(); - - for _i in 0..100 { - let a = iter.next().unwrap(); - black_box(black_box(a).sqrt()); - } - }); - }, - ); - } - - // lambdaworks-math - { - let lambdaworks_vec = to_lambdaworks_vec(&arkworks_vec); - - c.bench_function( - &format!("{BENCHMARK_NAME} 100 elements | lambdaworks",), - |b| { - b.iter(|| { - let mut iter = lambdaworks_vec.iter(); - - for _i in 0..100 { - let a = iter.next().unwrap(); - black_box(black_box(a).sqrt()); - } - }); - }, - ); - } -} - -criterion_group!(benches, criterion_benchmark); -criterion_main!(benches); diff --git a/benches/benches/sub.rs b/benches/benches/sub.rs deleted file mode 100644 index c0caa0118..000000000 --- a/benches/benches/sub.rs +++ /dev/null @@ -1,58 +0,0 @@ -use criterion::{black_box, criterion_group, criterion_main, Criterion}; -use std::{ops::Sub, time::Duration}; -use utils::{generate_random_elements, to_lambdaworks_vec}; - -pub mod utils; - -const BENCHMARK_NAME: &str = "sub"; - -pub fn criterion_benchmark(c: &mut Criterion) { - let arkworks_vec = generate_random_elements(2000000); - - // arkworks-ff - { - c.bench_function( - &format!("{BENCHMARK_NAME} 1M elements | ark-ff - ef8f758"), - |b| { - b.iter(|| { - let mut iter = arkworks_vec.iter(); - - for _i in 0..1000000 { - let a = iter.next().unwrap(); - let b = iter.next().unwrap(); - black_box(black_box(&a).sub(black_box(b))); - } - }); - }, - ); - } - - // lambdaworks-math - { - let lambdaworks_vec = to_lambdaworks_vec(&arkworks_vec); - c.bench_function( - &format!("{BENCHMARK_NAME} 1M elements | lambdaworks",), - |b| { - b.iter(|| { - let mut iter = lambdaworks_vec.iter(); - - for _i in 0..1000000 { - let a = iter.next().unwrap(); - let b = iter.next().unwrap(); - black_box(black_box(&a).sub(black_box(b))); - } - }); - }, - ); - } -} - -criterion_group! { - name = benches; - config = Criterion::default() - .significance_level(0.01) - .measurement_time(Duration::from_secs(15)) - .sample_size(500); - targets = criterion_benchmark -} -criterion_main!(benches); diff --git a/benches/benches/utils.rs b/benches/benches/utils.rs deleted file mode 100644 index c7fad130e..000000000 --- a/benches/benches/utils.rs +++ /dev/null @@ -1,39 +0,0 @@ -use ark_ff::BigInt; -use ark_std::UniformRand; -use ark_test_curves::starknet_fp::Fq; -use lambdaworks_math::{ - field::{ - element::FieldElement, fields::fft_friendly::stark_252_prime_field::Stark252PrimeField, - }, - unsigned_integer::element::UnsignedInteger, -}; -use rand::SeedableRng; - -/// Creates `amount` random elements -pub fn generate_random_elements(amount: u64) -> Vec { - let mut rng = rand_chacha::ChaCha20Rng::seed_from_u64(9001); - let mut arkworks_vec = Vec::new(); - for _i in 0..amount { - let a = Fq::rand(&mut rng); - arkworks_vec.push(a); - } - - arkworks_vec -} - -pub fn to_lambdaworks_vec(arkworks_vec: &[Fq]) -> Vec> { - let mut lambdaworks_vec = Vec::new(); - for &arkworks_felt in arkworks_vec { - let big_int: BigInt<4> = arkworks_felt.into(); - let mut limbs = big_int.0; - limbs.reverse(); - - let a: FieldElement = FieldElement::from(&UnsignedInteger { limbs }); - - assert_eq!(a.representative().limbs, limbs); - - lambdaworks_vec.push(a); - } - - lambdaworks_vec -} diff --git a/book.js b/book.js new file mode 100644 index 000000000..5e386369f --- /dev/null +++ b/book.js @@ -0,0 +1,660 @@ +"use strict"; + +// Fix back button cache problem +window.onunload = function () { }; + +// Global variable, shared between modules +function playground_text(playground) { + let code_block = playground.querySelector("code"); + + if (window.ace && code_block.classList.contains("editable")) { + let editor = window.ace.edit(code_block); + return editor.getValue(); + } else { + return code_block.textContent; + } +} + +(function codeSnippets() { + function fetch_with_timeout(url, options, timeout = 6000) { + return Promise.race([ + fetch(url, options), + new Promise((_, reject) => setTimeout(() => reject(new Error('timeout')), timeout)) + ]); + } + + var playgrounds = Array.from(document.querySelectorAll(".playground")); + if (playgrounds.length > 0) { + fetch_with_timeout("https://play.rust-lang.org/meta/crates", { + headers: { + 'Content-Type': "application/json", + }, + method: 'POST', + mode: 'cors', + }) + .then(response => response.json()) + .then(response => { + // get list of crates available in the rust playground + let playground_crates = response.crates.map(item => item["id"]); + playgrounds.forEach(block => handle_crate_list_update(block, playground_crates)); + }); + } + + function handle_crate_list_update(playground_block, playground_crates) { + // update the play buttons after receiving the response + update_play_button(playground_block, playground_crates); + + // and install on change listener to dynamically update ACE editors + if (window.ace) { + let code_block = playground_block.querySelector("code"); + if (code_block.classList.contains("editable")) { + let editor = window.ace.edit(code_block); + editor.addEventListener("change", function (e) { + update_play_button(playground_block, playground_crates); + }); + // add Ctrl-Enter command to execute rust code + editor.commands.addCommand({ + name: "run", + bindKey: { + win: "Ctrl-Enter", + mac: "Ctrl-Enter" + }, + exec: _editor => run_rust_code(playground_block) + }); + } + } + } + + // updates the visibility of play button based on `no_run` class and + // used crates vs ones available on http://play.rust-lang.org + function update_play_button(pre_block, playground_crates) { + var play_button = pre_block.querySelector(".play-button"); + + // skip if code is `no_run` + if (pre_block.querySelector('code').classList.contains("no_run")) { + play_button.classList.add("hidden"); + return; + } + + // get list of `extern crate`'s from snippet + var txt = playground_text(pre_block); + var re = /extern\s+crate\s+([a-zA-Z_0-9]+)\s*;/g; + var snippet_crates = []; + var item; + while (item = re.exec(txt)) { + snippet_crates.push(item[1]); + } + + // check if all used crates are available on play.rust-lang.org + var all_available = snippet_crates.every(function (elem) { + return playground_crates.indexOf(elem) > -1; + }); + + if (all_available) { + play_button.classList.remove("hidden"); + } else { + play_button.classList.add("hidden"); + } + } + + function run_rust_code(code_block) { + var result_block = code_block.querySelector(".result"); + if (!result_block) { + result_block = document.createElement('code'); + result_block.className = 'result hljs language-bash'; + + code_block.append(result_block); + } + + let text = playground_text(code_block); + let classes = code_block.querySelector('code').classList; + let has_2018 = classes.contains("edition2018"); + let edition = has_2018 ? "2018" : "2015"; + + var params = { + version: "stable", + optimize: "0", + code: text, + edition: edition + }; + + if (text.indexOf("#![feature") !== -1) { + params.version = "nightly"; + } + + result_block.innerText = "Running..."; + + fetch_with_timeout("https://play.rust-lang.org/evaluate.json", { + headers: { + 'Content-Type': "application/json", + }, + method: 'POST', + mode: 'cors', + body: JSON.stringify(params) + }) + .then(response => response.json()) + .then(response => result_block.innerText = response.result) + .catch(error => result_block.innerText = "Playground Communication: " + error.message); + } + + // Syntax highlighting Configuration + hljs.configure({ + tabReplace: ' ', // 4 spaces + languages: [], // Languages used for auto-detection + }); + + let code_nodes = Array + .from(document.querySelectorAll('code')) + // Don't highlight `inline code` blocks in headers. + .filter(function (node) {return !node.parentElement.classList.contains("header"); }); + + if (window.ace) { + // language-rust class needs to be removed for editable + // blocks or highlightjs will capture events + Array + .from(document.querySelectorAll('code.editable')) + .forEach(function (block) { block.classList.remove('language-rust'); }); + + Array + .from(document.querySelectorAll('code:not(.editable)')) + .forEach(function (block) { hljs.highlightBlock(block); }); + } else { + code_nodes.forEach(function (block) { hljs.highlightBlock(block); }); + } + + // Adding the hljs class gives code blocks the color css + // even if highlighting doesn't apply + code_nodes.forEach(function (block) { block.classList.add('hljs'); }); + + Array.from(document.querySelectorAll("code.language-rust")).forEach(function (block) { + + var lines = Array.from(block.querySelectorAll('.boring')); + // If no lines were hidden, return + if (!lines.length) { return; } + block.classList.add("hide-boring"); + + var buttons = document.createElement('div'); + buttons.className = 'buttons'; + buttons.innerHTML = ""; + + // add expand button + var pre_block = block.parentNode; + pre_block.insertBefore(buttons, pre_block.firstChild); + + pre_block.querySelector('.buttons').addEventListener('click', function (e) { + if (e.target.classList.contains('fa-eye')) { + e.target.classList.remove('fa-eye'); + e.target.classList.add('fa-eye-slash'); + e.target.title = 'Hide lines'; + e.target.setAttribute('aria-label', e.target.title); + + block.classList.remove('hide-boring'); + } else if (e.target.classList.contains('fa-eye-slash')) { + e.target.classList.remove('fa-eye-slash'); + e.target.classList.add('fa-eye'); + e.target.title = 'Show hidden lines'; + e.target.setAttribute('aria-label', e.target.title); + + block.classList.add('hide-boring'); + } + }); + }); + + if (window.playground_copyable) { + Array.from(document.querySelectorAll('pre code')).forEach(function (block) { + var pre_block = block.parentNode; + if (!pre_block.classList.contains('playground')) { + var buttons = pre_block.querySelector(".buttons"); + if (!buttons) { + buttons = document.createElement('div'); + buttons.className = 'buttons'; + pre_block.insertBefore(buttons, pre_block.firstChild); + } + + var clipButton = document.createElement('button'); + clipButton.className = 'fa fa-copy clip-button'; + clipButton.title = 'Copy to clipboard'; + clipButton.setAttribute('aria-label', clipButton.title); + clipButton.innerHTML = ''; + + buttons.insertBefore(clipButton, buttons.firstChild); + } + }); + } + + // Process playground code blocks + Array.from(document.querySelectorAll(".playground")).forEach(function (pre_block) { + // Add play button + var buttons = pre_block.querySelector(".buttons"); + if (!buttons) { + buttons = document.createElement('div'); + buttons.className = 'buttons'; + pre_block.insertBefore(buttons, pre_block.firstChild); + } + + var runCodeButton = document.createElement('button'); + runCodeButton.className = 'fa fa-play play-button'; + runCodeButton.hidden = true; + runCodeButton.title = 'Run this code'; + runCodeButton.setAttribute('aria-label', runCodeButton.title); + + buttons.insertBefore(runCodeButton, buttons.firstChild); + runCodeButton.addEventListener('click', function (e) { + run_rust_code(pre_block); + }); + + if (window.playground_copyable) { + var copyCodeClipboardButton = document.createElement('button'); + copyCodeClipboardButton.className = 'fa fa-copy clip-button'; + copyCodeClipboardButton.innerHTML = ''; + copyCodeClipboardButton.title = 'Copy to clipboard'; + copyCodeClipboardButton.setAttribute('aria-label', copyCodeClipboardButton.title); + + buttons.insertBefore(copyCodeClipboardButton, buttons.firstChild); + } + + let code_block = pre_block.querySelector("code"); + if (window.ace && code_block.classList.contains("editable")) { + var undoChangesButton = document.createElement('button'); + undoChangesButton.className = 'fa fa-history reset-button'; + undoChangesButton.title = 'Undo changes'; + undoChangesButton.setAttribute('aria-label', undoChangesButton.title); + + buttons.insertBefore(undoChangesButton, buttons.firstChild); + + undoChangesButton.addEventListener('click', function () { + let editor = window.ace.edit(code_block); + editor.setValue(editor.originalCode); + editor.clearSelection(); + }); + } + }); +})(); + +(function themes() { + var html = document.querySelector('html'); + var themeToggleButton = document.getElementById('theme-toggle'); + var themePopup = document.getElementById('theme-list'); + var themeColorMetaTag = document.querySelector('meta[name="theme-color"]'); + var stylesheets = { + ayuHighlight: document.querySelector("[href$='ayu-highlight.css']"), + tomorrowNight: document.querySelector("[href$='tomorrow-night.css']"), + highlight: document.querySelector("[href$='highlight.css']"), + }; + + function showThemes() { + themePopup.style.display = 'block'; + themeToggleButton.setAttribute('aria-expanded', true); + themePopup.querySelector("button#" + get_theme()).focus(); + } + + function hideThemes() { + themePopup.style.display = 'none'; + themeToggleButton.setAttribute('aria-expanded', false); + themeToggleButton.focus(); + } + + function get_theme() { + var theme; + try { theme = localStorage.getItem('mdbook-theme'); } catch (e) { } + if (theme === null || theme === undefined) { + return default_theme; + } else { + return theme; + } + } + + function set_theme(theme, store = true) { + let ace_theme; + + if (theme == 'coal' || theme == 'navy') { + stylesheets.ayuHighlight.disabled = true; + stylesheets.tomorrowNight.disabled = false; + stylesheets.highlight.disabled = true; + + ace_theme = "ace/theme/tomorrow_night"; + } else if (theme == 'ayu') { + stylesheets.ayuHighlight.disabled = false; + stylesheets.tomorrowNight.disabled = true; + stylesheets.highlight.disabled = true; + ace_theme = "ace/theme/tomorrow_night"; + } else { + stylesheets.ayuHighlight.disabled = true; + stylesheets.tomorrowNight.disabled = true; + stylesheets.highlight.disabled = false; + ace_theme = "ace/theme/dawn"; + } + + setTimeout(function () { + themeColorMetaTag.content = getComputedStyle(document.body).backgroundColor; + }, 1); + + if (window.ace && window.editors) { + window.editors.forEach(function (editor) { + editor.setTheme(ace_theme); + }); + } + + var previousTheme = get_theme(); + + if (store) { + try { localStorage.setItem('mdbook-theme', theme); } catch (e) { } + } + + html.classList.remove(previousTheme); + html.classList.add(theme); + } + + // Set theme + var theme = get_theme(); + + set_theme(theme, false); + + themeToggleButton.addEventListener('click', function () { + if (themePopup.style.display === 'block') { + hideThemes(); + } else { + showThemes(); + } + }); + + themePopup.addEventListener('click', function (e) { + var theme = e.target.id || e.target.parentElement.id; + set_theme(theme); + }); + + themePopup.addEventListener('focusout', function(e) { + // e.relatedTarget is null in Safari and Firefox on macOS (see workaround below) + if (!!e.relatedTarget && !themeToggleButton.contains(e.relatedTarget) && !themePopup.contains(e.relatedTarget)) { + hideThemes(); + } + }); + + // Should not be needed, but it works around an issue on macOS & iOS: https://github.com/rust-lang/mdBook/issues/628 + document.addEventListener('click', function(e) { + if (themePopup.style.display === 'block' && !themeToggleButton.contains(e.target) && !themePopup.contains(e.target)) { + hideThemes(); + } + }); + + document.addEventListener('keydown', function (e) { + if (e.altKey || e.ctrlKey || e.metaKey || e.shiftKey) { return; } + if (!themePopup.contains(e.target)) { return; } + + switch (e.key) { + case 'Escape': + e.preventDefault(); + hideThemes(); + break; + case 'ArrowUp': + e.preventDefault(); + var li = document.activeElement.parentElement; + if (li && li.previousElementSibling) { + li.previousElementSibling.querySelector('button').focus(); + } + break; + case 'ArrowDown': + e.preventDefault(); + var li = document.activeElement.parentElement; + if (li && li.nextElementSibling) { + li.nextElementSibling.querySelector('button').focus(); + } + break; + case 'Home': + e.preventDefault(); + themePopup.querySelector('li:first-child button').focus(); + break; + case 'End': + e.preventDefault(); + themePopup.querySelector('li:last-child button').focus(); + break; + } + }); +})(); + +(function sidebar() { + var html = document.querySelector("html"); + var sidebar = document.getElementById("sidebar"); + var sidebarLinks = document.querySelectorAll('#sidebar a'); + var sidebarToggleButton = document.getElementById("sidebar-toggle"); + var sidebarResizeHandle = document.getElementById("sidebar-resize-handle"); + var firstContact = null; + + function showSidebar() { + html.classList.remove('sidebar-hidden') + html.classList.add('sidebar-visible'); + Array.from(sidebarLinks).forEach(function (link) { + link.setAttribute('tabIndex', 0); + }); + sidebarToggleButton.setAttribute('aria-expanded', true); + sidebar.setAttribute('aria-hidden', false); + try { localStorage.setItem('mdbook-sidebar', 'visible'); } catch (e) { } + } + + + var sidebarAnchorToggles = document.querySelectorAll('#sidebar a.toggle'); + + function toggleSection(ev) { + ev.currentTarget.parentElement.classList.toggle('expanded'); + } + + Array.from(sidebarAnchorToggles).forEach(function (el) { + el.addEventListener('click', toggleSection); + }); + + function hideSidebar() { + html.classList.remove('sidebar-visible') + html.classList.add('sidebar-hidden'); + Array.from(sidebarLinks).forEach(function (link) { + link.setAttribute('tabIndex', -1); + }); + sidebarToggleButton.setAttribute('aria-expanded', false); + sidebar.setAttribute('aria-hidden', true); + try { localStorage.setItem('mdbook-sidebar', 'hidden'); } catch (e) { } + } + + // Toggle sidebar + sidebarToggleButton.addEventListener('click', function sidebarToggle() { + if (html.classList.contains("sidebar-hidden")) { + var current_width = parseInt( + document.documentElement.style.getPropertyValue('--sidebar-width'), 10); + if (current_width < 150) { + document.documentElement.style.setProperty('--sidebar-width', '150px'); + } + showSidebar(); + } else if (html.classList.contains("sidebar-visible")) { + hideSidebar(); + } else { + if (getComputedStyle(sidebar)['transform'] === 'none') { + hideSidebar(); + } else { + showSidebar(); + } + } + }); + + sidebarResizeHandle.addEventListener('mousedown', initResize, false); + + function initResize(e) { + window.addEventListener('mousemove', resize, false); + window.addEventListener('mouseup', stopResize, false); + html.classList.add('sidebar-resizing'); + } + function resize(e) { + var pos = (e.clientX - sidebar.offsetLeft); + if (pos < 20) { + hideSidebar(); + } else { + if (html.classList.contains("sidebar-hidden")) { + showSidebar(); + } + pos = Math.min(pos, window.innerWidth - 100); + document.documentElement.style.setProperty('--sidebar-width', pos + 'px'); + } + } + //on mouseup remove windows functions mousemove & mouseup + function stopResize(e) { + html.classList.remove('sidebar-resizing'); + window.removeEventListener('mousemove', resize, false); + window.removeEventListener('mouseup', stopResize, false); + } + + document.addEventListener('touchstart', function (e) { + firstContact = { + x: e.touches[0].clientX, + time: Date.now() + }; + }, { passive: true }); + + document.addEventListener('touchmove', function (e) { + if (!firstContact) + return; + + var curX = e.touches[0].clientX; + var xDiff = curX - firstContact.x, + tDiff = Date.now() - firstContact.time; + + if (tDiff < 250 && Math.abs(xDiff) >= 150) { + if (xDiff >= 0 && firstContact.x < Math.min(document.body.clientWidth * 0.25, 300)) + showSidebar(); + else if (xDiff < 0 && curX < 300) + hideSidebar(); + + firstContact = null; + } + }, { passive: true }); + + // Scroll sidebar to current active section + var activeSection = document.getElementById("sidebar").querySelector(".active"); + if (activeSection) { + // https://developer.mozilla.org/en-US/docs/Web/API/Element/scrollIntoView + activeSection.scrollIntoView({ block: 'center' }); + } +})(); + +(function chapterNavigation() { + document.addEventListener('keydown', function (e) { + if (e.altKey || e.ctrlKey || e.metaKey || e.shiftKey) { return; } + if (window.search && window.search.hasFocus()) { return; } + + switch (e.key) { + case 'ArrowRight': + e.preventDefault(); + var nextButton = document.querySelector('.nav-chapters.next'); + if (nextButton) { + window.location.href = nextButton.href; + } + break; + case 'ArrowLeft': + e.preventDefault(); + var previousButton = document.querySelector('.nav-chapters.previous'); + if (previousButton) { + window.location.href = previousButton.href; + } + break; + } + }); +})(); + +(function clipboard() { + var clipButtons = document.querySelectorAll('.clip-button'); + + function hideTooltip(elem) { + elem.firstChild.innerText = ""; + elem.className = 'fa fa-copy clip-button'; + } + + function showTooltip(elem, msg) { + elem.firstChild.innerText = msg; + elem.className = 'fa fa-copy tooltipped'; + } + + var clipboardSnippets = new ClipboardJS('.clip-button', { + text: function (trigger) { + hideTooltip(trigger); + let playground = trigger.closest("pre"); + return playground_text(playground); + } + }); + + Array.from(clipButtons).forEach(function (clipButton) { + clipButton.addEventListener('mouseout', function (e) { + hideTooltip(e.currentTarget); + }); + }); + + clipboardSnippets.on('success', function (e) { + e.clearSelection(); + showTooltip(e.trigger, "Copied!"); + }); + + clipboardSnippets.on('error', function (e) { + showTooltip(e.trigger, "Clipboard error!"); + }); +})(); + +(function scrollToTop () { + var menuTitle = document.querySelector('.menu-title'); + + menuTitle.addEventListener('click', function () { + document.scrollingElement.scrollTo({ top: 0, behavior: 'smooth' }); + }); +})(); + +(function controllMenu() { + var menu = document.getElementById('menu-bar'); + + (function controllPosition() { + var scrollTop = document.scrollingElement.scrollTop; + var prevScrollTop = scrollTop; + var minMenuY = -menu.clientHeight - 50; + // When the script loads, the page can be at any scroll (e.g. if you reforesh it). + menu.style.top = scrollTop + 'px'; + // Same as parseInt(menu.style.top.slice(0, -2), but faster + var topCache = menu.style.top.slice(0, -2); + menu.classList.remove('sticky'); + var stickyCache = false; // Same as menu.classList.contains('sticky'), but faster + document.addEventListener('scroll', function () { + scrollTop = Math.max(document.scrollingElement.scrollTop, 0); + // `null` means that it doesn't need to be updated + var nextSticky = null; + var nextTop = null; + var scrollDown = scrollTop > prevScrollTop; + var menuPosAbsoluteY = topCache - scrollTop; + if (scrollDown) { + nextSticky = false; + if (menuPosAbsoluteY > 0) { + nextTop = prevScrollTop; + } + } else { + if (menuPosAbsoluteY > 0) { + nextSticky = true; + } else if (menuPosAbsoluteY < minMenuY) { + nextTop = prevScrollTop + minMenuY; + } + } + if (nextSticky === true && stickyCache === false) { + menu.classList.add('sticky'); + stickyCache = true; + } else if (nextSticky === false && stickyCache === true) { + menu.classList.remove('sticky'); + stickyCache = false; + } + if (nextTop !== null) { + menu.style.top = nextTop + 'px'; + topCache = nextTop; + } + prevScrollTop = scrollTop; + }, { passive: true }); + })(); + (function controllBorder() { + menu.classList.remove('bordered'); + document.addEventListener('scroll', function () { + if (menu.offsetTop === 0) { + menu.classList.remove('bordered'); + } else { + menu.classList.add('bordered'); + } + }, { passive: true }); + })(); +})(); diff --git a/clipboard.min.js b/clipboard.min.js new file mode 100644 index 000000000..02c549e35 --- /dev/null +++ b/clipboard.min.js @@ -0,0 +1,7 @@ +/*! + * clipboard.js v2.0.4 + * https://zenorocha.github.io/clipboard.js + * + * Licensed MIT © Zeno Rocha + */ +!function(t,e){"object"==typeof exports&&"object"==typeof module?module.exports=e():"function"==typeof define&&define.amd?define([],e):"object"==typeof exports?exports.ClipboardJS=e():t.ClipboardJS=e()}(this,function(){return function(n){var o={};function r(t){if(o[t])return o[t].exports;var e=o[t]={i:t,l:!1,exports:{}};return n[t].call(e.exports,e,e.exports,r),e.l=!0,e.exports}return r.m=n,r.c=o,r.d=function(t,e,n){r.o(t,e)||Object.defineProperty(t,e,{enumerable:!0,get:n})},r.r=function(t){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(t,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(t,"__esModule",{value:!0})},r.t=function(e,t){if(1&t&&(e=r(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var n=Object.create(null);if(r.r(n),Object.defineProperty(n,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var o in e)r.d(n,o,function(t){return e[t]}.bind(null,o));return n},r.n=function(t){var e=t&&t.__esModule?function(){return t.default}:function(){return t};return r.d(e,"a",e),e},r.o=function(t,e){return Object.prototype.hasOwnProperty.call(t,e)},r.p="",r(r.s=0)}([function(t,e,n){"use strict";var r="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t},i=function(){function o(t,e){for(var n=0;n; -type TreeBackend = FieldElementBackend; - -fn merkle_tree_benchmarks(c: &mut Criterion) { - let mut group = c.benchmark_group("Merkle Tree"); - group.sample_size(10); - group.measurement_time(Duration::from_secs(30)); - - // NOTE: the values to hash don't really matter, so let's go with the easy ones. - - let unhashed_leaves: Vec<_> = core::iter::successors(Some(FE::zero()), |s| Some(s + FE::one())) - .take((1 << 20) + 1) - .collect(); - // `(1 << 20) + 1` exploits worst cases in terms of rounding up to powers of 2. - - group.bench_with_input( - "build", - unhashed_leaves.as_slice(), - |bench, unhashed_leaves| { - bench.iter_with_large_drop(|| MerkleTree::::build(unhashed_leaves)); - }, - ); - - group.finish(); -} - -criterion_group!(merkle_tree, merkle_tree_benchmarks); -criterion_main!(merkle_tree); diff --git a/crates/crypto/benches/criterion_pedersen.rs b/crates/crypto/benches/criterion_pedersen.rs deleted file mode 100644 index b9cf02da6..000000000 --- a/crates/crypto/benches/criterion_pedersen.rs +++ /dev/null @@ -1,27 +0,0 @@ -use criterion::{black_box, criterion_group, criterion_main, Criterion}; -use lambdaworks_crypto::hash::pedersen::Pedersen; -use lambdaworks_crypto::hash::pedersen::PedersenStarkCurve; -use lambdaworks_math::field::element::FieldElement; -use lambdaworks_math::field::fields::fft_friendly::stark_252_prime_field::Stark252PrimeField; -use lambdaworks_math::traits::ByteConversion; -use rand::{RngCore, SeedableRng}; -use rand_chacha::ChaCha8Rng; - -fn pedersen_benchmarks(c: &mut Criterion) { - let mut rng = ChaCha8Rng::seed_from_u64(2); - let mut felt1: [u8; 32] = Default::default(); - rng.fill_bytes(&mut felt1); - let mut felt2: [u8; 32] = Default::default(); - rng.fill_bytes(&mut felt2); - - let x = FieldElement::::from_bytes_be(&felt1).unwrap(); - let y = FieldElement::::from_bytes_be(&felt2).unwrap(); - let mut group = c.benchmark_group("Pedersen Benchmark"); - - // Benchmark with black_box is 0.41% faster - group.bench_function("Hashing with black_box", |bench| { - bench.iter(|| black_box(PedersenStarkCurve::hash(&x, &y))) - }); -} -criterion_group!(pedersen, pedersen_benchmarks); -criterion_main!(pedersen); diff --git a/crates/crypto/benches/criterion_poseidon.rs b/crates/crypto/benches/criterion_poseidon.rs deleted file mode 100644 index 853027b68..000000000 --- a/crates/crypto/benches/criterion_poseidon.rs +++ /dev/null @@ -1,26 +0,0 @@ -use criterion::{black_box, criterion_group, criterion_main, Criterion}; -use lambdaworks_crypto::hash::poseidon::starknet::PoseidonCairoStark252; -use lambdaworks_crypto::hash::poseidon::Poseidon; -use lambdaworks_math::field::element::FieldElement; -use lambdaworks_math::field::fields::fft_friendly::stark_252_prime_field::Stark252PrimeField; -use lambdaworks_math::traits::ByteConversion; -use rand::{RngCore, SeedableRng}; -use rand_chacha::ChaCha8Rng; - -fn poseidon_benchmarks(c: &mut Criterion) { - let mut rng = ChaCha8Rng::seed_from_u64(2); - let mut felt1: [u8; 32] = Default::default(); - rng.fill_bytes(&mut felt1); - let mut felt2: [u8; 32] = Default::default(); - rng.fill_bytes(&mut felt2); - - let x = FieldElement::::from_bytes_be(&felt1).unwrap(); - let y = FieldElement::::from_bytes_be(&felt2).unwrap(); - let mut group = c.benchmark_group("Poseidon Benchmark"); - - group.bench_function("Hashing with black_box", |bench| { - bench.iter(|| black_box(PoseidonCairoStark252::hash(&x, &y))) - }); -} -criterion_group!(poseidon, poseidon_benchmarks); -criterion_main!(poseidon); diff --git a/crates/crypto/benches/iai_merkle.rs b/crates/crypto/benches/iai_merkle.rs deleted file mode 100644 index 183c01057..000000000 --- a/crates/crypto/benches/iai_merkle.rs +++ /dev/null @@ -1,46 +0,0 @@ -use core::hint::black_box; -use lambdaworks_crypto::{ - hash::sha3::Sha3Hasher, - merkle_tree::{backends::field_element::FieldElementBackend, merkle::MerkleTree}, -}; -use lambdaworks_math::{ - field::element::FieldElement, - field::fields::fft_friendly::stark_252_prime_field::Stark252PrimeField, -}; -use sha3::Keccak256; - -type F = Stark252PrimeField; -type FE = FieldElement; - -type TreeBackend = FieldElementBackend; - -#[inline(never)] -#[export_name = "util::build_unhashed_leaves"] -fn build_unhashed_leaves() -> Vec { - // NOTE: the values to hash don't really matter, so let's go with the easy ones. - core::iter::successors(Some(FE::zero()), |s| Some(s + FE::one())) - // `(1 << 20) + 1` exploits worst cases in terms of rounding up to powers of 2. - .take((1 << 20) + 1) - .collect() -} - -#[inline(never)] -#[export_name = "util::build_hasher"] -fn build_hasher() -> Box { - Box::new(Sha3Hasher::new()) -} - -#[inline(never)] -fn merkle_tree_build_benchmark() { - let unhashed_leaves = build_unhashed_leaves(); - let result = black_box(MerkleTree::::build(black_box( - &unhashed_leaves, - ))); - // Let's not count `drop` in our timings. - core::mem::drop(result); -} - -iai_callgrind::main!( - callgrind_args = "toggle-collect=util::*,core::mem::drop"; - functions = merkle_tree_build_benchmark, -); diff --git a/crates/crypto/src/commitments/README.md b/crates/crypto/src/commitments/README.md deleted file mode 100644 index a0fc89b2b..000000000 --- a/crates/crypto/src/commitments/README.md +++ /dev/null @@ -1,195 +0,0 @@ -# lambdaworks Polynomial Commitment Schemes - -This folder contains lambdaworks polynomial commitment schemes (PCS). The following commitment schemes are supported: -- [KZG10](https://www.iacr.org/archive/asiacrypt2010/6477178/6477178.pdf) - -## Introduction to KZG commitment scheme - -The Kate, Zaverucha, Goldberg (KZG) commitment is a polynomial commitment scheme that works over pairing-friendly elliptic curves, such as BN-254 and BLS12-381. It is important to have the following notation in mind: -- $\mathbb{F_p }$ is the base field of the curve, defined by the prime $p$. -- $\mathbb{F_r }$ is the scalar field associated with the curve, defined by the prime $r$. -- $G_1$ is the largest subgroup/group of prime order of the elliptic curve (the number of elements in the subgroup/group is $r$). -- $G_2$ is the subgroup/group of prime order (equal to $r$) of the twist curve. -- $G_t$ is the multiplicative subgroup of the $r$-th roots of unity of an extension field. For BN-254 and BLS12-381, the extension field is $\mathbb{F_{p^{12} }}$ (a degree twelve extension) and each element of $x \in G_t$ satisfies that $x^r = 1$. - -Throughout, we will use the additive notation for the groups $G_1$ and $G_2$ and multiplicative notation for $G_t$. So, given elements $x , y \in G_1$, we will write $x + y = z$, but if $x , y \in G_t$, we have $x \times y = z$. - -An elliptic curve is given by the pairs of points $(x , y)$ in $\mathbb{F_p } \times \mathbb{F_p }$, satisfying the equation $y^2 = x^3 + a x + b$. We can find a cyclic group/subgroup of order $r$ that satisfy the equation. This is the group $G_1$. - -In a similar way, the group $G_2$ is given by a cyclic group of prime order $r$ that satisfied the twisted curve's equation $y^2 = x^3 + a^\prime x + b^\prime$, where $x, y$ live in an extension field of $\mathbb{F_p }$, typically $\mathbb{F_{p^2 } }$. - -Given that both $G_1$, $G_2$ and $G_t$ are cyclic groups, we have elements $g_1$, $g_2$ and $g_t$, called generators, such that when we apply the group operation repeatedly, we span all the elements in the group. For notation purposes, we will denote $[a]_1 = a g_1 = g_1 + g_1 + g_1 + g_1 + ... + g_1$, where we add $a$ copies of $g_1$. Similarly, $[a]_2 = a g_2$ and $[a]_t = g_t^{a}$. More concretely, $\{ g_1 , 2g_1 , 3g_1 , 4g_1 , \dots (r - 1)g_1 \} = G_1$. Note that if we do $m g_1$, and $m \geq r$, then this will yield the same as $s g_1$ where $s \equiv m \pmod{r}$ and $0 \leq s \leq r - 1$. - -The whole scheme depends on a pairing function (also known as bilinear map) $e: G_1 \times G_2 \rightarrow G_t$ which satisfies the following properties: -- $e(x , y) \neq 1$ if $x \neq \mathcal{O}$ and $y \neq \mathcal{O}$ (non-degeneracy). -- $e([a]_1 , [b]_2 ) = \left( e(g_1 , g_2 ) \right)^{a b} = {g_t }^{ab}$ (bilinearity). - -There are two parties, prover and verifier. They share a public Structured Reference String (SRS) or trusted setup, given by: -- $\{ g_1 , [\tau]_1 , [\tau^2 ]_1 , \dots , [\tau^{n - 1} ]_1 \}$ consisting of the powers of a random, yet secret number $\tau$, with degree bounded by $n - 1$, hidden inside $G_1$ -- $\{ g_2 , [\tau]_2 \}$ = $\{ g_2 , \tau g_2 \}$ contains the generator and $\tau$ hidden inside $G_2$ (for practical purposes, we don't need additional powers). - -Knowing these sets of points does not allow recovering the secret $\tau$ or any of its powers (security under the algebraic model). - -A polynomial of degree bound by $n - 1$ is an expression $p(x) = a_0 + a_1 x + a_2 x^2 + \dots + a_{n - 1} x^{n - 1}$. The coefficient $a_k \neq 0$ accompanying the largest power is called the degree of the polynomial $\mathrm{deg} (p)$. The coefficients of the polynomial belong to the field $\mathbb{F_r }$. We can commit to a polynomial of degree at most $n - 1$ by performing the following multiscalar multiplication (MSM): - -$\mathrm{cm} (p) = a_0 g_1 + a_1 [\tau]_1 + a_2 [\tau^2]_1 + \dots + a_{n - 1} [\tau^{n - 1}]_1 = P$ - -This operation works, since we have points over an elliptic curve, and multiplication by a scalar and addition are defined properly. The commitment to $P$ is a point on the elliptic curve, which is equal to $p(\tau ) g_1 = P$. This commitment achieves the two properties we need: -- Hiding -- Binding - -Given a commitment to $p$, we can prove evaluations of $p$ at points $z$. We will focus on the simplest case, where we want to show that $p(z) = y$, where $z$ and $y$ live in $\mathbb{F_r}$. The following fact will help us prove the evaluation (it is called the [polynomial remainder theorem](https://en.wikipedia.org/wiki/Polynomial_remainder_theorem)): - -If $p(z) = y$ then $p^\prime = p(x) - y$ is divisible by $x - z$. Another way to state this is that there exists a polynomial $q(x)$ of degree $\mathrm{deg} (p) - 1$ such that $p(x) - y = p^\prime (x) = (x - z) q(x)$. - -Providing the quotient would allow the verifier to check the evaluation, but the problem is that the verifier does not know $p(x)$ in full. Given that $\tau$ is a secret point at random, we could check the previous equality in just one point, $\tau$, that is: - -$p(\tau ) - y = (\tau - z) q(\tau )$ - -Due to the [Schwartz-Zippel lemma](https://en.wikipedia.org/wiki/Schwartz%E2%80%93Zippel_lemma), if the equality above holds, then, with high probability $p(x) - y = p^\prime (x) = (x - z) q(x)$. We could send, therefore $Q = \mathrm{cm} (q) = q(\tau ) g_1$ using the MSM. If $q(x) = b_0 + b_1 x + b_2 x^2 + \dots + b_{n - 1} x^{n - 2}$, - -$\mathrm{cm} (q) = b_0 g_1 + b_1 [\tau]_1 + \dots b_{n - 2} [\tau^{n - 2}]_1 = Q$ - -In the context of EIP-4844, $P$ is called the commitment and $Q$ is the evaluation proof. We can use the pairing to check the equality at $\tau$. We compute the following: -- $e( P - y g_1 , g_2 ) = \left( e(g_1 , g_2 ) \right)^{ p(\tau ) - y }$ -- $e( Q , [\tau]_2 - z g_2 ) = \left( e(g_1 , g_2 ) \right)^{ q(\tau ) (\tau - z)}$ - -If the two pairings are equal, this means that $p(\tau ) - y \equiv q(\tau ) (\tau - z) \pmod{r}$. In practice, we use an alternative formulation, - -$e( P - y g_1 , - g_2 ) \times e( Q , [\tau]_2 - z g_2 ) \equiv 1 \pmod{r}$ - -This is more efficient, since we can compute the pairing using two Miller loops but just one final exponentiation. - - -## Implementation - -The implementation in this codebase includes: - -- `StructuredReferenceString`: Stores the powers of a secret value in both G1 and G2 groups -- `KateZaveruchaGoldberg`: The main implementation of the KZG commitment scheme -- Support for both single and batch openings/verifications - -## API Usage - -KZG commitments can be used to commit to polynomials and later prove evaluations at specific points. Here's how to use the KZG implementation in lambdaworks: - -### Creating a KZG Instance - -First, you need to load or create a Structured Reference String (SRS) and initialize the KZG instance: - -```rust -use lambdaworks_crypto::commitments::kzg::{KateZaveruchaGoldberg, StructuredReferenceString}; -use lambdaworks_crypto::commitments::traits::IsCommitmentScheme; -use lambdaworks_math::elliptic_curve::short_weierstrass::curves::bls12_381::{ - curve::BLS12381Curve, - default_types::{FrElement, FrField}, - pairing::BLS12381AtePairing, - twist::BLS12381TwistCurve, -}; -use lambdaworks_math::elliptic_curve::short_weierstrass::point::ShortWeierstrassProjectivePoint; - -// Load SRS from a file -let srs_file = "path/to/srs.bin"; -let srs = StructuredReferenceString::from_file(srs_file).unwrap(); - -// Create a KZG instance -let kzg = KateZaveruchaGoldberg::::new(srs); -``` - -### Committing to a Polynomial - -To commit to a polynomial, you first create the polynomial and then use the `commit` method: - -```rust -use lambdaworks_math::field::element::FieldElement; -use lambdaworks_math::polynomial::Polynomial; - -// Create a polynomial p(x) = x + 1 -let p = Polynomial::::new(&[FieldElement::one(), FieldElement::one()]); - -// Commit to the polynomial -let commitment = kzg.commit(&p); -``` - -### Generating and Verifying Proofs - -To prove that a polynomial evaluates to a specific value at a specific point: - -```rust -// Choose a point to evaluate the polynomial -let x = -FieldElement::one(); - -// Compute the evaluation -let y = p.evaluate(&x); // Should be 0 for p(x) = x + 1 when x = -1 - -// Generate a proof for this evaluation -let proof = kzg.open(&x, &y, &p); - -// Verify the proof -let is_valid = kzg.verify(&x, &y, &commitment, &proof); -assert!(is_valid, "Proof verification failed"); -``` - -### Batch Operations - -KZG supports batch operations for more efficient verification of multiple polynomial evaluations: - -```rust -// Create polynomials -let p0 = Polynomial::::new(&[FieldElement::from(9000)]); // Constant polynomial -let p1 = Polynomial::::new(&[ - FieldElement::from(1), - FieldElement::from(2), - -FieldElement::from(1), -]); // p(x) = 1 + 2x - x² - -// Commit to the polynomials -let p0_commitment = kzg.commit(&p0); -let p1_commitment = kzg.commit(&p1); - -// Choose a point to evaluate the polynomials -let x = FieldElement::from(3); - -// Compute the evaluations -let y0 = p0.evaluate(&x); // 9000 -let y1 = p1.evaluate(&x); // 1 + 2*3 - 3² = 1 + 6 - 9 = -2 - -// Generate a random field element for the batch proof -let upsilon = &FieldElement::from(1); // In practice, use a random value - -// Generate batch proof -let proof = kzg.open_batch(&x, &[y0.clone(), y1.clone()], &[p0, p1], upsilon); - -// Verify batch proof -let is_valid = kzg.verify_batch( - &x, - &[y0, y1], - &[p0_commitment, p1_commitment], - &proof, - upsilon -); -assert!(is_valid, "Batch proof verification failed"); -``` - -### Serialization and Deserialization - -The SRS can be serialized and deserialized for storage and transmission: - -```rust -// Serialize the SRS -let bytes = srs.as_bytes(); - -// Deserialize the SRS -let deserialized_srs = StructuredReferenceString::< - ShortWeierstrassProjectivePoint, - ShortWeierstrassProjectivePoint, ->::deserialize(&bytes).unwrap(); -``` - -## References - -- [Constantine](https://github.com/mratsim/constantine/blob/master/constantine/commitments/kzg.nim) -- [EIP-4844](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-4844.md) -- [KZG proof verification](https://github.com/ethereum/consensus-specs/blob/v1.4.0-beta.1/specs/deneb/polynomial-commitments.md#verify_kzg_proof_batch) -- [Multiproofs KZG](https://dankradfeist.de/ethereum/2021/06/18/pcs-multiproofs.html) -- [Fast amortized KZG proofs](https://eprint.iacr.org/2023/033) diff --git a/crates/crypto/src/commitments/kzg.rs b/crates/crypto/src/commitments/kzg.rs deleted file mode 100644 index ea3ee6721..000000000 --- a/crates/crypto/src/commitments/kzg.rs +++ /dev/null @@ -1,455 +0,0 @@ -use super::traits::IsCommitmentScheme; -use alloc::{borrow::ToOwned, vec::Vec}; -use core::{marker::PhantomData, mem}; -use lambdaworks_math::{ - cyclic_group::IsGroup, - elliptic_curve::traits::IsPairing, - errors::DeserializationError, - field::{element::FieldElement, traits::IsPrimeField}, - msm::pippenger::msm, - polynomial::Polynomial, - traits::{AsBytes, Deserializable}, - unsigned_integer::element::UnsignedInteger, -}; - -#[derive(PartialEq, Clone, Debug)] -pub struct StructuredReferenceString { - /// Vector of points in G1 encoding g1, s g1, s^2 g1, s^3 g1, ... s^n g1 - pub powers_main_group: Vec, - /// Slice of points in G2 encoding g2, s g2 - /// We could relax this to include more powers, but for most applications - /// this suffices - pub powers_secondary_group: [G2Point; 2], -} - -impl StructuredReferenceString -where - G1Point: IsGroup, - G2Point: IsGroup, -{ - /// Creates a new SRS from slices of G1points and a slice of length 2 of G2 points - pub fn new(powers_main_group: &[G1Point], powers_secondary_group: &[G2Point; 2]) -> Self { - Self { - powers_main_group: powers_main_group.into(), - powers_secondary_group: powers_secondary_group.clone(), - } - } -} - -#[cfg(feature = "std")] -impl StructuredReferenceString -where - G1Point: IsGroup + Deserializable, - G2Point: IsGroup + Deserializable, -{ - /// Read SRS from file - pub fn from_file(file_path: &str) -> Result { - let bytes = std::fs::read(file_path)?; - Ok(Self::deserialize(&bytes)?) - } -} - -impl AsBytes for StructuredReferenceString -where - G1Point: IsGroup + AsBytes, - G2Point: IsGroup + AsBytes, -{ - /// Serialize SRS - fn as_bytes(&self) -> Vec { - let mut serialized_data: Vec = Vec::new(); - // First 4 bytes encodes protocol version - let protocol_version: [u8; 4] = [0; 4]; - - serialized_data.extend(&protocol_version); - - // Second 8 bytes store the amount of G1 elements to be stored, this is more than can be indexed with a 64-bit architecture, and some millions of terabytes of data if the points were compressed - let mut main_group_len_bytes: Vec = self.powers_main_group.len().to_le_bytes().to_vec(); - - // For architectures with less than 64 bits for pointers - // We add extra zeros at the end - while main_group_len_bytes.len() < 8 { - main_group_len_bytes.push(0) - } - - serialized_data.extend(&main_group_len_bytes); - - // G1 elements - for point in &self.powers_main_group { - serialized_data.extend(point.as_bytes()); - } - - // G2 elements - for point in &self.powers_secondary_group { - serialized_data.extend(point.as_bytes()); - } - - serialized_data - } -} - -impl Deserializable for StructuredReferenceString -where - G1Point: IsGroup + Deserializable, - G2Point: IsGroup + Deserializable, -{ - fn deserialize(bytes: &[u8]) -> Result { - const MAIN_GROUP_LEN_OFFSET: usize = 4; - const MAIN_GROUP_OFFSET: usize = 12; - - let main_group_len_u64 = u64::from_le_bytes( - // This unwrap can't fail since we are fixing the size of the slice - bytes[MAIN_GROUP_LEN_OFFSET..MAIN_GROUP_OFFSET] - .try_into() - .unwrap(), - ); - - let main_group_len = usize::try_from(main_group_len_u64) - .map_err(|_| DeserializationError::PointerSizeError)?; - - let mut main_group: Vec = Vec::new(); - let mut secondary_group: Vec = Vec::new(); - - let size_g1_point = mem::size_of::(); - let size_g2_point = mem::size_of::(); - - for i in 0..main_group_len { - // The second unwrap shouldn't fail since the amount of bytes is fixed - let point = G1Point::deserialize( - bytes[i * size_g1_point + MAIN_GROUP_OFFSET - ..i * size_g1_point + size_g1_point + MAIN_GROUP_OFFSET] - .try_into() - .unwrap(), - )?; - main_group.push(point); - } - - let g2s_offset = size_g1_point * main_group_len + 12; - for i in 0..2 { - // The second unwrap shouldn't fail since the amount of bytes is fixed - let point = G2Point::deserialize( - bytes[i * size_g2_point + g2s_offset - ..i * size_g2_point + g2s_offset + size_g2_point] - .try_into() - .unwrap(), - )?; - secondary_group.push(point); - } - - let secondary_group_slice = [secondary_group[0].clone(), secondary_group[1].clone()]; - - let srs = StructuredReferenceString::new(&main_group, &secondary_group_slice); - Ok(srs) - } -} - -#[derive(Clone)] -pub struct KateZaveruchaGoldberg { - srs: StructuredReferenceString, - phantom: PhantomData, -} - -impl KateZaveruchaGoldberg { - pub fn new(srs: StructuredReferenceString) -> Self { - Self { - srs, - phantom: PhantomData, - } - } -} - -impl>, P: IsPairing> - IsCommitmentScheme for KateZaveruchaGoldberg -{ - type Commitment = P::G1Point; - - /// Given a polynomial and an SRS, creates a commitment to p(x), which corresponds to a G1 point - /// The commitment is p(s) g1, evaluated as \sum_i c_i srs.powers_main_group[i], where c_i are the coefficients - /// of the polynomial. - fn commit(&self, p: &Polynomial>) -> Self::Commitment { - let coefficients: Vec<_> = p - .coefficients - .iter() - .map(|coefficient| coefficient.representative()) - .collect(); - msm( - &coefficients, - &self.srs.powers_main_group[..coefficients.len()], - ) - .expect("`points` is sliced by `cs`'s length") - } - - /// Creates an evaluation proof for the polynomial p at x equal to y. - /// This is a commitment to the quotient polynomial q(t) = (p(t) - y)/(t - x) - /// The commitment is simply q(s) g1, corresponding to a G1 point - fn open( - &self, - x: &FieldElement, - y: &FieldElement, - p: &Polynomial>, - ) -> Self::Commitment { - let mut poly_to_commit = p - y; - poly_to_commit.ruffini_division_inplace(x); - self.commit(&poly_to_commit) - } - - /// Verifies the correct evaluation of a polynomial p by providing a commitment to p, - /// the point x, the evaluation y (p(x) = y) and an evaluation proof (commitment to the quotient polynomial) - /// Basically, we want to show that, at secret point s, p(s) - y = (s - x) q(s) - /// It uses pairings to verify the above condition, e(cm(p) - yg1,g2)*(cm(q), sg2 - xg2)^-1 - /// Returns true for valid evaluation - fn verify( - &self, - x: &FieldElement, - y: &FieldElement, - p_commitment: &Self::Commitment, - proof: &Self::Commitment, - ) -> bool { - let g1 = &self.srs.powers_main_group[0]; - let g2 = &self.srs.powers_secondary_group[0]; - let alpha_g2 = &self.srs.powers_secondary_group[1]; - - let e = P::compute_batch(&[ - ( - &p_commitment.operate_with(&(g1.operate_with_self(y.representative())).neg()), - g2, - ), - ( - &proof.neg(), - &(alpha_g2.operate_with(&(g2.operate_with_self(x.representative())).neg())), - ), - ]); - e == Ok(FieldElement::one()) - } - - /// Creates an evaluation proof for several polynomials at a single point x. upsilon is used to - /// perform the random linear combination, using Horner's evaluation form - fn open_batch( - &self, - x: &FieldElement, - ys: &[FieldElement], - polynomials: &[Polynomial>], - upsilon: &FieldElement, - ) -> Self::Commitment { - let acc_polynomial = polynomials - .iter() - .rev() - .fold(Polynomial::zero(), |acc, polynomial| { - acc * upsilon.to_owned() + polynomial - }); - - let acc_y = ys - .iter() - .rev() - .fold(FieldElement::zero(), |acc, y| acc * upsilon.to_owned() + y); - - self.open(x, &acc_y, &acc_polynomial) - } - - /// Verifies an evaluation proof for the evaluation of a batch of polynomials at x, using upsilon to perform the random - /// linear combination - /// Outputs true if the evaluation is correct - fn verify_batch( - &self, - x: &FieldElement, - ys: &[FieldElement], - p_commitments: &[Self::Commitment], - proof: &Self::Commitment, - upsilon: &FieldElement, - ) -> bool { - let acc_commitment = - p_commitments - .iter() - .rev() - .fold(P::G1Point::neutral_element(), |acc, point| { - acc.operate_with_self(upsilon.to_owned().representative()) - .operate_with(point) - }); - - let acc_y = ys - .iter() - .rev() - .fold(FieldElement::zero(), |acc, y| acc * upsilon.to_owned() + y); - self.verify(x, &acc_y, &acc_commitment, proof) - } -} - -#[cfg(test)] -mod tests { - use alloc::vec::Vec; - use core::slice; - use lambdaworks_math::{ - cyclic_group::IsGroup, - elliptic_curve::{ - short_weierstrass::{ - curves::bls12_381::{ - curve::BLS12381Curve, - default_types::{FrElement, FrField}, - pairing::BLS12381AtePairing, - twist::BLS12381TwistCurve, - }, - point::ShortWeierstrassProjectivePoint, - }, - traits::{IsEllipticCurve, IsPairing}, - }, - field::element::FieldElement, - polynomial::Polynomial, - traits::{AsBytes, Deserializable}, - unsigned_integer::element::U256, - }; - - use crate::commitments::traits::IsCommitmentScheme; - - use super::{KateZaveruchaGoldberg, StructuredReferenceString}; - use rand::Rng; - - type G1 = ShortWeierstrassProjectivePoint; - - #[allow(clippy::upper_case_acronyms)] - type KZG = KateZaveruchaGoldberg; - - fn create_srs() -> StructuredReferenceString< - ::G1Point, - ::G2Point, - > { - let mut rng = rand::thread_rng(); - let toxic_waste = FrElement::new(U256 { - limbs: [ - rng.gen::(), - rng.gen::(), - rng.gen::(), - rng.gen::(), - ], - }); - let g1 = BLS12381Curve::generator(); - let g2 = BLS12381TwistCurve::generator(); - let powers_main_group: Vec = (0..100) - .map(|exponent| { - g1.operate_with_self(toxic_waste.pow(exponent as u128).representative()) - }) - .collect(); - let powers_secondary_group = [ - g2.clone(), - g2.operate_with_self(toxic_waste.representative()), - ]; - StructuredReferenceString::new(&powers_main_group, &powers_secondary_group) - } - - #[test] - fn kzg_1() { - let kzg = KZG::new(create_srs()); - let p = Polynomial::::new(&[FieldElement::one(), FieldElement::one()]); - let p_commitment: ::G1Point = kzg.commit(&p); - let x = -FieldElement::one(); - let y = p.evaluate(&x); - let proof = kzg.open(&x, &y, &p); - assert_eq!(y, FieldElement::zero()); - assert_eq!(proof, BLS12381Curve::generator()); - assert!(kzg.verify(&x, &y, &p_commitment, &proof)); - } - - #[test] - fn poly_9000_constant_should_verify_proof() { - let kzg = KZG::new(create_srs()); - let p = Polynomial::new(&[FieldElement::from(9000)]); - let p_commitment: ::G1Point = kzg.commit(&p); - let x = FieldElement::one(); - let y = FieldElement::from(9000); - let proof = kzg.open(&x, &y, &p); - assert!(kzg.verify(&x, &y, &p_commitment, &proof)); - } - - #[test] - fn poly_9000_batched_should_verify() { - let kzg = KZG::new(create_srs()); - let p0 = Polynomial::::new(&[FieldElement::from(9000)]); - let p0_commitment: ::G1Point = kzg.commit(&p0); - - let x = FieldElement::one(); - let y0 = FieldElement::from(9000); - let upsilon = &FieldElement::from(1); - - let proof = kzg.open_batch(&x, slice::from_ref(&y0), &[p0], upsilon); - - assert!(kzg.verify_batch(&x, &[y0], &[p0_commitment], &proof, upsilon)); - } - - #[test] - fn two_poly_9000_batched_should_verify() { - let kzg = KZG::new(create_srs()); - let p0 = Polynomial::::new(&[FieldElement::from(9000)]); - let p0_commitment: ::G1Point = kzg.commit(&p0); - - let x = FieldElement::one(); - let y0 = FieldElement::from(9000); - let upsilon = &FieldElement::from(1); - - let proof = kzg.open_batch(&x, &[y0.clone(), y0.clone()], &[p0.clone(), p0], upsilon); - - assert!(kzg.verify_batch( - &x, - &[y0.clone(), y0], - &[p0_commitment.clone(), p0_commitment], - &proof, - upsilon - )); - } - - #[test] - fn two_poly_batched_should_verify() { - let kzg = KZG::new(create_srs()); - - let x = FieldElement::from(3); - - let p0 = Polynomial::::new(&[FieldElement::from(9000)]); // Constant polynomial - let p0_commitment: ::G1Point = kzg.commit(&p0); - let y0 = FieldElement::from(9000); - - let p1 = Polynomial::::new(&[ - FieldElement::from(1), - FieldElement::from(2), - -FieldElement::from(1), - ]); // p(x) = 1 + 2x - x² - let p1_commitment: ::G1Point = kzg.commit(&p1); - let y1 = p1.evaluate(&x); - - let upsilon = &FieldElement::from(1); - - let proof = kzg.open_batch(&x, &[y0.clone(), y1.clone()], &[p0, p1], upsilon); - - assert!(kzg.verify_batch( - &x, - &[y0, y1], - &[p0_commitment, p1_commitment], - &proof, - upsilon - )); - } - - #[test] - fn serialize_deserialize_srs() { - let srs = create_srs(); - let bytes = srs.as_bytes(); - let deserialized: StructuredReferenceString< - ShortWeierstrassProjectivePoint, - ShortWeierstrassProjectivePoint, - > = StructuredReferenceString::deserialize(&bytes).unwrap(); - - assert_eq!(srs, deserialized); - } - - #[test] - #[cfg(feature = "std")] - fn load_srs_from_file() { - type TestSrsType = StructuredReferenceString< - ShortWeierstrassProjectivePoint, - ShortWeierstrassProjectivePoint, - >; - - let base_dir = env!("CARGO_MANIFEST_DIR"); - let srs_file = base_dir.to_owned() + "/src/commitments/test_srs/srs_3_g1_elements.bin"; - - let srs = TestSrsType::from_file(&srs_file).unwrap(); - - assert_eq!(srs.powers_main_group.len(), 3); - } -} diff --git a/crates/crypto/src/commitments/mod.rs b/crates/crypto/src/commitments/mod.rs deleted file mode 100644 index 0278e8989..000000000 --- a/crates/crypto/src/commitments/mod.rs +++ /dev/null @@ -1,2 +0,0 @@ -pub mod kzg; -pub mod traits; diff --git a/crates/crypto/src/commitments/test_srs/srs_3_g1_elements.bin b/crates/crypto/src/commitments/test_srs/srs_3_g1_elements.bin deleted file mode 100644 index 90a46995d..000000000 Binary files a/crates/crypto/src/commitments/test_srs/srs_3_g1_elements.bin and /dev/null differ diff --git a/crates/crypto/src/commitments/traits.rs b/crates/crypto/src/commitments/traits.rs deleted file mode 100644 index f90a542d4..000000000 --- a/crates/crypto/src/commitments/traits.rs +++ /dev/null @@ -1,49 +0,0 @@ -use lambdaworks_math::{ - field::{element::FieldElement, traits::IsField}, - polynomial::Polynomial, -}; - -pub trait IsCommitmentScheme { - type Commitment; - - /// Create a commitment to a polynomial - fn commit(&self, p: &Polynomial>) -> Self::Commitment; - - /// Create an evaluation proof for a polynomial at x equal to y - /// p(x) = y - fn open( - &self, - x: &FieldElement, - y: &FieldElement, - p: &Polynomial>, - ) -> Self::Commitment; - - /// Create an evaluation proof for a slice of polynomials at x equal to y_i - /// that is, we show that we evaluated correctly p_i (x) = y_i - fn open_batch( - &self, - x: &FieldElement, - y: &[FieldElement], - p: &[Polynomial>], - upsilon: &FieldElement, - ) -> Self::Commitment; - - /// Verify an evaluation proof given the commitment to p, the point x and the evaluation y - fn verify( - &self, - x: &FieldElement, - y: &FieldElement, - p_commitment: &Self::Commitment, - proof: &Self::Commitment, - ) -> bool; - - /// Verify an evaluation proof given the commitments to p, the point x and the evaluations ys - fn verify_batch( - &self, - x: &FieldElement, - ys: &[FieldElement], - p_commitments: &[Self::Commitment], - proof: &Self::Commitment, - upsilon: &FieldElement, - ) -> bool; -} diff --git a/crates/crypto/src/errors.rs b/crates/crypto/src/errors.rs deleted file mode 100644 index 0a0d83c3a..000000000 --- a/crates/crypto/src/errors.rs +++ /dev/null @@ -1,37 +0,0 @@ -use std::io; - -use lambdaworks_math::errors::DeserializationError; - -#[derive(Debug)] -pub enum SrsFromFileError { - FileError(io::Error), - DeserializationError(lambdaworks_math::errors::DeserializationError), -} - -impl From for SrsFromFileError { - fn from(err: DeserializationError) -> SrsFromFileError { - match err { - DeserializationError::InvalidAmountOfBytes => { - SrsFromFileError::DeserializationError(DeserializationError::InvalidAmountOfBytes) - } - - DeserializationError::FieldFromBytesError => { - SrsFromFileError::DeserializationError(DeserializationError::FieldFromBytesError) - } - - DeserializationError::PointerSizeError => { - SrsFromFileError::DeserializationError(DeserializationError::PointerSizeError) - } - - DeserializationError::InvalidValue => { - SrsFromFileError::DeserializationError(DeserializationError::InvalidValue) - } - } - } -} - -impl From for SrsFromFileError { - fn from(err: std::io::Error) -> SrsFromFileError { - SrsFromFileError::FileError(err) - } -} diff --git a/crates/crypto/src/fiat_shamir/README.md b/crates/crypto/src/fiat_shamir/README.md deleted file mode 100644 index 8c5edb54d..000000000 --- a/crates/crypto/src/fiat_shamir/README.md +++ /dev/null @@ -1,5 +0,0 @@ -# Fiat-Shamir - -This contains the basic functionality to implement the [Fiat-Shamir heuristic](https://en.wikipedia.org/wiki/Fiat%E2%80%93Shamir_heuristic) ([see also](https://link.springer.com/chapter/10.1007/3-540-47721-7_12)). - -The transcript should be able to accept bytes or field elements and should output random challenges (field elements) from a uniform distribution. \ No newline at end of file diff --git a/crates/crypto/src/fiat_shamir/default_transcript.rs b/crates/crypto/src/fiat_shamir/default_transcript.rs deleted file mode 100644 index 766c84447..000000000 --- a/crates/crypto/src/fiat_shamir/default_transcript.rs +++ /dev/null @@ -1,119 +0,0 @@ -use super::is_transcript::IsTranscript; -use core::marker::PhantomData; -use lambdaworks_math::{ - field::{element::FieldElement, traits::HasDefaultTranscript}, - traits::ByteConversion, -}; -use rand_chacha::{rand_core::SeedableRng, ChaCha20Rng}; -use sha3::{Digest, Keccak256}; - -pub struct DefaultTranscript { - hasher: Keccak256, - phantom: PhantomData, -} - -impl DefaultTranscript -where - F: HasDefaultTranscript, - FieldElement: ByteConversion, -{ - pub fn new(data: &[u8]) -> Self { - let mut res = Self { - hasher: Keccak256::new(), - phantom: PhantomData, - }; - res.append_bytes(data); - res - } - - pub fn sample(&mut self) -> [u8; 32] { - let mut result_hash = [0_u8; 32]; - result_hash.copy_from_slice(&self.hasher.finalize_reset()); - result_hash.reverse(); - self.hasher.update(result_hash); - result_hash - } -} - -impl Default for DefaultTranscript -where - F: HasDefaultTranscript, - FieldElement: ByteConversion, -{ - fn default() -> Self { - Self::new(&[]) - } -} - -impl IsTranscript for DefaultTranscript -where - F: HasDefaultTranscript, - FieldElement: ByteConversion, -{ - fn append_bytes(&mut self, new_bytes: &[u8]) { - self.hasher.update(new_bytes); - } - - fn append_field_element(&mut self, element: &FieldElement) { - self.append_bytes(&element.to_bytes_be()); - } - - fn state(&self) -> [u8; 32] { - self.hasher.clone().finalize().into() - } - - fn sample_field_element(&mut self) -> FieldElement { - let mut rng = ::from_seed(self.sample()); - F::get_random_field_element_from_rng(&mut rng) - } - - fn sample_u64(&mut self, upper_bound: u64) -> u64 { - u64::from_be_bytes(self.state()[..8].try_into().unwrap()) % upper_bound - } -} - -#[cfg(test)] -mod tests { - use super::*; - - use alloc::vec::Vec; - use lambdaworks_math::elliptic_curve::short_weierstrass::curves::bls12_381::default_types::FrField; - - #[test] - fn basic_challenge() { - let mut transcript = DefaultTranscript::::default(); - - let point_a: Vec = vec![0xFF, 0xAB]; - let point_b: Vec = vec![0xDD, 0x8C, 0x9D]; - - transcript.append_bytes(&point_a); // point_a - transcript.append_bytes(&point_b); // point_a + point_b - - let challenge1 = transcript.sample(); // Hash(point_a + point_b) - - assert_eq!( - challenge1, - [ - 0x0c, 0x2b, 0xd8, 0xcf, 0x2d, 0x71, 0xe0, 0x0a, 0xce, 0xa3, 0xbd, 0x5d, 0xc7, 0x9f, - 0x4f, 0x93, 0xed, 0x57, 0x42, 0xd0, 0x23, 0xbd, 0x47, 0xc9, 0x04, 0xc2, 0x67, 0x9d, - 0xbc, 0xfa, 0x7c, 0xa7 - ] - ); - - let point_c: Vec = vec![0xFF, 0xAB]; - let point_d: Vec = vec![0xDD, 0x8C, 0x9D]; - - transcript.append_bytes(&point_c); // Hash(point_a + point_b) + point_c - transcript.append_bytes(&point_d); // Hash(point_a + point_b) + point_c + point_d - - let challenge2 = transcript.sample(); // Hash(Hash(point_a + point_b) + point_c + point_d) - assert_eq!( - challenge2, - [ - 0x81, 0x61, 0x51, 0xc5, 0x7e, 0xcb, 0x45, 0xd5, 0x17, 0x1a, 0x3c, 0x2e, 0x38, 0x04, - 0x5d, 0xfb, 0x3a, 0x3d, 0x33, 0x8a, 0x22, 0xaf, 0xf8, 0x60, 0x85, 0xb9, 0x54, 0x3f, - 0xf8, 0x32, 0x32, 0xbc - ] - ); - } -} diff --git a/crates/crypto/src/fiat_shamir/is_transcript.rs b/crates/crypto/src/fiat_shamir/is_transcript.rs deleted file mode 100644 index a127539a5..000000000 --- a/crates/crypto/src/fiat_shamir/is_transcript.rs +++ /dev/null @@ -1,43 +0,0 @@ -use lambdaworks_math::{ - field::{ - element::FieldElement, - traits::{IsField, IsSubFieldOf}, - }, - traits::AsBytes, -}; - -/// The functionality of a transcript to be used in the STARK Prove and Verify protocols. -pub trait IsTranscript { - /// Appends a field element to the transcript. - fn append_field_element(&mut self, element: &FieldElement); - /// Appends a bytes to the transcript. - fn append_bytes(&mut self, new_bytes: &[u8]); - /// Returns the inner state of the transcript that fully determines its outputs. - fn state(&self) -> [u8; 32]; - /// Returns a random field element. - fn sample_field_element(&mut self) -> FieldElement; - /// Returns a random index between 0 and `upper_bound`. - fn sample_u64(&mut self, upper_bound: u64) -> u64; - /// Returns a field element not contained in `lde_roots_of_unity_coset` or `trace_roots_of_unity`. - fn sample_z_ood>( - &mut self, - lde_roots_of_unity_coset: &[FieldElement], - trace_roots_of_unity: &[FieldElement], - ) -> FieldElement - where - FieldElement: AsBytes, - { - loop { - let value: FieldElement = self.sample_field_element(); - if !lde_roots_of_unity_coset - .iter() - .any(|x| x.clone().to_extension() == value) - && !trace_roots_of_unity - .iter() - .any(|x| x.clone().to_extension() == value) - { - return value; - } - } - } -} diff --git a/crates/crypto/src/fiat_shamir/mod.rs b/crates/crypto/src/fiat_shamir/mod.rs deleted file mode 100644 index 378c490db..000000000 --- a/crates/crypto/src/fiat_shamir/mod.rs +++ /dev/null @@ -1,4 +0,0 @@ -pub mod default_transcript; -pub mod is_transcript; -#[cfg(feature = "test_fiat_shamir")] -pub mod test_transcript; diff --git a/crates/crypto/src/fiat_shamir/test_transcript.rs b/crates/crypto/src/fiat_shamir/test_transcript.rs deleted file mode 100644 index a3254f93b..000000000 --- a/crates/crypto/src/fiat_shamir/test_transcript.rs +++ /dev/null @@ -1,25 +0,0 @@ -use super::transcript::Transcript; - -/// This transcript will ALWAYS return the exact same value every time it's called. -/// It is meant for testing only, never use this in production. -pub struct TestTranscript; - -impl Transcript for TestTranscript { - fn append(&mut self, _new_data: &[u8]) {} - - fn challenge(&mut self) -> [u8; 32] { - [1; 32] - } -} - -impl Default for TestTranscript { - fn default() -> Self { - Self::new() - } -} - -impl TestTranscript { - pub fn new() -> Self { - Self {} - } -} diff --git a/crates/crypto/src/hash/README.md b/crates/crypto/src/hash/README.md deleted file mode 100644 index 1bb50c9ec..000000000 --- a/crates/crypto/src/hash/README.md +++ /dev/null @@ -1,11 +0,0 @@ -# Hash functions - -This folder contains hash functions that are typically used in non-interactive proof systems. The hash functions we have implemented are: -- [Monolith](./monolith/mod.rs) -- [Poseidon](./poseidon/) -- [Pedersen](./pedersen/) -- [Rescue Prime](./rescue_prime/) - -Pedersen is based on elliptic curves, while [Monolith](https://eprint.iacr.org/2023/1025), [Poseidon](https://eprint.iacr.org/2019/458.pdf) and [Rescue Prime](https://eprint.iacr.org/2020/1143) are algebraic hash functions. - -For an introduction to hash functions, see [this intro](https://blog.alignedlayer.com/introduction-hash-functions-in-cryptography/) and [its follow-up](https://blog.alignedlayer.com/design-strategies-how-to-construct-a-hashing-mode-2/). \ No newline at end of file diff --git a/crates/crypto/src/hash/hash_to_field.rs b/crates/crypto/src/hash/hash_to_field.rs deleted file mode 100644 index 003b43e70..000000000 --- a/crates/crypto/src/hash/hash_to_field.rs +++ /dev/null @@ -1,97 +0,0 @@ -use alloc::{string::String, vec::Vec}; -use lambdaworks_math::{ - field::{ - element::FieldElement, - fields::montgomery_backed_prime_fields::{IsModulus, MontgomeryBackendPrimeField}, - }, - unsigned_integer::element::UnsignedInteger, -}; - -/// Converts a hash, represented by an array of bytes, into a vector of field elements -/// For more info, see -/// https://www.ietf.org/id/draft-irtf-cfrg-hash-to-curve-16.html#name-hashing-to-a-finite-field -pub fn hash_to_field> + Clone, const N: usize>( - pseudo_random_bytes: &[u8], - count: usize, -) -> Vec>> { - let order = M::MODULUS; - let l = compute_length(order); - let mut u = vec![FieldElement::zero(); count]; - for (i, item) in u.iter_mut().enumerate() { - let elm_offset = l * i; - let tv = &pseudo_random_bytes[elm_offset..elm_offset + l]; - - *item = os2ip::(tv); - } - u -} - -fn compute_length(order: UnsignedInteger) -> usize { - //L = ceil((ceil(log2(p)) + k) / 8), where k is the security parameter of the cryptosystem (e.g. k = ceil(log2(p) / 2)) - let log2_p = order.limbs.len() << 3; - ((log2_p << 3) + (log2_p << 2)) >> 3 -} - -/// Converts an octet string to a nonnegative integer. -/// For more info, see https://www.rfc-editor.org/rfc/pdfrfc/rfc8017.txt.pdf -fn os2ip> + Clone, const N: usize>( - x: &[u8], -) -> FieldElement> { - let mut aux_x = x.to_vec(); - aux_x.reverse(); - let two_to_the_nth = build_two_to_the_nth(); - let mut j = 0_u32; - let mut item_hex = String::with_capacity(N * 16); - let mut result = FieldElement::zero(); - for item_u8 in aux_x.iter() { - item_hex += &format!("{item_u8:x}"); - if item_hex.len() == item_hex.capacity() { - result += FieldElement::from_hex_unchecked(&item_hex) * two_to_the_nth.pow(j); - item_hex.clear(); - j += 1; - } - } - result -} - -/// Builds a `FieldElement` for `2^(N*16)`, where `N` is the number of limbs of the `UnsignedInteger` -/// used for the prime field. -fn build_two_to_the_nth> + Clone, const N: usize>( -) -> FieldElement> { - // The hex used to build the FieldElement is a 1 followed by N * 16 zeros - let mut two_to_the_nth = String::with_capacity(N * 16); - for _ in 0..two_to_the_nth.capacity() - 1 { - two_to_the_nth.push('1'); - } - FieldElement::from_hex_unchecked(&two_to_the_nth) + FieldElement::one() -} - -#[cfg(test)] -mod tests { - use alloc::vec::Vec; - use lambdaworks_math::{ - field::{ - element::FieldElement, - fields::montgomery_backed_prime_fields::{IsModulus, MontgomeryBackendPrimeField}, - }, - unsigned_integer::element::UnsignedInteger, - }; - - use crate::hash::{hash_to_field::hash_to_field, sha3::Sha3Hasher}; - - type F = MontgomeryBackendPrimeField; - - #[derive(Clone, Debug)] - struct U64; - impl IsModulus> for U64 { - const MODULUS: UnsignedInteger<1> = UnsignedInteger::from_u64(18446744069414584321_u64); - } - - #[test] - fn test_same_message_produce_same_field_elements() { - let input = Sha3Hasher::expand_message(b"helloworld", b"dsttest", 500).unwrap(); - let field_elements: Vec> = hash_to_field(&input, 40); - let other_field_elements = hash_to_field(&input, 40); - assert_eq!(field_elements, other_field_elements); - } -} diff --git a/crates/crypto/src/hash/mod.rs b/crates/crypto/src/hash/mod.rs deleted file mode 100644 index ad9e21a1c..000000000 --- a/crates/crypto/src/hash/mod.rs +++ /dev/null @@ -1,6 +0,0 @@ -pub mod hash_to_field; -pub mod monolith; -pub mod pedersen; -pub mod poseidon; -pub mod rescue_prime; -pub mod sha3; diff --git a/crates/crypto/src/hash/monolith/mod.rs b/crates/crypto/src/hash/monolith/mod.rs deleted file mode 100644 index 26b624349..000000000 --- a/crates/crypto/src/hash/monolith/mod.rs +++ /dev/null @@ -1,228 +0,0 @@ -use alloc::vec::Vec; -use lambdaworks_math::field::{ - fields::mersenne31::field::MERSENNE_31_PRIME_FIELD_ORDER, traits::IsField, -}; -use sha3::{ - digest::{ExtendableOutput, Update}, - Shake128, Shake128Reader, -}; - -mod utils; -use utils::*; - -// Ported from https://github.com/Plonky3/Plonky3/blob/main/monolith - -pub const NUM_BARS: usize = 8; -const MATRIX_CIRC_MDS_16_MERSENNE31_MONOLITH: [u32; 16] = [ - 61402, 17845, 26798, 59689, 12021, 40901, 41351, 27521, 56951, 12034, 53865, 43244, 7454, - 33823, 28750, 1108, -]; - -pub struct MonolithMersenne31 { - round_constants: Vec>, - lookup1: Vec, - lookup2: Vec, -} - -impl Default - for MonolithMersenne31 -{ - fn default() -> Self { - Self::new() - } -} - -impl MonolithMersenne31 { - pub fn new() -> Self { - assert!(WIDTH >= 8); - assert!(WIDTH <= 24); - assert!(WIDTH.is_multiple_of(4)); - Self { - round_constants: Self::instantiate_round_constants(), - lookup1: Self::instantiate_lookup1(), - lookup2: Self::instantiate_lookup2(), - } - } - - fn instantiate_round_constants() -> Vec> { - let mut shake = Shake128::default(); - shake.update("Monolith".as_bytes()); - shake.update(&[WIDTH as u8, (NUM_FULL_ROUNDS + 1) as u8]); - shake.update(&MERSENNE_31_PRIME_FIELD_ORDER.to_le_bytes()); - shake.update(&[8, 8, 8, 7]); - let mut shake_finalized = shake.finalize_xof(); - random_matrix(&mut shake_finalized, NUM_FULL_ROUNDS, WIDTH) - } - - fn instantiate_lookup1() -> Vec { - (0..=u16::MAX) - .map(|i| { - let hi = (i >> 8) as u8; - let lo = i as u8; - ((Self::s_box(hi) as u16) << 8) | Self::s_box(lo) as u16 - }) - .collect() - } - - fn instantiate_lookup2() -> Vec { - (0..(1 << 15)) - .map(|i| { - let hi = (i >> 8) as u8; - let lo: u8 = i as u8; - ((Self::final_s_box(hi) as u16) << 8) | Self::s_box(lo) as u16 - }) - .collect() - } - - fn s_box(y: u8) -> u8 { - (y ^ !y.rotate_left(1) & y.rotate_left(2) & y.rotate_left(3)).rotate_left(1) - } - - fn final_s_box(y: u8) -> u8 { - debug_assert_eq!(y >> 7, 0); - - let y_rot_1 = (y >> 6) | (y << 1); - let y_rot_2 = (y >> 5) | (y << 2); - - let tmp = (y ^ !y_rot_1 & y_rot_2) & 0x7F; - ((tmp >> 6) | (tmp << 1)) & 0x7F - } - - pub fn permutation(&self, state: &mut Vec) { - self.concrete(state); - for round in 0..NUM_FULL_ROUNDS { - self.bars(state); - Self::bricks(state); - self.concrete(state); - Self::add_round_constants(state, &self.round_constants[round]); - } - self.bars(state); - Self::bricks(state); - self.concrete(state); - } - - // MDS matrix - fn concrete(&self, state: &mut Vec) { - *state = if WIDTH == 16 { - Self::apply_circulant(&mut MATRIX_CIRC_MDS_16_MERSENNE31_MONOLITH.clone(), state) - } else { - let mut shake = Shake128::default(); - shake.update("Monolith".as_bytes()); - shake.update(&[WIDTH as u8, (NUM_FULL_ROUNDS + 1) as u8]); - shake.update(&MERSENNE_31_PRIME_FIELD_ORDER.to_le_bytes()); - shake.update(&[16, 15]); - shake.update("MDS".as_bytes()); - let mut shake_finalized = shake.finalize_xof(); - Self::apply_cauchy_mds_matrix(&mut shake_finalized, state) - }; - } - - // S-box lookups - fn bars(&self, state: &mut [u32]) { - for state in state.iter_mut().take(NUM_BARS) { - *state = ((self.lookup2[(*state >> 16) as u16 as usize] as u32) << 16) - | (self.lookup1[*state as u16 as usize] as u32); - } - } - - // (x_{n+1})² = (x_n)² + x_{n+1} - fn bricks(state: &mut [u32]) { - for i in (0..state.len() - 1).rev() { - state[i + 1] = F::add(&state[i + 1], &F::square(&state[i])); - } - } - - fn add_round_constants(state: &mut [u32], round_constants: &[u32]) { - for (x, rc) in state.iter_mut().zip(round_constants) { - *x = F::add(x, rc); - } - } - - // O(n²) - fn apply_circulant(circ_matrix: &mut [u32], input: &[u32]) -> Vec { - let mut output = vec![F::zero(); WIDTH]; - for out_i in output.iter_mut().take(WIDTH - 1) { - *out_i = dot_product(circ_matrix, input); - circ_matrix.rotate_right(1); - } - output[WIDTH - 1] = dot_product(circ_matrix, input); - output - } - - fn apply_cauchy_mds_matrix(shake: &mut Shake128Reader, to_multiply: &[u32]) -> Vec { - let mut output = vec![F::zero(); WIDTH]; - - let bits: u32 = u64::BITS - - (MERSENNE_31_PRIME_FIELD_ORDER as u64) - .saturating_sub(1) - .leading_zeros(); - - let x_mask = (1 << (bits - 9)) - 1; - let y_mask = ((1 << bits) - 1) >> 2; - - let y = get_random_y_i(shake, WIDTH, x_mask, y_mask); - let mut x = y.clone(); - x.iter_mut().for_each(|x_i| *x_i &= x_mask); - - for (i, x_i) in x.iter().enumerate() { - for (j, yj) in y.iter().enumerate() { - output[i] = F::add( - &output[i], - // We are using that x_i + yj != 0 because they are both much smaller than the modulus. - &F::div(&to_multiply[j], &F::add(x_i, yj)).unwrap(), - ); - } - } - - output - } -} - -#[cfg(test)] -mod tests { - use super::*; - - fn get_test_input(width: usize) -> Vec { - (0..width).map(|i| F::from_base_type(i as u32)).collect() - } - - #[test] - fn from_plonky3_concrete_width_16() { - let mut input = get_test_input(16); - MonolithMersenne31::<16, 5>::new().concrete(&mut input); - assert_eq!( - input, - [ - 3470365, 3977394, 4042151, 4025740, 4431233, 4264086, 3927003, 4259216, 3872757, - 3957178, 3820319, 3690660, 4023081, 3592814, 3688803, 3928040 - ] - ); - } - - #[test] - fn from_plonky3_concrete_width_12() { - let mut input = get_test_input(12); - MonolithMersenne31::<12, 5>::new().concrete(&mut input); - assert_eq!( - input, - [ - 365726249, 1885122147, 379836542, 860204337, 889139350, 1052715727, 151617411, - 700047874, 925910152, 339398001, 721459023, 464532407 - ] - ); - } - - #[test] - fn from_plonky3_width_16() { - let mut input = get_test_input(16); - MonolithMersenne31::<16, 5>::new().permutation(&mut input); - assert_eq!( - input, - [ - 609156607, 290107110, 1900746598, 1734707571, 2050994835, 1648553244, 1307647296, - 1941164548, 1707113065, 1477714255, 1170160793, 93800695, 769879348, 375548503, - 1989726444, 1349325635 - ] - ); - } -} diff --git a/crates/crypto/src/hash/monolith/utils.rs b/crates/crypto/src/hash/monolith/utils.rs deleted file mode 100644 index a10968acd..000000000 --- a/crates/crypto/src/hash/monolith/utils.rs +++ /dev/null @@ -1,55 +0,0 @@ -use alloc::vec::Vec; -use lambdaworks_math::field::{ - fields::mersenne31::field::{Mersenne31Field, MERSENNE_31_PRIME_FIELD_ORDER}, - traits::IsField, -}; -use sha3::{digest::XofReader, Shake128Reader}; - -// Ported from https://github.com/Plonky3/Plonky3/blob/main/monolith - -pub type F = Mersenne31Field; - -pub fn random_matrix(shake: &mut Shake128Reader, n: usize, m: usize) -> Vec> { - (0..n) - .map(|_| (0..m).map(|_| random_field_element(shake)).collect()) - .collect() -} - -fn random_field_element(shake: &mut Shake128Reader) -> u32 { - let mut val = shake_random_u32(shake); - while val >= MERSENNE_31_PRIME_FIELD_ORDER { - val = shake_random_u32(shake); - } - F::from_base_type(val) -} - -pub fn dot_product(u: &[u32], v: &[u32]) -> u32 { - Mersenne31Field::sum(u.iter().zip(v).map(|(x, y)| F::mul(x, y))) -} - -pub fn get_random_y_i( - shake: &mut Shake128Reader, - width: usize, - x_mask: u32, - y_mask: u32, -) -> Vec { - let mut res = vec![0; width]; - - for i in 0..width { - let mut y_i = shake_random_u32(shake) & y_mask; - let mut x_i = y_i & x_mask; - while res.iter().take(i).any(|r| r & x_mask == x_i) { - y_i = shake_random_u32(shake) & y_mask; - x_i = y_i & x_mask; - } - res[i] = y_i; - } - - res -} - -fn shake_random_u32(shake: &mut Shake128Reader) -> u32 { - let mut rand = [0u8; 4]; - shake.read(&mut rand); - u32::from_le_bytes(rand) -} diff --git a/crates/crypto/src/hash/pedersen/constants.rs b/crates/crypto/src/hash/pedersen/constants.rs deleted file mode 100644 index a8400a4f6..000000000 --- a/crates/crypto/src/hash/pedersen/constants.rs +++ /dev/null @@ -1,7590 +0,0 @@ -use lambdaworks_math::elliptic_curve::short_weierstrass::{ - curves::stark_curve::StarkCurve, point::ShortWeierstrassProjectivePoint, -}; - -pub const fn shift_point() -> ShortWeierstrassProjectivePoint { - StarkCurve::from_affine_hex_string_unchecked( - "0x049ee3eba8c1600700ee1b87eb599f16716b0b1022947733551fde4050ca6804", - "0x03ca0cfe4b3bc6ddf346d49d06ea0ed34e621062c0e056c1d0405d266e10268a", - ) -} - -pub const fn points_p1() -> [ShortWeierstrassProjectivePoint; 930] { - [ - StarkCurve::from_affine_hex_string_unchecked( - "0x0234287dcbaffe7f969c748655fca9e58fa8120b6d56eb0c1080d17957ebe47b", - "0x03b056f100f96fb21e889527d41f4e39940135dd7a6c94cc6ed0268ee89e5615", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x003909690e1123c80678a7ba0fde0e8447f6f02b3f6b960034d1e93524f8b476", - "0x07122e9063d239d89d4e336753845b76f2b33ca0d7f0c1acd4b9fe974994cc19", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x07229b12e8428f710abf28f83336c210d35cce1fce0c3fb83f5bbbd1033f68c9", - "0x023425e83f32f3677ce1e46d004c13898d5170248300fd0f3ee42b6013a2a2fa", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x040fd002e38ea01a01b2702eb7c643e9decc2894cbf31765922e281939ab542c", - "0x0109f720a79e2a41471f054ca885efd90c8cfbbec37991d1b6343991e0a3e740", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x00df45299e7e1d2231116da91797315f672deabd6fdd83b139a782c6a3328df4", - "0x058d8082c3852c613f18759e1bce170c8225a2202456c638660aef996f4907cb", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0340f018dac64edcc62482a77357ffa46cb5560a935be398dc1f2d9072728e32", - "0x029d2c3fee3257c4af71536cb2c1a8b5eb960bb874444a956b92f1dab9c23495", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0550eaa3b906d70f681f279029342cb6ca7a18eeaf41f40460dcac94a33558bd", - "0x03290266ae4666f375765ff418ce895ec04e6849bf6c79fd1fdfa55a0d6dbc36", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x02f52066635c139fc2f64eb0bd5e3fd7a705f576854ec4f00aa60361fddb981b", - "0x06d78a24d8a5f97fc600318ce16b3c840315979c3273078ec1a285f217ee6a26", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0406a4903dff511fd555465a62c879070daff3f97d5dfcbc25a89b912b0f245d", - "0x02ac3ad5e021f342459f3e23535a1b76b546b7ce8922e8cb4b42272dee8c15d2", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x048ee5faafb8626a51f20bc4d9d7171ba796193bcfc4dc15efe2f01842c159d6", - "0x0436a82462639f945fe0cc7807bf3df035bf74aee299e0317efe51c3b7b29e5a", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x05e8906095605da0ce8bbcf4f1faa885239d46cffd0ec58ebd80d85d2ff17f5f", - "0x07363c43c0f6f1756a27bd9b67960f31f26a71777d150af9d9e9d5f397a13184", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x04e724e82fe08a75b06bf4601435f0dd7146b07f2ce99915f2fbb3e8d2739391", - "0x00a1aafde6e3ad64e28d30f25ce2ba34bb978fa70c374c37a1a506dd065252f1", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x07ed3443a478173dfc6e695e46866cdf09a1eccc24bac49f457bbc2c4a16893e", - "0x016376037b759d6b042da47d35003a7b926c66c4009a07188bf42317ddd0a397", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x00184e6a77137f5981ba3104f641272d4315dbf17942aa72306f5039ef44face", - "0x0383342a72aae69729343a8363d3d674c0c2fecba1be4bdb7c54e11548a1b08f", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x014b98c2eee31d8cce3ada97504f033be6f447d4b4b6e9374b5d0826ca1e1154", - "0x071178c2f4ca2c6fd7fcdfa6ae6b87f7c5cc5e46e3b577896f8afb376dc10008", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x06a0767a1fd60d5b9027a35af1b68e57a1c366ebcde2006cdd07af27043ef674", - "0x0606b72c0ca0498b8c1817ed7922d550894c324f5efdfc85a19a1ae382411ca2", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x07fa463ee2a2d6a585d5c3358918270f6c28c66df1f86803374d1edf3819cc62", - "0x00a996edf01598832e644e1cae9a37288865ad80e2787f9bf958aceccc99afae", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x00433ca5a9740fd5444c82d3a031842be7fc64c52c24731a68aaebc4b3fad126", - "0x0182d522a91b3605ddfc2b1a0fd2a7ccb705c6e6d1bc6b35bc75933748d2512c", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x03d4da70d1540da597dbae1651d28487604a4e66a4a1823b97e8e9639393dbec", - "0x045cdef70c35d3b6f0a2273a9886ccb6306d813e8204bdfd30b4efee63c8a3f9", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x05132ef198cb729635749974739d4096f25142db97af3fde079089cfc8fff003", - "0x03946837958b4b03428a4ae47c197a0de9e13d372282705f1959770b47ce25ae", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x01b5fd788e27a712d739d6b86cfc2907bf230a56762550ab90885d0d5d0711d8", - "0x0365ec932cb2e4cfdc9169d832101f16e4a9fcb17f9073ee96d9ed7eb779cd66", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x00f8e5df3d74ea43638d46640e3d7ec79590ec4fef664bb48a521a88005ff510", - "0x047003dcbef6a4dda6c5a6d22dd974134ac1aedc2b01730bfff81c5ad0f9e3c4", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x01e448fdbcd9896c6fbf5f36cb7e7fcb77a751ff2d942593cae023363cc7750e", - "0x030c81da0f3a8cb64468eaa491c7ae7b4842b62cb4148820da211afc4caffb3a", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x05473730c8a3fc843fcc70d2e20533f07a45689c7381857191137af36bd6a743", - "0x05d96a94e1ccf4c60b95b1a6d4482c2451c1bfb12d899e6d967df63dfe6d315e", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x007fac20fe7b0c59c67987b3a7e42aff5b857ee532b73d10a5a689e34280325b", - "0x03b20d3e307ed631871a9074e800448d807d2d6586edd3953cbdfd6876d5c3a5", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x049299aa96ce9e57b9ea9d4737351f5ad20bd02f618d3af294227e552ad3fccf", - "0x01b2c2c73c4839c94e9b62f8430f0f88da1336173a471a1af14e9d04b91575eb", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x015908bd905fc4b1ab61802421d99acad007eec07f9cc5a7f51a7c8166f42a7c", - "0x074532c685718fd5eb89b8a84fd5ea112953ecaeddf3310705a4ec694c27b369", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x00893e0cde416f8a61d58c8cacf2314af9eb331d6cfce6d36a210486b76c9048", - "0x0405879e7817d8dfac211f9d051463e8b8ffe47f911e8404557554faef193cd5", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x021fb8a6378be3dfb0af4a899e5c51b6e3f53c677a6fe34244b846fc065d95b3", - "0x00a8e9f5cc41dbd7e43bc24caddeaa2f6526d03b1acbc0c9780d2df18e57d29d", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x00abbb0ca4aa2018feca36fed8ffd2779ce1c444de7cf1e6f4336317239a229b", - "0x03a5f5f1372ed9a8811eb74154001c7b2d491d590c97e67e9fc3f4a881eb1fd6", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x06531acf1a7cb90a4eb27de0b7f915e387a3b0fd063ba6e1289b91f48411be26", - "0x031330f5daa091889981a3ea782ae997f5f171336ed0487a03f051551a2cafa2", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x054be016394d5662d67d7e82f5e889ed2f97ccf95d911f57dd2362c4040ed4f4", - "0x00c6cb184053f054d6a59c1bf0986d17090d25089b3fdcdaf185edc87ef113e5", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x06f4f4793df4469b4a89da52f3ff45832564fe7ee67a07c7aeb7bd849a359c48", - "0x02c47fb246409be043ccb20932fa308094142e0c1fdcedd0b279e74f00abec0b", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x035b9ecd0499ca1d5d42dcbb0c6b4042b3733c64b607ca711e706e786ef2afc6", - "0x05624b476a5b21c3a544f0712d4817b06ad380a5a6529d323bf64da8ef862d8d", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x03396de1b8034959c41bc44b32feaead8c07f54b2456c06403687dd45e92a01a", - "0x00c2ef4cd3a067d95dceb741b0e194b8b43c86964fe54337ee3a71e2d8ff215a", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x07f5af55179620a0e291bf2e4975d4e4fd17f695228b57b6ef30ec4b64f5229f", - "0x0103e752c691f2ceab9d17fd391ac5aa69596225ca9015817c421174024c7f11", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x007db0af269635778b2cdb98d49413e03419439c1db00891bf8cc23e17de0638", - "0x04aab69d21e8912e05b9ae1227baddcaf888a3945b71b1f0f676563112a94eb9", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x04ce0378e3ee8f77ed58f2ddbd8bb7676c8a38bfb1d3694c275254bd8ca38e23", - "0x05a16fcbff0769c9cf2b02c31621878ec819fff4b8231bff82c6183db2746820", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x058ee31b121e0d2343b525b8070124596d0f341e98b07afedadff9a74ec678b0", - "0x0649ebd69a4d612ac98f5cb10a3a59fb5080083488eba7ab6549d3133e936cec", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x02e9a8c9adfd58e886ff5e8835be0304c5b921fd9b7e0d9ba246633d1f9b5cb6", - "0x02cf5c605a67acd85f01780fcfaa36de39ad964555d670b5c1e594652311c8f4", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0114f01fdad91af9a43da1079c51dfb76f1c6b47d24811b7a161f548d1ce94d4", - "0x000ceabf891c2a439f28e3f70797ba51026253492d614576c431d4aa9e319939", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x03906297bfbe91c274b3439b2d358dd753500988cf4ab33b4699cb92852a8351", - "0x0628e4f1c3b085f71f276a0857e7aa033dc64cdaee9af2fee88055b53738e62a", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0372d1ef3230d499d5ddd8475e5b878a4d01785da806ba92226a7ca9ad379de7", - "0x073ab138117f1b679c11fa81e572187d94eec710164c3bc3523d0094dfceba37", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x07675b925eb1860917ecffde0db01d51770fbe7108460b808c56b3e354f70d97", - "0x06627121085781697f62c43c0d29833c3fdc257a8e6375429e620be7c63632ac", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x03804a310c3d332ae426a892e125dd13bf0a0cc9849a8e7b2a67be7334a1fb86", - "0x0700ff46ee391b0ef5d86438734ae3b4c1043c9c488a78a16bc552c516aeb75b", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0648d5c6f98680a1b926bfeb01c00224c56fdcf751b251c4449c8a94f425cfcf", - "0x072c05ac793cd1620a833fbe2214d36900ebe446e095c62fcb740937f98cca8c", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x00bd09be3e4e1af8a14189977e334f097c18e4a8bf42577ef5aafa0f807bd89b", - "0x06e0e72ed7eb65c86cee29c411fb4761122558ee81013344ba8509c49de9f9b6", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0060e98611c70049ba42e68adab8db06fa3deb24e542481cb50e998c1c444c17", - "0x0783ccedeb3bee6376f0c02f2b5cb54e115d36c71038be64b4798b1dd9e20886", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x035ea4e339b44ae7724419bdfbe07022253137a4afb7cbaffad341ea61249357", - "0x03665d676a026a174f367bb4417780e53a7803cb02d0db32eb4545c267c42f14", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x05fb52838d0a4e255cd59f9df0810b1f511ded5ef26f95eb20a589bd3ded9899", - "0x039460e30ec6a68c1eaa2b27a512bbf5097ccae98153eedb82a851adaa554e6e", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x029d553857527a9fb753b218628f7fa22d0b035e134acba9fe7bb603ae11a4b5", - "0x00abf9b2689831b7f8bfc5d3e8db2af3f5feb26645e0ec08bf5ecb7d4a806877", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x04cae397bcab2196aebb6152f2aa6c0ed9d4243c7f52cb930c5cd602f784cff4", - "0x07c84f8966daf03473e05e53439ada76452a4fd5bb485865732ba8bfe122fd40", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x036457bc744f42e697b825c2d1afd8f4029d696a4514710f81da52d88e178643", - "0x07c93715896735492a68c7969a024b3a8fd538bffc1521538107de1a5f13ce9c", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x026f3bdef66b4befcff1076c8733db1e24cd4e25640db0d722055ca05e03499d", - "0x05f2ac048f88ab6ee28dab048d6b0e6b7edb38ef93433a05b033b59a5aaf5b07", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x043daf55d25dd9b7b0048d14762d1798bb88c6a74a10b6379b17c3fe3ab662ae", - "0x0644593b6956b8c0f62c1a8eeeac8a2d44bed6a7b932d66337dd55a7821f047b", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x001db2e250ef3c59382857425e99183f2c512a90c8c02677cea5ce5a503bc829", - "0x06dc4e058d69707948cbbac387bedec2be560d4e0bcc3ec1ce82808def25fae9", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x078e58bc2fa1c4a6feeb772c23f80e4fc3d9c9a06475213e88e7e05dbeed8bec", - "0x009b6a3f676231e9af5b51e9348952996ca1854d3dbd339f78ad344b0abc53dc", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x013424b850382824f7b451f1542e06ba56a945f20494848cb33d731f947f7a21", - "0x0374e341c5cc8126591881817cd671d72d48f50cfb2fefb4b5e7500453072f82", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x02425cce7159478cbd8efd4134f8d91c422292bac4deee60e301057ec5359f08", - "0x0097301e86fbe625c711aba4a954c15c226ca2736c1fa621cc279cea3ac32443", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x06151a3c5bccd2285771488d6bf61ae5339450824597674e8f7251354c913892", - "0x02fa5b62063fe9db9bb29cd0b707aa0f5de65a48a4c78c17beb4574d171cbc3f", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x05b3a08ebcf9c109cc9082f70d9df2b9c11b5428ee23917b4e790c4c10f6e661", - "0x0009d7b42ab0c20f5510df7ea5e196eec99342739077e9a168198c89da859753", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x021883ef8580fc06e59481955d52ece3aca6e82c8c9fc58e216dcf46f96990c6", - "0x051a6423543e6e8a43e71da34cd90f5b520b8d33b67c4bf857573ab9e301aa4c", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x00efbdfb8d391b3857a053d0526ac1d92a3c855e82db9fa5744f0512b06df3fe", - "0x03655a1e71a701d2c9495c20035a6a80711fb6b1978e156677d0356a323c434a", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x019e86b77f9b581e81092b305c852faf53940a8f15f0a6990c414f04c0fa7ef9", - "0x00515630e35d4398c9c79fc4ee08e1023fa47d8e03c6e7819c6d2ccef45398fa", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x052cbca763f535966ba8be2b32f8ae9284ee1591f37a8958d7595c884ca57031", - "0x0762e9c04bfe85fa231f59afba1a1c52f1994aedc3853c89e7e98964b8ff6b57", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x078f1e6fbd1395703c932a3921efafabee829c1983221513337e3368b076cb75", - "0x034e5af4b2634492bf595b5ed70975b548660ee658d81b74dae90d90717602c0", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x048410d09bf47d33426af0af7934427a6d86325b3d7be2954ce996f19ec42a62", - "0x0022532d2cf20f61e1b63778626d3871658178a49002bd0f1e5fabaafec2c462", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x00888ab8eb4c31bb2ac5b54aa320dbe1a69c96b864e8a5f54d89c1d1a6b86c24", - "0x0730e148467f6a55ce22c5296f5380df88f38de76ef0b2de844cd3094aaaf3ea", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x042c95d04e4af3f0b485e7dc19d031c82ecd9dc8210ddcbedf651534e217d5f6", - "0x055cd9cab9d502856e5420c4684cf1561472f05b0f97788bc755c80c59dba7dd", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x03be1c8c0747b35b64f024328ce45f71b99df5c8ffa09a8fb79c326ee0a67fbd", - "0x05e4e474c847ebf98e3209d504f81cdbf58c993322ef2e08b802468ed719fe24", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x008b0d7f0ff79cd042af4b42bc5d384d9f8cb200216f9824e25d3b0a9fe429aa", - "0x0025d6198e37257433b6728a7d6f3a6b60f6ee3b8d462168422e06c79da2298e", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0509606f39d2df35826bad011f25a6850e017b32deef00e1974f2ed0934666e4", - "0x03d6ca71df802ca910b8e8afa8213e2fcb9740701e326e21c61e58bec33262ab", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x01e337495b39b3bad6bc211f6dbca74335a5c12da286cf0451df207734a07d59", - "0x02272c3b13aaa4533bed1a4485abf771a5ea0b96ddfa14fb3beebb0db767ef8f", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x03fe9f774fe0b6485d33f7bdf6efd1c12d31c70b2df9411515cd283a1bdc70b6", - "0x02fd7de11035a8dda9b2906f7511460fabeaedbac515810f6583731356e39a59", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x012716a93cf48c54fd93617650cbece5335eeb039f214a6a97a3ca0f3aa80df0", - "0x0271b36528eb2717442d3500177ed6c1811ae38e53003d8defff5a07fb4505f3", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x075e79ff13a894e7120dac17b7429c0c32ce7828f726c9973728c0977a5f5977", - "0x04960526e59c1c736561a201bc56f7d762641b39f609d273cc996f5d9197cfb8", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0640fe009249115d7254f72ecafb3006139e4bed7e9041af51458c737282d1d5", - "0x003cc6c978a575246e2ce4f7ef1fcc7f63085db9ff98a1b1f3fe374087c0332c", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x03830e694ae144b0a73d050a57a06e15b7d5c9a16998abcac3afcaf4f225dd7c", - "0x0270f5368cc52b0fd67dc3686652f2c08570abd38975ee210e27e7afac934de4", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x06d6fd09ccab7c26de9b3906191235deb5c34685580c488275356a05e209ca96", - "0x07157f81a34213dd8f91dea4f6df1bcfabc4ee091a3049eeeb3b7923d39b8645", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x07c6a612a9c2a86a3d96a0d0800b4cddb4662e204e5185f0a3c158b721130ead", - "0x0721caed7f45fe1ac7858cb9155058d886419d2c19a5a72cc5f0b8902c6871a4", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0478a2342289c28dc0846655731ee2468c89b57ce721298cc0200e27a7cf6cbc", - "0x007f689580e05c555a40803badb2177fc00aa4e0b7fe99323c03b7b5854c1cdb", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x02926bb76680ae6a841dfca3c3dc4baadb18ee48c8e2fd495b3a17627b073e5c", - "0x03ecc73926c07a0e4d2ede2ec610037993fdb626526bd5c5ff522fe28a2a6353", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x05531ca1d00f151d71da820918f74caf2985b24dca20e124721fff507b5a5876", - "0x0518529643d3f25e47f72c322223ba60a63d6bfe78cf3f612215d9c19bf29200", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x03e2edd1d2f6d2f8309263d45d4e3c5f53a08aa7018a73d15162d8501ea05d23", - "0x04753d9733655e9b4b9c0af76101be6dbfb9457e8b4440d8fc2d9da058bead4c", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x07f136709ac0667cf209936843d95b2b80e491e01180d1bfddab7c82f872b7c3", - "0x0412f28bdf5f186382c48b9159799ab4db8194d7bfcd7a6adc186729b655dc67", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0419df0b81d159286588f4ae84b5b5d5ed36017eb9c4ac46e76f664798fd0129", - "0x00f793ea32bc98aa47576cecc807c136192a8add62e1ab4df81e805fbf47f8af", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x057ad67b9a20c16c80f1b584a96ffec61b513dcec44c8b5bb37074753aa4123d", - "0x00b6bd4fdd04d4b386d11dbfa6443f0bac3288889e7f6ca950d420c1c4497e56", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0122e7fd3f587a9c678cb36d2d86a92ff6d360dfc5910eb435e7388f39cbdbd2", - "0x073675b1f1d80cbb437e6bc21b88ba6b1bc1e191f7cd70ae7f495ea13e63307f", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x06e433daad3a1cd8d35a2982818e0f8210ab82834f2d093b8969c42949813009", - "0x07b1fd0bbb89675604da04b1313625f3762ee723bc66a9999bf713bab185a7d8", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x00ec75fb16f84d66aa46b1d8398fa0293c9fa124aea4c8d8f23d5a8155af7d6e", - "0x065dee206316554f51e8f892645baed6dbe56bac1660ff84441e76a7cb4cf584", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x06192d454e4f8fe212bdfccd5b15dd5056d7622ffe456c6c67e5a7265aea49c4", - "0x02377a45dc630017ae863cb968ddb38333a70c7946d8684e6d7a6213f634b7bc", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0542fb44b4ef3640a64fdb22a2560fb26668065c069cf31d1df424819a39ff18", - "0x05dbae9b0948e0361aea443503840341c322aa1a1366ce5390e71bf161f78f8c", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x03c5133d3856680586e284eec9bd4c056eb8c1204d5e9a5303023bf902d3e8c2", - "0x05ce549f398e6267cd2974be878d8af04b1238b65aa4c3bd7809df5311b11b31", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0299ff3e3412a7eb4cb4a3051b07b1be2e7b1c4b789f39ffb52cba3d048b71de", - "0x01951d3175c02761b291d86b6c0a08387ad5e2a2130ccc33c852530572cb3958", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x05f856bb94f77c71e220e2a6b8402efd6129b334bdb7a8711c6fb00ef1b78fdc", - "0x058ba8d0578bb9fa33e15a3ee2d45ece949c9f6259eb05337087d56b920de8b1", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x050edcce31d3b563adbc1624987cf8c7b72b2743b1b50483b7d0cd64635e6b6e", - "0x05a6d296d418426c6524b4eee788ce56dad6fb5108ad8802409e133d2e68f1e0", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0351165fa9ec8c06e7746b92e952e0fe48d27ec73ad8cd78c1290e8e1c9e25ad", - "0x0682356c7a3ab57b9eda27076ff63971e7af69a9ca4c9233888dbd06a5679f5a", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0628ce3f5367dadc1411133e55eb25e2e3c2880d6e28754a5cb1c5d109627e73", - "0x00ae3e9b7d50964e28bd15380400b7659b87affdef5d2586cbefcd9be7d67c0d", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x050e9c67b03b31c31a558148ae9a9664afceb3f5cdaa8b9a00bd4e30631ae01b", - "0x05897e56d78ddde8d0add98a1e30dafd21593f8de223f9889cc239b2391a1bd4", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x05854498bfd63718edae41d9dc2d3a2b219f639d74785e866a34cb78acd3f968", - "0x05f68d1e30764b34862247d27e19556fbf35d5cc5d413f8ea2d91b3da878e1fb", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x02b7221dcd584907e4726834836f878093f49e83aa5d75e280cc13a8d24ccdd5", - "0x00be79753a6b45ef082f465ba6e0cf12230ac0b7f270cab3a4a5b25457b55fda", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x02c520c3ec539142f046b83dcbe511dc85a484e6597025adcb41a3d78f914c92", - "0x072c6abede13fb05f7bdfaa5a12d795141cda7249f049ccb70847d10ac2d738c", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x05133871c2159d5542c66c2c2d5040ae934c80a16c56bef96268abd876a6ee93", - "0x04f415510268fcc33be7a721b7ea6e7fd06cc61aeb87b18a2a18d49752754920", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x00d3c599e0e2585f4e527c19dc2784641da8b8f056d6b20d7f83130bc7ea4020", - "0x06f592771da9b47d4ac6ec283c75d4d3f7fbe7470553ecea6357c92f747eb12b", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x03abaa62706e3a1f0d1bc732695a5a9247fbcc9d470b31a0e24c0f2a97967eca", - "0x06696bf44d9f4e91c4eb2cc011100e039be6ddf5f571424e0c9ebbdf499788cd", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x06ea54aff064895eccf9db2283225d62044ae67621192b3346338948382f5933", - "0x06431507e51aadacfaf39f102a8ff387756e9b5e1bc8323d44acae55130d93db", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x028097d50d175a6235320fe8cfe138dd9e46895d189582e472c38ad7a67d923a", - "0x07f9eab4133d7d09a7ff63368d6135c26262b62336eca1b5ca33f2096ce388ba", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x056f27b3d3213f0f8e0f4eef2728932d1df85acf21088a390d7732ed6d7d623d", - "0x06be64a2e18be2f9e537f0371979a7bf76f27eb3ae07e1ad3dd8f00622edee57", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0619fd09cdd6ff4323973f256c2cbdcb224f7f25b8aef623af2d4a0105e62e02", - "0x02c95f0ae11d47eeae1bc7f1350f75f9185c5bc840382ceb38a797cae9c40308", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x056e37c9108a1d2fda8184f6fc34abbdde3dd0ce0b8be9cde7830a0746abbd57", - "0x023937e0878524b2ec775a75fe19b0cb66abef6470550ff575b1aa456c50c7b2", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x04743a9d13b8d93ca9be5837206aa3898c21ae857fc78c08a1b8005bfa339274", - "0x006876b5abacde7286c583e3bc26e8b3a52f900d8e4bd61ced78fd099f54f177", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x000257fcf60b12428f4f0f367bb0d30bad503e8077d9b170f39fef16cdf53bac", - "0x02a74981aee312c9abd84c496453263c15dcac33c5bade7d77dffe680c3ca811", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0641c18982ced304512a3f2395942a38add0d6a7156229c2a7c8b8dfbe9beb96", - "0x06f6288c9c659b6af5ac975f4180deffe53d516399b2cc62f31732e9d4ba9837", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x00e58fcb9fad01845eb1aab852c1e52e36adf03e4d261f15db8299cb01e655fc", - "0x016c88b6f8f08359d38396bba1f4fe9c891fa96489cbda34b4588cc2ac249072", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x01b40ff525d25d7f3183d7dcebccdb77a744881d4ea55f3e7d9f46abfb16cbb7", - "0x03901f0a3862c75c8d6ddd73fc1b3d5385b387b18a0796cafbaad4654ec4dd94", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0481eae9b66e9d36d64c6eccc93c19d58894663c3ef14fa2b381933c34db3d1f", - "0x0436b2ec7b91cd6759bb3c0da90eb57533c7a2aa540708b114b187b8b399d88e", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x05e00043d6d6bf4da4b217db351260f44708ac82f3271e66122f83af2bded486", - "0x00870b6f17d317dfa3bf74a9baeee8b4958d0fc6ad3c9ba0b5b5046cd064d1ef", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0214386164c5401f527177e159ad5aac65c011fac5a6b6bc099435c886e4a7cd", - "0x011e32e1b14c3e41b6a97459942ca4e876ac65581974a13466ef41928d8aed0a", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x02c7b7f80095baad8e823dff5264e2755dc30f65ceaa1aff244f264049c59a94", - "0x07dc1ca9a72eff96bcb83df039e955c8ab477bf573be1321c7ba831a3f786e4f", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x077348f0f64c22a974103026e3dd14504af0467ff0d3cf264f72d5885b4d1b56", - "0x029be64c099813861bebb51866af87e865f9c3f4c1bc111e59a88d6bdf0e97fc", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x058ab546e51fe49fc5a382e4064a2bd6cfc268904412f86c26de14f28a71d0f2", - "0x0124b7217943e7e328408e8afdfa7da00dcbc94a2bb85fd8e01fb162d2c2c0a9", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x00a82c2fdedbb26c3c762a12f7e86b0e01e65320e0a25a8399d665f6e266bf74", - "0x01a1de28e253f3e10f44d0111e8074f882d7f42e5900780ccbdc31da372d3fd8", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x013fe2361b752ff86102093c99e7a1e62c402c379b05976d9ff8d9a96d1c26cb", - "0x067d67a12ad6636829119b38de0d58244168d4888b151d3aebb6273e1018c870", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0744c725a7455a992e3cf5bd007bc234dd4668dba285f553f38350ad94c1615b", - "0x007f721a87f48798bdc4a9c0eb88559e2ad7a74112fd901e70ea159e67a9c33f", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0367132c7537dab3c343203c0ec263ae1cefa2b31b30783d624d748ee25e3ce7", - "0x026483d7d266b97556d1487ba741c7acdf024bace065cef0541dd6d79254abcb", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x04a5c65b81bc26c33368701f6ad8cfc5280724f24a987ef29c53eab4f2968139", - "0x030bf1ecd36032d842d4092c5141f19240634caf76c7547bece1750aaa1f6683", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0546bb08bed63f6f52361c14d757102e6e32621c68af1d0d3002d577d3fa7d1e", - "0x0544e48665da6f36379c5734cd2947c5c1f9e9604b3630c42e39cd9dc4b19dd5", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0434df142ddaa60f7881b6348d91687de40457de7ccfb07f0304b9e820705d0c", - "0x07fae425e3b53f97dd1f5b20e49ed9fe24ff1efc341ba5e017ac89cf8df0cc39", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x057de1d7ecf6257014f41031b7de385c7456d269fb5f13fd17e554c89722bba8", - "0x037502052d7da135a35d230b4165485a6e2d454f67c400b8a1a42524be1cd63e", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x071f2a596947104aa3b412f6aa553233bbef109d822908a70daeb4a9c7cf5771", - "0x00dc285aca4f3198411ab11a016ef79aa1d8ff625c106772b33308d5b66abc79", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x05e5209f472b8df53e8013f4ac455cb7e4dfe87e814c50d4e8186b4bad9a2112", - "0x06771696bd86be587c2215410cdeb302db6728272258430167238c26f38ef22e", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x00f49d2cbab24aa7f9718fce314274fe81720b9934b2115b1cdeafc8ef1b57a6", - "0x02053b559490b125437c64d6f6083f45e4d6a1df9a40465542d92487bc3e4df5", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x06045932e1a10c8b3bd9b5a6c0fa6e3df67d5aad8d990ed9c38edcd5490f8b06", - "0x02d98d4be121ebf85fb76e86c5248deb73aea7531cedaf560863886a4a92bd71", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0770cca3cb044b99c9d93344e663a4cbdfb9593aec5d03d558d870414cf1f480", - "0x02dd22cc6f8383122e80b80eda1071ad440f1898013e57974898df8c6e29a684", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x04b510386dc302ff3b605eb3c06d8e333d1b09c84139534c26b776a7f2458b1d", - "0x06d84a0c04de8517f28bc6b7ba10f12f520ec11016ffa9133906b34ff2f2e0fd", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x07a1e2b809dff46277021cbc376f79c37e1b683bbd6bca5317014f0dc0e1ae73", - "0x056790278a231912c334eff05281e08af1558e85516b4411ef64647c13bea431", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x04931b7990348d41cf8907be79f45bb7991fd18f8a57868351c92fa7a34cbcd7", - "0x00ca35091815cdf0837d396e25aad6052ad32d497a33b123256cffdc008bc50e", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x01d7e6ee48280a0c3f107b8a0ecacd04b9e1c4db62de8e136b40c238d5cc2b57", - "0x033057135b2a8364c61b5ca96638f651ade34ae5f23285adae5b7c0c8a6f0513", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0250b815d352fd89f8210b624b147ea7d0a4f47bcac49f3ac9b777840da93ebe", - "0x01173f10e9691948b7da7632f328520455aadcba46e017f891e0a1d7da2bef04", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x02f6203142dbbc95fb523363f51b4bf13baa257aee5b83a7f49d83f05bd2ad9c", - "0x011bb55422518f09d7e7778125cbaf5b49a74c5820d17dc85621bf2f8f19d292", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0281d21dfe1d27eda730a2488e79067e5b1c53c40ef8861c1d32aac141450074", - "0x02efd08f6011d0eb872a5650cb95cfa540e0c81b91552884283f141f6091e846", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x00582cb34a054000bedd3a91d515c9eca50187f7366a0a33b75396330e398faf", - "0x05d4e4f9789ed10ba497c69a932dec90ccae75defe67c7221b1871cd2e8b4e7c", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x02223b85032fa67292f6e1f822628e6756e5c3cc08fc252ab88d63d624e4dfb2", - "0x055619ba96a7dcec77832fcb22cd5c21c7dcebc0280d730cba0002b67e0a8c63", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0278bba40eabef3a5a90acbdedc5a6ba4ae7673a8548d754a91bb4651971fd99", - "0x06ea582bca1b0e0c1a71f47f263ba8471b63ce9906eea7278cdc0dc9b31e66ad", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x05efa75044ab99e653b10373bf2e11c050101d60182e5af9ccecdc40973b371e", - "0x02e48ec5dc905140e1b07badddab17c6e46745688b14d25c3d2520c3feca473b", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x066ace6d06507c12bd6072fed3e9069349cbd965ad3a797713368fbaff16fe8e", - "0x06853c4dbf2739b33a3479f789ed511c00f7865abee02efab4bce52659211e1f", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x00cf54045847e20eaf005f0ec2be53e9ac8836f73de68d07d8c064349c58443e", - "0x06754bb13c7ebca6603639a51daf38ab90b188e931008bb4fd3708354ed80b76", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x06ea7cdf0463d267f2f4f2d00a84d2f512378c6df89a346c54e7b58d7cb883ec", - "0x03cfa940ccb6f6f35f909251a54db168deff502373803115c8a8ced55bec0b5a", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x074562ab94b86dfa3f863469e2a4a2f609bb669551370854c19ab0de8b2b2c13", - "0x02700611fa6b7ca567c908488e2320b23346bacdd63d465db01063c5eec0a847", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x032a857deaab815f1a6ffd3f4d8716b01a5516c35625389d2a41d39ae125f796", - "0x055dce8162dced3648178e2468e2f73a60ae1094674a8bca96f960b48ba69495", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0249b131e04de73af9820d3e22492d9ec51bdc0c4c4f34d95352fa44dd61f245", - "0x07576d3b5d136368ff01170a77d8286d0d1c7c40688862fb40813b4af3c6065e", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x06777915d9b4769027eb7e04733f8a2d669c84fe06080f55e8a55674dfbf9efb", - "0x0640d0ff384c9635e1af364760f104e058e3c86209fa9d2320aeac887b2e02d8", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x06f81282734648ff7e5e82d23f1994f927af1c6d195c03c5b1b741a54e2ddbc4", - "0x023ff8b7e243ef2b4b2d00a55c856e2e413a73e3d5c5598e2c56d71d4964c9d8", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x02abe3f237681052f002414399111cf07f8421535af41251edc427a36b5b19c9", - "0x0636ce4deaf468a503ab20ccb2f7e5bdc98551656ebf53e9c7786b11dd9090be", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x06447231367bb8d3467f904e1d3ad78e010117353048132968b232cc057b4fd6", - "0x01cb766be58ea36e7789de7c2041a48b4b0bb334758dd21828eb53908764c14c", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x04b2cb0e9e019546afe025cb3d1e3953b79562b442aba1f1057f7e2cec5b9a4e", - "0x03035d83893a5713fd2988d9bfaa28f08d07d085c095b65c3c4fbc9c36e473f8", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x05f78309f8ea28a2c5294bde2d423698157d73ebace06cd4f43eb319c4173e6b", - "0x06a7e61f89cb41194648ba19c9762c3e7264f9d6cebda77a40b097c902f3d3bb", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x04d5cc5414758ea1be55be779bd7da296c7e11f1564d9e8797ceea347c16f8ea", - "0x01a680c4c410cf5ddc74e95ff2897c193edaaecce5b2cde4e96bbae5c0054eff", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x04538e6f7c4156453eac50f114a0b361ebae3d035d15f313574cc25ddb2dba3f", - "0x03aa95bdb9e21b0649579bd0ff3ed6fe77959e7af23e50aa3c60c5ac4fc9d936", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x06758a120cd5fe2dbb67a3f8d9bdab962f61164383e3036920a0773afae43b7e", - "0x0234ed0ec5dc97bbf5ef6c0ce8f497791471b562199c88d16b9699d53f67420d", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x047e79174afc9f3350519ad3d53301c3009cd5c671eb938195ee6ced6b04a2f8", - "0x02883c9c71301e9d54a5e0c2a5b1e20bb3d356f5f45cc4f3005492beb2ea7f99", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x00bb28aa289167e93b346ff0498f7920d447be3f53798d339c099f4ea6597990", - "0x056fa84b0d103472e21ad6a40b4708d361e4165e5ae2a7d8efad72032e005c5a", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x029ab53a29de90eb1ef751b6ea917e5a10874f022cfd575136988a58f05c8787", - "0x028ec1aef03b9ec7813c4ee50c4c1cb7a883f13c4cd2d065c96cdf2a564f63a1", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x06fa2f57cdb134c1dd5edc62476693f1e05c09301ce7b4d66a1e883dee04c28c", - "0x041b107d45024203bde56ce87f4b194ff607955a6cf43b28d38106d4dc51f507", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0768b03d0928644290b7d1b219daa677a8d5ecdba0f13776a9c137f3321723c0", - "0x01619d22350ed54c968bf733a549266affb5bc2f3aa1bc74f74f26473d5fa24d", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x046c375c684b30adf4d51de81e92afee52b1a3847e177403372c82109373edca", - "0x01eaadc5783c90a0261306423d52009e991126b3f620e9cb6cffca41ca096f4f", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x02ddfb71f51205888118cbabba8fd07d460a810289bfdeeb7118707e310cb152", - "0x01fd905d07b3933be886f2518246bdafa6f33259a174668808223cd7c28183c7", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x003b31ad1cd77f77ba5c56becb4b136b2d8a14741cb004bdb599f158a1e37833", - "0x07c4ca72d1cfa29ebccf9f20f27001902b1684d442c229528e91134a8b4633a1", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0386f3879960713d41fdb3b1e41bbebf26b1c0e27a9a75bb1adcc1a0d3e8547b", - "0x02b21498c0f34ec6f17c720334dc0f36021c2f87afbbbc8847d0bd536eb265e5", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x04c4b9ea052f688961c3c379a6cbc38c6c9ab5803b78b68ca2d8257855574170", - "0x023acc43c50950adfdc0e95fe717421a68c54d1226b0a4e527a0a698dc4c0b71", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x00301bca1c8c68beb18de87861dc92ecaeb5bbc5722563289eed8c0b59f4c60e", - "0x016281c02661387ae85149686f6697617337850712ceea2e101b2264a6994d23", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x00fabc1f9e72fd4dbd6211edbc3dc7b03cf3b958a9811ac60c566a38a2964698", - "0x06b2f7e58a0ee4ca917fe2e83a2f861c6dd68bd8a4c09643559e0fedd820b9bc", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0407eae62c6c4de3b942195afec3f45efec71ddb5e6edee3d427631bcdbf9b90", - "0x0436e7f2d78268ef62c4172d2ff1469028bad1f1d0f97ab007064418e61caa8f", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x00b9d3092abf381cdd44c9027cf96c997fa5626915d348024b526b36a23d124d", - "0x0714e16dded4f9d2ac216528e8d192671a266363bafe02a0367e5c8ee59dba75", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x017fe1cd41a05a31b0b68e402c0de14e65de3c39331526ae63853209d51c1295", - "0x05c928a5972db2423724e67f503c452450ed7fe2aaf375547b5ef485d97fd5f7", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x06767ec2c42436d377daecc7ee1ecfc667fa381d27533ba675eed8fe55f0ab47", - "0x0134d2bb23030aa6366262732f9576ba8fd39dd4c25b1eda04bc12b303fc1233", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x07b98e553566e2bc8fdd132346b69b9f8aacfdb0f742c4f0ca49525e3392133f", - "0x0208f4941b84d254c014170905b6c54c66dce9c37c99b031615e7d4e0729d214", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x02127c55162f2641193ed8b7443f8297d34eed24c997eb046ff5b1a17430ddb5", - "0x03d3487f78c38cfdedd56cd01948f1a379230113b41edb5ba69356757155e045", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x074a3f027b432d92d0c69c22088138b581d190cf4d9eddcf04616cb257936d15", - "0x020faf1a59b803c02c2f11ce58a7e6726c92b0d15d665a77e7e7b232c6e83cf7", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x015ee18ca41ca3fc742f7a84b0764a0dae8e33ebcf6a20d2e055ab5f22b6aab8", - "0x0288807ac2ed330292a7c59ba06ab509e8a4f1f3e217e32db06602e4732f5102", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x01b881175e21201d17e095e9b3966b354f47de8c1acee5177f5909e0fd72328f", - "0x069954b1a9b8bfccf8ec384d32924518a935758f3d3662ef754bcc88f1f6f3ec", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x07d545a82bff003b8115be32a0c437f7c0a98f776bcf7fddb0392822844f3c5e", - "0x034b6e53a9565a7daa010711f5bf72254a4e61da3e6a562210a9abc9e8b66d69", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x04d1c70bcbeaba9e818640fa41163dceefd51b263878a4ef74a3f3dded987a12", - "0x0001fa627351e1254784faf682544604cde313e86aebb73481a9ec4e637a490a", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0299b9fcd4fadfc4b6141457a3036aaa68501c23df579de26df69d4def89b913", - "0x00b95bf2c2bb303c38bb396382edc798ca6a4847e573ce19b7b08533d1912675", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x006ead816a3f81aece567a9f0cdf99579bcfc97cf7db471c727523d6fe238569", - "0x018ce8deadb0ccd8bbcfaf4cc877ad57b4a68746fd1f48a090e934fb86044cfc", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0704982253eb1e57cbb5f4fcfded707f25eae70a7e1c61fcb0bcb9364181a5a4", - "0x051243d94f1f9e533da0f8eb82bd981086fbd80a25980bbdc6efca9ab7f0ae48", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x04cd143414fd634368429d33a659a880326c652a58111a177e67ca7cc5931d82", - "0x01ef5a5eb84d0040e639f365b642151af708e8c32ae2f0e11e017b35b5738995", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0551f5a4dae4a341a3e20336a7d2f365ddd45849351ec6dd4fcbedfe4806d5d5", - "0x05865c977a0ecf13ce85ae14c5c316872080bd36f0f614f56b6dfc7ece83792e", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x016a947ab797fb370353b4ea510f088c400733f32ff2eab29537548400fbddd8", - "0x0168ff79965be55ca10fb41fcc8c90591a6507e8aebfe125618f084e80f3f260", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0110f706235d35daa7c586b6be23cbec2d74cca91f1942777866ff132e9ae95d", - "0x04e9a3b34987ac6280f3326e752d433daccbbf7429e87eee3671b59067d0f889", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x028c3edfcfdd88b3f57a58ca614346eb1473e93709c574ab961698e47e13b3d0", - "0x06d697633d1a49f8d8e2458eeb9787cdc72ffcc6c41e07898f261fb558180c88", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x01bcb24af76973971e380b64754161cacda07bb0bc63ed0906ee06a90d7b3cf4", - "0x02b43aefcbe1197640cedf711febf20dafb9f0df070af6544a2f525844ef5c6a", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x05176b4555b18f97318da67f6bc279600e9779259cef0a287f23da68165a769c", - "0x06dfc1596d53bf2a9b36892663eca3fcf701a977ea715ba2d4b3a05d91781a73", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x035d98529f7c4be68dde9e7ded8bf1902429a8a1cab09122dd64c4854e609616", - "0x03677a5bbd9c04fa34e56d656d2341882a0f3ca9c812f66a380bd1084a691506", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x01457a7bf6763c8bc8445322012e2037dbf43f00ab2d2760ae607d5f76136d04", - "0x00cc5fc5c98ea895c9c5f55b4bfa6a6fc488d37f5acf5e0c8aa1f36ac4781b1e", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x07a1d69c08e68c80ad8b310736e6247a53bcba0183b9b8798833bc696a0fb6e2", - "0x03ce803a20ebb3b120d5eaf0ad64bed0522fad1a0f2ce39a5c5cbae98c4438f6", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x028acacc0bc41d84e83663f02b36981a2c8272ecd72d3901164be2affb09c504", - "0x07a5aee0b160eaff5b5968ab1a0304ce58c3d5ae0148d9191c39e87668229e5b", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0791670163ec8ae6e52c1673806a05d3933d3f936f8282ca7424caf8a42c6932", - "0x0132c0c3e2bd499d74566a3c71122bece1c61c50266811a1288a99fdab86d3e0", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x01f78cfdbcc767b68e69a224a077468cdfcb0afd6952b85bccbdb96d1fb8500b", - "0x04772ba173c6b583284eb001cfc2a124104833f464ff9df096443e10ef3e9dd4", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x02cc9d19317a8e4d41e8b1878175cf76622c32d69b7c0163d7cf5d5c2a1714ba", - "0x063fa861f8ccef9d7277af4e206bc97f103083566add9d5374975290f232c5d3", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x060b8808a0b4d26dc6a8469f289466a0340583ac0a42c11a8ac285481c9baf82", - "0x02ebdfb367d07bdd719e724692fa2746467185a09bb6ae2c60ee7f10ec7a359c", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0771eed999db52d032d808c947ce60cf98605f217905969d1413d0aa4c0f263b", - "0x049684fcaf0ef530d58c70cdb76137b51dcca9c2078d9119fd50578992daf4a9", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x02774108962ca9897e7f22c064d2ccedac4fef5fc9569331c27cdc336c95774b", - "0x009e13d79b68e8dc8091c019618f5b07283a710ddf1733dc674a99fc32c12911", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x00e47ec42e9659d96c44a25da2cf955e006c4284c1736e49627130e1d6ef83c7", - "0x07faa74393161e0fa276029865f7fe2e93be99ddf8a91ebd87cceab2f8778234", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x015a86e35fb1dc4426f5856d1c3faeb126876bc75d34b03b37325559f0190686", - "0x052966463e31911fbc859370880a747f9af765db7937ae4585a7437c7b0cc90d", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0729ce3b24c4007e27c63d3ea7ff1b7a6bee2deb1c999249879476b5c8eb9c0b", - "0x05b3c4c99102f00ae20615f4873c1dc76cfeaac57ef652918c8d2c15d08e98fd", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x05bd96eccc79ce49f3f74d22fa9d027f1b15ed3b4103cd043269857e3b61310d", - "0x02bc5db36b14e35c134cd907651a4110302139970a7055e683f3ae5616b1ae28", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x055fc3fc54fd080b46e9022c5867a23cf94ec12cadd57d713ce591f454e8717a", - "0x039c17c15dde2c6b4e934c7f1a7b43d32a811fc1e666bb2f1e89c440ee479210", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x05452a315bf5c79bd5adc042777d9cde7997dec81b15ae4d4224e4b638d1b660", - "0x036fd9b88ce52f2493f00b123664289e0fc9677a796716fe6ce781ccd6bae877", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0746abb611c99652227c9aabda92e91448a740d35a7cca71611e6a6e9cfd490d", - "0x07b82e74092feef8310a9f7fa1d04613ae6fd6f453564a138ebe1a41cd90e756", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0770d116415cd2c4ace0d8b721dd77e4a2ef766591f9ec9fa0b61304548994ed", - "0x042165d93c82f687635aa2b68492b3adffd516beb4baa94520efa11467a209fd", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x05e6e4ece6621e2275415e1fda1e7c4f496de498b77c0b913073c6a6099394b9", - "0x03d92ce044fc77fa227adc31f6fc17ef8b4ec1c5aafc44630c0d9195075bf56d", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x077c29753cae84788c0321eec01213b285795f3b86f9820b25144f2cd79bfa65", - "0x004a4cd6062dcf8796de9136585ceb4d16538bc2e0f915e311a24afd83dec3db", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x06e69c717b5d98807ff1e404a5187a9ceaf0110b83aa15a84f930928b1171825", - "0x01ee7cfc3a9744d7fa380ba28604af9df33ac077724374c04588bd71fa16b177", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x06524efaf1ef995c2fc022b07a92ad18aaff67ff1b58d26588756603fe4eb5b9", - "0x0433e654f7c7263ab56043059421a9aa8c0a2e93bae57cbf4ec33288b96a83cc", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x04bf84f9ea7df23758cd894491e480d0a476ff631e9ad7ff718e0083b06e0f74", - "0x05b3d884d26b0f1a5f8ba62dab0feac7235a39cafacbc1e792ad27a23d409142", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x079bb7d217a1ff047a278b13301a97a8c495a9ef6f499e55410fc60e98d323bd", - "0x03b55c720a835ae732a20039f0d994dc0a2f812947897a35c7ad72accb3ece94", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0404318f2d2ceb44f549c80f9d7de9879d8f7da4b81e7350c00e974ebf2daef1", - "0x03934831b5af70d17a3f1da9d2931bd757e6acf2893236264fc7e0d92ff1a1cb", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x034f76980361989a9e712f25b56b0f3b50533f9efd10a2497a9a382bc7407121", - "0x02edf097fc9b674ac310a5b519f59a57ea6f37ee3115b7c799978c94a97913fa", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x067abc6bb1f23ce52c19312b447d96ec01401436f27bdf4ed618852508100563", - "0x043ae7cd4a8999e2f0846ac6ca0e9b583e2abdfa093eb1d39ca2cc075f7dadc9", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x012f583929f3e0de63f13fd9c1f0af5b11b284699d5dcad27380591fc8bff89c", - "0x0200564e5252fa89fd981d626b7fc56551c17bbd042e0f3ba836c7de1a31d767", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x056f93fd763768ee8e3183be29faad3050682f325f71a9b7295ddd3a4ffb3df5", - "0x03d4d30d4ab48a9a30e9e1cde1a2fa265641cbfa109941d8397768cb05d68ef4", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x07aec427b526806568b3661318e76c109304cc66e52b7d60f693677022debf55", - "0x07180910240525490405125aac39729e9fdd2318c823ca068350a7232262f627", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x04c49cd4df5c6b8519131c6bb53633286c7b6e6eeeb0c4f8d4c4526be0f5090c", - "0x041a1abe17ff78b4c71ca1956b9d81f78786bde00657b4a0efd4d38298b47ea9", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x041aa0e6b8bae1a7b37707abca58fd09ebdf61c6b2976c6eadf3618d5bad00ee", - "0x0550466e111fe2c8ba2a3598f512936e0c83b4d0cff5aa59f3e24d5a128d4848", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x020dcb6f394fea6d549b2e75748f61b7ec03b6e52319cb14163373a9c22bb9dc", - "0x0106a8c96cfb95a331618b7416d1498554730499e194a58fbf63019890480fc7", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0119000f277ccee013e6bb121194ec1ab5460fb6a96eb702a14079865f4170aa", - "0x01737a32f5415e8720a5606ec1dd4756f02e7c6817e3723b453d091f2d192773", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x024b7a9216a2ac531c2c318eee682180d394a7596e7219f64a4413206aa62108", - "0x078fd489922cb3c6e2d534d4e8000f475d3864b6383a7eaae2d88e3db5c960cc", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x045d0fb5cd95db76d05dec3faa12e467a308eabaad363a062353db3cd2d9b749", - "0x00ae08691b5b0cdd19ec499132421638f470f493320e4003d123ab1da761b965", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x014455bbe2a196d9320a6010f4a5ad6a0480f47a096cdd43b7524b13b19e187c", - "0x039f7df161a3cb28b249b666815853502548fc3d82c30cb87fd4b76e4a22a75b", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x035645b557df8e66f0665354012e4e8b46ee19004547f4f0b1b9a2b9f4b7f27f", - "0x00ff38d481f14c324ab3995fa6f5686a60f0d41a6c73046da6f6eb75497b4dfa", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x058816cfdd17d8f7e00c0e3b522d13adc6985831f84ffda523b5b15be049e712", - "0x037df06c43abbac34c74fe11a42dd2d08fc52b3123ce3a8184bf32341c7a9af6", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x01257b3e65cdfb6367c6d0942327e799bc66eb221e70c6573a9862889eb51c38", - "0x0593309fd45755dd2cc4afd2b9316bc4638b0c5ddb3009694fcb7b250d0c8a2f", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x070c55f523088ae4167872f9e0728817b8c8ee4d11ac69fac00935d2e9d278e5", - "0x02abc80ad7b1b2f026907138873052010ee5f30a853e135547e9c40f1f09b140", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0121be713b18775280c58ecaf99f3fafceba6a0a3ccd78bdaa0b039a01dca700", - "0x05f1a670cdcae1a793c509878b353de576044fbc774c4858ee066797ddadc241", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x015274bbb7a6f089d769cb07bdaae45fbc82e3d7809845715fbe431f58508637", - "0x016ccaf74f364d3a84c87cde65ce9556f3ada0dd6d48a328fcae1feff5d68aba", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0689bdbbc1f6067756f775d2c80ac97dfe182686fa7f95b86d57f772c6859a3b", - "0x06843cefd0215f0dca692c37b1964e1481130705b2f7af487c3731b1a19cbcaa", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x059f7aef17aea8fb52851a7d4f1cbda97a5df236488147c4f7a5d2149633dff9", - "0x05da3dd360110b43dbd06ff2c3b76f220b48c7c80a3b25f0dc1a1077177b0559", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0124c997ebcf7cfc0c4ec3e4d8ef8a1487f4c4bb18cd2fdfa55a2f33c3bc1955", - "0x0796eff8a3182604c60f8669b1a14bc61d23ed23ff977cb1ecb0359811d5b9bb", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x01cfa692b4ea379c0a4a59212fa5afe459635081d8d65b73f659b5fce53d9959", - "0x001cb133c2d602c16f17860ef9255aad4b0f3c9fa5bc3f85f8777a82b5c2378b", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0186dcf9950f72e868014a8accf14aa36e82a7a2a29f86ba37f6632da4189db3", - "0x055684c9f7a043fc523ed78f756f834b4db823d5e4161bd79602c17d55a5cd8c", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x058791d5569f282f5c3b01ecdc9388df7ba3ca223a2dc1eed5edaf2a1d302fb9", - "0x06298d7dd51561a045bb4089deda9f40b2865589ed433e56d54554f8b45e79f0", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x049fcfd1f91de6367d15699c38f80b4253d4b38c3acb3a3ff7071f2344344949", - "0x05a2df8383e58f480df2ac020453a2638d25dddedfa599086ffb50cf2c726a63", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x013fd87144aa5aa4b24d5a7bf907d8280d15937fed262d41084898cb688fc28b", - "0x03fa54367770cc4479a857411ddcabe86627b405ce1cd14ad3b2863bde13abe4", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x03b795c36f08d24b5759983245b3f87bc0bb3a7bacd8ce469808cdd3dff756a0", - "0x054d1a63e20429b960320b732ee75c788cffe37d0af089af80a2bef759f2512e", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0493f307b898d0224d7abe27e8c3191e9c4bbb01252f2b58a85aa89d2202a2ca", - "0x05a938afdb84da82376707acdc8728bd7449e3873dad883cc864af9f1dbaaf60", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x078c9fb111ffc902e5f4b4147306d461a5bb6a11d86a84fb10d2d0920d49a454", - "0x009b4364ca713d208f2a0acd560e778b1b6da7b896bb241f8851aa557495dc0e", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x048118139445415f0c1879224e2dee744ed35280ff00537260402a1741ec3676", - "0x04dfa39dadaabecfc54ecb7a25319444f8e952782d863790e42a9887064fc0c1", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x05bb002ce2f21ec59ffe50123b20ea7478c7095bded4b03b5c2b0c6e53addea9", - "0x065f10da81bf42af85f11a9da74d169fdd934a01eb42fe6ae9fccd6ac50615e2", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x03b894f7e29397e50352e39ff99109a34329f6dc7b7b873dfb8f827a57a8e686", - "0x0431dc16e38ed4c1c8db6baf71d362f53923d925fc3f3413514b3d7386f1c197", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x03d56abee17f1121d53fc762d5310a4b8b4fb0bbbe6084702de5ab72aaed74f0", - "0x07ba9284b8907e79fa3cd3af5bd5ac716b46be3d8e48fbcb93cf5b02013c9911", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x02d16100e9e6fece514857e64cc1c3c54225d800167f7fd0c3a93bc8f55f0c79", - "0x045467e3b674303b6128258707d2767af5dbd7d46753597275b648b26653d1d2", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x079ed57111b506958da71cc5905714b30fffd18f1178a4bf34b20600e10f9442", - "0x0314cb3044c8449d3507f7da2698cc4fec7f96ccc81cb6cf452657c2548b7f4f", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x00fdf811b74879bf3f8b12d9bc32302452392372de0c45ca0e3bd0185b060ac6", - "0x03c6850f2d56034540f35910398e1cb375aa41497641493355e4f3c22640e7bf", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x00a83defb97b3fa0f1dd7bd5cf8383cd9d8dd7a41089d8d4b2e2dee59c8eaf0c", - "0x04f917752be2065d7f0f1fb2450529046b2c752597a1932ed4488b1fe0d0dff0", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x04ad031bb9eda84f2fe5d354c7948d41558ca657a04508654721810ee72ef158", - "0x0620ebd5d0086b92c6009a42777b946a351c2c7ba852b57d3c9905fc337459ef", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x04a34abb016ad8cb4575ea5bd28385d2348e5bcc0cbba90059f90f9c71f86e8b", - "0x04f781829ad83f9ed1e1b6de0e5f4ac60dfdfe7f23cb4411e815817e705e52c8", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x06dc7cb0b8fe8517f123122f101d89da7531947432e1a83ace06b661b107086d", - "0x0796fd2f8bca27b794a3fac0102213686c335a812fa9a4efdea35b7621630d7b", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x07fc632d7512aab5356b7915dca854c8b12b369ab54f524fbce352f00eb9b9f9", - "0x02ce80b944fc9158005f630b34385d50c3ad84450a9e1e529925b3211dd2a1de", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0651848d45cab099b169e802527e4060e8df05b0219a69c4a5b5bc066a8dd6b0", - "0x03ccb493049854709b23ba862f92644256940b5ade5fe85c2102f2c2d1f35a82", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0520ec6007cd7ba1b5d6a83945931269d2d01c6d50d431ddeadd6903c242fa0e", - "0x038725464ae187f670443c0cd4b6261e67cf906fc02372626be86e5292214de4", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x00e3f542a1c823c7f15fee11d64f0926a418c74ed74291b8cbd50c56f7ef0444", - "0x069aa4fcdf4905ee290355b41068753fe996463488c67301487959e42775884d", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x065ed10347503cbc0216ca03f7536cca16b6abd18d332a9258685907f2e5c23f", - "0x03be1a18c6bfa6f2f4898ebefad5a8e844c74626d5baa04a820d407fe28bbca6", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x037017d19d22b9af3c1c128bfc5a2145864d13d40c4c40a2ecbc341fce33aff8", - "0x02f375f28593711138cb277e7f6e7fbd02f288d3e1d0dca94a5ca9bf2ccce5f4", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x078c2600f40a1b5c13922effbd632a2d937ad8d98cd19684ef71ada4d1178dbc", - "0x008f2cfb8f04abafb6cc4ca8b86836c03afcdf1cdc2a76635e033e42467b5d7e", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x05a4bf8a155a3574e74b239ab778c451795db097f9ce2b654808db277239be57", - "0x03bda0a709a5a163640eb57b2825c3ac381af0c6542496e69f758342b73ced37", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x00f731ace9f24a83c57eb3604e761bd77273ce89d8c13c421c02c3350dfac53e", - "0x0347d11e9c6113b2da709b4b0cda353b53607e03e2afed8905fd196a4b6fa6c1", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x009a1b04db6a517f3ada896fc9a29104851614ff184e299673a276c072c9d9cd", - "0x02bebda66ce439885be6c1f94cff0ad63acd9e05388f5b394158f95ebf5805b1", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x07fd4b320f0c1420b58b5f5d4680032a8584efbfcf8b4f32ee46ec962bc6250b", - "0x02a192f37e37affad5eb037961365be78cf2444de270925edfc93d5a2c3a100d", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x079a4df1c28308725cfed2ea6fe4d67f02d2c80fb44e47c4e6a338bcf8cf6775", - "0x01d052c945d0dd6b44320bc98997e7c5682b680948a987fe23f3395ae0c25ed0", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x01a8abba1be2e276cdd1f28c912280833a5ede1ec121738fcca47dc070dcc71d", - "0x021b724378bc029a5199799df005922590d4e59cae52976f8e437bf6693eec4a", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x03a99c22dafcfe9004ebb674805736a26aeed7ed5d465ae37226dcbe270a972b", - "0x05bf67552af08e1e6e2a24bf562c23225e89869cab9bef8becb3669175a3c94f", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x015d1e8b43ab3a789f64b02f034001f77ed2114448d424151a19618e1c763bc8", - "0x03ed8e0cd6836f4fed494c59daf090ad560e35371f6b6e0447224c0bec42cd25", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x04a6a5e4b3501f2b7bbdd8da73ea81ffca347170bdfb6776a037cdd74c560fb4", - "0x05af167ebb259c2da88740ec559ee04052bb66480b836cadd0e2590c32d7111b", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x031cd0f25ff0f756bad489d479905c9774ba0b6b2453d5d25ac59237ecc37935", - "0x02e9cff7d5d53a009bc77c12aa8dd60e6184ebd623cb2ddef07009e2ae8553b8", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x01d8441c409d3c16b46a120d1e451faba47fb974bdd9b4474d4bd605034f34ca", - "0x059a9970eb948deb08bb15e69c6f1f33c5f73c827e0800467bc1f5d72bcb384d", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x037f363f5d5394b00f8a3fd2e2e4b46f6b1a5a20ce9054e75b051c1317b8b7fa", - "0x060706ddf1ddf615f4496d838c9a4200f7b13b3a675145aa7b00fac917da7643", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x06890d95308525f0bac9dc25cc1189eb92d29d4b3fe61bc8aee1c716ac17b1e8", - "0x00e6f23f78e882026b53ea4fac6950e56e3da461e52339eb43d2fdb2dade7ca9", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x047366c405bb48ec3f31db4705fb7e1de598db4855e5374b5cddb076debb8924", - "0x07b6340f88acd485bae466f8eb6bb22be8a1e25066d316adeca0aff52d24b8ce", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x05eb2c383a68bc62baf668b6e26fc2efc2ea0297bfa90c534b46b98db505681b", - "0x026fbca84685699c605391febb3c8adfb1153b097641a6d02eefc4a09089a8d7", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x01091b7e9d959164fb0994138ab9cc3adc206a097b559237fc91db5b157b8d7b", - "0x00d1fa9c33283554f5489a648bb64233c0b7a03a476436422c24ba104e9c6b8b", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x06f22fbf9ace529f20bcce8e62e6f2bacc079365ed4a1df6af75f5beaaddfb8a", - "0x030f298c469fa107d50902217e20909c7172ed64fe26075ab761756bd9d178d3", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x033f1b2c7d271c3283ac0b2a1dbef7b3bfb38c7cfb8a3379d72c620e20e77235", - "0x00ee81a23414092d4511f0c3dd6802819193d91bce3116a3c649289cf35e9049", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x00515035941b0f7b272f508f01db2e1ce055b2a06fb533b96c2afc0d1581ea06", - "0x0400cef56fbf74f3127376bb8619c1b32dfe03a572f4f4e65e80e04bbd6f08bf", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0663bad148a41cff759f0883290c27fab38a276edb56a81a20bfbf43810f75b2", - "0x0515f465df7de627f352e9e5ff25ae2806dd552a5f60c6fd2cdde0cd85b34831", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0748f4cf4f027efdeaed7c7f91ef3730ff2f2bb0bfc2db8f27aadde947f7d4d5", - "0x03a1cbc550699411052c76293b8c41a3a8a1ecf12cbbc029a1b2b6ea986fca93", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x07321f3f581690922cd0dec40c9c352aae412ec2ccdf718f137f7786ab452cd3", - "0x05be5130c9277cdb76d7409452438ec15d246b211dd1e276ee58e82a81c98fd4", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x05afd91cb45deea19ce415102a59792b8afbdbe77f6702d87d20ac7b3c6c5a14", - "0x005818722265b7a654ad6e361c4f53c712faca32ff1522841055ee39c48abe86", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x06c4d6cb7e7ae70955224b8a912ff57ca218635a2436b36cee25dce8a5cdf51f", - "0x032f8c03c6db3246946e432e4148e69f5628b200c6d7d72449df6eeac0998039", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x03a2e4c3c6f92402776a9e30ce3b4ddf5fecbdae85546d19220f43f7de1fd878", - "0x022b17c0a936104c38cde32caff0dcdd3e628e59cf271393b70471116d9eae87", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x00d31707e941d8e4a39df7bd581a6a2ab3922c426f5af9b8ab34ac369ce20c3c", - "0x04acbde4fca11fa1975fc8a3e1aec32ba05bfe7abaf7f99cc799bb4e9a4bb902", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0796c11d20224e4030ceec79bbd02ecd406e5884d68542bff6662bb6c53083ba", - "0x04177d28e5464e1cee8ca4d6e5b58a2e0795930658f94cd538c5ad1df980d098", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x01dad5f2e795ea6fa5177f110989516eacf8fb37bd6a091c7c93f1d73a2fe309", - "0x056b2298c538180e99dea3e171dbb5c6fba0bd0a9ed40537277c0c2373a8e2c4", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x05d08a975cfbcc4e964468ab9d314471b3b338325f53148a31f2c93ef981e348", - "0x04c8e1817b63a0b2cfb8312666f5ba9c4021a8a7b72e9a86310047fa9ea70d07", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x05db5c67073f4ac1f6524393601ace3ed5e457a33c527dd1f2cc8043f5080b11", - "0x024bfec75d33b8cd0e7efe321ae0f9b3188413c2e39151f5225d4970f92e58cd", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x05e6905a69d972f6b6ddae30dcd6928c2633e5b4af187d77ab31b6470c74b585", - "0x023f3e8a974ad97e6c79374d60bdf1f7b58f90528bd6033832175e4028826265", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x05b88521d73ecf48adf5808c375c56559ca051c3d842661edc5dec442ae87a5b", - "0x070b425ef3e83b094859c69fcabeebe78a6243f26e577a6c16fc62d2289f7d21", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0598ed112427bf6edbd5c7f6d50f03582163fc37e23ccc2bd982fb00dfcbe054", - "0x0606018e8717c1d2c3c5021a1dbf9928bd494cbb7d6dac5c57e724aec075d5b1", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x023e37d8c4273dda97a6209821b03c06d008bed9757143cbb1aa2f6009e9534f", - "0x0043d3d6c86d7946988d63be931a9384e804b7e397ef3d83f3dbfe4cce7a6427", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x04d6ad42369d0ca6e8f591d28d271626b43532f2f3d4e1d6c89235bc6e7e1bfc", - "0x027fd3756441f8d93382620a2be4de39fd7049a17cedb053024a240616096225", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x01610605baacc9bc62c4cc923dc943347cfece7ae241e746fbe6c2c878221dbd", - "0x0431a82d657e0d109d00dea88cf3fa9b999845221b7b5590a20c40fc71368c1c", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x06a4f5c787fb09a5be2b04d2eafa1e6f3d3c863ee22960eb0b64f6eaf6659162", - "0x014dbc3eaea6146ee7eaace5a91ed9430dad3a47e9ca2f68b455171f8fe6a7b3", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x025e73ef966cf87711e4c367ec01966bb6afd402171f695a6923e60fb33bba72", - "0x0738383b20ba06b299bad3bc16e6eb971856c89cdb1a4828f2de81d5e8f63540", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x000738415b73e55412b0e582e45ff0d7bf4b1bf2922db581783fdcc75559f40e", - "0x033825aeb3fd8459999eb418d15102ba5864b069c6ea517f0c6e9eab8d9aca47", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x02957724f392dd396f07242001af51214fc4c35ed15acf507d2bfbf79da66350", - "0x021d1f3c135e28b01d550228ad041cff4014194872572221d0063755e66ad1dd", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0468a271b950a44059cc93b8d6000b6ba378e06b5cf0a9d92fb01883776931b3", - "0x077829a2c43b90b4f351908e4d3d8679bbc879d0c2a9edb477f5e49cfc54783f", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x05748f5c43e13d0620c50e2f73639b56e623309125fe14695291681c6ab8499e", - "0x037c565885e96f59712a545d1cc24da9c510eecad6daadd55e11a3ede6e4649b", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x02603e72ce53985c70782774057a17944f7b4ce224a809be4e2b5af3606aa1d8", - "0x0092822921809c42318f42dac4d773325f41c43069e990adac7818a45e2554dc", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0207755b2d5186c09ec9bdf40830aebf55e4c09b18785ff1d9fc5b8a84b78a10", - "0x02b055c58617086dee864a2a48eef389c6f51f0417dcfe4e99c80835454a17d2", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x059a15120fc2d5b5f32112189104832feb9e135a4e18880036e875f95d89b8bc", - "0x078a7382e1f991cc976c50084c8425dddfec0ec4d3cbedeb5edbea347ef14b8d", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x057683840d1e0325dc77187f01c631d90a722df60ce2602da5cb126fa1a797e9", - "0x07087ffbf35b7d32c3c5df2f9efd8514b45b7aa1618034488d661fc349b3054b", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x043db4cdae07442fd7ba6843a782b6e3aff1872d82ce8dfa037c8058d47cf84a", - "0x046174366bfdaf149509daacd555745bc75fff47fe7662653ccc19d5a5575b6d", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x029e63215e51f9162da1a131dad5c4de499d62df1dbd7a386f5b9f330c5129a2", - "0x07111a09f63a9b043a594c65454c9f39c4c868c2eb64a3d07efa4b73430870a0", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0326c00f300157c86e4ec7171d83fedfea58c414a97123c6bacef59680476233", - "0x0599822be567a0eecdf908d1a36164b9022f91c4be30b311355ffa17c2a6921a", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x012e680d1872aca17829fcf35b925442d0b059a0b7cd6dae930d3338c980ee53", - "0x0500f390d87f2b3350fa15aa9fd2578000040a5d3533da05fc9161796a5ef2ca", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0181cd967ab4615357cc96c82eae9152ce7598c1a1dfdd91a458bddb016ae9fe", - "0x05d562fdaeb0e12647e230e50eaf216bed52fa73c6b7378821a3bfc4cd66d4ff", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x01121726069b9ef5954ba6490100b226e0be53fef3e071b7c58a1286174b789a", - "0x04b25594cf4e9eb2d14b3f52f2661a9992234fc222c0a0d44517cb77deb9c16f", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0059d70f1f5af1e1b0be565b9a38d422e1fcebf0ebe7100dd22a63db42521407", - "0x033e8210993e81268e03fdbaf4fbc89f4a461041d45b3960c7c3a6a9be80ab68", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x00e543663969b915337f105f80995a77b356f1a51d8b4a4fb12d44364130e873", - "0x034b2e3c009fdab4cb7349a580df2e64c0098a123280078e5da6623a9ec6b44f", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x01f61e12d22eec4bda7a35687fd0dd34d7b1676db852ecfced3347bb59957d37", - "0x00497e1f230a83d592b734f03fbcc852f58a27f4a24c262003e28f56733a1817", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0096359fcca452303402d17f99817b07442ed93862e4656228081ef9d0f41452", - "0x004444041c5f31cf617dba4eafc8af0991851edab15d6c66f7cb4e4b9f7dd497", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x029a66c92d3739b24d93b87288b516603615c707cd42db417ad5c420db7a891a", - "0x06f2304c8334a5ea6957dad2c70c73688ed76fd692353edbb6a8a0f49b05d24f", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x04e2f8909bb62de5ef65600e61bbf969293815296b6e23702875e049b3ce5c45", - "0x003cb81f2c21f22a7add26fa38a9ce5d9cce1bb251bd2698f90c34ff0a84f7af", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x01c9a943d8f663621b4464b72d851a4a550b0038061500c60cd5ea88d0704947", - "0x07e73d2a9720b043c0fdb226f0c2b878460add44cc26ef134cf020e52f5053d9", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x03755e10b47e2d60435e16c7267a270f20abe35b7df6845fa78f400c573391aa", - "0x07daec8b1715f5612e8ed9dcf587dac5d2319ec71fd94bec0d9dfc006974d6ab", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x044dea1759ddc7f182adb3ae0e1941cd11ac62a157556377cf512c175ba0f9b8", - "0x0326df591f7894fda39cc028f6a119b3f02aec12a14963e66c5ec2daeaa6e3ce", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x00864f3640295da5d276321d37628bddb02e801399a4a505352ee3ec01412687", - "0x0110f880c70ddb4e08f8efac560a8ade433650f973a4aae4f090193c4be024b8", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0454064383ffe86179a98c7d9e847f7e6205c7f19b77b4d88b6017275bfaca17", - "0x063a3823a4da2801efe749c94124e1357405b6bd176cada1d70789083045e508", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0327029691408702e6e430533b1f456278c19e2af5785eb20ce6c99838c5a7cd", - "0x0436bf52e02d031d5b219d5b49a44340734be38bcf56fc87f0ead935dd284a9b", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0522231210ea3e60df50a7e47e09daafd82377dea2a8bf10298ca000602ce582", - "0x020e4f50fbf2cb1590a8454e4b1812a77ceae12af0b30ac16dc0282b68f7db98", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x037b546e403a1ba970c17b67c2f1361ab9c803f8d2b5cd93803014faa08861ed", - "0x037079184ea46272f5809b523d060686633f7995167897a153be1772fd6566f6", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x027bddca77f7bd7f66b3693567a4238f2e6751d95b0bcb409f6b24d08f84798c", - "0x06417a85cbfd6fc02df560d3963a241a986baacdfa423f65d7227ce49a96c57d", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x03945a4beffad92a5a08d9d49b073fcca77acc8dfd41fcfeeef5d334b4c86ee2", - "0x019f6fea2dd54c24148e8ce0daaff3652d448698ef564d7a2c640877b0e30604", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x02de71a39aa043057d1bc66e45f804542acddf18f7a6d88c0d7fb0ca240debdf", - "0x0306c1ce39ab46300f7cca0f3a2fbfa77296a27e24bc66b0b8044968ec0ee413", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x068d064e368144ca4c8eb76f2f88e4ee77ecf909f22105b83a9db24b72eb1ee8", - "0x075f951d9b573048f0ec3213deb1af979246719dbb3d0c148158573b7518f618", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0008f8e0befce09e70eb4a6716d8128f0981a7d81cf687488163cf4962c797a9", - "0x054cc88cf27c9e9ee0f96879a4ae118f906f9d9a6df98f8b645ddbdc49b1c52a", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x05c758c0075321475692a682072aeb333fc4d36d7aea945987024cef80cbe0ab", - "0x0628586e827c609b776cdb06829f6f5f53669254339ad265e41f0365abf84f13", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0307c877154364c0c03534e7327d5a88e1380ceef6481567ade37a14ee7c1a72", - "0x03404bc7dbfb33b95d922d0693aaf9358f77888d7d95e773c38d83dbe2e5f995", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0459f3f57739281c648ef1930e4cd39df0fa584466a8b10953b896118794a5cb", - "0x032836fbfccdd1c73bdca25a155f203346b5f179a68882665a1e3e1268d1abd5", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0746a8f305ed62347cdfca3c043709b9dd154c6ea1f6cadae9d06cc82d1728b4", - "0x042c178827e551577045a73e5fcc24f09412071d003ddc67d29e65352e070bce", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x07c641e896b9c3866dd899694e8b19aa3f9dfc1eb2886189e014f811a2ed3951", - "0x04b513b82e65099ec1aada8bdfb00c92180597242446255ddcac67608f5cdfb3", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x06f79d579f1fec937dc715916a3368a4082469d98baee09f4670c37d547fa383", - "0x00407329a5f2339464bebd4abe4049b146bbe240ed94e1cf515e98b1414c9578", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x01231bbc6fe7253f9153355e24c945ce6621cdeeffa6414799ad661098e730ef", - "0x0060c4334a1dfb438b647b635d3c404cf110b187fac6edd8a06cc9df780c48c0", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0062f56a0a7e94a612fc879df4bd1e4f2519a8e0645328467c3eaaaa3fdf1f7f", - "0x0757306400fbf5c3d5293df3e936c9a7d8568f9272d311df40f57a5dd08015bd", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x07b86081b991cb4b765458a8c6921d01beb78cb2c5a55a9ee7137142fd5cb28e", - "0x0494fce0a7f58737e7f4a69f8529b5c381e682c374ee5716c8e888a75213425c", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x079f09ff7c60850e5f5ea020722659a1ed27db4c95dca131f99552f785c8afbc", - "0x040429528c099349b426ddbf129497176951a64a53db5f9d8bd2be0252cb22b2", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x04027dc6b56d446e5972f35464eeac85c5254ef377c902d9fe37aea841bb5292", - "0x07c3ea37689ef679fa2f5c7e031a78e23d484a8317990fd34d44d95cc1db3717", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x011c27f9ca5aa652cebed25ab12bd6847babcd620dc296c22e41ad109ba0d3da", - "0x05b838159b4e62be7389f00f3f2ad74080aad8b2fe6f8791f7c89f2e9bb8ffb5", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0645dbf78a3c228c4b7151450b5e65edb58e71f37e1e4bc5f471e0f1abd6d9c2", - "0x015cfe7850f327b256e23b00627451560c5c6ab60db78d45b7ab286afb6f13ab", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x06b095a3dd8dd4e59b94eb894883744a8ecb23ef0ef03a203f52626848d30f1d", - "0x020f532e9dd6c71cb9a92337cae5b6e9310b7547158aa943900f184ff9988772", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x05b3132c6e985d667d1ff6a3f25433324ba238d14748b479931d4b15bb98a089", - "0x073be3acab3d3d3f34fa8c16aac3fbb76351f2300b6bba98bec67aa0f1861272", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x006958c365c5c17504d559497c102d2e3ba8c6924ab8ede1ca87350558dfd893", - "0x04d5148bb677361907d20614f436c632bf5281631db26f536c54f03d6a180dc1", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x01503ca373757677ad1d911a2b599d01c46eb879d1ce21ae171c7e439846a85f", - "0x0583eb269b7030da6a0c324026919de3f9489d2ff6ae0e6320c36f05469ad66c", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x07d43c4400a7ade9fee36a22518996847b17b543f4d2a7de777c4ac951ff5fa6", - "0x01975ab13353ec269342d117dfd67eb1616071dce91f404056bb3997b8190d24", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0499f4fcabe7c0ff4fd32d67aee7874642d75578417629952b9d9a99405b77f7", - "0x00d42e30d5dfd77550074ffec85bba3b22ddeff37d51a0ec4ec45cd1c93e6c21", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x00fea9ae779288a6c9c01f80ac8ea877d3577727329b5cf6837b44b10adee9a0", - "0x0650d890ddb87ae39ff41b093e0cb361025b24051433f4f75608c4e80dfd5409", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x02612191653c9a60d5019f825129e555256ba642cf7fbbd620c0c88324646f95", - "0x03b813d57bf4a09bb675c534ff8b543c9b8e1986dd5adfac551a7eefc13939fa", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0530a660937b20eacaf2ef4e2ae54788eb0160b21e92f2cac659c124fbe3f5b8", - "0x049330524ef1934ed75b7ac6626e48fc2d727015d6d5fbd2621f213d42abdbd6", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x02e556484ea475a926816e102c9413ec9c0c768285511a0770e0af1c9c117bca", - "0x00d6ff6fc23668b46e29740ebc96b75e2ce3099f523c081b5e88d13ff6c25b93", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x047098b1cb935549a63341c6f4d166b6aef7689e4c2e90c809740acc835db5f5", - "0x04037cda3699e88ec219eecf2aee240a49dea1a0369467e1c58c91dd4f5ad023", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x066e1819ba3ec4ad4ae9f7d7588d23baa004e29d3aad2393d52af204a81626ca", - "0x0505249980cbe6273b82ad5038fe04a981896f4117345ac1abcc67e2525c0ee4", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x05ec20dbb290254545f9292c0a8e4fbbfb80ad9aab0a0e0e9e9923f784d70ed1", - "0x00bdb1ca3a859227cf5d00eaae1f22584e826ed83b7ccdb65483ed5213dc4323", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x01acd22d622dc63eee1c988b91cf853217aabd30047c1c7b4440a45b00a02c80", - "0x03b616688c30255a25c10568c063903e2e3e0fa49284da52ab28dd4b183db99b", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x00a5c1a5011f4b81c5c01ef0b07c0fbf0a166de77280f0ae241f2db6cba15194", - "0x04444521fb9b33d7dfeb1247d0ee1a2b854ad166cb663d9dd2e686909362a689", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x01c5ee84a34040624e802bb53fadfde50b8f2a8f9b54543d940fadbad0091ea9", - "0x03fc87aff5402328f0f239740e6607cd82f06a58da72b8e7753d9134db78a732", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x07aa75ed7172955831eb7a20a75e867122c03957c11cef89fb9b58ee50ff88b0", - "0x01194797f8829b1982f66932a6980c834e80cdf4c4a96987357e7bd8e8045b21", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x00e0661276ee5aac208777768865856677a66265d58f4828fedca7560446f3f4", - "0x007ba8e58ef481c78a148c3ba6f060feb661c295f7e9b79163c384056e12ab2b", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x01f35335de40e00c62642dac2fda8b30f071986ce4f11db849df11bc45ad4e0c", - "0x007801a2c761b90fd4477ba0be9a775003d5dfcd959b1ed198b4681f15e7acbf", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x00382e78bec2fe8d53fbcb13826d50848ce028bb445f2b191f54e5d519c801b0", - "0x02f261e72e518135126205006fc736e9f2efbf39ed1afe3bca034da090c4a288", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x03e5a1e8dca6c93d9827a259a0049c98dc678b74c7950a32bb56d64330ba8245", - "0x062f102c147a4daf93c6ef0f887f213444d555deeaf3cdeeb6cb8d8aceb68d80", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0273bef66da95c6ed715440b340d1eb9730f19fa7130f9013e5a2c62cb05a9ff", - "0x0588eab72769e7094578058088632a46c735d63da01b2a486a8a96644e5b5503", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x03b0c51e20d61408e347dedec0cfbaa34ca04fc1e029b9e71438c58573a82a8b", - "0x03ff0dd46b01caf3fd24d127cc5775d6deb26e02d5e59e6a9752f29d9b0ff68a", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x077bd59dc7df3015ac199afdad5db86d349255176a03f761ce93b8a65bea5297", - "0x0681750b67dcb4aeed1474dd113950a3a1c80df2eb44c98cc77cb4de64baec6a", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x077e6f8594a91992f0e44d3a96178b86f5d125555dd3f9189e306fc369b20803", - "0x044c06eb3adc7267369fd9072d714d8f528eb3f0367641ec9f28826fe3c092d0", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x021546915709005f642d4e490aeeb8fb2330f6cfe0e45e667decc1632b0212ba", - "0x065b23ab2fbfd3ae968f3eb6983f2ac34fe0a5e75b3dfaa15542ee2fd1816e68", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0048db4798cf6821c1ffb8178b1d3bb6020e04186c96aaf4670972d367f4ed5f", - "0x0781019494df95b888f1578f1b4a3f8e125ea60eca47ef9207a10630671217a3", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x017f653d904210148a8e74d8e719a3061683c164aa6d79c902a19f185ab437bd", - "0x06780e97985932c3860d810af1e065d454b1cb4be0e7ffe2d8cea7d52526e223", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x02d1b540f579838588dfa276f3a195cb6ca5a6b6495185777fdb5664b8c29f6a", - "0x033d40a323d0de3ee07aad838c2cb7ff519f91dbce32b3e6d7461785f8e0f04e", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x05c4d0c7432f9b0070436240f9855adae1467cdc9826952ae01b68cd52a3ad89", - "0x01c5747f968ed91261b7ae9bf1023c999da9816e37de602d6a1a50d397752bff", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x04e1c5a8d17673c52b85020fb4b75aa52d827114320304d47ff0447ef921e41a", - "0x03338b901f02c559cd742ccbb04e5f2e842a17c4cd7aa84aaca196d182e11d9b", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0433c74ebcf7dd0449dd00312cbfcd36670de81627fb7f0fbd95b49783b1446b", - "0x07d592a22c5f7aac250c05ce39d0c0f90d42f1014622ad39578a811069c590e5", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0645fe11244707738663cebb6c5a94aacac9ff3a37e7053874907cc0811ea7f3", - "0x00b5297d575b121fef7c0b6654f1435bb51c6c5026f0584517963de03fcb7e40", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x06fedd7639fdaa2f7bad4ca0b391710f6f8a7e890250ae8ae4252bb8b39a1e58", - "0x0436a215f655a3fd3778b2335ffdc9aca6b98474e43d764c1f8362830b084f0e", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x04fc250f6844e9d58f02535bbde5fcb66c84eb36ab6fa74477be6ed321a278fb", - "0x030f67f2ad2446c64fc581d4c2527a83893dbc562be63afd7e7c9adad0961751", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x00c668ad3fda304b586a20dc152eeb24e18413050c136e3c3f75a4612c814bdb", - "0x0625e2716d8eebe8ed8b89942197b4aba8605e7ebf3a39213e3ab1ab0a965eaa", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x076b251def8b318dc92b09cdddbe3f54ed8848f5cfda788fb0818f023dfc4d0d", - "0x0641bc104fc17ec9609cc5df8fbfa769788802aaf56e58b8d0791799c6268876", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0667eef30499e978444ec5b80cbbc39f7d3fc9e0a98dc85003cd13b6f33fefe6", - "0x002efe7acb1da522fa9ec46fc384ac3b6f67c7a653ca9aa8bf133c85097c7b59", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0455b390a2f72c9184adea9bae17a62c8f5d8eac71b8a2de6b40f4d676282a23", - "0x0459927256c1e015ce8389acbeb3911b43fb9b5a4d559f2b210f33ddd621939e", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x07af1ca3fba7c2ac09de2ca757a8cec062eeef6e289728781e52a5aff87e61d5", - "0x027f6cfc40e6da7a2be02b3f55a7b83a6a84c0ca97d044ca679af941343c8160", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0291594827d151e56f42702410515bb0d3da66ef8ea2e29893868651ebc58dfd", - "0x01b933c4975a2420ad30930645214311f0b35f2420877297b49bf0a5623a2562", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x07fbd45a889c5e9d127bb4f8474d6be7cb9796bbfff923b75e42a1ad4cae37d6", - "0x0484bd12622a6ba81cd53049c550d9ed682a8e765b656b1cbff9bbea637bd1f4", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x017d984d47937263f7966a3e7b1eea04071e678494bd749c9e02b48b3234f06d", - "0x07b341ff08722c4e161005d0037204a7a2001fdda7af2cc1a0b04a027f115a0f", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x063b08255b049cec06b86800f14da03e7ee6cc54af59bb5cb623f3f15e788808", - "0x0080d5906cd22d6bb1a8a109fac00f27b33342e8bd0cba525053f0ece61c9f1e", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x07f1822045db45ea07e1519c3ee1f7705915f35fe4dd8db1e8921b5d1c740edf", - "0x033d41e06b93320ad1b3d9580380ec797a05dac3f1cc8008899110ebefde2f78", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x059194ba71a37f172f4d8a4ae88cac43d5dbdfd5fb22cf8ef83e4eb4a28ffe17", - "0x07ac142e2df173b9fdf0c0f51d18b4fcda407ff9fe2a612355fe87a535bf0d9f", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x032a3e995437b109dce5f420308bf606e37f118523b7278d2eacd1219e9901b8", - "0x0428a6e1ee864aec33aef8da76c03a808721ce7778304f0d7fc45b8de4dc5267", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x017ef03a590baf1a2496b00645fcf726e82895e52e1340e27c129e200d527c58", - "0x01cca147ce1b706184a1c8d45467f873706364e10ae9045730fbd401690d8f2d", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x07b19453ecb74b7d0e2a66b9890ff73bfbbcd61a266abd6d82dbe665bf32f34d", - "0x06dba2355420dac582b1f349609ea1c89b89bba2d1a68a0642f1dd12d86e73cb", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x042913c4376deaeb58c556a8828d17a9bd6f81fe5bff018b378934c134a9f8df", - "0x0644256a32e89515af6bcc9902051ea5df0c8c8777cc73a86ecb4a13816eb764", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0273d3003dec5e98d9f965c4f1680fb0c68111a2e3fd6863ce53087fe65af0ed", - "0x0531ea6686322bb17a41acb26b45c498d34e9d15eee45e883790b82b38b7de96", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x057ef83d7d570a9140bccf0cb23d0f9adb75c4fc23411bef91e15b22a22a9931", - "0x02d0bd457ceee3f2bedee242ee3a4e1f507679c56ec68864ec8580ce81c7fb93", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0544c0b2c2893b4e67fb034cef9367daa8aa2e82f19a80ec0690328e12a06f46", - "0x00a3d91100884e2df807b1c6362f5092beb732576d4b462f1e42472ecd135e94", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x00904eeedf44da63610bf1987feef89d79ea936052ecca1b471b3710e56d6d04", - "0x07056ef600b82464518b30d0e80f1f787c9ebb35497f5c366cc2015a7e5a0d4e", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x03e69b16cd25ad9345a3776d464b2038c0795c119f624f1feee6bdda1f1dd8dd", - "0x071121b1c8b054a6379cf436bf8690ab5b9a1ad4e1d7c8f8d9d3720fc82de359", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0381fce0a606184fe957de3fc04185f184446b309569696a1b1e879ba3f9fa4a", - "0x02a7d4a0220a4b8741192f06d8e2b16bc321c19f787f2235334c6c9c60a81cab", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0273e82a15f395ddf2489a95685bec8bac62c4b459d1b28987d3cb27e4bc9128", - "0x0653375b48a4cf5d5b101c9ef533039bedce5dbeef3f59e8f168bdc99b06ca5f", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x03006c9e7fc6a553d8eb4e8a47ce9f10d1a39576ac255ae9e0a4ce3869e76212", - "0x065fe9e2ef2aae608be309332d464f57e28f1df5de1a6a519751b056971f932e", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x049e5a6c45b8d82b9d7cfa292b542afacc01cebb5408e3e042d5cd438968265c", - "0x079f12098047b9560274a04fa94ad01c52782cd45fbf78036c6ffa7ac5afd819", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x05e8f384c8a4607fbe9789fcc52d54249d304d698562597d114c1d81452d3dee", - "0x03c8bc78066b5d947dc1e405e326ee55ea606c7988f666748d259850fa259a22", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x036f4e45b4a817a313dfa20d99c9f212bf5e6cb4d6e3e9fe77501b79b95c47b4", - "0x052443861f81ee35d1f4c5ee727cf2ccfe376cf6c28725af939bf82f5e37a0b9", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x02d8ca29c488b43b6a00a59ed65fd2ffd8d13f81767f745b57f267af97a5d95c", - "0x0436e84f52329c4adb73142f419fe6da804bd8920ee1910ede5b00255c818102", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x06ad159b6ad14621711c17b577235107d2db5c6a8a6c1b1f4fe172bce0cfabbc", - "0x07164bd8e0a0c41919c90e0a91a7f52399bbd0bbcfddea5e9580c06daf7831ba", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x07841b2102e9aa103fb53a642b3e167b21113ea44751ab38e0b5ef8312654db9", - "0x071bf5c8308fcf9c4a7847494cd9bdd946fddf7d3a37e8bb0b201ff2343deb8e", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x008f4137f8cb223a377efb244ce5f9ceac155212f8e9fbcbf1cd34904a6c5db9", - "0x01487ce95f025febd74347e8405be27da5e36f84f39580688ed7d5392a714651", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x068ec1b3226d4f91400b325c86730a882a2326cb5574fcb54e05663ace637581", - "0x05ba3ed46f0741eb85dcd6e9aae4e418d5e761398415f7d9b7a4efa11b62834d", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x033c828201a063327834d4d59d7b99552c26d0d7c7f19d60d4dc499f96818c41", - "0x0442c4de6d70c4779eb065e1aae7543e67c70ea34606db0810d9d04b83947d0a", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x07cdcb994fb12c4273e59e19f4937ce556df37feab2f60167e197fc8bba4dd10", - "0x002907a8af1319403426cf33cb0d467afa70cc70fe87ff3d2e16357c9df55d5d", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x06621db2707aebfd207c1b9b5a2bb0d1624ff3bdae0d73884269d1491830e7d4", - "0x025a4633b141e6160bfe93567fe5043e75ee1bb5128784ae75d3022258e73904", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x027baf81c94831c602e61e587f2cbf2962cfad26d40f4d12eec66c672698f867", - "0x078f085f78b9aeb4d3764f89fb13e82ca4fc0e8272e68f849d2f7984e9cfb273", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x074a608cce56959affd8eff7e08b35d315440dc3b6fc4354860625749b700094", - "0x038c5d824438bf5c8b43cbb05c3517d2fbcef8935542228397ce8772ae894e17", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x040f68027420c11e3ade9aae041978dc18081c4f94943463aac92d887f922a62", - "0x0499c6062594a6c7e21a3cb91ea451813393bff365a27a08f1a515439b83cf42", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x06ce77a50d038b222634e87948df0590b79d66087b01e42b9b6d8fa30ebb1465", - "0x035f5c46bb1be8555a93f155a174d54ec048c2ac8676e7c743054ddc52709d37", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x00736c2ede8fc02ab8815c5cad23261530a22e299427c9fd02d2ade08eda86ce", - "0x07957d7cb14c9a2dbcb29e1e4fcc17b30ee39624a0cb68cdafbcde1a2be5f0a2", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0604f8b9f2dacb13d569262864063c2d4bb2b2cd716db6eeb2b1eeabc57746f6", - "0x068c6799e24f3b44eec3049973445174727a66970f1614a782efa2b91ab1e457", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0777b0a8d908bd7e8bf654b59908778171e39eb78925c86e1ca92881fb006248", - "0x03bb8d2b69e24fe4ae7a56b1c7bef85c5da3f35d00c6b478ae808ffebcca3efb", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0575aab8d6745a981572baa6b27cd2599dc9adbd100c130ae2d12aa96d1c05b8", - "0x078d9e6e0da2e4ba68a36d9e1e9b00f7751b2e26aca93a2abaff048c3f538342", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x06b9099675f39dd198e5095f78bfa64f80dcbc8c4ac95e9af535a3856bcfa80d", - "0x077df7ac8fb836df2dcb099bdb8d2b1d5f7344b47a70c9cf040175f41bf80e50", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x073d620f3bfe77f672943d448d7dc05327adf64b8e7af50039c469d7f7c994c4", - "0x04859deb36eaf0c802f0d1514602368143a33ec6ce8fd55248b59025debc6afb", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x03243094da07edb8466fe2146c1abbc5b8e7cf95f40fcd0c650204c288e0a5d2", - "0x048f594f9edb6710658ad3810ad7a70f2928f3bd4eb40128a2a5d23d367a89bb", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x07c2ac93d02652c7d72461dba1ccaab48240e230814624ac3ca2bc5e7a9f943c", - "0x0728d95900302a5e8fca52a56f5922eecd1282a1ed5d1ed381dbc299f9303509", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x01e10190de36549f56aef2f28c49727f29fdc6e73c467f6d779214bcc79abb71", - "0x019dd2b7f4398b9927a3a54d5576642532270358d78eb2e0d38dd694ba8fefe0", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x043d8c30ff16f3bb40143b41e90b924a973895aaeb7dfb784639bd3ac9281aba", - "0x0401c67318ce44802707746c58931d20b82a74339ee59dee44dd7950555a3f27", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x05d53add964c98248725920057e77b533a942231895beb322346847efa23ca87", - "0x02f2414ea6d9d8460c23e443592a682cedeaa0eb25a027e28dfe536f923b4e99", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x007a24b85593c3c9b8484f1624240804f302b110e0e90ad67343e786632ed595", - "0x02b65319c91ea4315500bb235eb1432640ab8a258d6f262125eec8003bcce754", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x041c5492d3fbc9798e05d36f119698a99ac1e22a57abd71e8539b17a6bb61ef1", - "0x031f01bb7cdd4487d730ca579e1d1cfe29bf47fcda5e3f243f7e09c86277895f", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x003fd2bcd1c89d706a3647fbd354097f09c76636e93ae504973f944d8fc3bcc1", - "0x0677ef842cf5eb2444941f527abec567725e469469192354ad509a26ebb3d0e0", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x039222ea924ac17b533c72ffb2c47ffdc11d6a7f7c70fbde3a10fb0b8f35eb2f", - "0x020dc4bd1089019bc1d7379b4feb3eae6eb5af59e9f253845da9fd633057e952", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x00058e8f3c2429d8dbaf94ec77a44983cfd2037d2dd2fd2578879a9d1ea8a81b", - "0x015f742fe4d87d7d11bbb72f21ccc8eea1003679fb2a0e633af4523982ba879f", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x00326f58994e1347f62e4102183215b5db956378d2f61f14aba4dec94577f53c", - "0x007a03284c296003bbe05178a1d82efdb7b8125511d63e20e50aed789c2e52e1", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x02ef3a8d8709c50fe7f607c521b28a717ee680318d92249e39d191813ba84862", - "0x0657e7807042474cffb4d36623584b64dec4040f327846f6489dd0f923be66c1", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x02f5adda5b6e74a339dbd6d433065146f7c987bdbea8dbd647e7f33215492e76", - "0x07e4a3cebbf3d4e2becbe1d5da42c11704f922f5fdd35519c5cf037a234c6dd2", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x03a0a41e20690f89c04364b9ea4cef27780b946881c2a56d2ab05d5e7b67a332", - "0x0062975a106b472b87e9babd70d286527ae0ae02647b1afb52c290be1dc49243", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x053aa8939c74d4ee58f03bc88bace5a45c7bfcf27466201da05dc6723a5f5632", - "0x02e32535ca7732904a048183247b04b426ecf9b39fc393a9cebe92fb1dc7a7f1", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x033622fc5febd7a1733cae0368bd6013ac3a62917157d83cbbb2f1be2d77dc36", - "0x01dcba95e11cf5b194ec71772017b43b8784c5170c60e126b745ff08721e1b8a", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0776b06bda00fe3fda9bcf07b015596bea0e2a7db0fbad721789ca5a2585957b", - "0x025fef87e5581035d3c88d356ab525fb0fc67bcee59223c44f12931c5e66632e", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x05c70f72bd29b783054b7b73650cd20a8f4328e9c659de01ba53eb1cbbc0f9c2", - "0x0300f816ed54b252b912a0006a969d1dc6d626f85268c315ca3bd6800b85bdf1", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x045735416545ebf55ab8268ded26ca46aac19cd8625767196e49f98280d1cda2", - "0x04e10fc036f3de8b0dd1a34e879aca662f650e7a89d82baa059e2345c8e2faa2", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x017e15c79671804de5a83edba2bae7b79290c3af1589353be2dde9846badf248", - "0x05d2863f56bd6a8c0c65bb714c0078671aeefc7db6ec7f4f664b79b057907253", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0036df455cf7f35ac5e5c720802de319a6fb8ffa32e921782d4557eecc9a9b8c", - "0x00f8984ccd5855a2e87ce2e0a34ae1167d4b16e45f558cfcba9803d85022545f", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x011cdbd6637bfe390cdad49b8984c26631dbe1da4eaa3f6ac34c85c90cd8f2d2", - "0x07e879252012298c57672e058648ddadf18f55629e471948e50bccd156e33cac", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x06cee1a03145e93b3e826e6067005f09c06099c98198c91c222407ba5c8c132e", - "0x000beaecad1274e7c6e5476a100c271aa1a6f86ee5a9fa5c2f26124d5886fa63", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x03ec659b8175e1be1bd5a252108714776b813e330393f587814f5f1f32a73332", - "0x0529a5cf9f8c237ae69a94217d173c8d19c156952041f5c980da557990863fa7", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x060bdc9ac2d8e1cd3c90486fe917f9f977065d431ddbd8123b4ad4f45bb44555", - "0x06ba39632d53507829f72392e551da1f4191775f3c0696c323b73047c15f8cb8", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x03d66ec5963d0c534d4139c8cef2e1ac48b3e7965fafabf58be26f903318af4e", - "0x03d3f2de7a95f59b683725ee6283cbaf31f97c4b600df9a4621413223a468740", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x07b23cab6b512634d1c2e565331bb403b350d5ed7a5e22c1385cdd960905de80", - "0x0240a1a7714af6116b0d1505c58753560d4b16166e0ffc5027b89737cb6bcca2", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x008a601282f8873dd487930f20093207fbc902ec8d5dedc87f834b4db3b757fb", - "0x002f77d4d1ab812fed8535221975b965c3fcbac91bb93e4450492d9d4fa227e5", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0030def56bd4e29132972aec831407eaf099e0626d4fe5eb1723085aa0050d54", - "0x015aa92072a2db3dbe4de580e1d99705d51bf89be0956a79bddf441b93dc3a9b", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x007fb38ace8e0932fac2ea0d3eb676db8d684db1817e2e4d59da7996ce398b4a", - "0x068f92bd5768cdd4710249f9d49ef1d5654e497b9a4ba10bd2971366d83fb400", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x076ba0551c2dab7a567a2aff9eea6fd178282a2aa011dadfb48eee3f10153879", - "0x0742f4e18972603a52f4c680c58a661af340c934fcc767a9837a3bc4a487694a", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x01e6a1cf2f702498da2360a66c82cc9e46c234ef3238dec2c8dc79ee3e43b51c", - "0x07ec1a1a703eea055b94034b40b4ddaefed7ab5324c5c7d3fe2413cfb4d2bd9b", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0628bec0e634d99426ee04faeaa6f02a924fc9f91a8d757c2feaf7e2e47a0c3a", - "0x02121fe07a837c17ec63fb0edf211cd04434cd8558a9dfc6b621fe5cf6738612", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x05f3dcbb1dabbcb0aa7a83fe921094fd3ea724778e17206c879d5faccc49c207", - "0x0623b1886d63143476de67c901123518c650b157cc76e0c674eda5b7242f65b8", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x063004023c608a28476b50569b4413c2e2714f53e329f9a7eeb50d64788c60f0", - "0x056631a9f9131f416d1f724ff0a9e2da06228447221ca46eb844414c0c222d02", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0350a8fb91b446f8c813204f9185f1a5fd2c204bd2f5325a43291a0bb78e7cb7", - "0x00b52ed1494be6a55355674fe4068c3b054bbbac93eefb99e7f6215540a65ce5", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x070945db4cfb76ba070207661b5f37ce68480d5ffb6b18aac313b985e6ff75b5", - "0x07e4baae4279d55e27c7c1932bfd235eda3c6e90e93374bd6d66e439669e38cc", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x01c4a49314d6b4969cdd142c76ceb7682bfb868ace7f7568b0fc8635bda5a9fb", - "0x05fc0519f1f4cc10b5771312458748c036313b87707ed0540026ac64a5955aa9", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x03073c95d08d3b97caea5f0be16b2789bee766f76b7e5499f8ce8f96abb0f344", - "0x052a8974b4eb9a1f6a0ae2c83cb4715bf18d73f057255fcb3f63b74f7e78f590", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x02f493140db59436dd1de2ddb348643d308d886ee033ae676b9d3fb50f474d6f", - "0x025492d5539b3d34a8c5d394118810cd7646337f577695874e972ca121efc474", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x044485b16d597a5de3604df6f7ed7e00b8aeef9e7e8dea8688255153b8bb16aa", - "0x06cccb0ba170123266f24b5d93a744397dc2c44820edc4f8f5b9a0f5c9b3b940", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x07b88e5bda35c3ddc0bb34cbc84aed39780c2364b34b892533c16e05bffe93b2", - "0x04b04900b29083f981256ca48bdb0d344971f5ed11baadb8ee537e1d45a8e51a", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x06155b935a4d5e3756f8bdf2369c083d6cc7cb6fe96e511123626c4e21c07e1a", - "0x039b3ac9955320dc6fef13891942d5c89a7f0ac3f299efdd13e9e8d4575cc8e6", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x02ab492f3cde03f2ceb134101cbf28faeff2c1f246699bc2d481c2c73d208fbe", - "0x04e512794f963776dabe762a73fa7ce55d196ec7e6f244a87a0d70af51f2093b", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x07618f77b7b32d512688dd62e0b48231d9574c6361e8be353a7dc04f7c3a115e", - "0x078ffcd16d80636381ca231aae70d99c9e20298b4f5388fd823ea9fa2b8ddfd9", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x02c105d843cf533acf8c46b48b18fe28b9d5e8ecac1063ed424adff044c39c12", - "0x03faae6ae39fc772e2624729e54af7e2e96cce9fa7f970e322618755a8d119ee", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0427b2be45144732c4187dfa89ea2949ad626a171680b0fc2ed177bc44637e00", - "0x0391543793f251d168c54851d8dc2abff460c1a4b2614f343ec153ad57f75f9a", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0723624f9a8581a0c71d59a53bef2509c5f57757d724081e31c8ffb895944af8", - "0x036c2421cef47f30e195a15bf1f74306af89242845189519114e30465af95ea7", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x03e38dbf68eb9aea4554097eb81707107df9ef7e3c7120c0be6d4864baa87144", - "0x000a8ac8a044e150a92e1edd7d79b26e9b2c0658d359a0bc9ebdc442f9b7da2c", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x01ef4d04b6d7492ac32ede99c9fb5c081037a1522a49260a9b7bda4bdf4b9504", - "0x07468fb23e2859b8e9d975c649d9316e1691d3bb52e822c0378eac3aaeb2cb70", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x03aabf64df75e14a762df0d1ffbc359ada60c325c9aeb0c20251d6dc47f30f38", - "0x02aedbc55f0361cba2fe7d850f5da78257b102004041f6f129ec9942dd373d1b", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x07dcec9fd4895655868db3cdb8bab0ddba17ec37eb10c2a7791edb78f175fb51", - "0x045bba291fce13695216fdea4a3311ddc61f9a19a7598fda2472fcde1a7266f2", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x07dc82fee1ef95cf5b3720fcc07f63246654bfe39762627839da40e51c75654d", - "0x04c0ccdd70955da74558de20c88352df8a02aa97e4d5971c500e884740a8cb62", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x07fa5d460dc10cbb418b444d9bde97e92c70a99a222b99f244dccee7e62cc04c", - "0x0636163901baa5b7576c38c43407af578b8c4607e01e86011ae2dde587a89f84", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x049383ff501004b4304c44c4cdb77cdff09b98327fbf5e7d816c0ee3124ea36e", - "0x00dba84f57ee6993287afaf2c4383805071559cd35e50f9153ac415c1f0850b5", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0758930d46006623a756c89bd0cc378f6a3c1f43c9a0edbb42274c35e75c16d2", - "0x01d74dd9f81c2fec811b8cbd6168a745b0a111932b2a345265ef2853b50b6245", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0201e3448efabd6295ada494cc25c13bf75b4abff8dc1dbc5159c839cc67f54f", - "0x01cf540c5874558e0555fb5ddf0405e85acd0acac48ad3059ac04b22a645b2ae", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x015f222b27045576cded582cced08fc2b19f5b4d57f8e13855485d8e33a40ce0", - "0x02c0e54e64666bb76eda7abb40305d64e4ca8bb61704a571469983963c9c94b3", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x00edf543dbdfe6ddcbafe5841f3699be7b08eac1d1c8df2e02b3a05a10ef6f69", - "0x061effeae5e5858b9643c50dd93bb0683abaa87356998b9c588d8bf1b1c19432", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x07332ee0626b044d664ef228f8cb84df7c643e52f6a2591ae1c9007ad61ec16e", - "0x0229bd8e630572cbdee54283234cf3e9f060e6382f99943bf234119d47b54470", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0431a7fe6753d9e82afb116dd16df51a2b91722c875686593515ed96bc623539", - "0x07815bf5a34e5751d4cc6ecda8277e5175c9d131c8ed525d8ae349df2add1ce6", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x01f23b9ce4093965a57acb732e63fc722e846bb9979b0c5ee848f97bb16bec2f", - "0x024fb68e0c8fe249b7858e230c12ad999cc8ab828c68412f752a834481afdf0e", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x040f5914ade7ded6b8febb5129570039b6354a78c631b17ad1a23b62cfe62c6e", - "0x01eaa5e13d4d91e4d7106084878134eace23febfeef9c471f22b5bbd7889d599", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x003996f47efd51bfb012758dda84079852638f6119e35be0c02e310033a78319", - "0x032c2e9b7ec376ea5b73fcbc3f355c176bf91af7e6bc52d7d76569399dbdeb49", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x049d6443aef8d3c0718745cc1281542b154dfdf02fedfc3cb500e0baa06b62d9", - "0x01fedf6cd7bdaf6f9a0d6633969a217d2e788565dfbcd11e7764047c4c87c937", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x021bef5400c8f0a3818adcc18e195c615ddd6378608212b8f402de78befb233c", - "0x036fa036cd7d5476a0d14547c369397c22557df229f413ff38cef2644e0cf35d", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x07dd794ae4a6cb6b03c006cab6591be079f5db4f19279192e5ea7b3b7b3411d6", - "0x0637128fe2ada86f5b4ce6a72647f3b48b3a861f09610e2750c7c3ff9cf1a25d", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x078a16ef803aa20a075bb2f66c61bb2dae5698bebb94a0995fa74c3d53de1614", - "0x0246d588b68edb6fed96c128349908c42dcd64c46341b205e79f4aed9b5d3675", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x06e1933939bd03b67bba753cc0cbe7d2f25bad68c993887ef8c9e2fcd59b0647", - "0x0599413f7c204a11a5ce315eab11299ab7326603412bb00bc1c59ff75a37d6b4", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0400daabdc3decd249c4a0fa0c83d39c217111258d7a7fb5244984e38f5f57f9", - "0x0290a80b6a7cba1124f4b32a9b51730b4c54b0ddfa57cd4e0de72a93aeb291bc", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x04a79957a5a1888ad063b51c69565a2b48e8eb917183e220a1c8d3374526d30e", - "0x01f092de0e069bba7fc5386e2e9a114c1618f88c4b95e220cd35ffe96f99fcad", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x053ecf3179881cef98202f4d449c2a4c0aaed4d532990820eae84ebcd9c2263b", - "0x041055cbff3fa5dc97b11c50987eca28de21e098bfd2e550d93bad5bbe661250", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x056fbcda667a4b4cbd34e63291391f172a894115a73e0e5d2d2149c7e19b9b88", - "0x01961501af46d484a0a40b8f6b83b1a5e4bd3d8cbcdb8c1a09c4bc2765628aa3", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x055cf2c8ccfcb88f862cd59dbb4659c79baea9f9e2c25b9233eec8e3eab1df92", - "0x03bffaab9bccf8c6ea96e1f19e59c267263820881cc005b0f9a3de6a8e980bb5", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x03148aa3df9ece39aca84f59489f2710522216f14be6055ee0027529d1d55e2d", - "0x0617e9a52a92975db0ba1977f71116f7058a0d31b869ac7f3ee2fd80b0c5100c", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x05c74caef872e1514f2404690b5c38e909aecfe42312e4da88f31d1b5564456a", - "0x04d1b42f35aa9486cbf8e522801ee432d228b71eb84a6de2d5c1988671754ab9", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0391cc66116225a1a583f6de58c536058afa2581617c09af0c22af9d987d71db", - "0x007e05e6295d5f1819526a2ea4f1257819eb3ea1fb29ce5db70d05209c67d3a6", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x02894c5075f1d8e2de65f9ce539c7ac50743b33587f9580ee2d5003b936c7a92", - "0x05c36e6852aa35f10a22bdd6ca2b0c9d201359885dab6ed43bfd5f9f937eae9b", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x03543fd025a9322c91498be81ec250464f23fa087c0a587c8d99e28db32a7ae8", - "0x02ed30353c16754135fe543d9714643a2ae8ffe0b3b85e659ccac1afdfb154b4", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x021bedcb4de7dff71d1c77c58ae190abe603bd156f4d7daa139677d874f7dd40", - "0x072f99f04058a67ff13e3c58baac805a9bd93a2cac4fe1781d5d94d46b5212d3", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x062e05fdb23c313d3604bd41e19ed5b37aa311bd1e49fd9e4df6d3ddfb74edd7", - "0x02e62c83217f60c85de77a36c2396ff56ce130cadcb56f8791ef4839c9cdbb7b", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0113c38415f18fc5674526e0da14ad4bccb02dc7a537c721e65b2a044aba4678", - "0x005610233a56e9ed61fc20d4ba7d52bb5edb821a60aae36e7f617bf455a4c28b", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x05c1188e72384160ae39d07328346cda4f6c12d227448e6236f04dc971625287", - "0x01643006eb3a3bc6aafd5f685cf054f2a572e6ca58c0118bcec0b833741f116d", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x03f72efc93c9b71adc4c51d8fc69d3940b20d08733af2b7d05140fdb1d1c1004", - "0x07399259987c8f4ebfab46e522380707e58427d3962ee0c2a91760813f76d232", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x061d180f9282787e8599005d2ecd62822d304ea4905ec9c3240c26911433a1e7", - "0x07097c017d169259c2973e6ad150ab2eceb164c38b10b880b8a989f819f971a1", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x003129b34c03c51aa8f611e91d5cfcc9bd3ef108ee66e6d3ee35a0e0e50055bb", - "0x0563b18b5650085efb4cf179a029e6afff27b1d3091cd28eaa68d24fa1f801c6", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x03ef215d16a59a465b24ef292f33ede9a1b3603c9bc0910a7dcc6d1283c99aa5", - "0x0278d2edd231df071a2cfae0009c2054a0f77e789d2baae027a0ebaa19a38801", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x053fe7fb2ae8ea282fc7c316794a94b5a97bc9ebdf0b8e010104aba052739fd6", - "0x03763cc93d8d1a641208f8ffa49e421b15436253d5db5cfeaf420c076bdcb4dc", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x07c972b2e113187705425a63ec776bfbcd026fcfd6f9dfea22cacfac71c57940", - "0x06ab379f8c7b3390dfb85554decacf2c4c315be9c5f81a81808ba9e96aa5a8bc", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x016eac0f9fb4c67cf89a7fa4ee615bbe731d8edcb709a1b9b50c7d873a530f52", - "0x07ff8288b6e199ca8f316192881424a37fb080c29daa76b1f0edaccaf580a80e", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x05d0f2c06196370107e11920037856235ca07e931db16aab37e0ca010628c30b", - "0x01a1713df709c2b8694d84f275d80c4fcb3dcdd6039deb2a2f4d21862644909f", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x04aa276ff992d999de7f4bbfb87f5686c545528830dcb1d621fc42445d03061e", - "0x0031b2dfe0c073dff14912d41f3710d194b8c60927c165752f7c1af55b694452", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x004a30043e64d178a5f158d4290518177c9c41e81f9c13a40954d5b8ea627864", - "0x06057982d4a60b7dcea50c150ff6b9a445a966af14a89c7c79ceecefa35cdf90", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x043f655634b90c92ec4d9baf1bf96ed5db16b6ff318c9b624fdfaa36ecb4f386", - "0x05f3d2abc036efea68c94a23f4f3eab9119790292015152e26dc917c9445b41f", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x009d6a1051eaa69ae4195d5fac4cbddc293f886ddfac0cbe15042a3181f1e9f1", - "0x02891ed5ea58c0c8900cd32ddc44cf8d424aaa94c1a58cbaad0f85c27e1850c2", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x078b1ec73658f7bfd41c8270a69a1e6b429323ac047c4a53e0e879045d4c30c7", - "0x06479c010d0a214b77e33c3bff347b09ef8458f4eb9a99ab0c73449e250863fb", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x01c74f51c8add189de1bb33305aca6468a2c7b8c876065840abf0bbf8e0bbab0", - "0x06e01e455fb831b59195b137e22e90282dd7b616c969c881838ed98deef29711", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x075f6b6028c43ce832f65d7e8e620d43b16cba215b4b94df5b60fc24e9655ee4", - "0x035e9ccfaed2293a8b94b28de03bcb13eb64a26c831e26cc61a39b97969a2ff0", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x03c6152fe093bd6316897917ec56a218640ec1b2148f21db9b14fc7a5ff362e8", - "0x06eef2df27ae7d63a28856b07b73e7aad7ca94f317201a1e675ffc6f9a1710dd", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0769c1bcf7fbf094639ac6270e308935c7abdd420db20be3ef9c924f083f7598", - "0x002f95454d140d3ef3d2527f398573fb86b98049bc4853b5723470020c591dfd", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0054e01b5fe4fd96052aad55b3f26b1d254dfc7e2525fffb9ae0a77eb8cc5579", - "0x07c3d39232ab333675b219abc766ed9b4782c840e6b046614dedb8a619696eb0", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0247d2d40015336b9cae6954d6245a7d1e913b550c43c78b0aec49102bf0728e", - "0x0421be1aec4cb3039dea78d70554d7f59dcbc6df7b2bad82266191a96eb30280", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x073d64eaac7fbe52b666fab12923bb8e2a01ff3dd4953441da0183b4840da87d", - "0x066e5f63df403d34ae4350658ffb1d947ac646220e8ef8730eaea87f12d09641", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x02f4e77c205fcff382538d7ec45493e8b9d72fa287ff0832e85b230a78fb51c8", - "0x0599d5a1a23ef4ec7c6b2ee60bc8592b1ec426562214fbc79f8d4a8d3bc19b93", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x00d1e63f8ea8a76429cf254a6d3b668761f0dc572d4bfac4fd56d9eaf58fb6c0", - "0x02bd0a84d3908a63085824c9329a0983913006ba155b56a58eb3f9becab29c45", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x064fccbbedd2913c29bc85fcc7c779b4827871cf4cd31edcd26ed1dfe946af51", - "0x030847797daef49d1d1e5da3f88d6fd8a90d75a3736bca6d09fe0e4949d8334f", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x01bfa387cf795a640ae41a1ae76ce7f8a97129214cb8223f296341ded6206417", - "0x029c66c79ef035dc507e781ba2114b2cf5da8d10f412f4c406df57cdf6fb1929", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x03cc0fd64500be95fa1a24172d1e2edd37f7f7a14de897693b0983049ebe9d6a", - "0x07ce0592ea04673f3432f50dc96f7f704113892516c3948cc67612f4b4b60f22", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x07cb8ba4668c359e9fe675e1414dadc1b975a10dd7cfdd8f4762e207c5a05e5a", - "0x0413220916d8848b893613b714aaac5c309919e1672727b270f2b6a0fd89f062", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0681c6f30f687f86dddf187fd976163fcc17d4aa56cb752da5b87bcb6c79561d", - "0x0091313735ad18bd6608b6c3893a8f6a473aa3af9271ef472a8aebf65d01007c", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x068a829db3a7838f1cf0491aca8497afa16d4fdfa33fb119d2af15b34c9c1fd2", - "0x01d21169297e9e6bae5c93904784c702d199fc079e0eb3cbd0ef599a543eca98", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x030af600456837810a6460915e86e16108e476b755bbf923f3f1fc2577f6eb41", - "0x01ba0024f5eb4a683612705b6255692026978c5ffdf0d0f700c7f89451a19e24", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x02d6122f2a702edd4da7385b1580796a71d13bd72be94cfb3fec01149c006c2d", - "0x070eb282fae992efa6f5915e578b640653549f23385ef3a29ab29b1b9b8ad63b", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0752fec14beaadb5ddbba6b3a17fcb86579fa588ef407fad0ea07dbb22a640d3", - "0x03feb6728eca21a1e84e8f9f23010387a53a96a1cb62d86fb37996150a1299ef", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x00bfec8a42f3e09480b24243b96a91dd6def40226212f54e87ce4e9b65d82d83", - "0x00eea33bf270ccf743000e1dc1a8c3e98df9c7a1156dbfd4dfb1a491f93256f9", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x063f94a92f27acde8f5ed949b459506f51d70c85bcc61a34d647264ecc53c65e", - "0x037e5dce0646ee66f4fdb93b82d54d83a054948fa7d7fa74ab6b36246fc7383e", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x05002cd215b8ea16eb5b1950ec498154cc4c8ba93fc8f43dd3511190ec9adfd0", - "0x03ed5b198173b24511446cf76ae5adf844e9ad832c9f3d7867cea0a0e6a2cd39", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x05ac3edefe1d8fb4b2da42a53b499fbb737384d3d283df5517e277dc7889f6f8", - "0x02ac0201990a6b3212f7f51932ebdfd34ab2568bd2b8ea9b6903667ec090d00b", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x05d3f93a44bd40cf71a66eff9d211e097a7e8fba96dffdf38df77fdc2bcd22a6", - "0x0049626bd838fadf2fef43fb7b62c11ebc88ebdbdb593db6e176b5a426f38cc2", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x00d6aa909287a2f05b9528690c741702c4c5f4d486c19a46c38215f52ef79c7b", - "0x05ebe1128dd81093df4aca0df365d58adab848d1be1a94b95eeb649afd66a018", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0303106fa10743784a87dc98659eeec97fca5cd8333a88c321724f3b5f62141f", - "0x05c80313575f5650cfbd3deb5fc17c539823a9a35f962ba7c1d1d3cc6a270123", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x02ad41d905faecdb1eddcffbff6e5ccb5401d4deaec696c2db7ec07b4a5b5355", - "0x00657efe28e730a5680103b3276c96fdca1c748eef347f921395a43c6d8ecba5", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x00b7f3cb92b3e41c358427228d450317117caf62b4e6164094c6a24f6a090c2e", - "0x03251ed3ea7b2922cb76299f91cea3a7e0cc195e167475a98b8967a33d7ae793", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x060ccf33af2f1a772c99f5f394aece266ba24e13f222e99563d3b3eff5e2a64e", - "0x00b0e4910879f0c126d9d6733b09bef4864da97c921e03a4ea22ba8a99bc094a", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x05c124f6159a945cb472819eab5854349b38a9713ec12601bbed025a0ea13ee0", - "0x00e9635da05d681fd02efdd93c9d19872ee753914beb7e081b4b70d83a2ab89f", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x05a7225cff28925c3e9ece7165cb9df7fb2dd6a8a6f282c7711f4d76a9e67890", - "0x07d38d7dd92f4f94a4385c1cf5faa90badcfa07e2cb5e7d47dae8f1385fcf349", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x05a6a93b6416f59a9609fe30faa0be9528b0d3db1836940f08d5583b8a13ecf9", - "0x0205fbbe8d0c66e651436868bc1933cc8ff8c881de2520c0f2ce755bf873ef98", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x012866812b3053e2f7a9572bdaf5ef2b48c6fb62a0eed9ff0356df50e7d05557", - "0x06785f7eb2cd1c120e4c7167b46861d10117040a2e9f2ca86a71e9d67df90613", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0046a730d05330b1b13673cb8a1b8f45460035e4a9f1a1751cfba099c4355c1c", - "0x076fb0ec6cd16a8141cdcd875c8b2de9fce42d296072643d148ac7e7fa7472df", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x012558b6af651c5b4c11d2009585a380dc1e5becc19ccde1567aafb1c000187c", - "0x06d734f3d9fd2ea0200ebaf6a359f71b8eda6eefe896944bdfcef483def43424", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x04bd4380a22900bd34835e0a908eacf4b6edb61eda0cf483f9212453b37e7516", - "0x05e9551cd20d8d7ddbf4366880b7d5267385afa1966ff30da4baaf273b009d29", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0141d84640fabdb2fe42167367090bd14094bfede29c4744648324c74576c9bc", - "0x06ea252f822fb03131e4b4a7102438b456e48f1be71674ae5d786731ece37580", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x02ef674cfec32daf6830ebd28be01497ec0a7281cc4d0926daba649daeaf7696", - "0x04aa2dad078b15232d6d4f8eedcdaf3e06e653b133ccb633b58f278d9dbfb9eb", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x03f6e7adc7e80586249f70aa7d788c673fcfab4a9676c2a09fafbe444860c3f0", - "0x0318f9617b963cacff3707c6a0abe9819d39b6605a342fd4d8d79c42ed906739", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x071f1994ad40baa2922424ae222663a64f93d8b67929e9a10f9e4c1ab19f3833", - "0x0085320fe68ec0d37cc19fdfd03589d66906ffa4046c80e1b094a85f27676346", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x02adbfa2623e2d7e3ba8d03f25aed2898f523422282897249d85afa0827e57a4", - "0x02e5aee3306f7c481c74e2fb9878187ebe273bf3f15fccdd880bdbafcb15d4b6", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x067104aecb75678cf182b3242a14cbd22bc3afb4c32f807d1cf362abc42339a3", - "0x04debfce01ae28a60cb2ac3b1175d5cc4feba73b8f8ee69eff1cd7c63efd079e", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x050ea174fbcb611aa93a4c6a7d4a99bf7089b14b81c3193085f242f474489de6", - "0x05b15597d23a48a7c40f0e7f584f118ca2bdb38643aa70c73b090589c7e07861", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x077fdcf770cc91662a012bfc8a4e8600206e623e893a95c80f8d71edd8d1d65d", - "0x079b06e6a93c598850e8dd825700549dcc20621c09dec81b9d2b4194fec325a0", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0515ecf3b03b8edb1e3a9739c8db804934dd986303ae007493130750d9b18780", - "0x073d0f68a3951301e6a3ca60173beed21948715c33781f6b58980aca2dc36788", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x005d539ca6817226c667413c4ab0f5a33f3defd6816d692887a3c35ce6610853", - "0x02725d0fec176a2159ce9a375ed67e615cf2d7943eac93bedf7afe6910ce6ac9", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x03e0f29b22535be8ccbe2fb1ad5df55348f94fe0f8106bbd5ab0dc9108470277", - "0x07649212d8561c4da490dd54cb8e3d6776f00af31d83eb0935da8e3f59bcf277", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x05a63b1bf5232f28f808765c6be7ce1f81c52145b39f01c879fae0f4303bee61", - "0x03bc5d6df68bb6d0577bf9ae2ae59ec0e9b2dc7dd56ea179fb38a41e853db950", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0161ded55ff1087032381e6c1449704f63ad2d88df82dfc44a71890fa09b3941", - "0x0078a52e0013842037274ea75daaf8eb4afc04ccc4b07bfaf3f5ee47d165e01b", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x008f244f5dc085b560077de5d796b22e4f12d2c41257afe1848b6abb51eebc09", - "0x044d58d9a4cad4a1218b3ffb76dc83a1fd8d4b110ceac30fd64c5c8cd4f36c4a", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x01bfce5229c5fbff5c0f452a22317fcfcd9262f23df41840f84fe7d44cfba1a1", - "0x066b387872c00e63c73006a955d42cf49c46c5708fc9d1579b9ae38341b24a3d", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x04a4185cf0b92cd9a7ef7aded90c581cb1448116c2054e7ef0943500c8365a5a", - "0x03202bb9679748781dd684caf45939874fab3233cd8a71adf5d8a3a9134e62ec", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x07a9eb86764c411afaf46891c3d07b0e31679c0b5ec6e4b9371e009283502550", - "0x0668ab40f81fcb36d15c53d290a1be30c379af40bcd42dbc1b3006f78df31747", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x01c4129456194ba97b0d04a4e8e0b3a0e9fe5669838912bf38184a26ca615a29", - "0x00cacbcc4b9caf170adc761e696ebd250c0cac3dc055694ab4a4aa910f49411d", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x056d47dadc9cbd1dcb2ee3efcd5d4af5e6aea71df10815c68b54a14e81d11b44", - "0x047e966ba54df48e9b612a903685e0060a67e4725402e8cb4cf654e54e813a3e", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0796a911a7b9482a53c123735afad648aee1499cc2f6487ada2ba9d647cc2a4b", - "0x0333936bbe15bd2f3d29e93cb06619c1bac3c44e0bc126e36bf4b5e7ef91a09a", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x051a6fe8647886b80c2d0c765aabaa46af9d347950295f342c13e4eef745c00b", - "0x03d13fdd494dc851fccf24eed224f5d84621891392787b729614779ecb98aaf5", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x054f9b38a7337bc066ac9e408a8bba8a108e70de4587a3ab5c648cc65f9fc6c9", - "0x030e48863b33010841eabc4407922003041c7482e233851910dea30190d0545a", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x056c1899a2e07bc45484fb1df3cbc3787ee36818e047d82a7feff84ec8459048", - "0x02aeb1600345d5ddc2f455f620593d275bc3ec218a0868700514dbf532596368", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x051e6a409ea80df3009f348da5a103c419f03294836340da806d98f7bf4efd69", - "0x063c9da210b70d94c902327d8a9e1dca1851a1fedd8add1965825cab38efe707", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x03436c3a0cfde8476c47db4fdd88a03ed71019b1b218fba62ab35f76eda69dd6", - "0x033a15679338e89cdca4dea0af203f2f9cca2dd32474b9563d77c6154ab59c0a", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x04041125b3feba9448788a2b2038efcba719daba6092c065be6bec9358b73483", - "0x023cff0973b4814380a2a3efe021450ceea203a3114e5fb913cfae847271a2d0", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x04b1c44438afd4ddf20a2cf612df2ee494ce84c7274c5529e857693e73018491", - "0x0430403bd31d8f0677e06abff7159384560f27b9622943fea1a3192f14bf40d4", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x07f7281728fc2214aa1dbf13176a4624b53814734abd570eb6ef7c7e32379606", - "0x0312da47be347fb3fa2c9089b38df372560dcace2effeeacab4d96ab11567295", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0361c555ac7113411f792327d30b6da983c6eef788e64d9f23c7a30e22cf21ca", - "0x04eb51e586003211a8c37488880f172fed8189b7b00838ddd72d544cda7a5a2f", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x016a28884a1be8183e0d3fc0db84a9afbf47126fd3be548c2a584aaafbfa7dfe", - "0x07c3f57b3b895564ba562c1cd80b71fda6d2e611665c6ab87744f5390858fe24", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x00a3afe6f17c078d41833d67a2c34357518427be4c6af71466c42927f69b7bae", - "0x069da69ba34beb77a2ebff22a4b138da6baae3387871c0455b012bf2c8f01b65", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x000dbfb7e943fa7643594dc1576be3d7968eec8dc0ae4c73de0849cab07af319", - "0x01b5864ffbeccee2d2d7c6d256181d444d43cbaf5660d74e0860fe3b96d74157", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x05f78a3512888b833e2a57f8c339947256cc440bc7bd86de674828370c70d3af", - "0x00012623ef4842ee9c2c2dfc744ae4750a0ef4ccde41d3ea40f4b25b6b6b7cf3", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0323339f37b327a731232a9580e79952063c7c232bd1380146d8a83c285f4b8b", - "0x04f16be1d983c7232f92cce6b9690695978d42cecc8eeb8c206e125d1098a265", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x07b27fc7316e417061113fe0333def1c9145ad8faf1090fa42ed3784a500015b", - "0x0749e9081f8869237dd690bd8345319a08d29443dc0f1374d0773c3fd1b4f809", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x028e902b40b9f524f5d8f1b4261f8f8cd7b3346c36439169c4d6e30f9e7c80d8", - "0x0180c44ed7a1da6d622ed86c1314d362cd8ae2a99975933bc276d0a8f9fd2837", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x01832ccba1b3c179283bfcef0d97dbf8f2ff85918a318382174bd21fb4c934e5", - "0x068f34ffba795aa898fb28ba2c5d1a6cab5dc696ae807e2469f2da018e87ba2e", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x05b75aac2527cfb212920c21c6ac6fbb994dbc986943a9a2c9f544a7478ab457", - "0x008b4b68a1ba77030e4d51f4820014af3b103f97992b36be2b1ce6b1b56281cd", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0120f81b50527ca7c59c113c091058b239837cffe5b97c5ea315e64516b2e7e4", - "0x00f86219c54026690183519a0cc82e41278bc0b6a1b3d110b77c4e1d19129701", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x04d5dc0a06b24039c22a1247d51d46886afd4fd8bf79a4214bb8b617b27de04b", - "0x0358f66ed8796925aa063b59b8ce6936bd5587debffa8f616cabd7c2d5fb354f", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0655ec274cfad2f709cc08f0c12f28ad21e0dfd6e6fd72c1507387d62a34bf18", - "0x021c07e86c25dd9958e345c8ddadf51ee80773223c2970059ac5f43f8f1a1119", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0624d26cbaa197e104eb83cebf2adeed09a5cdad359993fe5e3529d4d0def21d", - "0x0261b7da3cfb55c788977e0d8d640e3e93ae5a325d962ce85c816d7d32cfc430", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x00f24ecb7ee83a3e28dab54a330dc93d0429a7aea36412e922dce8fbff40d60d", - "0x00b043e36a258d1df1d21b0cc7be9c4dcae1bd4ed326c110e668ac23d86805a6", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x02eef103e51eaade5c9e2a5455371d2ec3fb3cc17f233adbcf8f3911211d7377", - "0x04feec301ee93d99c5f5aff79f6e2ddaa5dfb7f8005afad4913168eb7843a6c1", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0686cea46b710bde1231483bfdbc700cfa3da6ecd5841c0e0c782f9ea24328ec", - "0x07eb7407aa58edd6911c7c7e8d1e03bb52ead4a2415a0c33325872ff3a521dd6", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x004a2a16ab498725c5cfdf397938cabc126b02ea0bb94af16d1d86a0c0a29d17", - "0x04d0c8aa8b38acdcbc04a81fe89b20bf471146ecb16950e88b253d5dc1ea6538", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x03cc071360c540c8d30fbf735901135622a6e82ce350cae5f0ad211124fe8524", - "0x06a91a8fb7257fcfe043911920acfb35f6b14d41d72f1b4720a0e0cdfceee017", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x050438741d0b4354b4f0680f3e9f506d4ef54222007aacf2b66bcc8934ca3105", - "0x02470830b430da95f78448fd294d64900744bb6e8d42c51001f3a5e2b9d2464e", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x03866ee1186264549df3dfcdf8705c0380c9372eef6d4081c2454d3aded1720e", - "0x0634c6d3e8eb8af652a4be73e3b613452c2213104ca875b66b4b15ee5b1716af", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x05707705c184cc75c401b3da7d41a785d3592f5b58e1249252f94c149061b5f6", - "0x0544343b68822d2ec4576d757d419d5b14e6610ffa05d96cd4eb84e34cd2c106", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x05ff838bb1ccc370c4d94eafc43b3e16747d3a0be73764e7b95d15331952d8af", - "0x04055d4430acf34c23dca80817012c31d36b18dce4269a4661e4fa5094ef3408", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x02319db44ffed66bc077a7ad28935efd1473435b61d71f9cffedb1d13289c664", - "0x05ea7aef057b200d6d815b46f60e930278430f6cd289c11858a58746111b1c05", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x01e2f16a784a0ff0fa10b345271ad0cf5a4661a186813add95d03ca59d55bf8a", - "0x01c0a95a45f91b45c10164071f9b6409422bf87e194cc5d9aae7096bc3959d88", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x03a9319d4dff897c877e589fe7561b6e0e49445c81385f71021a5d7656cb180a", - "0x0036bcde128a5d354c6df8e77ee87778cebd1610daeccb1856833510ca16cf3d", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x07dabc5744ff138803b884c8dbb3c8ec284b29c2e9fc571185c020f7de3a3e8f", - "0x0201751247793f1d02bb071cdd3dbc5d660dfdd5268511d870ea8f72e5a3aa59", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x00cda124f56d5f16096eca01f513670b429913fa092e543b7fe55d4c05b20862", - "0x02ad28e3558feef27bd6dc1dd9f4089b26ad4d37ca1bc7c124b6470fb200c244", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0484c687cd2969a1d20a58cdfb9a60f280a473284503b1ecff5de514aaf8206b", - "0x034d44d26b7427e51a646d1b924084762f5b461685450f21d6a472de565bebd8", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0203561333771fa0fe22c4033349f7b877d15b0542a5598e81e067968768247a", - "0x02b6a533aff6e2163a36a2a89cb7415848bef48db40f952ffd380f47676707c2", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x000f7573d3c3bd33c72366d7fad4b74554b3121c9fd8913b92f5062b7cd33439", - "0x00586bfae026cf7ebd5a0faadce96f4765b4d11bf8d546058889bfa177138cb1", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x02ffa6cca6233695760251206fc5e34c8d3692498589478cdd3d5b09f0b7c05d", - "0x06c57d605478fa9626c4ed769554d075daa53e1a1d0bd4d94174d3bfeeb11ad6", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x00ec14171ad6c6f1af8d4199060a1b1a9aa926e875dd1ad77bcb4ccc09dcc8b9", - "0x074a61d094dab9efbc87c45d9af4e2d82801f1cccf85dac70dc1cac47830903e", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x05683fc827e3f80739ff1b4c9aa59a8d7b4435bf4b9f2d4651e520931b68b10e", - "0x04e04466c47e1829ccb2a487b10890f21685b0bb06dc652f829065bf88443d65", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x064a48f9f8b5cee75d0346b7c9e7d88faefe607db1aff2a618898d55ad495c22", - "0x03bcdff5425b6e3cb12f9bc8d4186d73bc3785d46e2fc2017948c25cb1fd167c", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x05dccf0fa46a5571f204d0b033b45f299cbb3d9f80fded57253ea4f1c64faaef", - "0x030a38e131ee8756ee5ea2a3e16618a5dbc28b5b9311308bf037ecc2039dfc7d", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x02e72b8f34da0240e0fe1214a4e3965b4abd58bfad3f7a1ba50a7f3c1c5d7bab", - "0x078fc90cbd98d9159ff0d9c0230870b61a0dd89b02f874c2d6fa800ffa2306fd", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0061194b1ee46ae1522187bc944aa77fe31244dac20400d9b862bb1a92fe5cf1", - "0x015a78f5d70c02690ea43e1fa562ba469aec9051a8054d051c3670199e41b324", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x00d92d4795c00c6aa4248370de90301896bf5b097ed74c0c1aff52aeabd3165f", - "0x03ecedb20e1d7040d2d5a8a95c2f854838570d7b01fb5c85de04e48f1ef30d01", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x034ca6a734fbf11e86b55a5548ed2c64f92778155b9bf27808563f72168eb448", - "0x05d097dc68e2c4ec523779154d78720a3c641f7a6d5c0a570c40baceaf0ece59", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x040b34d454a8023bc6fb93493bdd2c88451ecb52a651a67499250f76be4ddcb1", - "0x0541c99ecc0ebca3fc9e53003399dc5631574a869762ca7f56a6c2be78548c39", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x00bf9df8fd600cd47853c0cb8d380a6aebed73a3cb60f0669f5a55aa0e661585", - "0x071e469402547d53f2e851686f7c30c7c5ff5f8355f26d2f9b390fc495ed4680", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x06fd3b29168bad0f36b53f5d1a6f13bdd618b4dc3f414f0a37ee6b999328ffc8", - "0x06c18bb0c5fac9856a8bd160f1e7d51ec32974c0582e52880de2364412c5ebaa", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x057b0a2eaebeafd950221facdd24790d7d1ab8883e5c5d55635f0d14a1ee4741", - "0x07b41cc478fa6be38417271db8ed12efc0da6982552c1496025d2df0576bf4ad", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0611b5725101f611c387ccaa13889ecf3bb5595071a179ce350029bfca4ad7f1", - "0x03129755977abc8995fec7eec1123a1561e429fde37ff36af002d3211831ecf4", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0534d88606700023320ff5a1a864060ff7ac1534c1d9ff8d50fc85fe7bc92454", - "0x071f4e0f548f94a7ce308a876397d13343221370947c0a1ccffbce7070461844", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x01c06bbd0c52fdab9fcaf680c7a93fb821e538a2ed79f00f3c34d5afb9ea6b31", - "0x03873d3bdfe0be0157bbc141198dc95497823cc222986d24c594b87bd48dc527", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x067e2011abef26ef6a1684febce9e1a18c2383bd1c3f3cad858edb8bc3e9ea1b", - "0x0407da082675445aec21d9c87ed9d4f5a57c485ece71ebf1ae2c0cbec61bb3dd", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x04a380429dba88681b5fec6c38a6a06ddf668e75f9b7dbb7bc4ab83b0d9dc8c1", - "0x05632172ed9fff3e3889e8ee9c116a189a862f81acf3293ef8d41e0fcbd784af", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0333df8266d79493541885b7b967410b554e888fd364d42902cdf377e4f29781", - "0x02bb17fc03d0e512c02a9579bffbc4a4c01189d850fbd2c0a3d2a0c6038bcb0c", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0275cdbabc989c615130d36dabfa55ca9d539ed5f67c187444b0a9a12e5b7234", - "0x002b7f723e68e579e551115d56f0ae71a3b787b843cc04a35b9f11084b006521", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x060e28977b857537fad2dd2449da3b3d60dec4f9ec74f3ecdb2b586bb8911676", - "0x07c958adaf4c1fc141debc849fe14d62f6226f7f1c0b78f27b4a215d02775284", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x063f8833b2c1c1b51e8edd5a1c44f785d947771183d10a9f308572a6f8259def", - "0x055bc502846d1383d5e76bc1891678e2f099c0fdb6cb87a18c43ee071c7a5187", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x041b1737d1ee973200226a0bf3dc01ad8c73766b49c0983e82a7583355426eeb", - "0x00e356dcc2b91c94f773918046f602dbfbc4d1360b9853c6b47c0d3b65b78c5a", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0128010f9d97cc1ae5f79cab6ec1ac131b19716b094173675555b6f99fc620ff", - "0x01acd10e6d5ee79197af820e75921618e278e154df94db828d3db79e15178977", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x02e0e363d5753266478468ac1df5e925e90fdc55ba85a869e50b5acd3faaa8a3", - "0x039f1b6a67dc45e025f26b7c1834d670ef112f96e4024ef1ae7051e4d72b3310", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x021aaa2ca4f504e66ff59b1c1de5c5256eddc1948f6d1c9180a24430fe00aac3", - "0x01a1748762ce81f2d3c5c6ff35b17861b7096f922cdafac43fcf423730a6e3d9", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x06c84363c06e7231d3d9acd69e46b717d469a3b120152df40bef92b14211d94e", - "0x00687f02c9a1637265dac105ca718897a7ef18a0a7c44c8ae657672a1d0a6988", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x06cc702eb20f8b5940c7da71f8b1801f55c8c2d8e2e4a3c6c983f00bc1ffdd95", - "0x05d15b3727bc66f3aba6d589acdd139fae115232eb845abe61fbdfc51341352e", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x044defb418700cee8c9bd696b872adb005490512d8bba081f8f99a9f15cc981c", - "0x03b2072cdb1d919b2b65b5cb3557f0a3381d7ca293c267ca4a38f83e77bcc96e", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x048b78d75afda201bd606d11f7128354a8bfbca47709cdf1c528e8013d3297e0", - "0x00823c014201b1519da419e568652aa2d60b24f43d7aad8ec447c3f2912a1280", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x00fd83ce77b1578b3a9b8c3cbeaddb1504d2fd4a19c901c21ac65961224e4966", - "0x0110cbe64fc10c6b9c66f15ca406a35f50b723b35d83c5eb9797a57f8395f4f9", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x05e03233a11fb6ee8ddab18f83418077b008faa304d28f86a0d7fd7482f4c78e", - "0x06d88e4c51f4332ea05160dfeb860534c084b0bb3a6a48e350d88c7d771f28a8", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x04a7bbcd5605f8f5234df6197187e57ed05399ff867026b4d9caf161f3ce9cdb", - "0x015aa5421fc2b584be5db1f8cbb2db1cc6d4c313c9e4a6091b4d290bcec7a06d", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x069cc4c85825af1c4d9b315512f1a4a9c65096b49eeaa5942e5f847dae9e793c", - "0x05888756df8444e211ea00b039b03e7f426eb8008da72bb3bd9648803cb6bbbb", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x009dc6ff90e341875e113bbfb507724dc7095a280d2f32cb6ba61a1e0c2d2aef", - "0x04aeb622896c852c2747454e8f172c9482955a42ecbe522d6ce07ecde79d0a51", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x03397ccfef6a9e896952ac317ff57326c0784cfb0d034ca7df91b10382596a16", - "0x05b4bec200ac1abd5e155bc9120d87cbf109d0115df46c97954e22a86446c6d1", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x03afc87c1bbf6ca2453c6614842e80dc513d153b6888f4d6ccd032f9e48b5e84", - "0x07983d108f7829fa62207e49be6e8ee78f762fcb22453a92f215fa7fbc701646", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x03bc6fc5b4c073051c49dc626b1df9ee429eead401f188ad672f667c75b1061f", - "0x07931fb39ecfa6ef08557dcf037d5a2e0532e8ff81a86d7b853877006b6e41dc", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x05d8ac61b17dac5579424bd6ba61f9e6a5ff433625c96319815e79d9485ac146", - "0x07f34b39ec531f7762c7055222b4f682f8f86cc92c66b1f96f3cc91f8a9b3091", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x074d9dd8e7d8ee6b87d4ff8577244cfef1050174b9629154bb8a7b61dc165770", - "0x03f80ba57249545dea47a6eadc29d7e691f279f5667e62678a5cf6d1aee99ad9", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x00d6612c8202dd509b75980f904a8977c8185b78e64418e52fa3939952b4d950", - "0x05da4e6a9d1e1b97f5db7bb794bea787efbd5fc3786a5581912e7afb9795a6f9", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x026ef94d55fa03989b937643eb272f9d4f2cd3a86932a7968222ec77f3ea8355", - "0x00f58f502ffd3a58623453f36d4f56574ea26cc42a52e9b9b7c59f08635f17c1", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x071c58b0e47b9dd9107ebd8a8c8fa9f0534e78231bac612c1ddc7a94edf33eb7", - "0x07f90edaf4792bf8334adbaa0f4ee7c654312725af188682d75f34874c4eccb9", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x01f6de1f14988778ceb2dfe844f92394f1f1e72fd1581ceb3bf336c95ce50345", - "0x04f6007ed4e022d2ee9fe4ca8207c5f6c766c4f3b85260e941fb24ad0dcbf0bc", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x001c25186a0da54cbe3ba4c73ede4249f5b26e471155a2cd40ad4ce564ecca52", - "0x019b589555e4a9095b28d5e819b704a5783f6e7649b709eba1c809698a0cde87", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x03ddc3ac25ede4a67a97547ed27dc920239b585fb3624177e2e8d59eba678115", - "0x00a9afd8f8bb759cbd1dff2addc63f47da4ba1291ea34229c09c0637dc5c8d24", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0472e2cc849dd25e927ecd762187bb6291425d677f40def5a4b204a5c294e3b3", - "0x058f716919d2a17719a14d72d398a0a460eae4d1944e8331a047dca802d364f0", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x07b1fc000286e8f7a6b268f1758c0e22656f4ab87fe2cd4d196b88ff60026223", - "0x02a34cf0c815f7df065e8ccc6ba7f2164c1754ae71583dcf5da7704d4d822c69", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x053dee9166f00f6ded28ee1950071d4fb25835f9b4b67691b26e72431ffaad44", - "0x049e1258ce0473f61b0bcda93e462ca0a4b8776286a5eda0645d7545a868bd3c", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x000c56b0269d8431556e471cab9d70edda3a37b391696f107b2dc370631de51d", - "0x0729c52f6b134f733eb750c14bd9f95c077f0f6f6ff4005701e5bedc6544599d", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x06ccd902e91b17091ee85c2f63658eac609748debebc615825a6e7351592ae11", - "0x071934d01841022a29c3bd9fc764ce02464ce54df03324c741b9c57de466caa2", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x04115ec6c94edf032eb80b2c75c33645891d1a403cd55df10a1fa1f39fcedcfe", - "0x00cde9950c871d15b8720e9598242c6c33f0f7dc6dc56e4f232bf1453108b62f", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x025ed5dddbe20714431f925b8aad44b157fcec576dc92b617b43eb5f980f8a01", - "0x02db255522783bf45fac701873ee413dec183449cd4af42a904622744c12da4c", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x047d284d7f8fb7c87337ec19e063fe4a471ddecffb2a630a7a6e35458e14a012", - "0x026a5fdac510cef76014b1a74eb692ac339ab69f59389903555fcd60304fbfb9", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x01f7641df9ef20a2fcc29c19bddf669bebc1070e45dc8fab8a6d533d177a9089", - "0x0091590699b44b2a73886d25a7a58c32a57943e414022969dd42666503bc72dc", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x00a782d9462094566f35dfe663f9e9319298aca16693f7795544f02aaefcf889", - "0x03dafeba6c703d52059af170eeb870ded629f4ad876741f20796a38917ad4ef9", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x01a052f00989183b003eb979269fef8c84480a3e46e45cfee0455bc589bcae97", - "0x01144731d763961a1421faf04febe8b643c0d72b321f133d04ebfd1c4e195796", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x044d32ce19ac6807cb22e4f25fe1486a36a13926f147fbfa054b63ff0446177d", - "0x0212a21e8c124c9cd37c80d2dd66913ceaa6b6f666522f115c39382b2d5925e8", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0035dfc16f3ae6ccc06a267bf6d931601e52f3e45359ffc513570b65b96adc4f", - "0x074311d10f4bece01b5ae65a6affe5c931463aa1b73a3320eeb41bbb7bb1ff62", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0420c2b90d0c14548ef5cda3da07aaf2a8e6171c01afbf358acaa69576f1269d", - "0x044025acd7660f15fa341f10b111983a4407b7240658c0c08a7361983052c0a0", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x00e0acd9d2d907031b319b80121dc90699d003d220ea785d50e5033cdb3b1a03", - "0x03911ba78d6e507485d6374b0f7d2e6198f6462a7d6d3cf046404a07af690357", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x01025c1030382b6e6f614b08b587d976c4dbd00fdfa871d1cc2e946fa1000a9f", - "0x04fba10f3839035418d1b8737ced238633ba2138c3d3e103efc4e3d61fc77a83", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0761ade8ccb2a59bec3c850586207ac8ff3951d1949a8cb4f0c03204f0697734", - "0x0092943b4aba2b09d568c36154b8311e034501f17ac72d4b7aec959a486dcfa7", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x068931e0035581e3ea3dd02e469dcf553ccc40595cfcac0f19e159359b37a574", - "0x05c304771c7610450e3da8cb656b686015ff9f54f41cb7b8aff64400622a1724", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x03c57918ca254c0cb7dac251ef4e10c7d82327969552eae15d26c4c52660922a", - "0x05fd5f5ff3f14e671548074114c72c48409df8a2e71fc8aa3c8acb506e2a88df", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0393963888652c14c45549d63651990cb82ab06c3133c3d7dab3d0a4d6fdb43d", - "0x012bceb7d92b63d1c73c306ea3a4eca4567c76938a6a2933986abd99f5851ed2", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0440f508554c5e863ed6b45c927b8cdb0041aac51cbed9374dda2e101d0d9272", - "0x07629196c45abbe213ad90cbe7b7e0ad4ee5406a3579a4de00346e028d7f27f6", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x01f1ebfbabab002667188577797cea57ab0fc2d368849bf390f770077ec28db7", - "0x04c005210fb301c187e7011b8b12335211fc118d2589e493785fc7ae1e3214e0", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x041a9f79b7494ed4e346c5c11eca7e92557f022597e13c2b57b008dab7a974a1", - "0x02ed67246fac7f9da43fc8e08e6b6ae7dea1d2e6793518b8eac91949de94d5e1", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x026eeac8f3c44b015f2a1f54863877cdd49032d57a8c8fbc58aca863274ebc05", - "0x02481d5ecd480c2843be0241c46a91e6d545e5a89634648eeb9d0d1b1d43355d", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x059fb55fcf372e96ae9d576660b52aa6ebfc0f24d18cfa9e0ef56c06442de276", - "0x046783bfb29c5f4c056830b841425da6fe1118a7fca9c25805470d514ac7882c", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x01d46b2f1ab549be129401971da46b15ceaea911167b33714915c9c19a9f0fe4", - "0x051643906a861e733bc43cc355d4570d73ffb7bd71a6ff73ae974fb444c17120", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0222ad8b61e219ba2b581f606b7c996516850a46a3db72fe1f72b5a9be6c324c", - "0x072015a5e2db648112abd284fd867b59fc5606645177d26cf6e9a655c9912d42", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x03c86d5d774bc614469768ad38f7be9a53e9a233942c5c553b82e49aae684764", - "0x0480febea8229e130dedffff89c11f3c43e11724e6bd89d5566d78752859d41c", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x02d0970c2be9cb6120895a6d3f947297902fd946e6c2a8c30c60b5d4e7ca6571", - "0x076162cf8c9bb6382ed15c8dedf89050035b9f5fa5c5085dd5fbb90d5c0faf6a", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x00adb73bb8352d0c10175df371f7868ef2c9e0c79ac788430c480c0f7d85c187", - "0x060b564785248111502e6f39c4994d6293fac22bc25f4d764b2fb1957d3c9bd8", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x00b7ca8292655bac4aaabd53f31bd2649fee32da17bee88b4dfe8bb0cb7853b1", - "0x048b473689a22416f26fd615dc08d3c5d2518abfcbd0511c7b1b76bc18117931", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x01d9d84b55339c229b5c3632494e59cf3f73a849fc07c7b3f4c23ed14a1c6f07", - "0x06434948b8081d7d3dd1df868200e46cf8c33e44154049c16c6af3cedcf3697b", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0647d50273b09e3417cdfa03faf938a9415c9705d7caba2c65b46c6395267108", - "0x052d8ee9202fec44953f621e178202c3d862c3347ee5b1db90cb65afd582d623", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x03836ab8b46cf4f453a22532c886940b982029b29c42adca90ded5bf77e6bcb9", - "0x07b15e91d6355f147b171a90b064a9d8b2d7bf3699bbf4987664c61c950d8996", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x064d0b6d17bef5bc98fd7905f117997f9a021f2c4e260fa5297a7b22f938f173", - "0x063163a07b21c7898980bc23931e39b397deaae2bf117450963e24340f40b251", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x05ec0644d2dd23eed036ca45a58ed446d0c17132ce0bac9d8960baa8c7ee7ce7", - "0x000a5b30a784385e146f35e5abde5c39d47a3c9884807f62bcbc98607aa72dd3", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x052014f24e6c1a07b9dbc608f11a6e8584969bff221c6beced53c9b35cbbcaaa", - "0x01e03d3b16e4ba42d2fff5e47356c4b82210b017fb3d36aaaa77cdf54ef016d5", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0319ca0aa9bdc80bdbbf76f14f0e07596b5ea123956ccff6c376202c00330a2d", - "0x046c04407519d17b77165402dcc6d168227ee9d72c97648a719190aff252ca91", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x04631d4ab18d4a07478c37472dc5ec104a9e9110d5b88585a06139a40f0a753d", - "0x0062aedc709ff2c28926b1c91fca55c4b5a4840f9b0de84f32ba4c4047617d52", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0560a88ab68def3d133d978ae6ea62f3cb5f67bc3089ec5cf3f155dde2f36b85", - "0x07b677d6aa2920980bb0e3a83f4354894ddd40475ff5b6ff3ba8abfd98d3a0cc", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x01cdf983c83be7473b6c5187d41983f83ce01ec07e902c97aafcb0777c6a9beb", - "0x07741301e24db34ce1086c2eeaa666594403548c3a24f41eb05f9f63510cac2b", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x012ed96af1a97c45ec31f1531e96f6fb28a03ba52ab8484545fbe0dddc97bb32", - "0x06d1f522b6c6cad0940cff8e23decc72bb8d4164696af031415508b025aa8be1", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x027382994ae5878223ef802e9b4882f481a1b4008f1eec8484483471f7aa742b", - "0x00c31750d242b3975b0026a0e86ccdd17d0f680a8c6f53f197fc25eb1f777917", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x02148d73de34495484d84816a0e0901233d96539f43b2373f9acb45578a7cf4b", - "0x00ebae1afcf3122640b22e382ee8a9829b9c5ecb958c7db152ad6a0f5f650cdd", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0431677eba3715455bc235557518a74f3b111a88844ef13e159ad44bc16de3e6", - "0x030000e1eb6a17d9df776981e65c6e500fded1ac12003adc9446b269812c9197", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x02c0a92fa84264abe2a19418f4629fffc159c4ebbff51ba289803273ef6e786b", - "0x0796c49944c3c5cc22223a0ef78f2db12baf4d57024137fb707c53da0c813d23", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x017bf82561d58e92cbfb8f61aef57d7aa7fc5aecd644e350b62ee5c9bf127a6a", - "0x0263a8951a4a0c9a19179524cc6ac216f8b5fd40d48744a78a0afdeadf25666c", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x00b239da82e298a6cdfc564fa3751d43535f1fee823810bb5e258fc70c32c4ed", - "0x04408db51699579aa590ed9cf9642bae4c43a79e17a59cebed9d1223055c410d", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x04b563e6f42589671579eabfa2cda5502b361c46a5ac8d45c8ed44741a925b33", - "0x0627bdb41678443fdd1aa607709e9699b652308615f4bea760a3b79ee0d9ab5c", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x07c5fa431f474ce848fdc97484aab384f638e02f086ad0fbd9b44da6c5e52df4", - "0x05e35c0faf1b3266257c9a7592d5ce9777f1343b1b4bcdd14ca18e24c98d9de1", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x051eb273841e9c95bc2d5432fd0a2010cd7f4c43020ef2c3568f7e61cfeab90c", - "0x03a994ce81794588a15e3b7cce63223562c1a7c7a53d5df20ca7e1d698b50eac", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x02be8b2ac06e4f80d8561bd0d3461fd32a9ecfe8c36ac073770bc6447d5b904f", - "0x01203fcfd2a0b21a0a6c61aaa80eaab51068abf1cb75df4802bd8d50f7ef896a", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0063497eec407da7a6584d103961649d767bc55ce21317e62305cd1c701e7364", - "0x065bad74cd20b191e42eafb57f01aa2c7d4bec2f9a23fca8dcd7d5aea6e781b7", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0268b87223eff0ca84fa328dc26963d671b6dcc02282c88ff328b25d95ff4818", - "0x0155383241414813e5b3b89df457a0aa7c62563f7130676622a524d32bb6a22a", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x01cd01fb7dbcf1a3d2b50bf0abc6e80d91914ce88cf6a050ad629ca7a6e9621a", - "0x046652b17d0c5468794a413b6af84f46ed815b2477e961f76be857faff9f4069", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x059a444dc5eed0e2a2898414e2ce49a3037466dda53aff4a1446af91b55ebd6f", - "0x077cb38dbf12e343460b378ada644ed434e7471d2c2fa4df4b77f087e74655a0", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x02932fd3f81fc973ca9def6b7f1bb50f980fe589187cfe9e9f52ba4d356cf2c8", - "0x01e6bfd00fa976c4770263a227048214c38850fe0f059e7b3d2c7871ef07d68f", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x00e44e4f3d96d9dec775b996be57e57fdc28e7c68023109b221c414a244a0dbc", - "0x0058b1e52fa274812e5184e00e9ad812bec2463140adfb4bea3b2d665867dcc9", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x06846ad9722de2861fb2601bb4fb5ae73f61cebf36d4523cac3d6dcf28e3e0c5", - "0x02d33fc71f742c03e492f3b5615bcf69dd93eeae048bdead003b76c18b2d7d1a", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x07fcb89be1f4bec745887bb891e53fefd665c53d00a9e74de16b8a7e1f7adfb5", - "0x074af0b06633f779897e199609c71cc5649bbb65bc2c0abd4c678f0480c198d1", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x012dc9e413f30f2c4a31a0c817fe53745678a416ada41df5f6d16ec1cc7de47e", - "0x041a7db3f297a185063ef5449304c5a623314bfadc8f30abf8a99f32c406bfda", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x03b56dc90e776ade9a27aa6973e7c4a65da8c018546da5411f4830597041d24c", - "0x05bfdb79523764025d13c9bdc8ac82b4aba0a2c0377e24f2704af2c7e9bcb28b", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x05645fa61c1f4ba2ba7d72d394bda48e8e6a14d076b1dc9ff2b9c2aaf6170308", - "0x01a1616adc34b274f50b3e156b71c31bc6b9153b60b5825cc5f867a9eb1f58de", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x062a381ffb904ea3ff4d451d4c8459457cdbc3dc2fd2da646a95d8c1e90c0b7b", - "0x01ba058658e09db9e319fa73de8ab4a992b71e4efc22c273725bdcab84e2a315", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x065a04e4b1988beb9537c99a969d1a6309701c33773a7e4ec353ebccf6a6d0b7", - "0x0669d76d634c76bc1d94866e9280bdf89f45bdb65773c099e4f3621c06b074fa", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x02c8f6ca447361cf789037b5f5757db5ca9fe8251cab80096a63f33c4ca491e7", - "0x01823694ce31bd83b356fe945b669f7df41f34fe168731ec9f5664b6b8c6a81f", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x062a0ef4215dccf112831a4f842c67322a5c061e7663710cd3308d34b9583df4", - "0x0420db8176d16172247f61bb4898b032aa537a6de5d9dcc2b667cfdaaea6d7ff", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x01a340b3a6aa365fde137c1f63191e5931569067f813046a765d8b1df0bd8560", - "0x0036f5d03aaa9effe8f8774608dd008f1f97d326f51c1d4c0178b6a5d7078dd7", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x002ec944d2aeaa21841e70e5c7a1bfffe87d6be95bc653f0d132888490e7eca1", - "0x01d268478716141dd155bb433e0a87cf76f83126ff5ea3b85c5ffdf50eaeccca", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x07b86fb1d0d40133e7ae192492d212b3340bf6cb5c021e137fa26f819bac53e2", - "0x03c88742cd28d490d0f7e58de6a93cedcad0f070eb48e8a317e4f04d056e11fd", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x02fae39160059ffae1e20fc4031762b9289639869d61003f9f49db4b73b1f838", - "0x004c6eb92e40146b8b36a9f257d6405b7d717c4799fc2ed4fd17571d0135e264", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x01b0fbb7a84c67e668450a54449c7a46261a2d355589f8b84ebfbaf9a77ee938", - "0x0044f8fffa33dd33a6146c35d196595e22cc4a215f61ee9197cd751400970a1b", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x078fe920bd96a356d4d95ee34adafe8fecf071d3107c36f047b4024ddc4b3eea", - "0x06162f29607fdbec10181fbac6e57d5cb41b922c5791fb24bd28bcdd75d16c41", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x01dc3bafc577709a33473edf7ef50bc16883fe17f5641268aa371937bde21189", - "0x02a6512e334a9c03ea96bea7e5fcfe9852c6cf2aed2a59dc334cb6c6f3da164e", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x05629b849e026e65d119ac11821d7ab7efd9c52226f75c7427505d6818bb0c8d", - "0x01539c0f90970ee8b490e45bbe5568170e5708521a0e59f976be680595906feb", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x06396195b1ac7eae21320c20ba5da0c6f7c9720028cd7d14a5164a60a1d67e76", - "0x00957015fc5930cea0981e8b371e07018375ded46e8700d34f286f4ede8f3b4e", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x00492cc823c805f6795331c1bb3fec3dd6a2ff6b69b3ada97a46fcd5d401b92f", - "0x01fed5ac67dc00d58dbab4c5c9cc6f614f9316abd092570e92de8ea574c4055a", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0082879bb0dac70fd540b8ec4924fc06591aa7c5add7dc3d28327fbf206ca32d", - "0x064e30e4d3ba2c61b6270621dcc56983fed19951cdcec9ece4da8fdb71eab5ae", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x062bc853f349bac8c6e5921d27ba85dbd9ba20a375d70a7bc008928f3e123b04", - "0x06acfeb1de05ba43c3ef1a9110a983a320e77b3ca294abbc04aeca19b194f26f", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0790e2f2f5d3a780b92b73821975d3a49adfccbed19dc8c1f2b727dee1aa8171", - "0x050c6b38ce050c4445b625a88a84c8bef5c2d00de12de51846aa940831aabde0", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x04c836990e790d02ffbf4b4953e15123d79af2d9ca04ae2f9f65900c183bb2a5", - "0x03178f65e1f0c5214294697b7507ca8a4ef614c21c5c515776d0f7033070e55b", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x01e951a0f13d7a4752d05d188bd2961a11c78f32b0131c629a823762d9343fc6", - "0x031f3feb36f3c363250aab536ddb30a6939470e4947fb24ea4eb804e07c9f4df", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0635a065bc09198d9690b777508997bb4e1c4bfe421d2344636440cfd3c79b50", - "0x070f087231ab21ba96f8939ca9b02d8b644b97ef2885110ceea7da6e54d349f4", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x01ffaeafb97922fbf2bca32f0f62dd98be866340cd17b49664983a874f92db05", - "0x06b81139c899d89d4b570fd3674377271d7bf1f2493d444500cf0ed139292c46", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x04934c80283f0866459c0c571720cd78b50b3d26eed1d33e1d5b4138d0d604fd", - "0x02a812556c46a34be2152c73197a02538fa3ddb6ce8c5b634659c48bb5c4668f", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x023409d867df6cbac5868df16338412e16115f25f9c8b5b6dd22fecfec02e3a2", - "0x0488b92c7b001f7225f03347dd25c84a48bcabe2bee51a80e8a1ff42194195f4", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x04cf4bed663464418285cbae359b5d84ec76b5997d24f3640984c7663421190f", - "0x000941f818e3e3e8fb1568da85217d17f9250ebc948379014d900a7b1a848494", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x052ff3d9ffe9a302f6dfaaf74bab57c08027d5cb699a69b30830540c0a2d47a1", - "0x00987dd8876873778d933fbfed37aab2f7d6f669c37024f926b1edcb2ca55782", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0527b1301c348e4a2f8626ba9209d316fd01bf68c6753cd0162c16066b2f9adf", - "0x07a0b350bbf53839a52112af4e8a6cb065f526bf68d5787eaf7766c826494634", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x01109ee32f0bc53de6bfa457060b366e909d7c18061ec9845f46ac715496897f", - "0x038f36f172bdfd454b9285f86e6bdece8fdffc95182c7d801b03c671cc55139b", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0429db4350228f9501cf7d615071815607a6d3d425f918e4606bfc6b493087ee", - "0x02ab8f3ad69a7c797a20cc5fe191f24641563b4b8c0289617be7d5c815a60989", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x004ffae056c2b824c7a38526c2fe913d52e28809975bfed7eb8eff28ff38e55b", - "0x051325ddcbb6240173fae1c3a3a1a2bd8f02af1a5f13afa7203a27152d85de73", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x027e049b547e5053a60b4a93555ac20c663588debd1f228f12a023446d096dcf", - "0x06c213b2702c89e107aa5d971ff12bf2f96db358ed23c8a11eac172d20cfd643", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x04b4482f1d84efe23dadf3bb10df3dcaa251312dcdd604f616f1eb540e1f3232", - "0x07c9c149dcae9135f940fb54482f9c3cd8193721643a6e23157b8020410d439c", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x07306146650157978f4b268a226bb00936e5c3f88cf505b7d5ff16abf3536955", - "0x0535f08a06205a1c7f80957ccad62cae26781d00efd3eab74d8c8f0b976fe795", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x049f5e978d14d4e8efdd9e96dcb616805a934bdfc551a43ad78c4b29d1eabed2", - "0x02ec4ff772c902e9921836f3ee5feddfb1ac1e0f75e16ccbbb6cb28e7fa27995", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x03c0e4f177444f3630e78ec694b0e58bb2c793d3c59b85bfb8484bf634e4a991", - "0x07ba13bd579fd5bf1504d4cc010472dea9c26cf55122fb06b69849818e2ffd51", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x00cb36d89aa5ad3a705b823b4e99f090aae802a488b348bde9464064a341e06c", - "0x042a158b3e1e91512e306f24e983a533608da6c7283a2759a18e2a9447c34c05", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x06e77b9dce963db4176185d3ba91e26740b2aa908a3c480e20f36208fbc94f06", - "0x04fda2d11d78c71e4c5b4d483cd0f129ec768e5a97c5cd76dfc4f38aaf11613d", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x02759d50d6c303eb8f3440c9cf30598da418d99b26180f39b1d781b2df9ba7be", - "0x030536ae8943fe3856cff04656d82c2116581b8e7da78c17842ede40262b8f86", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x06c92fa88f7f78c4970d16f45968010cc543eb48e323c70ebfdfa066bbb169b0", - "0x0621460854f3a789a6a8695b957c165d108e74b66641a0ca6c9f5e662660c47b", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x069cb459b9e415b7581ca163611c470d875971d5d7949de732d1f0f200544a73", - "0x00a7136fa9dd00c0469863b7def3f83a5611ed628810d7e807e7a873da5a9897", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x00b66a4e32ac9a4baa8f64780acd94ed3628b2b0ea874ba4dece629af65f9e62", - "0x024328ba9996a24389658e3467b8b90dc3927ef8419fe28b3f55b1c1aaa51915", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x037da1e4e8602cde371b3497ae73599edbc9dea033e60dbbe4a457d48126ff00", - "0x06b0c77e59c5514a07675e7c953384321fcef8064a50c5c652cba9d8d5d50b47", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x05ecc3080062dd451236de0e4eb91c5c75100733364bc5469f5fa76f79021ecb", - "0x06da4abb9031a27b5be94529324fad8026e7d871570780081b0f424d4fe543c9", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x019cbe53344765a866ab1c5d910f396a3bd838c2f0f21be599f0e00613b95225", - "0x03f7a6ce3982eff776497894473d7ee3fa9cd7488a01b844d1ee8ef73c8ea52a", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x07221da860b384859ab1a50a1fb9c86270deebf75df946a47b6e35c7141e064a", - "0x0518b7d44d4cb7fa6ad1919f382baf9384cb05859750dc1bac288fda637c59ae", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0781ea0d1bc46ebef49ce1b9d0f6d12c68c6b35e6b069f32532861348a50ec62", - "0x0483b5d54930c3fe545cd03559bf59d47eef8de9d7bae402fa90540724eb5855", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x01e3146f00880bb22486d5bc73e54367d54251f4002bcf342d0393b05a4b9ce0", - "0x023b6fb8e945d3205f633ba724202db5a99305f807137edf942cd60eef867699", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x03221c723737943a5dc0c7e74e781e1c98accce6be1d84b89f37fc01f89ecb95", - "0x0371ad10746ed2e47465249f981cc13bac3db505ec2c341fd83d8b159a260358", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x04c6575c751bd0aaef9e24b9dc284131a19d458ad8219bf459a01f075c3bce02", - "0x04b50c007fff0fe40189baf8ffd70e206c4d6b0b8e3e9c3f9f9ed47f8457a24e", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x06616b79131333ccfb5dc96fe61c26d5097486923f3a3fe42bb6a2253f5c37ff", - "0x033a0887b14cfc1a2f51d7189fcd0c1c6c1b209c28d0628d90a5b03dbc49b58f", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x00aa53f0b7ac8d48cbb725ac302ecdbb537645727d30c0764778851f4ac87d2c", - "0x05cb395f55be629611fafaaedfd627ece586303c57f344ad72e6f387770812ac", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x037d5b9cb0e2a9f97fa0fc26cd00805588f6e4a21de2e0737dec75b326acb626", - "0x07d34fe6650be7488cb6b7dec609dea8125e8b54f54fc7cdfde90ca4a523cc7f", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x001bc6ecad63ec5c7f8f0b0db03acd057743492ff1dbbccf00773fe581737678", - "0x059d8be572bc6fbf7710c34c3e48cf557bd1880bd8e6da1b391ef99812401723", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x032885039ddc8b160171802cf05c187ad2127bf5fedecd3d930241a2d066ab2e", - "0x004f40ad14a0752f34305bfe5fd315908979087e9dbb80b67917d9eda103b4bf", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x02e1da8013285598b899f026c6974185db12c97b4c63509769d3d4ad1d18a4e5", - "0x01e7e7b668674d1593c39d58bc7bccbf568208732b3519bc2cdf93db34366862", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x00d26c3f389d81709506f184b53871497c8d36c5c9eee8e3737358204c1acba3", - "0x034649c3d39f3b825947fedbca215ae30c5a5995e93b1c8efca4944cf85a082a", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x002cdc55304abe626bc35092043c09b06c1b2692a132405ed0970283d79c15ce", - "0x020e8520bb837de7bd8b4ff7c1b268c5b09420866a618ab3d818375064e37c63", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0091300478a83595d548f32f259033291fc7d083953b0b8bde88c7559660c563", - "0x00e5d2bff57fc6551e9b80c06ac7314a71907cdcc66ce82f2cce721a670df10a", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x04bb03a18945b84a6373eb925df1e3f01175c32eed9a075dce2422eee2fe8cd3", - "0x05180166e1f01dc697aed464fe1b09ce4196456250de2630f79148a7c3df043f", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x04fbf02374b8bcbc574f686d838fb28bec8aee59a3b0485644a6005782621a94", - "0x0392123b437c8439059d84e77afef0c62315ab20ed46f55172f3766e6016afa8", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0768caa0b9b5ebefa3f5af1fe2fb3e38cfb47de7cd0bb43b620e60aeb9463cfe", - "0x023b51f3fb1b7a7fb6009230569e97888af74138b727ede5ba10861d79d2284e", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x01f7abcb9d462c63ffe92aa56619ae8590089cca4d93ee3e5f34a63882452cc7", - "0x07e9f85c7b7ca6e9a4f3a026d1048adbeef69ea9d876c6f647c257b879a81bdd", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0572426011020072b59e28f98ff8aa1780ebe2ec272a038db8917486d19e84cd", - "0x07c51e7eaa4ac7cad8a8b9f8af3a84fd879cacc760c0b7d6d474c0a1fb7a8e55", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x02968ef4d9dd1b8aef61ea432ebf2ee5466c297613861243c0a3e9c81e1f88a7", - "0x05d6f0c3fbb464d4c5d07dfc660bcd5f4bbfee371001e4d734136322326565e5", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0014f01391fb6d31a8a5445afaf9e1650636dbbe349be822e88e0f24cf75b283", - "0x00a3dba6361c6d972eab7a3ca59811918f062a89f7fd793b8a769341daca37a2", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x04f751be55a6f85062796c72997c3b602f869b51084b2f6407b1f669fb7dd0fa", - "0x0238393a5f4a768ee08d18456ca7e9c1d30cf8ea4754942fdb5f5d2c86f35821", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x04d4741d3299b7bbb31cec532b71d44e172256ea07813df153a5c785bdd725c6", - "0x03444d9a0d5fe64b773390572a8f74bc4ce5e4a15a307ec0331a87da7a1cb442", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x04df37f84271d3f2e51ced3f0558fbd37e34576351e326f0753442a200c82dbf", - "0x0146aba4e6d484d914c0d4bef17d9d3481cefcce82220d4175666ac58a8e7fa7", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x004574b476566219b42bc7ed59eac3dfd82620b28add0987027efac175f4c6d6", - "0x0193418833fa37f88ecc54fad3b5536afc3a89fce7ad785985d61f8ea0bf80fc", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x04d2caa1323012e4c83b0ad387308b8aef5637bc35ddd882e7f5e41cf2ca410f", - "0x0047150e808c81a540b6f8864e9d6636589cacaa516f82caaa96506edfbd6f0e", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x03c10a6083c38351deb3e6d1b386827d0acf48979b66b95249eb8700ec26b069", - "0x047e34bfe561d903cffdd1d849b85aa3cbd31cb4a9bbd8cc2e5fd2f95016cabc", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x05b53ed4f24d6425fdb7d7eb14237ebd3bed6fba1e05c0c415957e2c85f423c5", - "0x03dcdc770edcc7ce9bb5ea50427344b9bdfd3c09a0ba745c22ab04c585b15cd3", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x00758bd54868eec045d0b4d3d2bc415d24bce13fee47cefdfda46425c109b657", - "0x03392a7c66ea3bd7b044680bbe9f78ae86752097404c067e9d2572f55330df83", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x00f96ffbd7dedac4aa2ee3eedba5e23adf118ebffebc61cd22893eac90cdba24", - "0x0174b143957fdb13f347285daa6211853402b68c8144278bf25cb19d87705eda", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x077858bcf3d732c7047fa5963a549d2cb04f51417c4e26814910b715dfd8031f", - "0x0007ef16a77ddf99a464c9f7e17d3fc155456c0046ffad0a2391c02438c84260", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x05560327ffc72c93962497178e4d06b11dc5d331277b5febad8cc675c2fbe8ce", - "0x06f858336742a532ebfefca704d9d12f858ea414267f0420efcaa5ab87d0cc13", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x019e718e0ca1d2d6fadbc6006ee7dda7a385430e29f5e239cdd4bb7c3fdcb2f8", - "0x05c68249b7fe03ea2e13481a63b6cd4bf74ce42009a89fee0b3f8f968b3ec709", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x04c5e27e68f69bbc4696dd50e1ca8e48a75e631d264ff498e1f2d1d6d3b86bec", - "0x079bbb8648c1a2d22c9613129f11f56b62ef535387a1ea9fceaa99201be70dfd", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0177d4a8d96003159a33cc36cc748b3edb08f4411900ee5050f097c9e26f23c4", - "0x074f349bc165900bc522ce173111dc9705ed6419ea61114256a7836e909f539d", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0550d63a152be00d61aafcc6e1c8124de40a1000e88ef8ad94a0296ed198dd53", - "0x078ec5b1fce73d8b513e7fc3b2028313055ad78399ce64e5d676c38787ca8fd2", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x04bebe97ce3a074f4c771681875ad88ba1e6dda5a50d4bf9374c5058ff3f1fcf", - "0x01968ded44bb52bfbe62de6ce6738ebc05458ec800483d1e2b9a4463911e098e", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x040b1076e145968d6612febdd16eae576938f8d4243a2c7fc405854ea200982f", - "0x05cb4251006494ad97f116838381ae308ed5d0ece47432ee91f5e38870fc4ea7", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x00c4653805d6a981547c4af91e4e48643132be20caa372f0fb1e9468f4bd385b", - "0x0044f9fad9383751deb8a29421fde721382030b7d49c562d84f4536a9a73bb6e", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x04f2aaff527411a971e4ecbd1d6fe2270c3e847ba0b23f72c19bf9e35daf0561", - "0x05d0a2b800355634366fe5c510a56c03187e0599846ecca358416dec49aba95a", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x028077f57ea62401806367e6d54fe45d02de5b072db787ffdcc3854e12a3e855", - "0x014f3762689072f5fb41d03e94b01808c739f6d42b7b785b0e464100b150efd2", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x03b8a8cefd017363ce867265af3293cec081fa589fe561830f0078778cbd338f", - "0x069ccf2383cb7b4f9c806d72535812483e7c5e9a1a5928529d64ca7e085e758d", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x05c0d311a77b2fd419324f818d6f73fa20b049d1b33f16cf92ae5fea4f1ff8ca", - "0x00dac5abf087ca1b97ead9c5f3280624b2f09c8c5ab59dbfa31e3ea11c400b9d", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x077878f388d22161a2953e5aca6bac1ea480e102f329574b4b201640d44a296b", - "0x07eb35706a90a03aff7c2fecca72659136547cee98038746db5aba16fd7178df", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x077d38821217473825441129c4d0ddf6d879ed3ad3be6f4f6a419564e260fa7f", - "0x05b94a5a32995515b10cd565337c965d3e8b13f7657c9c7e57f1da81006fd114", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x05130b867b4f876a11b8d3eb90bc54b79d0649d67f01fc2071bcddbfb141bc81", - "0x0793ac682a1d447524b6958395537c948cc7853c13a1b48533fac970f6694fa2", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x028f54a4412efc429b2050acfa69341ef9d5f23ad9e07fa78163e3a2110dd910", - "0x05e9295abfb3ac059b245e66750e770f1fbaf36ae419e6814120dfb9c0e8cea9", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0097332e6da70961f2ef31b7b628f1018d21db8db015922a301fca7d6fc6a8e6", - "0x02e37b06f639fc7a82601b744570a2619e543cbfaf60e474107fcaf4686d3223", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0101877811b23c19968e225e0a70eb20f542157045438cf08a9b14143d6da8dd", - "0x01ee60ba9782a51bc7aa2bdebb9e44a9bbb8045e4ca1285e47f0d0c76a48227c", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x00d852a0069dc1775aa8283db719df76a94df9ffdeb5c9c034e621cc8b419c56", - "0x07cd079f80cee5bc8d58dfaf7285d62f5fcbc94f2424b6d2580235441e133218", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0581ee77a3da2170b5107c722f6a185b5f1188f1939e8dcc03087697e364420f", - "0x02730f16e2c7789994aee2266c003c501d1b0cf9ac16d4e443e1048e032e4d1d", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x070f735a05ae6e62f79507335a1c7e06a198e7c9153cfd2645faa89be8fac070", - "0x063f832b42bf9e8f1db0d68c25b00bf615033261517379565ce5f5708357476d", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x037a6db1d597af51d6ea07bf3fb606182906222689a4782f41398651edb0cd6a", - "0x06205f96c84c74b5ea51ab98ec4975143ced88d898f44e638633b91f0a9feba6", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x03102084d2cc51ce1249caef5e970410e35e92a67026c736fd4cec07d71e387e", - "0x02c29071a77eed579ad7826ea899eaa20b1f1395baad34c463ec7b3d52c2f90f", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x063bd66fd96ad49bac08e7550bbd576dfc8bdbb6f82693bc72e050ccf7509f98", - "0x0027cb2e583bdb530039bb144969698ffe7c21e812b8952dad52e11d75be369e", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x00a81518d452d3aac48bf0386c3ff170ef4e684a4def242c964e129c64f4d647", - "0x037506e44c85908ec7b7adda9547fbdcc2e3605151fefa77fbf127ce3bc938f2", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x00e80336b2220b1d666074f6b0dac85353d0e4c2e8bd0f37055a2236a6a9fadc", - "0x01cae76d73eda7a5964c5d9d3ad6748aff51f5543c56441d2fdb7b444a39846a", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x06e1d5620b4717f966676ba9a82fc2eeeff7c164dee2290001de6388379cb8f0", - "0x02de551b0034c8fb670cfa6f64b166319043a1922b0cdde42c895a6826846517", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x02c01fd8430ecb44e066f352c4f697fc9fda177dbe162f82862d7b9ea8c918de", - "0x06e1dfa99640fdf5b30603d34c7c97c1aa6e6b7f3a2c52a21fc64b0fcac7d591", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x061acbe6646ba72c7fec41a5eb17bcb1a428c02e732f9565e7779d7c9350fd0b", - "0x043591b757f3e28aab5f0a38a4104d4f1dd01f301f8281f06315f3b9b399e940", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x06ec3165176b8a7f35738fb92f3ea8eadca8dcbfb16ea0ef147693104bb85835", - "0x05d5eae1de56a2d0ef62aa2164687e79a44c4c887732b47d40921cbae9485cea", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x03fd3d0db872902f8c2336586b8dd878bee6998854ac113510a040664e0c84b8", - "0x020c7700551bfffd7775ebf89164192f9379638d962587eab54b6401c97f1ed8", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0744e37b511cd0ddcfe15f3581947014c159de81ed055d15a13c7a2d1fa39f0f", - "0x0685caa8ff6979a6c63640ac638a3f9c75737f2031bd55322a47384357af164d", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x05b82cf18d19f9f33006fa377aef9562d70f00c6b111cf906b5fc7ffff445366", - "0x07c1e725760d2adb177dd5f60b33d6afda9fa3afcb9c4a84862e3c871a5a1666", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x02ac6dd6089a9f0706ff43e5f718865682554c8ef18b6fd8dad96ba60c64d8b7", - "0x038245d5b860c7c6bc27b15fb165a94208bf46204464f6366229caa1af7572de", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x010fbcc48d40e7171ce955140a5884ffcbdd64e51d4d703ac0418a259bb3ee28", - "0x03a9d61527b21b3596f13f69736b249f62c356f2e719679405ca2386aa87cc5c", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x04b21322ced1984734dab27834ff0835d7e11dc80e47c5bb1ca4322770ad1604", - "0x04f0f918788566ceb4c80d5dc44f93c3892e53f4225654a722b8d027c8888b28", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0107495d5480b5fa2bd416b1f3979d74d8c3c7940f23cb2ee398f7d1bd05ab68", - "0x0542049eaf3b747990ab8813601d94a17a8e646ad77aa800365c9b7d2aa8c780", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0026ae5b603e142298005b6f72038b576b6b2ae57407cb6df9b77b2037e6c7c2", - "0x065e85e4fda4b77ef3bf46d163b9c8dcbaf5dce559793047da74362575591170", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0697b161b7c6de343de22e5f88d2aeb56b42b7434351f8788ed13e1560bb9c5b", - "0x031c6fdc17c2056ef64a337cc2075c57dab41624c2784a8e965c383371599c79", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x040e627ff84e1a7a9068b4368770f5956128a4d9e9e33e9cf5e24d9a242149fd", - "0x02465bd6cb20bbdf810e2bc5c3c458cecf4f3aa163a7ac99c2579e5f33417f2e", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x05f635af7f554a17bceb6ccb6e637abf89ab6dadd399189b0a0390e87b1896bc", - "0x02aa6238a69f89665646c0e3ca2ba5f709cc6e14351cf71e1b00ec45201417a2", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0596b4fa4b4f2be91a068f6264c195a85a37ca3afc4801b36d0faf20a3164eeb", - "0x04812fa4485ae0617f9ae36455d4512893b734ae144693343c2ada54f5817dd9", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x05edad3063c9fa8305978d7e6a4e037c9fa519b8023c7608dfc3b66e5c1e8985", - "0x049f405d07d7d01919da51159ecdad1031a5ac208c026fdfc14d38f633d92183", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x02ab75fcb8298c34b6602eda6ae6ec7b174b1ff1ea72b7281f09b6ab12f794b7", - "0x0021d51d24be430277af92562e8255ed53ab630f2d2d2f96e2a49e49ad58223a", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x06f0805363a53455e4750f55ce882c44767ff348748ed5c7acc382fbde1fb8f1", - "0x07a2f05341caee328ab86eecbd3b696182988ee8ef7da701b05b58d8845a9796", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x02dd7afd786b5c13ba809590227e66a820232f9f6d6d6ecfc5a3c619a2553196", - "0x05403038267e0f0d01e9d35bef21f034043941240a4f44339964bc5bb802ac4d", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x02fdf2e8a45858c12926a1f25a62255fb2d02d0149a15ef669f859806683e649", - "0x061cfb686bb31e2524470d4ad2ae09e3cc91b16305a21d748098feb1d8ce3b3d", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x02aa41f69fcfb3047f343fef5cee789dadc889b475a9bdf3da0c80a79b4f50e4", - "0x010d686a8788c9cec2ecb4b56e1a080265c0adb9ac13a22ed073e33c7915f563", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0164aaf5945ec0709666b07bd1c6a4ec64e10e818f6eea2bb65f92e9d4cba0a7", - "0x02db2ee0fdb9c3d3059a4c4641817c935048c345f5729019195fee15b83ced37", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x06af6d604e815a29a69ed7fb8f01d34fbf93e511f1657b376497b41f9a583dfa", - "0x02d437f9ceefa2c91137f8a6079d706ca7b337b44f8058d7b57a79d4186e6e23", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x07ea26b17dbb2f147c1ba2a4755f4db6a7e02f9971b57b5e81a406894d7333e4", - "0x05550c4ddba908424755a2a32ea4e0b4a97a5439e1e3e77d750e6cb7e8af4469", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x000535267265c637a80e79fbb3b3801f91e847178344f2c647c191b58ff377d9", - "0x0130382369b6adb9209922a513de990be6ffff1ce98fa0a58fee158d0f6a1be0", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x079931da10835559bd6ff885d0fff6a1aa668dd7199b9e8dacef8bed2d706096", - "0x0025c52b0ccdb02a146cf5836246a0faa8e46571c493619e9050a527c9db08ee", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x020a6724eb9658d77945e66697da9532e730c28093694baed1256592b65567d9", - "0x06a0455ca25db01fd56897a5fbb8a76818103ba0401da3350d0e636b389c685d", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x00ecdbd7c37f1dffa3943977278da3bb429afdf948b4ea6cdebace3d3be82381", - "0x0190b67fb34f7f3ad6afd3d6b6427aa327547d8ac0fb4deeb0feeba1f63d6c60", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0233021b483f578dfa5222f8cccba5766ceee0ac65f6d4a3b1673b302a21fb3c", - "0x07d4b6d44d175d4b593f06f5a6dcba2cdbc4eaa2097abaf613123546866cf4ef", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x051029f3049a0ed76fd975a5dbf96f95c5b8071fedb3ab22ca083170d756f5c0", - "0x01dba5faa0820305932740596754768ada613702d1e2de597b714befabcc41a2", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x042db4e953c2a7a743de9fe20c5798f2247f51db4eabc6f40e86c13909a310ce", - "0x012c1a0764a0b9f3666e431923ce15e7fcd0ded5ab153f0b48d362cca1604e65", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x064b9aa4e47859754e7ab88bab75a9bfd1af39328a6d4781e04ef6ab80827a20", - "0x03e9c0181198487589210d982f9e8ab75a52a389ee64f9bfba1efe802ccd9a55", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0478b386fc0fa614edf59e9acf8328cea4ee522c8e12ad76e8d6fad6d0585fe0", - "0x0425a32f3ca1469672c8fc4b42e65b2c1f2eb9918cee7de5ecde296419f4290b", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0494e197a80bbf91143b6fc297b6bd713e43530c417a1e8efcf75ed54bd1d108", - "0x0260151e5dd70acdb8d2a9f039d41f68e640583a27ecd73a532e54bdcce6feb1", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x030d539e2b545fb957e40e2255f6463b52d227c9808472cee6a3d521aa283a44", - "0x05f9eccf747fe6313570f99e845db32b40070acee9ce9e34da7f3c29ca53a07a", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0556e72a989a93ba4f7ff619f33baceffdcdf04f511fa5a85c87febb6ae9816b", - "0x005923f16d1dcd28803b4c4f9887226e0a6b8f9a4d02de3e5e5bbfe3482d5526", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x05eb5dc7881f1abdba0cc5f972aa48c7caa6e5e55f903d6942b88c869a36ac78", - "0x01ef6017ce1503269642c5540d1f7cad8ea2cd969dff046a78eebb90b6a487ab", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x07a9659d97d654c41e0290ce6f01d0ee565e882d19874c72c18326b6132ab319", - "0x068747f827fed829de8852a4e8054658ef01ad65abf10d2b4091c48180a09bc8", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x025762c16d4ef475217a408df6365e01c9e80f7e08770ee0245aaa027a67920c", - "0x05dfdafe1ccf4653e1ccc2a5b6a4302543549e4dea5d174a77a8c9caff1d6aec", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x07b12a68b0e65a7c159a9e746b43ad689f2770d88646556e177351f1e390dfcd", - "0x073866127f7ccef3018574cb4c8c3c24643476c76301ca1562595db007e18874", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x02ebbfdff1587fcbb837a3a59399efb97d9e5703d01f69460e24253a9c96e052", - "0x013c5ef28a8fea7d55c86f13dec8eee8d3b145e5266002e022f34fa7918cb558", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x048029ab9b4e0b86f9d061a72b02ac89b594a4f353ddc2eb6c5adec7504d25f8", - "0x02c092af8e180b5ac407efdf7206785ceda893846649a5b70aa67bc71d7af7c4", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x04bd64e5ade3e2733580a6116b4af328751198e7128f9acfe3a3496b545efb5a", - "0x04d584768900dabfc0dbaa086632b8051bb3905ef79b84d96c01514441d0cc93", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x062d6e771f02e591557197d13c3e77dfa2d1794ac1808407bd8227c4be31b466", - "0x05c6f5607c1808e899ba36a425911fa8566b7ea9cc80de8a80538c0fceb837c0", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x01c17db731647525315a5006f75898ffc3b7a53f5cf21a7ea08637c689991ddf", - "0x038c4d265fe7ce9943ab7e5346100caff63b38d96b3975c6a336f745714a8463", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x05ce406218cb2852b1d2fe1836b19462f664631785216e87ffbce26030e2101f", - "0x005225f107743c255ab50e7be4a090fe39478d1ef4ff558468559d8cfa87bb94", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x033e45993f953dea9cc0087d52024d87d941d4bf2e509e68ed50b259095cce3b", - "0x00cbe182982e1dfeab42c35518862f225365fe347b28fe254b13053fc22e4ae3", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0519b060a183f426c47c74561219779e29347c69d3b51910ed4b79fb0ba9aca8", - "0x049fc4b63fceac087d70bf823ce543d11cda3991a494824dc40ca66a8d6d49c0", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x068af44505ffe94e26652f548ae1319aa989f59f8efe43952d7b33c6e3bac277", - "0x009949cecb638169b38c98df2147dc476f96bb503dced20e40acb7d3e3cc0ded", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0670286486e8dda3dc66b0ed3149be7697d3e06c8279844079daa7e42d5af728", - "0x026becabe7430380c56e320f5ae3329569cae7b0af06fd5327ee23979d200eb0", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x06f3efe2d20646cc6e806ef82f8f2f130173ccf94686fa7cbc4c962ef16b1b45", - "0x055015d129700edee2a352ed976655c05c09eed935152b89a5cba022030abd05", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0239c80cdf883fd008cf25b5add33dcf7093035e460052c99f84bd25f9ca1d76", - "0x022e4ee53720e10c5c1296b2bec17a4d52d56b086ca78c8a090e91ad73c54d6b", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x058e0b033f76f5833dd48a12b23d100011e04c507b88a83cbc093b5a57ff1167", - "0x02d3ed5b8698128f533d67c6ba6a28c1f50d0fd53b72c6a8939d331412bab946", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x052afea921387b3463ea450060c4c30f5f9d9afffbcf0f9c28dcb2bc14ba6cdc", - "0x03fda9f9ca9b9840bc3fd186ba2fdbb45f8be60f966c2b131efcfa6ee2e8ae51", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x025b98f27cd574a32078c99ea97c8d6a1a06d7e86c45d6ff0bd45574d4d851bf", - "0x07f035b3a6438b2f7ce7b1636d55987f6bb634c119cef3df144c4540ed12c1a6", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x01ac708b03df8eb469da19f0db6c20ba49152b3c2d36fe97c8f17c67e0a7ac98", - "0x029e686736becb59e5b945f235d0c0ef5f93ef800bd144bfc09e702f3d873c57", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x01b6835d1eb2a092e256965ecf72910dcce98f0fd266021296b91e93f718a0ca", - "0x00b85fd80a6a422fd9fd05a746ceed661728d90a6650d0ccc5c1f71f8226c304", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x03ef448df33a4394c43e93e5850cd0c5a6dcb18ae1cd865d00fe8ede9336a9f5", - "0x056711f6ab7e0e4f7365ac34e284ac2879f40208c46f6febcc1dcf7146ecf015", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x04b63fc130288e92f2d6ba238caa7a6364804e29829ac037c57df32fbf762bc3", - "0x01eb8c80af55278b4113286c038fff2bfad2da62763bb03426506b869139da0e", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0239033445549ba6cdd16832e9a1fcad3173b950fc400ffd4074721244e1d389", - "0x01d352dc5e2c354a08ed973cd8f381542bc3a17946a53d10ac7236d1e7ca488f", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x04e7e998557b29a95f805a6e2e26efc1e970108272d4755738c04f28572295c0", - "0x0097cfcc2f447bde61bde71049d8200a74a3028b21703bc139143d81a3623f09", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0521199f9446fa5d400b26435a0a168d0d3742b20a5f18de81fd04e3b81e4cf8", - "0x07cbf600a847e8b9c5b66ecc148409d81cbdefa075414b05b031bd8b7aefd185", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x001f43845a2d8c93cc2bf5becc94510c8fd4f2e683a22d6f7d7938967be3d196", - "0x005e98376cf19793c9d416ae241d441fcf8f0b3edc559573e63d79c3d00903b9", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x029d28ae549b22987b41d92dab8b01e7dbbb246fe68a1e0d6698eec0cb952f61", - "0x04d2bf6aad6108f969db49579167a0a606e368d509677fac5b8dd3e30fa83b22", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0574b67898f02964c408f68e9470e7b615be037e40b824e6617f89cb56c21219", - "0x049392d5f8e6740a1b0b7444f56d7a17363f8656c6e4c628678c86223f2e46c8", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x078ea7321924e2ec59229cdebd581112d64d8853248a68930533477f76ec150d", - "0x0258e8bc39de0bf3d409c42f214e7e12e2fa57aff0023d995205c08c842eede2", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x03ee62bb150966cd8e4c311bb0d44aff011cffe3a1fe9b0138c6940a6d39c4c2", - "0x06267246353880cb8bd575593070bfc4cafe070efd44c5abbc2f1007b4915cc4", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x01a1376586c9cad5996152a2c91e66487b78b49779ba222a1e56027b3b452521", - "0x0058614d4a2593b3f345a8a93e1d7f0884ad91924ec53b8f67b2d3bc67012103", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x02ea507b8f9626a7bbb31f57da7acced896776b243de80fa4171dbdc0d364616", - "0x0553bb634865ebb75217ead801db3a3eb9d2a657923f9c047f5ce04cda31d6d7", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x04cb4c44c846d16756a832f94d0cb3b34255fd0af1acdd8afa6e2a139900ea5b", - "0x02e4a6000a2b0558568d521573a17b3b4d0352a7ba65854fb8247feb029eaff5", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x05717689f7852539acb02bffda0b4a4f5d94dbad8c102919640316d4c8908e29", - "0x01b042f9bd3da92b2781375832a7124b83ff4387e2ef05eb3a71f0b024ef13bc", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0737e12d5cfa91f74b566eafd2e1e983237df9b5e801810cf7e5df439e0717fa", - "0x01506afe2ecf878cca82b1ab6c4aac0a89d81d6a503defcad62b41d0b51d08d3", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x07e8cb50ea5d5c1b09e219e7305bcb601d99b6d7185b1c388aa8e36fe1e56554", - "0x047fefa308645455c12ccb5817da338f0c4f423b341aff4a9d158891a4fd69ba", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x067266dea9e71b4ed2bf24a597a823dd048cf31e725db511edceac72998c9ef6", - "0x0039babd65850befde1f7c28e41dbdbb4caf82bbcf3bcb5b33161f1c2960b2d8", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0428e9e475a6e9cfc7dff82c61736a59493213532d7b5fe156bc938f5351c95a", - "0x02e5329d24947f6d4b33d93f6727e6296500b32bba4efd8b6b9e8fd77a42d36a", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x063e99c2cb9c74eb9227d48065e27abb8f606df8fc83b2c44e4ea38b046bad2b", - "0x0060494a53dd13ecf34e08079d343c88fb655d6d810785af81f08d5aa9bcdcf9", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x015bc22fa74be34529d89e9caa3447c8aa69b59876f5c246b172fde0e633a804", - "0x04b060da7352bae45339126507f06de98e338869ab7af88290eaf05fec133ca5", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0581210824055a3abab598b09969b9c7ee5e1647a0829296b494dc9a5d5d8790", - "0x002c3bea3a5a48b28edea978527a034b3d0eedf31b5e2f84bc219e4eb4f87091", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x04154df9c2e7cb35dd9406bb5abef8fdc604a878645fdfcaa20c74e0f90ac26c", - "0x0232d1195108b296641da56468c5f966c0747446000e6a1349118fddf0a73b42", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x03cf0600b0f5a2a4eb78c487cd385350e8c7848e3f6983231881d7f1bbe28543", - "0x056dee4288528de609976ef6b903b652127c37b0590e91a2fdbebc3f11df2628", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x000cb84502bb9f4d522687fb257a294ba567777709db826c9bbff2279efb547b", - "0x05ba8245f44f9ed7d910adbd1612fbe741e4c877ecc3a1a1a7b278cd96b5bda4", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x070cb9810272a4b0f942f633fba3803e5fb8bc0bfd13486ff2d972ea75b443e6", - "0x07d74d480975481b74055b6df39779e94b9962b4025e119c762bb8886a777226", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x034e5a569337b4f739505bb37f71c338fcd9d2c0b44b77f33d77c32dff812221", - "0x036d152a8c4621a21c0b3d29182083c2039ae18a77bfce79a56fccbf6294a4ee", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x00cc765de9b91f130842acdc9359394c914f3b295e5090a48aeb70b55dd79d70", - "0x04153f4dc6bf533f42e74f0ea7e4746f71521f46ba551a0677eed8751e2bf6d9", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0118250c46e57fe8b35a0a491b1033b1f20de59a06abeb56cb6654ba07a2d049", - "0x016cd5be1a5b84e79bee29d6ffb0a591e6f780dccf409654d1f39dd30bff7bc7", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x079e1ba69cff094a869ca36b47c370062be11d3ca6529f36505394f6a822c2aa", - "0x0327cc3c2def161f2f8c5165c465f9900fa14c8dce3baa1b74d1debe8a30e2e5", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x043c260e99112734f558d9fe7e6ca1ef8c9a64f881e0fd9b480c899bff94750f", - "0x06465cd19d30d38e42426829e012f9adfcf2cd319e01b2d92f8784d17ff635aa", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0758f09245fa4b8b23d290ee2b3bfcede199b4fdb11f3cf2502a8ceedd61b129", - "0x0622d9baadfde781e985d9722e0a04715666769a4cc7a9bea0b96d6386be1746", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x038e1a45b81492aa95d7abea2b08b8c14dc0b8a41108b036871fb737910ae18c", - "0x0145c611262656385e5ed6243568cd3f9f59dbfed7a01ba11e22bb8bb272e08e", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x03bf6bff466cf5c43854a9373c81c31628165be11694ef1d6701e3342a4150bf", - "0x031c1af03ee713ee15b08838a9147a6fa56a15eedd15f5687a1df41f9bfe100c", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0206e54ca53a2f155bd4fc45bf2edb77798ae6623defd4cf22f2dd4a7d119dad", - "0x06c94e7f0825ad81680e4cdbcaaaf4df806d57a0d1fb2331926c3fe2b79d22e8", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0302a4ba23137ad9bccbfccb328dc25b8e1173e097b59810d79f207cfdf7ef6b", - "0x006c952eb7fb3f585322f8d6fd133f6b78303b191618a189d6ecf7decf966412", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0106a9c62c5405334ed27a16ed12341ab2c17e913a05ba001cda623b06d88adb", - "0x0158427080f8ce96d15a3128ed44e4f54e23419eb8626601d270ba52df8fe942", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x04a6aa904f920990b1ae1ade15b37a3de3f93e0fdad78e6cb4fc262edeec939f", - "0x030071f5b6c44b91d0fb0be4200580393b7eaa2db8eb52c550413b8f78e0a008", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0056e98d2862893caebf66180e84badf19ffc8b53041eaaa313ae7286a8fac3d", - "0x0526306f9c01afd6e0c1198ea5de17630f5a39c4ecd02d8e6f0d613c355995c6", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0384acdf41ad76c51f898f1b8e66f6fc984b43f70a0303fae6847ed28eecd6c7", - "0x04ceeebe5412944e58b9d16a5a8b1a0bf6b90a872e52d52ff8c2214137e8ec56", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x07e28dc225059412f4f6a2379038e7c00ffe6053a05bf41fd6c0c2bb653d083e", - "0x065ceccb9843608d93a1295920c8bddd8e964740b432e83bfb40aae90895ee74", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x05f29957de1b7dea65521a1e8328fb263952bcb5e749bff3fa07c7c7326d9134", - "0x047184c0bdc7dd4f44816eb3791cf8d6b757876abd3d9badd5cbe9060a90ac82", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x042a869b5b30304833d084c561ae7531aba65dba7f03acfc9c71f61e7910fd2e", - "0x02778f94f220608443d0f6fecd1c09118a7e6edd72929c7421317abeb6eba5cf", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0704d2c3675084df3e661f548142c2d3fb8d6bfbf13a2f52ea53019fd2e96388", - "0x02805669a0e5e23fe38f58fd2b70e0e102c499d91f239881d8c635e75eacfef7", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x04e09727e95d09ae53f9036380e0bbd64e4078688eb8013a79366fe59d65986a", - "0x033478da9435044a5f47d2dbfbf0b015e575916f4f32751be1a433450e5516ca", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x01e52b3479c49a28a60362bcf8f0e607462f8d50fca5d4050c036d56ab6d51e3", - "0x0056992cf01cf709780853a8696eb929fcf5bce93eead6638f36d6d931c7996e", - ), - ] -} - -pub const fn points_p2() -> [ShortWeierstrassProjectivePoint; 15] { - [ - StarkCurve::from_affine_hex_string_unchecked( - "0x04fa56f376c83db33f9dab2656558f3399099ec1de5e3018b7a6932dba8aa378", - "0x03fa0984c931c9e38113e0c0e47e4401562761f92a7a23b45168f4e80ff5b54d", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0450cfaadfecdb8a2fbd4b95c44cb1db723ee5ac9677c9c188b3d7c8eff4ca58", - "0x01a552bdfc0c81be734f1f6ca9a6dd3ab4daa61c11fb53ebb7046eee25d617c7", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x05f3c42968a2445f7784eca32c75bd399e9a455b96cdeeac3fb1e4c4b7d83c50", - "0x07199b045fbce7d84b22893681d2ea2ef18ac5eae7cc54d3206bbdddb8849b5b", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x06fe20e5c8a8004e33eafc84d16ef770f2f0b7bace19adaaa150f987d295a34d", - "0x028a35040a2ebe9a14a162d3208d5eabc6e2f3a8310f926bd80be65aa71775e2", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x016133bb52fbae14156cf016efff8fdd75ca7015430f57c435c2ff037422bc09", - "0x0512ff1fef37e3d58a1e440c5b657564eec7e28423cbf485e101647d0a98bb9b", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x07b538804570a7441a0ae68824b42b2bbd131f5136f3aa20462538f2be66cee6", - "0x05a43a2f1cd9473c474eb7cd16145e663d0ec7d6d6615d08b971e473301f906e", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x062241bd2e386591860d327f584fdbe23a527310158b45e70a182a73366f6e6a", - "0x035b798367c75565fd2992d8116cd7b319587fb860e91f367309e7f49c3d1068", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x01bd65f45a35bf62ae8f9ffcbd7de2976b90518b6820c219f039c50043bb1edf", - "0x00fb5f0f8659f9b6ed7cb0ddd7999506d0c20b26bbe69d1915a31842cfac41eb", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x003c5bff101300681fbf53fbcd5d2c6336e2bccaac0f50bc13d5762a3a0fd1fd", - "0x047cbfb37b195d96a8d6e97fd77f5d4d021372d1182c31053a97cdc382b5507e", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x05c1babb3bc6c9523c8fbe21c7b2f47e78872fec5dc8d7ad9059999e61688858", - "0x03b26d5842e25a83658936c2f9c73006e8cb38d5ade1c12a019bc15ae5d8a540", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0388b9b148376f3798ab8a98e5841e51af6d0776010d1cb1ab79fe581d836f02", - "0x06b1fc368952a8a3ba7a2e7b002fccefbd502268ad3a45a2bccf773420797dd3", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x051f7cc598d932c162dc4feabc484e032122f13f0c23ef1e2683c147d763fc86", - "0x0292f639305626362e677e5ad5520674d996012c6dfe9f3c4f96e607892ad73a", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0698ea44aef29dccf0ca89c3e0bb993a8f2d1d8c080c2c9475ae0f8116ba9aa0", - "0x03ddcd75da2cfe6f55dc28e76ebd22bcae65153297f9c8c09e93b958688623dd", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x023650abad25aebb28499719de43930a68af8c9fdb76fec5e4e52b319c7fceb9", - "0x031664dcdedc69ddac2aaa4eeb3afef71c3f1dc0dcfd9ee78cbace6f7c897d24", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x05106cb5ad3f20feed8426e97c2bd3fe9f13e2d0c69536700d14fd041e5d2a9d", - "0x079c66396d43746dfc11afc1446bcce12ab0078226db534dc6642a6f1b8f750d", - ), - ] -} - -pub const fn points_p3() -> [ShortWeierstrassProjectivePoint; 930] { - [ - StarkCurve::from_affine_hex_string_unchecked( - "0x04ba4cc166be8dec764910f75b45f74b40c690c74709e90f3aa372f0bd2d6997", - "0x0040301cf5c1751f4b971e46c4ede85fcac5c59a5ce5ae7c48151f27b24b219c", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x021cfbc678f5a279ebb6ed124273c8df37eaf12a2d04180403ae6b5ec0b1e1ef", - "0x04478ed6a346d899ad7b0b10350270aad39ddd5b68529297e4c91a54357f0a7f", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x04dec38409d5dfa77f4ca0a06c15426874cbe0cbc6ea99142650e0686187835a", - "0x048807c844c79d243a4bab8ba138e983fef7560fa3f2a0763bb42f96d8df9d6c", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0350bfefbe3d864eaadac9cc1195c14159bb736be743aed7380d2384cadd2046", - "0x05e2a4b3ad0e1d7b9b8ef72b10d68a80e5ee691d7db591fcfbaad6240d41da8b", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x019e6016dde5ddffc7e7f0bc62a6d8b2449c350b1fd840ccf884c8ec50e02c19", - "0x059af2e3bd3001fb9204ddf012dfc235d3ad98cce0baff76e1b5b9f4619b0d3d", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x00efe19f848cb8e36d2f616fecef330e1b623af75aaaaf2e11d292a64eda55ec", - "0x023ad1628d291b58a643958bf7cd5b98ccf79c24fcaa9eb078b7d961e34bdc0b", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x07814f68038950e432aff0cef8e96db5e4527a84cf05993c1e1d240bc7a0e6b9", - "0x0572872b92a17a89d5a2683f227b4770cda6257aefa7ce8fcf14f406739960d6", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0529acd569127f73c8d34345f87e96cebfb48ee12a00a3861cda209337ed94e6", - "0x003120671a89b705e5bfd99b0e7fd2118b4914a3ac309b3d74527cacb5ad7491", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0304f0eb6b09116061646f88e8a7721c656716c96910633d18e3c0756189877f", - "0x027ae2fd82000986a20fa0206eabf4b1b7ad5453dbb6c61dc6d13abceb49112b", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x02101df17e022d3d0c125e8fca21268c512cc2c0f85477b42914ab1f4cf1cd8f", - "0x07d825bb842cc4f72cb6d230d4d7f28c635205c7857470bb72059c580538d670", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0660cc21392a3befa682f428644662749be4393f65a16a7618d09946c58ffe84", - "0x035ce3168f6d250dfab31726d06d40dd5cf95e1371496a0471f83ebd4f809b70", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x052e9229d27d295d6ff60ec4778d701b4d128814b266bdf88be36e43735ac6a8", - "0x0655ec98747fa55c63ddac1b84eac72a5993268802c1056c185dec406838fc7f", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x037b591892f3493f6d0513bcc535e03321359555c87249955899babc04a06845", - "0x024c4904a48358bfe7ffb2ebe06c89952e1b6b912ba646c166dd766f5692c386", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x034c93ecb083343ede2a6083fe14b3d9244160f1796d80b2576acc8276fbf0ea", - "0x009d5dd7ce78850029b7adeb6a402993b64eff29f129977d3d1960a0a154de48", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x05ed0e3fb9cd1367a1148d1ba9f0c9f83f59886499de433833312a05cdf5afe8", - "0x0457078a1b1168fad9859d5d21173e1b601b4a7bd203390ee298a08cbcdd5ddf", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x055d3d7956a97d10e65a4d8ffeba40deaf0db0b57f8e022cdb3df6df613f5c6d", - "0x0159e59a6f92f48fcf85aa96c1a03749a4c4e2cf9e2bc94dd36796daebd9b8b9", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0405f019ee8f2e972a005c549b0884b5051f63d1e78480b73208dc07d8c65a1f", - "0x04301a3d0c285ad309ff24a12c100ead7f48ba1368143712f32ac141ab4d9e8d", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x004a3a53db1f9b9da9081b4aee2073c7621b3ea3e06a0afbd312802f6af2b612", - "0x01e51ff9719903acbd54bdd956980b21531f9392f1932e9c80123f27b47b5883", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0376d59b298d982f02dccad0edd5bbd4e5e8fad7898750675ed0856850a7babe", - "0x05233b12bbc50564eb61cc098a17d3d97f06ec7a230380e4c5d3b725cc318eba", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x041f40e1cd7cf3527052219197c3d298470d78798fb0df1b1326a1c5f336ead0", - "0x00c4e8c3c95c83107325cbf0d916ac4c639fe0509fc07e3cdeebfb1c61494cf1", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x010c9eb3842bd85b01e1f52736e6e177d38b537eefafeb808d59367afaed0e89", - "0x011fb431eaa96cfac413899040e10611cc2bf8973dde41d3e83608d4af4a0f4d", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0548a0ce82097f8a4ec3388ac2987143e1d250878886beb3d551483659a6d763", - "0x026317a88f7055099b51feb102f57181d122908210c65169c5bea4683ffe1371", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x02f55624af6109ef04b2ed035a44a904ace8627f55889f011f768aabf4de9a38", - "0x07f64209ce7dfb63337ccf3d8c14f4093295f86996cabfee23b1655549aca089", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0418bed0918cfe3322317668e820d25b6bc78962ec5f55b5c7e11a1b88de23ee", - "0x073b10b241574ee4318c5026084b11054321bffadcc42c98c09a218c33275a14", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x03c4fe3b6c73cc17a200f5a4b8706dfcf2d91722c93d4cfd2ccaf40145fd2e2b", - "0x0139692d8a1d9d39cda0f209d7700185f8b0f75bf84f24378b4ea39f1a29c855", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0742001cdef8cbcf4eb9c804b75be16e990a265956f141983172193b8779b4dd", - "0x02d626fc4750300560c321172ec01964d3b5f93e631a87d8cda92cecd5fd564e", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x013ce745617e5c71273a73e2464150a2be427202b391b6c01a32b91595689660", - "0x02b8b4dee326ec51aa3b76dc79f98050a6386a01ec2e06071b9c5326c8e7b57f", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x053c4b17a2b539c6a887276dd1d9463a0cdf4e888a215768b9a4148e2ed64e0d", - "0x044f48ba14d14bb381a547f66fb58676c19be8761d34f00b52d94efbf69c1364", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x075fede304ee6293c97470c326ceae4ca168f566eba8a15f17c260b2be1d6980", - "0x0451a66bf37550bc4f592d04a8cdd2e754f4cc827513cb9b5dc12d9ae11b3221", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x07c8a1c03451fb029b3fd3114ebe650f54bbd520e7b1e0d0798e2cca9cf2ec1f", - "0x044e2a33de1c4e0b455c64540f77f2ed95817c27a1d4cac86d646f5fb5b43880", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x03b8965e942bed2714bc2e685fb103496e1e3595ac6a343d6df45fb5ef6979ed", - "0x05b7cac7a165cb69ae103dd9052fb39c00ed0aad47989005aee53972d82d45b5", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x07abfe3accdec1eae1a50049efdd9a8eb7c2921a08e8bf1fe606e9d5a4039ec4", - "0x03af178e7e831f8148244d2d2b284a32991852db6212ad0a9d77540ef648a5fe", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0794fc1d8e830e2f4182ef3dd6b41685ab7d85a06c7716fe4eec0d0c060e89ba", - "0x0042a20a755edfd403c1d228895db56b980d5c342e0c01c4bc7d8be25f54b979", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x004983196df6ad7d6f0a8d76f86af3863ad8611374a03fc0fd00793181dbde9d", - "0x0204c1f91b70f975a21d24a8face664e496f00f602daaafa69a3b56098a4cf89", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x05c36cc51a2930800753eab7cd35277c8b4a2ac14d3cb77e32befd481a0bc5ba", - "0x00bdaa1c3ba1e4278c4108e03f9a90a822792983d9f5c26c49d6402c3c290183", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x010ef4b6c1285d98fdd0fe7f864232eb5d47e38640eff4446e45a67785f7c328", - "0x03b677e28c8c02c7efdfea02fd49abdcb2abbb24f0b34ec5606fd7e9b928e0a0", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0663226ed3db0a6c2a779d8e2b1de45b4800090b84eca8bcd6ba6d9cc47cb009", - "0x0072ad587d2603a0ac6060b87f2fb77b06e66e2d25b2d17806d96b0a87a32d54", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x079e2b91c1531a3b16dbd53e72d94e16bf265cbec261658151acfaea3718ea72", - "0x03d9bdb47e8b148c1c5e9e694ffbc2cf71aac74ae1a85e8d8c3f77e580f962eb", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x06e5cb8b0bb52870189e94e9c7c0d703bb08769d66cf0b4bdb33ad7e8fe7f4da", - "0x050b26e2ae3a8b70d9aa791beee8447910557170a4c60310332fb20062651acf", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x007f19a8e17b4e687c9a5205df4c69f1307187cc3f20dc7001bbfe52053684c4", - "0x011e63df803fabe7527105605ea85944d8600c398f6e6d2572392f69085e0d3b", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0782a55f8a32c94b46dde8241fe8e4f51b68eb40307f103587fbb7258ee0cf39", - "0x028313026a74f835a08e84872999486258cd384f6ee6bd56fe9b2a027cb26849", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0302691b7d41fc2d3686bd46a94845c0c5cb1c54117c1efaae5e4398421bbd55", - "0x06ed8532eef856e173800652bb6b48802ae14b4f96e24709cfa6724dfcf9e4eb", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x04bc2083470c9df21669c3e31f49aacfe21d516c78d332c3d311322193ed70aa", - "0x038cd1630dda3d20c3cb5de34e67b3cd4b5094ce1ef71af88ffab8f6403897a1", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0010ee4dffd9bf4d0fbdee753fd0a04cae23211db39b2ec2a6d0d742b896accf", - "0x03cb4bcbd4c95ab4b1d7a6a11ecca0fb18432dbc412ce4ecf3d6fb3e3fed5853", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x01a418a71ff9f85580631ca6b5dfa138e3264b7990b8737159267051fef81681", - "0x00a0fb1f9cc1f3b9f0f43c99da31524c13083d50d615f3743c61af8e55a24d67", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0297efceec61b3be17565843cae465c52524b4ecd9331a4170f54f7de8c4556c", - "0x06ccef1733624cc8b973ac63dd54e7a53604929affe81c3439525ae5ed6af993", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x044f04b1966264a23ccdc870c8563ad2efcd4c8087b5469b90e792287a5581c7", - "0x01c417f0e9829fa3d3cbb7c3cf4dc7aac04c5bf66ff3f86b833a42c533aed1fc", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x05dc4584988cbfcc33f3e44af8e7bad9aa648b99756459a1698d19dac0efd46d", - "0x04b9a9aaabc2cd918f9408b16c02a0a342ce1219189e76ea771ae7e15025445f", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x06ff83f5d8b51db3be0bda80eed2e2adb7037f2f58f705e88f0f98197431ac26", - "0x064f59b8428894c2b7afd740866065ded42e716c7d48accd3f117f22768ed9fd", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x019da6c65b8bde1ede2fd0a7cf8335333b7078b3a209cac17e7bc118a8cc6977", - "0x042d6ec6092eccd2c7606af389a7a60573021ce675586f288bab5d86b3de5711", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x03492095a2562f7056fd844f9b750017e5364386e821978186fc3d3f5e06044a", - "0x07c8e782407d703e5813b3582fdbeb606a993bd0a78d3f8fcaacda63e405d7d9", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x05e49cbaa5d643915dce7ce34c4b9be9094098fb0a79e76dfded0d27bafbcfc8", - "0x06b97baa9a1e2b83d63721813878aaf93f08dd7305b611fe32227c1c67e078a5", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x014aa8187c9559f77cd1cf96b2dfc949182529936f2b0b4050ea56e134073b24", - "0x05f36508c68b1dc586f3fd3f4e2bd29c6d8258491b8a6aa19ede811ce0d3d0a1", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0392721dce24417d439835e6189cc7cb0b4c7d87607323f43a9d49e87f468f1e", - "0x06f920028379ca332b57819cda9aa5c450ad2299d71a38506324ae8da76bf372", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0126c25ba42d272b1bc55666a0dcd24c63b3ff81aeee1e76835685fe47e60202", - "0x072f98d116acf388dc9953693c9cddac4c2a26f2aad3aa2e8346b405fe2a3318", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x02c35f6890d2ae7cb854c03dcf56e7f4c8fd9c57b39f5096623f451f24fedfc8", - "0x01c1890164ace4a805869a40d7a85d58fcd962f8900348d8aea6ca79f5335c98", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x04ff71b18f39cf7dc7eff0f119d2e452566ef0057c6850721e0bd2631464cc45", - "0x07208b6b9b541702e766e39c168e706cab97a95cce50b49ba3732f92eda0810d", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0457650d1c9037944f7bcd1982a0fe91bc8dffd438d5b4b59c6ef2e3b49ddc27", - "0x06fa885219734eac81707e715d49ab8aef7d1fe7d84729183e461a6cba318f83", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0477297e9ec0aefc4b5037d448fadb7b7845d1249d7c7f474ce29a29786ffe2a", - "0x018b36ad5b02c6c189bc89613ff3d82ce1dc809ac6cd18c25607c71978830e29", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x07c25236998ea2fc7b4dfe6d26d182c0da7ecc535b16df77426bebc454aff8d3", - "0x03002a12ce4bc624be6dc48a4f0494df50cc5421de416e5f762dbc72ba5087de", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0095e8882a68c5000d1c2be7c0b43e7f2a6f8de906485241f0285a5c73a27a83", - "0x01e4cb67207ab73bc1e5d19fa2146fde6d03021393b77a55df4ddda1fd28f5b1", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x02ae0704dacb3da47d564514b4c3543505b403ba09a248c6e74593cba1867ff5", - "0x05a4b5818088dc9ef4066b90a8893ae80fc89584f987ec1928ef9d72cea2bd67", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x052a9102d7634cdd5ec71edae5ef7f59c55e9cbc77ec63d07b27d3e05eb2255a", - "0x00edf49af004ebc4cba54bc4df23e3eb1e24b52c66d54308cc8986f86eb9b71d", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x061a10898a76fb99989e51c0e823cb60b95ec7ccccb917c42b2b28014f5fd94d", - "0x023d8ec1de45366d3b86c64c2da05a2ce3d171adf52ca5522e652ffd0eeee795", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x07c68ec77ab515d4bac862a4b90e1fc0ef32c9da28e24c9ffe74ba9f4565ab24", - "0x069cad35c20cc963bb46aee91eca0b7b299e000eabacf30a6996821b82af5803", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x05663a91851318a04e98f52440f6f1461455e61909e943bf5a7891a0902dbfb5", - "0x03445d4f7e77e36696cbaedca0a47c020fbc433f27aaebf4c42b39e0f88b1575", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x07a4874656c722093e4208525c37ddae4577d8e3ac7e1a5004514d576b67d226", - "0x008fed46e8bd3d3f4a4ebc2b1fac16ce2ea79902ee0e6df2a5d201fae91e4e23", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x079884133c879cf07734976fd64de220c5a972e04c2a3afb74c362d6c3beecbf", - "0x02aaa0e6d4891b792b5643fdf09873343cd0e3fbba3cbd0601b481a4083f32b6", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x03f508fe9b4a43425789c8f53e7b93c1e378ded92d1b7837ea54f3d12b5225c1", - "0x00c4d7536903f82252d5b89a8a3001dbc1f03db7aecb73eed546b272cf7ab8a3", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x03f9686e24827d7f1c2d30b665a5d1f27edf570cf6df5d3ba5cd2b12f769c9e5", - "0x01201efce49c7ec51aa9170d4b81ee363cfe5a98931ad29cba7baffdcc7694a4", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x037ed78121c5b646af197d8f3078f8846af79ec7838fcb43b0d1d236ebcb5b3f", - "0x05615d468379ed19bcd56dfd14d714c18ba22864bc09b2f0afbf8a5f0e193387", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x06e08b2acfe06aa8464877cff567e0bd75ef81e4719291badf7ab703f5635b6b", - "0x0258b4a2c5eebfbb92535e63909f066a6b6574b33b2f00468394bdd934b2bfdd", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0585c61ac3cc992056b23d8d9567e3da0044333aed49549e40005f9c3a97468c", - "0x033533784c46a6c1d288650edd49e00a2ef73df4031b0404cf46ce2bbfd6da56", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x02fb835007cc824401ea1ab04e8627066a142c9752a1767d58ce3b4f4f0736a4", - "0x00dffb8a39aa9576af43b24cd2494342b20efda29fef9fd40f8401f767aeb4e8", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x05f1142d250cd5e64caa2618681b4010d35c3d13e82d5c5f3edbde05fdf55966", - "0x02cb54492d2b5c2fd0f2b4ddcb69cedba054381490fe4783e02366bc39d58fd3", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x045f73d2fa82be6c5ccd0f62d2237efe8727c479967d27cce28e42b9a44bad5b", - "0x02fa4932215f72d56d8be5205c5851c9b3e5f2a14468e4a7acace5437c6b27dd", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x037f53f771850f52f9c8f87b53c6bf0c93c2bed76f5fd1d5697356d0b2325007", - "0x050f1a052b79b446fbc7b93ffa1a4515f6c3be3a76a2b0bc5eb8ff327549960c", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0595aae3eeff3da2adae71ed984a66b6a1340426c2d76c3b7c25d5f25f886201", - "0x0658abdbb349b9a1bbfb966ffc2f22f5bcb13cd408f7da9a3fcda987881028d7", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x071bd6d23e0d2f312d47582efa609101f15b9ccc571fca8ac4fe3457c67fbc9b", - "0x03b3fdf86bd4c7fc26d60540a6439b4d179dcbf7b91efb0ddc60dfbff9a148c6", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x05f35b6962a25bdc293bcdb2dba373c4922d7265357d837ba7b6229c3256c4c0", - "0x03243985db4540b453d94dfbb81ca571290fb1217ba7c5fc423e55221b93ddd8", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x05be11112b9312febf8183b9a0f66434281447f525438c25640187a4d19e6b27", - "0x07b610e898e8fef237bb4a80f23f21e9b015ee86b89d47f7b7b905655244d591", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x01970db181ee595110297c223257017378069666c88c2ee770e0f62af10c6e0b", - "0x0254fbcbf0f8beec2d56a13df037d888eaf82136cd0aa743b03cd10f21eb20f4", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x078219ba049438385b829c13a4993874a4a326c4143de0dd581c7b9956f99b06", - "0x05505f1268dcdd4ee01b77abac3bfdcbf3f0513ab097c69ff777b4a631aaf256", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x018805b9c8377e95bbb6b5d934e1a10f78e74469caf6ec2bd47c535cd5498e26", - "0x01f6ce8c91fda451d38a6fab4a6eb66986d0e3bfeb3b0e17190200883b8caa8a", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x058e529584d6d7ee726754da514cf147c80dcf79f24013b37c54257d7b3e84f0", - "0x001d8d14a75a43719dacf70ce24e5ba6b7b58660895c974483ffcab319c0a5e0", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x01b968d76a94d38228ff5eefebe51cf628030be3034bdadb287de61fc6c7be92", - "0x025995753fbf161c9bf7bfc5873c82c3770488142132719c5baea6b25f9f9c54", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x02dfcf10c0b304288087a4b4fa5dc483ede39cce6b1b0f57c1943dc67485558d", - "0x03d6a3a696b1548b7a2f7b70a4a97e0d0ed57c790d9d310b67c0245ca91bc73d", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0700d752eca6ef16c1d5642c5f5e760601387f22715aa1259608ca00b07158f2", - "0x0259a841ef4c066645233371b4c42938a1e36b3fed9b6776dc8586af884992d6", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x00bba1e79a8b8fa7175424b7d98bec32b73aa6d5d2c996a00c28b7eafadd57c1", - "0x01d9def7a03c3828c833a77b79cdc439c1c3564ccb600eb1af22db5c6687606b", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x01157f24135a5e8294e654996fbe020b1e2a9f166813d5a8917944ffe088fad8", - "0x06596ae5aca2e72e368112b738af2e82e9140be8a48abc30f0979aa9c9f7da46", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x00b81e924a86536dcf68bc5a2ca2065a61103ba6c9eb0ae4cf8cce9dbe286f15", - "0x0653a6dfb51acfe8a844fb8362795e5549d424aed88d3a090366a44f840b5b83", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0441c0d7b7aa705046dc0e07ba5f33a7d9df23f694a05192ff8c2d7be2aa3fdc", - "0x04c06568c0902bb99d428bfa0a946ed0f0ca0a51fbf07cad88e06e9c78e38a59", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x03728639d7cf8eebfbc002a79b328a463ff24601183321240acadcc9587e3767", - "0x0671aa472daa74f5993f9992b4a82475b338cb7152fadc2601841c22285d6b1e", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x02569c8c78b6d6b92533f29f767c95720d377fa63ad5a3b9827ee0a74b0488aa", - "0x04b59c81d3cfe08834f946d9d57614f5366e0bcd9349475aaaebe01341196fe0", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x00f6d665342b80627ddcb069b61ecabaaf8f5b213442a00b51739ffe39b4ed0c", - "0x02f9fc8ef2fd66b51170f471ec06e21b87ff4d810fafecbbe44e323a295d5245", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0001e5ca1d1ce63423cda48ff8978d27908ec52c86b8437e847fefe80a2fe766", - "0x06d7f1588f9166487e42a63a973a03880d4fcaa56b7a49af283e0c611a2f2a1e", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x034983ecf4a4e7cd23703493ce702c1b1cf5c28976b954d2250e9143035f0ade", - "0x079c3fd94e3896f2e41e4ecdf1bbb15cdb86f67e5f421b46e37985c771f6a616", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x03f2fa285a0471647b214eac652bbad9d58a9f2dd2e812aff0210d0d8a6eb32f", - "0x04cdb18e1c2848c2b52c1a6557165bd1a8f55c2f7562f5cc0b326f73c25b696c", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0214ea0dfbb162fd437ca7bb7bf7d6ea9a5924df8666fb9ee2aef21fff4fca94", - "0x049a0e19daf78ab1b0648cc5bc48b245d39c269e7025c9ac3d2a8a5403266541", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x00db5bdab2baeed2b87b0269c2047853e5b6dd84ae6ae57f79350cbfcf80fc39", - "0x04b052085533263ac1ea1545780a0c24d212bda42edd26abc9c11826f5327883", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0380aebf6666de3b6f5fd8ec8530ad175eb963b35764cfffe0c8caa6636a6083", - "0x00b92de03383e48625af0a70fbecf4db732212a19ee310eb0a0d870433fee3f4", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x00cb423b34696fb300374349ef4239196492ec2efb7b9c273412fa65d521f59d", - "0x0719f279ee42c4618731d74637ba537fe43e251c342964d86f55b96ff3a3f325", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0594fe9f6e79a24e348766e8f18e12b9657f97b37f36382f8e80d8771a83f04e", - "0x0287eee382fae17d61002f1124d6c5573bbff0ec55fc32c15bc68ce0b4ed97c1", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x016b7942b9394d9041ee1e797bcd7130202849eb688a637ea35498ff52c9b1fd", - "0x01ac4a56e6ab7c0e51a19e5ee3e41b88052a4639e0bdd2f5c6f08a4043c9b1f7", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x07145415c6ce8287a8219eeba64f96691ea0c951c6bb6fc7bcd9fc3d37ee5387", - "0x05a2930527a2e7ffb29a2502d108f9e3e5842cb3c072fa3367bacdc43bd03a48", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x05bb5141ab4fcc5290ae9151b8045a2cd8391547ce7b3b33cbbb10f8fb538092", - "0x05a36bfd52acc6a83a9913b937ec086cc27fed030b5fa70dbc5d3c12c9515f56", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x003f3fed272edf91aa7f8ca5d70005d390fbc67830ffc69c5fa3ae17582d2771", - "0x0459057e0883c44d8776fa217405f443e5954f08c4a5db68e437becaa664a999", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x00e3955ba718bc1d4b50eae9a2ed33644c041737ebd736b249f47f4a405201d7", - "0x025e1b48486661d8c38be7728ef8f732a86be0cf58fa7bddde88fa6176154011", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x05237ca6656237a717a739a4509f70db1b9dedbb6cd232f60c9bd8c4563a6b1f", - "0x056c7799dd02896dbe7d69dd8bb9718270549592099569d107b7b49c34bf5a49", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x05fb749e9f9405a16549d00d561872238afc21080d3fbe0bbce2f530dcda950a", - "0x05d2d48b6d12c72519cf8d38d49a9ad1e765000eecd4e295cf2cde0487451a8a", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x07940ed9f12f77e45d5b668dbed8069ba8d83592ef0f204cbbfa2c2fbd8cc1ae", - "0x059c53213e1a5a58ea78381203df409ce0faf39f3cf9786374e2ee5ed6b87ce3", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x030d942fd3fe0bd92fa4de9b46f8eb71709f208aa07e44ff0540d8196e83708c", - "0x078074a76b5965fb635465a13086a2aef4d2220cded3003948e96e50d6835a86", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x01cf6b8499ac881e0b2fc7def9bc1a28937033b2fc52de99e75909a620c7a281", - "0x05769cf4f735366fa386b6858043dc99a100f86fbc77b16d57d77766197ba27a", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x05f8de2f9c8fcf6045100b1d6900b8b6f3177fa5420bcdbc0a68d61e224f8ab4", - "0x073caf2f738970e95cae1d2e248f836729ba04eef9d8fdf15aed43d5f3d4832e", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x03698a5e6cc83b6fb4d819b9e1a112700275ae53c55ad4ad23cbfc35b6a306f7", - "0x0339c7154689dd8cb0ef6b86131d64efc9670d67885a536078a649dea001350a", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x04ed313f3baa288e51e69a2fada972bb7b315819446645dba7a67a857d6a2850", - "0x031e42c11a16dc9ca7135a25d6e9a050d43b13a5f54991d3095391370879a42b", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x00b2e1f52cde371df78821dbfcafa437d60d271ddef9434bcbc409c46052d394", - "0x0362af7b4d8a941cbff9d17546d2d54e7f60e685a2929e587034f8720dbca5b6", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x077ecc001a1986145dfb5ec99765f2a3c2dd0a91f09f7fb84182b0604b894e3e", - "0x0758db97f799a9fe0a74baf42359a94843dc6a1101ad223d3d9cdcb465391bf3", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x062e91224aa6e13ff6f552f962c71e768e7dd327b17cb41f43c4424e3732e3f4", - "0x03839967aa18168952fb6333fbb1def6fbb2f3ba8338eb7ebf30daccd11b4499", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x04074c5ce88f6521a26bb855dff1336a67c391e32560d8e0e08d4f2fd842c58b", - "0x046563301af63b5a56414499f3afb1c14f21273891b15be45ac6e1c023ff6fcc", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x01b74b8a6b86dbf9638cdb0601e1a332b8d880753423d38c3394902c57f15e40", - "0x06bb2dc10d2ecbb913219d0ebdc8d3337d644ed8b6c4e70637ef4c7e50887488", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x061e4da415661bba52a4737e2bcde1a837787c4796b2e1854778534f1582c29b", - "0x027c43e632cb7652e8508c9c38e3b4ad0d3dd6ba748d42dc84ec2685e64b9aad", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x04ceb49d6ea67777518845e768d9099ff63f1d385e0460237d4ea2d1c409c20e", - "0x0784f7046c1ff3641c5758621b33e070481b8756c730cf1c11996477d67b3647", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x07c460a204d23f20ce86596dae6ac9b36734e4a9f7c5b43262c97a36c6a41c6e", - "0x0481a11f9300ab4c4bf6924c5ca884728cc361247377065920966785d043fbbf", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0316e41a001dab34cc2196a5ffad1e33be174e85d538d2c27fe7b1db2360df3e", - "0x034a01bc3eda96cc2561ad828f99a178a8692c5ce313706dbdd58193cfb95faf", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0759aabe4f4690d005e6b4b3091842a1d9254f8e5e5db6ca9eb7854ec2bb1de2", - "0x028ccf1ac0ae95b72ac2e2865da2e4d95fdfa91ca0a52387ae601eb56b27c4c1", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x01adef58ab446968fa727b46f8bc251a2f50bb7b8f85bf930dbb170fdb823616", - "0x059977dc2c4f2401d99a8361fb5b9021956c342c5019d3acc07b4f5b48f101f1", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0124ff5e55e4effa40daa5b9618d75c49c8b6fad95cbe8c0bfdd83cb9bed8316", - "0x033a2ea15d0f71f58a00de71acd7f22ccf9002115e49dd1f7631faa0d32f9987", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0717fc14712a3dcb1a658f3d2d1220411dbda54f0a99ca199d9a77cb728afa3f", - "0x03d2f2be108b9018f522271e64d7eb81f590db2e7381b64c44b2b51803c0e7f1", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x041603d7260f1bf0b5bd9b1dd6cf113aeaa2a243e26e78474d18539cfa15d293", - "0x07c0a2900502f33f1388b1bbd9590d16f010412f42aa7e6e984ca119289fe705", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x04b1681c44c44aab7b7511aaf4f1c99b1dd50fb060966ea6eb219271b1c2a528", - "0x00f9b801493f02c463001bbd3cf049aa449d1f455ec7f5a205f665e143530a30", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x02525bb94b8339dedb194025db9caf7ff468ab2a7c88ae32b3fdac7462bb2198", - "0x00dd2887cd3169a98a76502c324665f78df35170f8d4e8ecdd5062624754843a", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0008f6eb23a88507d5fcdcf483599fa9540803bf633904859e19cdca674b08c0", - "0x065aa8947a971309c13ebd7c66f21b1a6fc0e8e7694cf109e1235b0ec08ebf52", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0387fd28efe4e0cba41ab3a99be2d4ceb9082bf51d3ad702956acc3de2752553", - "0x065a97eab03d453ba028fc908ba4ba093fddeda36c5d18eaa15435e93c44647b", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0170e087d168b741ccbf9f9d0d72bcb8e5c6259e89ecedef709f70d837200fe3", - "0x0213f42d633f8ae52683ebecc40e4ce63fe696c22ac0a6ca82c190e819ba6c7f", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x061c9f8fc86715e95ff43583a865c5a6515f93381839d557ef884a68637eaf4c", - "0x05877daaa42bbab9083b571e12648a9d62ced4470d71653092b6546f4a5acceb", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x070a6b9a9e5d1fcc07dd9ebef6d8f5fcf04c6cb34932d0fe2335330ac6dc8d3d", - "0x003f0cbd332ac56922e886656bee74f6e9bb4bb88f7af7bba9098678af1f38fc", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0739fd7af848404a91fc63890db1117183de6febc061764d9caa6a569e4287c3", - "0x077911ad848b26fb4bc1f806d170febdf16544aafda4af9dadcb38044f6dcc5d", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x041db8a0f1ea78443a39e08a54323743c8897eed1ddc28f41aec6f2655040d9f", - "0x07d4bf32f8f4719c2e4af8b7889f3b65cfdd033dc2f971798a12170f2b26efce", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0159e2b2e323d484a7675e1e3ebdd579cdc386e3c65ccee80c742c6742d543ff", - "0x02e511a7903bf0869f7bb670b79095fcb0c3dc13d7167427d7e855abb2ce51ca", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x05cea50f0d4178ffb718f7e4c1e42aa34d30bcce6e552302d47e74ede2d9ddd5", - "0x06529f4ebf630bfa3d449213ad7f6f7a052f7ead64676ef6d2a6003811bbdd00", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x06c59f931da4afa684e7de19a00aa6af64c8f21b548f2b98d5161365248bdc30", - "0x05d0dd17421ab33d2d2085a78144af93a5bb8f70f06e44c66b9a50279bf17e14", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x00062f035e01acdfe841104942d6c8c07f0fbd618cb85998ea24bcc24cfac1f8", - "0x01caa886104b7d753fda93645a746989794cd825c62473b526ea34b3d51b5771", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x006b541e895fee53f6e9f37fd1e0dc287a3afb0585682e983214b8c83c2e259e", - "0x00ee12c9dbeccf9be03b893349f5be20cfad96abc7ff04358cf7ab3d9f9009cf", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0169f96807f4ab71cbef3cc922e5cae880ba86cc6a07f79ac070925d318bd6f4", - "0x04d19f0ff1e6f6ea77b43d84384397953062235e09e0842994f3472b2109a608", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x012fdc0e005f89db96707421317247234001b03f354c65317479d474b8e2cf23", - "0x04e654668f2752517a06bdb29283ed42d3a0aebd4b38b198c366f8139970cddf", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x021016c3f4a04b5a114abfb28b8f78036a22729165c9e03819db7dc7143b4b03", - "0x066c99a8ae9171bc602b4fd226ebf0206d1c42a9908528d18f646a472bef5043", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0644046c819e1098d25efb3001f7cf0a49d110620dac15c66bd49bbe02efc478", - "0x0709e88424ea2679173ef3df367c057e4d513ba356c6f6a5397806bb40a638c9", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x075a4f8e18ea05a26c68dab86da57f5f19630613c416af913ea24a47eae5b830", - "0x02e2b8706783f9712c6f41eb4f60ad9ecfcb9d326cb2e6c54c9ea4908e9834c5", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x04e438503af6a0b82d98156f5ff38fc5f1b0944adb2d6807eb8514ed41f53a0f", - "0x02821130b45f532ad531463fd8ec07ca86ec513e2b78a50881e1c1b6719ac5a5", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0441c6f016d270e86c19843727b83b864cec060cafc813b23d7e41e5abb1a60a", - "0x029fece4e40400f3acae0586f4fc8ed535e805e472123ec38d662d8a0b01c086", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x02c791ba0fb0b66177815c98191fa6188dba9c795e34a7c3c8a19086215e3cee", - "0x011123151389d4b330db6a665a560407e7cd8c3807c749e2b0cffd9c3074ba77", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x02f0abc241d61b901a8348e4e109dded5fd1ecb13c19a7cd053ed3e3eec12b28", - "0x01d2a9b55626ec6904202ff176923dbfad7bf4ed4e03822cbddafde156b77e2e", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x05292da4ca71ae75ed0554c267747e39c7a129b3b863e1af3ebb3e368439c4ea", - "0x063af6a5016deea8cc674c44f16c63c1db31f09af4fb4d2ea7917c28116661fc", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x071649676550675f2bf10141bbe9ab21614f2ef59e8f0e71b416db801021a4f0", - "0x02d77359eac272be257b8e264af49eb8ced935ce3e8cbe215c3229f8390a4450", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x062e6f34672dffb96325cdbb2c77eedcc00b9ec6edbb6d43b58744c9816e9089", - "0x0762e7a7e25f8e7496e6170722848eb44ae5a059ba6d442b60ae2313fbdff970", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x00804c254f5a01dbbcec99491b4aa7b4ae2ffd3382c5d5b86c24f73bfe8f0650", - "0x01765a69e51ca3fa2de05b32159237a7a8d2822c7c08dd76c8c5437546de54e6", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x03367388d5d1b7758dc3d92e244f227bb8a54e3d9909e7b7dd62ab5965e3efc7", - "0x07ffb4833071e4b03ea755ccb9938487a478248fe9b1158a08f1ac298801c092", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x020a064c7395c76cd5b26e05f444e8976c44d68e1b1953b78dcfc181185cf830", - "0x033011dfe2b9788b973344248519b14a193263db8178f9ede6c99ecda896ccd1", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x024ced8f62cc5106dc96b89b02d2e4ca9d9b7c52f4df379533d72db68d74c72b", - "0x0110175b6ff45651c89650285231f270f4f01ed507bdb44d2317a25a6759205d", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x04294e72376b0bd4c9a0140979a4231ff802219afada3be76d3d606a6a565343", - "0x02b036f2f62efa2dda4c6e607f247655e88dcc3762846863e5674d40f1fe35a3", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x00892c8ba47a582f663e4372414017c6abbf94d38cff76c774bcda2b175d2f66", - "0x01392a012b69525a9e00fca0c6848523efd63c66299a884fae1e50323abef0c1", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x03b1d6c1294ee4863a24217440f9e23e2871c9fd544ae95e9eaae9dac55c3923", - "0x0436520ddf289b25ce699caa3f2006474d2fe2c853a0e4b50b60eda24811985b", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x07a6c3f4c7a4ff83500313505241fc1b8fce63e885ec8ba2d72f45fb0cc4a3d1", - "0x04343bb67e7022025b3d097727fafbcf518c2ae0a84b5aea761a3fa95947ec75", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0172535754b3bd4b319a52a3c5589f1ba5de87cf6d7099ff661eb30639d6f61c", - "0x068b064bd40b15b12ed2975f734aa181d91572d2e0b873c9cb6b0ae3107747a9", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0095c863314b7f18090f8eee602403be823a367a1b416d54c32e5f914e67d922", - "0x0159c2824f899171deee23e0ed520d4825bd667983df0a8d45d3a1f7156d91f9", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0621c6e08b3c57404644ad49ac7629832c141273fa1f323781b3395393fe985c", - "0x065d1eb0140652958c4371ebec791e03317d6b2e689d90e304666f1b610783dd", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x041b7777aa32943d95add393c1499a667430b222e644902af5af8b7178df48f5", - "0x0769135182534902c9848d7b4a38cd3e876639a288760f9228555d6fc3882f18", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x054313129bf13993952cd2b31ed06013aba85e74c1b8a00e062031f32188a84e", - "0x0680129efc9eb8ec07fc180e8f6877e5f0f9f44e3000a2c586ed4ce49d12a313", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x02b513b910f25c66a041e07a6a93b9991fe2dac8069eccfba1f1bf8814e97146", - "0x07208fa7df0c2a562648e33a17b57f436758bbf60d6dadeccdba3f0a0a7d47e8", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0516a8512e7a5592ff0aa93c0ce7121047357ee0b5b42dd5d3c5cc248c6994ce", - "0x01973ea1f0021ac44cd870290b6f8c995e454aafd86f38ea88613fd13638595a", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x008dbd47f7352d90f73608a7df59af329e62b24d082240e8ebf0929a2ed7a5ba", - "0x06945dc36b81dd1f46a78d5af3b3ef04377a67970f2068f673c4a17794ac7297", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0021ea57a1c8286bb45872e78617853c47b89091670ba51c124afa3362e7260d", - "0x07087e5c1536df233ec9bfe2f983e8d7622892b9bf64c450c9823898e2cc2fc8", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x03d3bf4cf73db45b5dcfa9c96464ad4f96ea37b5bffd4eb5ad8872d1048b54a8", - "0x01ecd83a43ff5ef05c599592e857850251085939889cf68cea8aaf5bef7c30d3", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x02d84eafef737e8e8d42e419fc64b279cadc660b4406a3ade9543284a169b235", - "0x063e43a876bcbf00f3aa6b5b7cb31d5c8b0e7dfa9d36f8e62d994aad2fd4b7e7", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x02d22e1af66c7e876263afdec03835165eda1d528093493b986f7e85a07afae5", - "0x01bdb69904278aa70502845292bec0dea986f5dff4cee952504e1e133b248ffa", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0289b37bca81aeabc77b1a36d3421e14bb5c87cbea062ec336449aa2b807f81f", - "0x04f72ac8b842499c729f88a690c129fe1bd9e8acb954d1f3ecdff39523ea5d81", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x00d3f48e575f4f36b6e5442c9a8a6f03544a00a677ad95e8f1b109cd463a14bc", - "0x031957ed6f127945cd8ee5d7e0b5627db6d894a02a9cf0ac9c8ee7378748811a", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x02854289de6f8e0e63f4c1d0bb60a1491401ef72d9db342acf1b4e344c09c00e", - "0x03ff63a45d44c2aed954aedeb7f2320c979bb63f965b561d620f91759293882a", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x03fa2852a7c7f4782b5889ba2f60a403b99d2b0ecada8c2d7f72c0af708b6e94", - "0x002c99b4d2005a1e5f2013aa8a398b26b8e4f8f209c5b248e59b011f35b6936c", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x03793b05b99e7a57d88db4ed0dbc3b771285abcd9052da50f88595354409f3f3", - "0x012164105041c056f127e737c7cd63981e05f246bd2b6b65d1f427019c7c3801", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x000befd345cef5fcae22ac37dacd6b9128cc58cbba3e3fd774e11b421c2ba392", - "0x06209d25f24f88f7876ca604db23d05f78e6b3b67fb033f2f1bee221f352b8c8", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x05cad32be449c82850a4007d3b3c4b7ea28e22172075746e00e6f5365093b979", - "0x033e0aa796ec42e28941a9f79f7b74abb2f714eab6136728700a542f8115921e", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x015fa536045fda4c65ff74f10b4e669ce88b9996c6772288289d3ad725987fa6", - "0x030e0c2124a35e265e931ccc66ce5ac3697d982814beb407144ff6762cb691df", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x06d480c6d389b792b68f21e41f717f828ddb529a17281f09ed044668ad2c32c1", - "0x0344fad8d68d430117f6fa9a72a6f39e57f7938536bfcd65ddff5cb267757503", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x013aea09a0f7b4514c5dfa3a4f72f6dbe05662ffd780e2eca7483ccba1d2f173", - "0x016bed3a91a3da10f5edd537fb6702a4f975111bc360300f5250ab2859a324bc", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x00eace861a8db6bdbf106cc80370854b39ebabcc24b2f803cffd9cc21e97de59", - "0x002f6c20793153f7cf40c6103f9708e1e589a767e89a2fd24ce96bc6bc465d37", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x038b795bd77ac573576dc204857a488cac2cce19809882631ca2069598c577c8", - "0x0786ba555d55ebef688b068bb9186a34a08cb00bdfef51619bbf911890ae9a13", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0173cd1aa758920be5961068bb592fcc42861380472c80ad00289db52f15723a", - "0x03636d38557af345abb47c77f68fa9bff95f0c6d9520db375585cd4679f71391", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x07b3d7e2364440691e4fa6fee5c2199f1d853aadb4992c20bfa9abc2ec10f557", - "0x02778b1959387a2a91a539e503ec59aa35d042344680a7dfaf7c2bbc851d5037", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x054f2da545ac99aa172926ce71483937b107c90ca1e752da043dd77cdb1a687f", - "0x076ac79ce43918f1f5c5847ccfd2a30f43b3fe54a54e3bd2131e28c467d7d1ae", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0177e16f8cc0dfb7e49a42591383b53e914c2cecf5b49230bc5d4c5b672e8444", - "0x0732658defe4d9b4e3ba28eb24ab0f392f2497a7c8062d744dcb7350678a2265", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x05d1cfd8ab18f6cf9cd47cc6240c373b8913d7bfaf129e1a64c4fd2b0d2ab80e", - "0x025d7bcd17eb185eb1e4ec0d0261935a41c59cbdbc99031547f99edf26c7e956", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0282a6c8145ce35184f6227df76eb668dd740a4993c370342b31a954ec2b25e6", - "0x06b5aea7261b85b75e114fed14d76685170eb7e8c60a42d3e74507df913e522b", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x03914ce111868d81d8cb2e87715dc00f8272f8e6cd6d909138ecabfce292090b", - "0x0096dc27fd0ff7bcaa6b91a014f3185f19cb3d676650f25dce44b75daa830310", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x06c66853592196c3eb8d9526dc155205e2c64097adf8684bb0e15eb460ce1c72", - "0x01bb4ebf654f4250c8dd1061a4e1b464b31a8a9999ac9960446ef8108a66871a", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x05b08dfbc87ad9c00b88e78816973ad2f9c10c70f2156908892cc7b7a2a1fd30", - "0x01151f407a77e2556073173d8f5c9ff561d8a23742121ca15f7d0ac391af50ea", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x067a24b09dee0bb902ad5434d3a806671d3a7224ddd1671c537e9fbbb16d4bbb", - "0x05b1b3e8203f239afecc5d5d61f61f8797444b77f3ef087961b88be942acb4c1", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0309190eba106aa6ead54b5ca5817969aa68b4b4c627700799a49fc6bdd32ba1", - "0x0505b6a2bc7b0d78ca6ce2abe7dfb7312369918a4599cccf8a615f6701cfd851", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x002c1ff0aca0d3f74a56d9abe58130a0a0c7007e1155ec6c1b04625008b79fdd", - "0x01914edfa6ab70419c2a930913b75e71f78dfec19f136efa3644501e09cd3f42", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x06d77f24ddb891e2031ee521f591cc4c8a86609b63e8b6c850a98a02b30b58a4", - "0x0474140abfdb05ed935ae0a64bb2675502f11eaff78340d79629864f908a7cb5", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x00f123a294e185d6d6c3156cca2d4cb7516d70279fb2f099c7fbae1a9e4ab770", - "0x026b84939e7aad8ac077cdc5f2d9fcac10c69a787b285479b32adf60f3602f89", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0089cc205966af08acc8910d563af7443d5dfbb5d88dae79c013c678c65dcecc", - "0x01f8cf955694b246a423ac725791231257b88936e00347ecaa1e17045c0ab540", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x02912aea3d45f7b5b945fb13921f27f06bd8a81369aa4faa5fe33bbd60d6493c", - "0x076b3864d28f94c9b785ae3985e508b37bdb887db52921ade33dc776f8fa0460", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x07e17a6136bc3388c6fc1d63d598e27fe05d2225747a86eb8bcf6833789d0eca", - "0x01ad5f31e1194615d3a76ab3194435be709e2a987cd716f077fdc6a997576d08", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x05732c263a4f3f8a898a4a05b4bda4248cac361ebda93bc43965643405c2c558", - "0x008e3c8b16edc7b9d805ba26b7a8c9f6884499a8daa6223ce71dcde8bc53bf83", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0385b5587c464c693887d511531e76e9c248d9e74d05e495167d2e0ce2a02840", - "0x044ac3a9befbc5513f68dd35a42c8e3d1b401e0c4000317c48373617eff49118", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x051c918ca73965795e944ee1aeb194f43e1aa34c12adc0e01e779b42df6503bb", - "0x031239df2fc80758b318b8fb8108ea1ad77acdf0d2face94bb70460591610816", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x00b68b8d3dced3585eae8b820aaaa45fa2522edefdaae7b3d48a4a557c467f70", - "0x048e9889eaa63f65e1ee5db89d7290188e01e2721e36140fa2c70176657dae3f", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0137b0d71e68f57690cbf5a2357a729501a2b90a89a58ff96ce85a6a7274e5bc", - "0x020793d55a72449765d0a846fa4aab0ba479fa165c650a4c9eecd6a51a7a3ca1", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0480086b61a80c36cf1e1a350baf554e58ee8d9333186b70c9c512fb9e9d5a84", - "0x0511edfe58f8d36a6170df743731da1ff525cfd5108be20e30ac4183d1281570", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x03caf14fb1d2e90a13ad4eb091250fe37133aabf6029633e905e5a93ead41dbb", - "0x049122aff6059dfda19e4b973aba5ebe3804c91728936c6381c1ed1ea9380920", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x01e5ff67f17a15734714c64ac2cb8b65e071e9e53b73b20e0a63921138fff4c7", - "0x06855c48df6b8e729da7a709d7cfe41c5b01f583e00a276c434c869b9d56a5fb", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x066d1b8fb2cabc46cd79741ce1cb7326077ad8ea3227a6427244bdd3806bdadd", - "0x04a52eb74f4d5371ba3265dffd61c844f9e68d4ff0b44dc4936182f9280bb66b", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x05b8c55df79a4bce0bec0e694914e6014a5d4b37d163346853ce59823b5226ae", - "0x021248280c0c0641433a108cd46a76569313fe73b394730e2a4eda43ba0ce803", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x07faf291d2d8b98612d2ee1fd82ddf022d706570d03282ab5e518e86b56c89b5", - "0x025f7baae2e8cf4c85cf27dd9f852dadc01c59cbb1c6e4c1f4152b3d0a20167d", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x03b0bbdd094d53a35ec8d9c8fc5fdddcbd03337d35c21cd77141d187f0c1c010", - "0x06b73f732c08eaeb8e0507c9227acedea85d87eb3fad2f1437c3c2b0db1f749a", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0373330c5afd53c31257fcc9050fef873e15ea9f81d9810f30744309b04e02b3", - "0x05889806607b3dc97a9c5b0c8a2f16d1792099a22866b879ca480cb89a11ef5c", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x00c628bf6f8b2f79b4fcd28392dc35d884f4fb3809e0f2bfa49df7a1dc781fe6", - "0x0003286c40fb6b534247c6dbd8a95fbbec1288b74d6495adf1857edfdb26c29a", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0379861d375911d79cd19cbd91092e14edd21a1821d07b92e64d0169734b947f", - "0x001a01962f1577f85d233f9b7826c268fd180f0f382a977e7ce5235cee1ac79d", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x05b3fb1821541a27363330fd2bf4a7f2080a3b8295febeaaa5b720bba2dd45c6", - "0x049c81e666c2175a45fd694e6b436ab039f8460d90c1b020f2dc7604382b009e", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x01483c8245f046f274da2dccd69892bc487e66e7fad66374064ba33e75bc25d6", - "0x0328e0457e0e610ead3668053de23b3d5c2aaf81255808321a6426d702384ca2", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x063373d7e3f10c3db5244df000ec9403a759f0d738b7ee5070abcbf48d4f21a5", - "0x05465cab97ae98af1eac9860a4bf46e86462dc8e0c5fff9062ce9470984225ba", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x05cac64f66aec9739875e532847c7a059621be0d3bf5f23fdfc0646158f9c641", - "0x079139808cd23b4eef3257008cbf06dcfed06f0a23518180e26f9f167689d958", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x03ed77e0f6b221013d7e1bcdba9beb814a392fc26e3dc26e9d8968364b44359f", - "0x017857dd5a6ea1283e68491045fe13ede7c340de9b394934c93ab8ec234686fd", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x026840d0ec69a22c6818ff64b8b14633b531508c866e21d1dc9239778ae9e8c7", - "0x0157971f9a6e3a24d3b307be0e7c8cd352e2eb5cad33cf276270c0f309ee63fc", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x00ebb84848f1c38c19a754d1b5d9460e39624dadbb30800987c9419c0f933b9f", - "0x0517b297cf32f4064e6d6c8e761ba8db89809604a701c7b3aa1a9c6beb370ea7", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x00036759c37a28db31a8e3c42442f602f4583b64e550adb36a3ba9250fcd44be", - "0x01c2e0dcf12987c70189b7dfd3d4295f4cac0d034b8aa92ac27c2a3a5f04f4cf", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x025780380bc0795ed0dca727c55240f1d63593e552d224adb40df2d3721c0f66", - "0x010215fb5a893e0275e9f1f66b217dde35addee91ed0e8f7d79531a2ff57b8c8", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x00b2accfe81760ca7dbb0579dfd7633b1939684ba449e9c3aee248cd5292f919", - "0x074b9b81fd6a8be878587ac2af9913c56cd59b694b8ca6c040311cb20f75a366", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x02ba2c29c985429d9147aa8a44cf985aebd0d35ca75b03c4caccb2fa57ad7f0b", - "0x0548a9cb38c13ae2ede1e234208c3a807fa35c3631c363f4a6ae4fc291ccb753", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x03320f28107b2f3a7cbd90c8924efe42281a8046685200d787d9e532969b0ba3", - "0x0427b5d3c8c520e718a58b375229b2f3c53214240863d911fe784209c6816f27", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0243e1581cd1abfbf18c31c19a4c3d1cedfe69a40bb57b607c9af2717eefc742", - "0x01296c27929f14535718c3a4ebe045f00afdc60afc74c7d398d8ce1b6609dc0f", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x05405558e37b9d40a867367ff5b1027fd424b131ed33428c404cb79350927f28", - "0x017a38e1941307c9f989d3a8f3446ad1a9335ed6abb87b87de170cd3e4feeefc", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x051fce5c2d263bf18ffe6794d81e178843d6cc6da1cbd3e2138fb265be64962e", - "0x02d5a1f98c2ce81c6375617f88edce8f7bd4f9868933db43a00a64b73881e00f", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x017337999536b145f48c0941dde2c9e0ed472e94e6190eeba3793f4c61c6885d", - "0x01432c153dbf79c9f38523ba954a4456ecca800f45e0301273aec193839c8578", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x03fc9057f7e2cfce39b11d9b776ec041f8cfe34a4493db17c4c68ae862794916", - "0x03c921ceac3d986cd550bd5918ebab5d0d7c3828207fea97746bc92eee7bd19c", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x06ed213087ef501c36f27065e2262f2b9eb2b4d965d30bf75e806927929cff38", - "0x011968bd42ea3c62991079e419bee9cd333ef062120e24d2692157345aa81bd9", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0081a35ff2db5857c0c69c4d67a252b603adc2d72d42ef12df200a0bb2adbe00", - "0x01923d321fae7747c508051e84ba72926bc957b46e3b185aa6bf114ecee29615", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x060a77e1743f09308dbda0c3fc59aa5fd3c391ef9f8902139a76aee73a70301e", - "0x0037aa9778a5c1ba0e0520a0bae7edc3e66b3e6b37be8146a434f8cbf174652c", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x048babb8649e054bc8e0b902c89e6940c265f48464520649502ef1064eb94562", - "0x03235be7852b0526d1a16f6969ec0e5b0e09cedaadc65863dea4e47f4f398264", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0592db7c27e63489ef4bcef2eafce89f40067cd9a1ba48bc3dc76b5fc62ad9ca", - "0x048b7711b570cd9ac65910e75e752f4b751fdbfb4091a28f59b8c046d3d9f8bc", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x02a291d461523e3b02c74ec54712a467b04903d0470e139c4f0c747e1634346d", - "0x07f52d5124172a406074503d8101a25fda945fbac97c70cc4748005ad099195f", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x031d133456222586ae42a9ec7ce8539ee04afbe0b2ed00a2564dab0798d9b55d", - "0x00a77c52fa1fd718db5c83e7fda6d7d4d9aafef9ad95cad621470f2b753729e5", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x01dfa2bab5955e74de7b5bd827fde466d48fbbebbb56dbfa05a258c6640fe2b3", - "0x04dc9e5dd8a8a1378d75de98091f8e5a6d977156e357264753f22e998dbbc52e", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x031d6e6ad963214c1dae3888291a8297fea6a67dae4eb3f8be5d2b7a0d6164ae", - "0x076f7283f03053bae75ad5e6e91110d753e064999cb2d2d0031b93dc52abe8b8", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x042dae32e58d27693d9dd1d9d4762a88e87830962c123f7babed9a653f3d59fe", - "0x0041ee7846bd1dbf4c12a217da3786f52cbedd4d042efb541bf41447a36cfb65", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x04651668379883521e7983aafcb93811b4a72ef2975b3277773746708ef3e3fc", - "0x0512507f3f544d80ba5d47f73b571881e8d70d7b1d305b9704bdad036b7abc47", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0588f0bc31b7ae0ebc138cc71391366bab7f3605806213f65599af79f6566765", - "0x06ecc22cea23c1a0277beea58192f33b25da4ad6ddb1e0be85b19317cb279326", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x06dcecfa12506355201b5b32499869013985e27c94c5fe3682b4bba582785226", - "0x06a21c3ec3238c93bd19c3474f7a317e1bd72230cd41b840864c6ecbdc736997", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x031ba85e3450e3fcf88ec91f99e23b50cb1aeae90d13558574aa0c31d9ef8375", - "0x0579fb170e8c2550442bc58dd13c27884702246864d5476b04a1489452d222e9", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x04429f11789162d86545ee142d280955eef51713efb298983e42ce965f8b4e4c", - "0x0206583aadae1bbd45bd8d29d789dfd2ddfd4a7194e6feb2b4ad607a8c4376a6", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x06e5a5991f756775b9f5ff04bdb4e384b686a2bcbe982fab58c4224fc593b00d", - "0x0128662c953b22fc1b9cca844e41a849bc266e966c7ecc00632c1d00b3c161af", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x018500d60c537342f068addb42703d3146d41afb3c4ac257b9b27bea5002db4a", - "0x012b1f12393525ca065c5051f71958f6ada62d6cf1d4122756d5ef184fd7c24a", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x07b9a2d1a8c4bd9845f8c87f22678592c841b183b84378b6ef9418b5b1475726", - "0x0436b2a3e8b2cbc0f0446d3a8a34ba996eb2040b68a83a0debb801e78239d53f", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x026069e359b2e847affaef604f772f36224608b7642245d0e643889ed231bddc", - "0x075ae1ec379f074ebc91270077c74b4d34347ce183b676b4dbe100bfff143b9e", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x03196d01d1fa11dc3803b4813c4bbc6326869f61410f2bd14bc0f570d875aebe", - "0x020313217cac79875bd2a503db1e86d1e5559911667a02524759344468d9561d", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x03fbe35d14b4de3dc76997af6fa1049d07dc478ff753cb908b59fe243c5c6c9a", - "0x07900979e0b2de8766c8f7067ad9c7fffd9eb81f1d4dfe93c220130085ce3700", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0483256607f75f06fb126addc60cadddd602154cc4782bcc08351a48745d0b97", - "0x02950a7e500ebbe9775f08be37cc2e62ccf9030de18948d1bab07a4a9173f75d", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x04786957152f902149e59349a32336c1075c224c96141ed79e5b9097f46bd0bf", - "0x010a6bd4efa286277d929decd51007c075faf769a0cacde005b5824bf920acac", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0304e839c5c036fb15649f8c79402a0350dca2ecbffbadeea10df3be8197ce59", - "0x017ffa0f204094e24fb615ddc064280d9b1d5d13e3ddd64e8ab8da4d3d9c147f", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x030d3f665d6793c5871338dada7d35a8a935726c529e47cc86c2f41bf9468ef6", - "0x0596f13cca6d4fabcc54d73b0205e819baa4e159559744cec728d7d0aed3de97", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x065f07b6050a2fc6eebe2c29ffa62f764060f7f9d3c82d2cb5e4e368aaa442c9", - "0x0562c9654b646cb84a213b41de203c871b3eae0a05c9c105a66a53c319c06373", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x03851ed963f0a72c26648887e2f3668255c9dbda6bff83f19a34a01251756b9f", - "0x07b009a1a54b0417a5b10140eea512d4984fa626530beb2412f3898494bc20a8", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0073d3d1d3d4d20ca50a5d8771bf6f049dae5d54a4347a423006cee8deb32173", - "0x046de16660a2f06b327a4d34536b7cac730465b97cd94cb4f4538ad246164cdd", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0771b6303c6ab96037ca86422edaeaf746042d1221782016410a3109a62e81d1", - "0x0257d719b33f6d3ef05a2ec4daff7dafea6d4451d30eacaf62f4b6506f7e3ef3", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x07720a11436b17f330cddd2dfe9ba42b66a70dedd9a92f2d751850aa337dd740", - "0x02521d91c363b08aec46cf70f328f9c60ceaab2be0a8da60e79868c0af477e7c", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x04681b09948838d1c423b1f9e93d1e43835623fadcafe1926f85348e916ce325", - "0x050738ae3794ba5116fa7170530b7f5b7249319f1b43fdf2c7fe6a3a2cdb1928", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x07b5dfdb0972fcd097c3c39cf72e4f72fc3425ea481422c15da0021bdb4d0705", - "0x037e0029ab002fdf7b1898aef603ebd5820bb96897db53c20d8e26a5441eff20", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x05f9df4ae9aa833b7bc48d6de4bd8dd952d1ab64e45974365deba56e729029f1", - "0x05b9f6468a2514fcf1837a60e5c6257b75119d227aa6d6801384d450f5df9fdb", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0284870f6181c43f3b01d94baa9c5b6ada0deb861145523ad9169580eb7bed35", - "0x05e03e6c40c1cfa3cafb01fd0622349871832a9d35499d06408a83edc1b76d02", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0032229810a52137f0e6c3d37595c46f6132822d4b05f42674b48d7a7ac3ad85", - "0x07babde959a0cf2c53ee59fc52c77c3adf899453f077f441965629f9aead30cd", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x076d0f876ed9d4486460cd0cb29080ae229c357a03ffb5dde47928223d742e2d", - "0x029104a0b8f698f30b94d7d586e78d2a0122f4a43d143826b9b05fb043dd8b8e", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x01ea8b98a6b85e74e0a2fbc18b206e290f3ed94ce99ca665e8e2351dfade990a", - "0x0478e93c4724115fb1648c8d5347422adbc1a0bbf962b2312e14aec80e1be742", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0022490883a28fb1b9642b29c4dd20f61b5aca5f1efe2d73df4e74cee3198da8", - "0x07e87095c97f950ef2b2f58df349007b53a6d6f102208f444019a3b45d7636b7", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x008586b1b5f5d5bce87af6ee001d9ee4d4ccfdd017e30c75bea17dc733540862", - "0x00a5a443afb78dd4a45cb6c0150e009c2e718720d7161238ef38ef7730732575", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0039136e5e45ee3b8cff7084ff8b8cb525eb9048ac785075d51561ffc144a7c1", - "0x02642e9ea6e12ba28b3a66185afdf5a1ec22253986be68463801b1a160496344", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0270cbaa08c79140c85b864475a0bf569cc03ac785e57f543dc444f37ce746cf", - "0x03a9b8d894016680ae9d1bf3deb931d8987d4d8d8bfed45b81ccc595ec79046b", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x038405dc4908a50776c584f2065eadf3cd91c8b505549931c9d781bf7f6303cb", - "0x067f50f671d02cc43c0849220c7affb458d6cd86cb8548713a231156160be965", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0421e9546780efc20075b61f32c8b76ba00956424cc5a7a8b06ff5706abff4ba", - "0x015232929e21d844839daa7565cb0421397e9fed94a3be2eaa2d5e2aa60866a6", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x05adf460eaf1c6ed6be3b086eb7cbdd20fe7cfba975356a93260472a06483c37", - "0x050f0c11054771d816ef4a001992296d8e7392f82d24f6fe4c89673cd520b7e8", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x01d624adb487d389718d1691be787cffaf992d993e27f1af9f373d359fdcbae0", - "0x058bef53b1c1bb6b6624e73b02c0e9802068480395e6e7bcdd71b0b013a8cbe6", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x05eee9e6c1e31bda405bcab0ab7a81f439cfc31db44869e39717fef50ca81cab", - "0x04fe6a9ac7ca39665fcb43450ab02f2197d0d8c95410350fac071124f887e426", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x006ee29b897b49664cf80ef8ddb140f53af3550721a1527c08beccba7cb1e706", - "0x05322f0aab4aa3815e08861d52c668e68d0ad56951ad95eea127274048e62574", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0787e7ab7c02036711355c331439e80d940ff2c9a2d3d888ba17a561fc6a22f0", - "0x0102673a0e4131d90ce88c1c4e910adfdde78ee7f88e4e0512fdabf08b7e4fd1", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x06943922708b8ae5b40dd7031ef2e487abc4ac39a3591368285e83d6c9c51f4d", - "0x05f157c37d09634e8cbfbef90ea50af59815d011e419a691c67ca3402b5efc33", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x048ac6a80979fab4912cf0cb557d917a0bd68825d8658ec100496eaae6ff62e1", - "0x02b6931350ab183402e39476340eb1177b7006f7a552915581e29a79bd7203a0", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x00335de016042ac8754fb87914940a07075a9cbf0eb8a506a86f1b0fbcc89387", - "0x05bd030d293c047136c0e03d60bfc8e71dd53e28387e6ffc5defcb26a6219d8d", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x00e3adf9517d92ef22d1e2a787740a292ba32d5ca69faa9e8675f63ed816dce5", - "0x036bccf69bb12dadd610145a3399213248d193660d8dc90a2e206f23bf2c7997", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x035738b673f82d1777c2e7db5bfe1b7ae3f265afaa26a9ba9fb86fe499d9039a", - "0x01d39577e8212bec38e799e9d331c3a31ab7c97fa23e91dd5499d7443f996cbe", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x05f2585ba667312b2bff9339f8a833d82d6b0f21dd5f78989b819510fbc9ede8", - "0x02a8a59f2f5d0cd9f06b2d988b8630251502fdd101181dc4fb24d3f4f663fa15", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x05b0482ad579465ebaac8b7ef2eb1c05ee131d0b67afb283e6d31aaa0d943c16", - "0x0674d32043c956e73e39e65844bd28bcd625ae29285d156ae484b8a046bd53b1", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x05e6c8ae5afb2fa470f767581f3d578cf6a49547e4b78665edfd45776948bef8", - "0x06cbfc11953dd7e195d2ce74e52a60df524767b44c4608bdd755be4bc85eb74c", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x048623d0f9c6f55692a003783e7e8b371054a0b2793e5c97c9f1272aaabdce0d", - "0x04d0d74d29ab398ceb1be45392ab3ab1ec3c2e60aa01e2d1806887bf73bafaa5", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x00495dd9232e52a4c9d03460ce5bae043ff3eaf16b0c489b0a5c668f30a3c53f", - "0x0285811214abba2f0e71f1763a9e5a525afe2feda54b64313cdb7bfc78ad8a23", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0244292e6ebc387f91de6312e0e259f50e283f828b482b4e5e7d4324a18796cb", - "0x0171ae42810a28e4f5a8ed4982cd98f96726b4ea0c4fdc4b42ff37b7a91f2bfc", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x002e4cb67bec44a3ec81cd631050d567ac6153f0e4c8d24d8943ebf1f44fc5ee", - "0x05b0f3a050c08b10d535dddc01714014b7a53047d1714a81073e65b190549d23", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x029459626300d862cf28ff3f48f8e32ca218753f205012227fb6b80cf99abf64", - "0x022b57e041e9e76b618c9537e079a941d1addc2a9fdb2b7712214dd619b3c631", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x07f27f2d9477f25150ddc010f5ca97df36c04720278f253403e1b4c348e32bee", - "0x04c87b67f6ccd623089fe1ade54bb1d9c646f5722397e9abfc4ae9340e373a3d", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x030736e373eb8c7853c3b053cd2e1d3558f9239474eaa07f86ec77495317315d", - "0x06c821509c1587c0de0d86dd7408c38a3c554628b9979c33ea56b557ca6c5b95", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x015a576a1242d39300f0db3ad770983825988da0457718ecd596c63a0a0eb4a6", - "0x069a42e5f6f5a63349b57683a4609bba90f556a1680fa1ec3b02ee7d3211f903", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0274cd14e4fbf2ed07402e8ad8075b320c5f76b7ea45ea36af523e95ed63ab50", - "0x06ca640f9557c5f2d8b27f6ce95b108880ff4e4816b26b70b6506114389ce656", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0629ff44972a58fa5549b98cd9cd9087ed688a4ff27b8c6913606e0f97b228cc", - "0x03fb8eb904281feb502e20c82ab77b45968c9f990c2fa14eb324b3ee535cda63", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x04d8284e132e2fe81c5f71be1e3c79ab51b229e2c56c323e207cda179999d123", - "0x0116cfc00e9fbee1cf16af6282123cdf20eed13021c2037ef4c86f94eb6e6cba", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x02c6b698701f17c06a6b27cc28e69fa9ce242daac5f99fa3930a9be709b72416", - "0x03406fdd57326ecb8f10cd39a28674cc1a327ed1bbf980a335499ef7c0077d3d", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x046d6508209086f0769547349e80efcb5a7c0422aaa97b29b00a600ffb782848", - "0x0430d660d10ad6679af831f5c2f1160c5f1602609845d92fd4bcdc378b87ba93", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x03660fec31b8bd99f5fd1818c4f2a0091514ebfb52bc714d7361cf93b51ab454", - "0x05a864c33cd115942a0bc590cc23707d65fdd89bb2418ab02e3e107f3ac47fe9", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x04056194fb5643e97991942ef5b63cadd89080bf57a01489c4398aca03f0980a", - "0x02e2cddb434fa6f6da7859c3d518f0ced8795eea043a6c9613fb3e020103339f", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x00a7a8c3b7816756f73d60f285b027dbc0e11d1e57eb9973405c5c338c309666", - "0x04425f9b842f1edeec11d61654f701abd09e1e14c89bf78eefbcf5b0dfdb07f6", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0719a61ce6ab70a26f7a13141e6e9597136a54cb092e044e0a77e06034075ab5", - "0x024b10e38ee13478045cab73fe09316031df88f38f396734aefa2837e4a0f7fb", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x018e086f2ff85bf94cd19aea65cdfdc21407ea132c12e10e687fbf1b1010743b", - "0x0108ca1eeaa979e69ae9373d184deee75ed941599f383f3ff5fbacd055e6c9de", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x03e43067b8c950e6b98f4c4dc215f2df7cff32239e320595fb47d353ce2b4ba1", - "0x0011955e1b65495469616917a1dc8f3265ac8395aefe9865d31d768bb6c1fc9a", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x077ffe5d82baae42fc11a1418524d78835de6377dc574cede264e8a3ee01ff37", - "0x00c8b4dafa79ad57c707a753c85e5a1a28ef44464b872b0447662e94fd51bdc9", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0525d6d4be6fba6bc7f3dbd519ea72992b2b215df6a84af71c36452617ebba6e", - "0x02b1f77cb7f7df2c4a38d7ca6cdf2772692c6db593d761b35da607c1d95c2986", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x03446d5fec558aaf1ad20a6db37e174597f21387e9fec81363e45fde1336a23e", - "0x01a5f33f27b09d8523af4ac9778f4142013ea6a59e0154f97f600fd168577695", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x05d119d5c5ce532afc0875e0ee9b026d878c8773d34237f90a0d0670da6f01b3", - "0x04a79fc025ce076b6a4742fbcc8cad313d0a8220c58024a41a5a674c0947e64b", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x011800ce4061d99b9d53fd4138802335258f7798c5a935c9979f5a949ce1d483", - "0x036745a4741a5c7290eaa8f2a3f9ec955ccb7ca323272e5d35d35c2a724ffac8", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x04f5f49bcf1dbb6a549e90671717f1847e24385b682c1b9a4f834ef131296d56", - "0x001c61d3c6c48dc0b9ed63b96f9b22680cc25fccacbd158b67d1427084edbdb0", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x04302525bceb97fa642fd5560a4a39fba3d2c06f68e6aff3332ff1854439ebb3", - "0x00e31edfd081ce82f8177b2d7d96e69851d09e908c2517114ffb37ee12c0ac64", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x072781ba09df21ba11617060a9689a46d962ef1d84a0a77e2e93525599fa785a", - "0x000c1d281b5fab2f7ab4d20eeaa852bd09816ac562bc776a0307de99bd8ff39d", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0058e95e404048f4bfa65554f7333839a8f84544bd7e58b3d172788d40b53565", - "0x041e5e2f7a7b27f3e64ea4b76dc249751c4f9cdf940dffa7a9ba408906114ad4", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x06c1f528b2757a944ea30805a69034c4cbd99d246ec88d654f988980edcdb990", - "0x050d9ed12e7393b3b5270908d4258467a29260f6f9b27d5a12ede4421ba7a5e2", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x02f5fcbb96f0a66fd3bdfbcc78bda361cb812570f50e7c476533d56eee01c0e3", - "0x0527428a34855b5695c479d8fb7e831a299f7897f36682a74169cc60d160df2d", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x015dbbc6a872ae720cf20ef28281d9da953fc53b6a2aee201b529b4728dc768a", - "0x07fb7ecb7c1cef7eb9f4e8e5ef65c4b62898cf4fabdb25407b64d2d415bb6682", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x03a60bfe56426c431fb68061da7bdc52a523d11cbac619c75cb3274458cb363e", - "0x07a9f7fc212e2f3e9f1cab0b9b02508127b5b44e3e610eadeb1dacc0e3f27472", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x04ac58ba046af84c02d6dfdb45fa3b4cf412f6fac1250f9fd2f77ba6b48eef27", - "0x04c24c36b94c948247bf3013d55bb34b69f824225fcaf6e68abde59fa92322e9", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x015767f416525ab20c40e6b08996c154fb3b494f5b54571a9324b9b757df711f", - "0x0327e0a79c8d857057008ca1d4b8298deec742d4896672b8c7c038fea3c9279a", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x06e6fef073f6194d94de54f1562bb4fff224e46ffdb42cc846c400eeb3793e46", - "0x014bb0764de3ae3d35ce1e51bb5857773c0bff20d3b0030a7c626139891d337c", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x04af3dff0f7fd9f14de1144b1c0f2f321864434d97665abf39273c6395fc7df3", - "0x067fc3e2856b864e1bb96ccc7e40008c3d758826a33d445b7445171572d03f64", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x019dd220b8435e9791ea43456745870170b4e552d36f3a6dbc576130207f97b2", - "0x06db199b0169aca22c2c8f78532204b22f853afc115a40d83aeb5595f6be84f9", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x052167df045ad0dc999b98de3d035aced9da4434211149b8cf4bf20e774580cf", - "0x019051d2a1ad3fab190c5dfaf45188b49b4e90cca22aae54f0a785562d3d3f41", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0541b5332491dbdb2b6f6bccceb7634970c046963891fae936dd950f4432b961", - "0x078fa54da996a51e3a9c06091d58c2405a806649da2bb1f323807c4eec50eda2", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x06488440d63106ce22f779bae9fccd2c4b6ec8c21057d1338b86faa94ad5311c", - "0x062c27acb51112fc6def6e98290db37fb4960b30d3f05f069a2fa1139a92eb90", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x05f11e973da659b7738f87ca5bd4f3bd02207dd3c8d978f0d3e83fe81030febd", - "0x0137aba7027069f62d25caed416e13537687bb1428e71e5f0a0c52d52f2e65bc", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x052e4cec6a1877076cc5b6c7a41e465bc1b7027f9c66edf5b19d21eed270fc56", - "0x01bacd26e904a5aa09062019d6a3b5f97261e215c26b1ec8dcb08bdcc38b054b", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x05e9efa3be3a7497059afdb7199530038f630154c4c7c315dbc505c9b444f010", - "0x03720f92f0677b8211b5a08c525d368d0f1dbafd14d26f88dbfd644aa20a3f1c", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x014300a56b403893e8e704674d6bd44dc4903190cc3e46b7097023f561ae8b68", - "0x04a5515068a1d390e4c6475445509b6faf76197abfb3b700fd2282933c1d5f5b", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x015ec941ee6c2110b819b5541be52981c09d83484c9dc735c43f39f5778718b4", - "0x04561826142dc5b56acfcf605a78a4090472bb61235bcd605a765e05d0a7e549", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x02d49bbd8057cb348ff41c9d36cd7579b32797e1bd4f78fc17476ac406c6e3e0", - "0x063b2b0b25bda6f532821cb16d7096c1f67acc6e52d95cf404ab93ab2c0f2c95", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x027c5cb464c908cc17ecc703cbee04ae771585517f885ee5e65fda880f744e25", - "0x044494a61e69348e1f16f7cd716b981e3d1ea1ac8980b4771d15a5747848868d", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0054fafae112161c6e664c8f88485330fe91af3aaed91cab9fa7fc95409f47d5", - "0x005216db6b0685801414ab643b6a70ced3b8f9d66f1a023f98fe0d63bf491354", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0346319e34155dd8d1834c2043a03f773b605863832ec9274c2cb233f6583b6a", - "0x04d76097e8c49620fe8ca52dccb72c35ac052aa1168b4627328711d09f57bdff", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0708b449cda79653a35336a47fe7859bc278bc9cfcb398e764531fc90c7cdd96", - "0x02d4ce9f498b2aa462b29010886cd3d2d3dd59614643e0bfb8d00915749fd13a", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x022420cf72cc212faff3cc21c22a952ba83860f50f91f2088fe288f19da5b914", - "0x0243ff85318204eebf2d245c53fff8c72b4f13b701adfa3af5ad43f97e4ee093", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x04d2843f85003bd0ec8139463a0ef39243447b27ad9f619d5baa0c3d15c191cc", - "0x0585f2eb1d999fedcafd5c7634b50ef682c69fb1d2d713890a266c5774e65404", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x068ba398736d659522f484406110b43c68158bf4992094acf797a38979c587a4", - "0x07c1d9e1702e28afddf22fed7a7a79df4315c174d0c6c4f4c75bc77d9b56777f", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x067889cea31c81a429fbae643a4fce0ecd690a5c32b99397e39ed6d7a08702df", - "0x07ea277c80b671146c9e455b98f42f45b941ac95ca2d15c8fa9ea82ee9b45e01", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x05bc3c9c27590089e83affe30c56d847665a6d8f609f5d72f2c056335a825712", - "0x005169d9c96cfc10b44b95a2d7b850da492249f3c0a9ea9442c8fa02520199d9", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0596f2c68390ac26505d3c2eca5c77d46f8f3acbed192a2649d8c525a58d2334", - "0x049f3bd8c62c610d5c19c52d970bde24b270c4ff7ae900453b909e72483974a0", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x00eb17d5483462d97842e6092d61f826fcd1153b42e507a581d51059dbf782c4", - "0x0720952ff0d37dbb03dcde5596bc61722815cb5ce62ae25b8d1c6057db1c377a", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0161f66d00b821c9f7559ef6b77e39ff2b723059d1cf622df70de9d6eeadb35e", - "0x045e24d2de389c9fb8e58d724af473a485d5de4a4a9c24a4bf05e035b10375db", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x039ab8f276ebc1b3d3996286814c3d6292f1e9b9b46e3d50d310063b47c1105a", - "0x018b4da223e8546ef4c336a2431491f8d9748b355ebd2ba850b4f56c4c311e28", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0567779fb8b0afe592cea284629e3621ccfae3c4d7d3dc559c9fed750591a395", - "0x06010bdc33f1cdb374facefff537e7910b72a1120502f312a7ce41df0d552ddd", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x038b1879e091f6b9d58fd7ab746ab1cde83b869e0536bcfa0661c970998b6a27", - "0x05f75f576dd11a570e94088ce84372f49fd669c4f2fe1cd63a337b854eea2cad", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x05f3a6e334ab394b3845139dc43da172cde106db7d4501eb443f04dccfd5279d", - "0x053a040aa4c05d47e3be6dad84be345dc73d2ac85e46e0db6343278f2b0be200", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x00323241e175e43ee77a8825380a573a3f1e33d7f4b22191ecac69129fe63c32", - "0x059671572a243d06abb6a6e252ee580577d42d2254027d83758681da392e8aad", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x00f98b1882da6722c89d352172868b81f3f863c0b2645733918d15295d9d9974", - "0x069ef392e985d5f339f77ecc9d0b3604d21291bff2cba15a63c3f8ee482285e2", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x01cf9756c5046dc3bdc7e3dea83cba62f2f1f5207d9adc94d98564a2979e26c6", - "0x06f888c58b87f40f241ed3bb05b820646686301b79a809f765793440846531da", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x07fc9d929c3bed99b5bd6da8c41c8fbfc4bfbbab1b12b00544bd09d51601a8e6", - "0x0343372ee15d556f7706be279950c22738f7803418bbbbf82339563c6e757639", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x075fdcea3af9a95899edc9ac7f180076baefb2de7b00bd35c76185e728bdc4a1", - "0x05917308a6a4c422144c18cc22b627b68f80fffcdab455d193c6f83f1fa1061a", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x000cebed0233e810aa6a29a8b0829d28f1c92f303d14dd73d6b12da98117dfc7", - "0x04bdd51e1192a00df23aa8d0673e4915877ca41ddb8c9eaf21d39dd167fde7b7", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x04c7085f066adeb6781596771972b188177e63f2e2b3788d03e033cdd5af1f06", - "0x02929ee89f525862b0cedb3ab9b5166e1680cb77fb4668f10a6a3d76b5434566", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x004ae5450e87529a22fbfb1f65ff72573df15dfae8d477463427694cc749196c", - "0x06aeb6869e4405ccbd4aa669fa7de1920bb0d782153086ced211a711db921a60", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0760e341bd836899c226176f47685f69438270c150c6fe7744cd723cd1e72359", - "0x01bf09f2f1aac1a10ce8bdf20d5d178db747f01a4aa0aa8a5e4bfeef562cd94e", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x005bd1ec5463160846df4fcfec271b6e73e577a63bc8a4db16fc535119380917", - "0x043cfe1bf45f76ba1517f27fb79f2b70122c48654606694bc4b5f8c5f871ae21", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x00d8f9a93ad040806bf130d7015330830bd975afc29aa32cd3c8a953d1d2fdf2", - "0x0640324ec19b5019260cac9f6b260caa43a014893f47dc054f4589ba73ca72b7", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x04cf2335caeee34e6ab5896eff52829df5a7943c8d1b8d2817f96112e48ed0a2", - "0x0488317dd8330e1454fb692d4dcba81bc8f823e43ef904c9eadb02afcdd0ddb8", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x06016b94c00b54920027ef64902c61478244b1936337d2ad41d9a8d43dd6a4b2", - "0x03bf3dd9bce7f6d6f120de87fcbce6219340b59c2c1d75ee0d45105d33aab1cd", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x07b17cbf947701e3b9a78d43ddcc3ada7dc6bc0eefe1a46040bf962749fe724a", - "0x052a5e4ff42573520b98c64353902aa7d50d43b452cf4d7231b63051fb7d500d", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x02025b5d8b9c93bd4583ce9e0626be2ddd3d16dba433534ee9494fdfe18cbe1c", - "0x009bf8e4b5a16b6b703cc8866fd222dd8bd76ab37ec535169eaf008fc85da8e7", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0136cb029dd8a2fac3a2a501f03dfa59a91b285004db593e6a52b1487483d7e1", - "0x0571e04ed51b7d0212c57b32576d2dfd74741e1d98400230f95889c165b80cda", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x02dc92873435322fcb9ae36298cf505d31943444f66a9efe749b47b80d7f2cc9", - "0x04d77c1aee7151c69ec03360c387e35579a3540bf3e9699254025e740202a43e", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x003323a4ae01c1b24bbda9e1a891306f9e383abc61031b658965138259652a76", - "0x07819843902b27a87a49cd53385bd6b0d8ee11b82c342e52fe470036029dae42", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x06298c6fa3e632b6fa00379a05dc3bb084dfa88f20b65faeb032e8291b76b059", - "0x01c5ee1f8d56cff5449b1eec3afd6dab6cdb2ba31e02fd41f3ca29a8aded297f", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x036afb96ea22319033506070616a661363a42a845246444d5b64d0b1d1336af4", - "0x00a575496f5b9bb1d0bea99e4e744133add3036aecbed91b3dae73803a56439b", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x04929e44ff692eb944d1045bee96e750219cda3bda0500029f0df49a1db30b5b", - "0x02e138dcbd092242699004b4ce98764ffe4e892841f56830af298581cd1e523f", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x05972d0e526311bacb70a04e88969b6c63c7399b578f0dc28bbd00d65ef01da7", - "0x076b22bca9ac12d26530e7b0757e646beb3bbc5680d0f3f82fb8ee57ed4b5e39", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x070d4dbdb1079fa04209b811c88c0972e388e4d986807606ecfdfd6b54b195e6", - "0x0066a102b9e281367f666d568010c1e158a762dadd3768d70a07e1add2c73cac", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x02ca0a42a26e26934ca2d48db960b4719113d87c5e57fb437d557c5eb4e03ac7", - "0x062778c02561d4ec5d83a132afd7763a8349207c6b5d01fba70b56ba660cba2e", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0228b9ebb80a72db2214bed395a2bbfd962bb6adb5ba761aa467d65c8b9f3532", - "0x01494e7d332f3f24598f1f488d6fe03bcc3983f63ce5e81d94cec33d630f059f", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x04747700bacc0e16e500d0b9cb961d2a39c4370a781d8238296fb746e083eb87", - "0x0497c68c50bd920f7aeb16ed31a4879d95a4c9e2b3ffcf235c1db0b93eb172f6", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x01f101e30862883fa39faa19fc711aea7eae2384cada38ead56ec9615ca34027", - "0x01e08c44497a1f643015c07abc7d69cfa90a86a4e5db312c6fe7e6c46ad7ccb3", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x05137ee53f076e21a2c23da09f63c0d275408c31e4634a6b6373be5cf13e6c00", - "0x014fb446c077beb78e04de3282a63bfde12f9af85caaca4ddfab506cee31c0c1", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x02c4425fac7c6ee5a5df956745621b21ca3643d35cd01ee69a322e2fb40fbb71", - "0x07a833f30eff801b2e216901734b3bfe4a8cb55d2906a01d90beeb777ff7bcaa", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x049afbfec06fbc5afb59f4e0153f4f365e788cede51d4da9bdec903427ea73b8", - "0x02181dd110a7c4c4a656be2c90a1a8262558845a96f55385019dda80ba59aefa", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0676e3432fbb766c015809ec94c89d6ba75ff71bd5b0ce60ee324e3a3818ef03", - "0x0496e28478a8f3f7802e82cc57a1d45c3b7ecdbc9bd0470033a2bbabca75a347", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x038fa143139cd6d2ed4394999ab414d2b5c78b82b4e120d4bdaa27bc36161111", - "0x04ae0c965da852f973522eaa46606492d1b7b6d73f4117bceb80e6d6d2242d05", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x023a9b680ce4ff2e32e1b8cdee298548951b0309ee2f48a195db231c4b9d3122", - "0x0650d45ea9f8b073feb7aeb682d6f4aec36ce8428f4214c0d382579293088c38", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x022b88ca505546624cdab874b49f01b6bb064a257552358f17e60bf259bbe239", - "0x01d6393f60607d195544a91d8736b4f957d327d24c89b7fd573987b82c717e8f", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x01d73f93cc26756e4617ad3501c27ceee9a6ce5b33c18cc86239d830492fb1b3", - "0x05ae7c42e908441e39948fdf7dcdc85c35d4b6e84a0bf463972d90b9d52494f1", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x07d944853d1627b63f560aeda33acf640d35a4ee4d23a744957a2dae9d5b7c6c", - "0x00bcb411a210710acbcb9ea12680d89e3e4e652228b6786d3886e95f4d9e6970", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x037d412c2ffb173a728477446b60b2b702d07a5243cb5fc8963e623a5ee75843", - "0x0672c79968908f92cd0cb0b4c65ba86e8f359b015623a89441e1bf859bba84cb", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x075dd420bc3c20c75707992047fa3c4c352f7f005dd2a0c9a741f68ea61ea5e2", - "0x00f8222477bc6846d468e2992d64d886943706f87b47cb482b0657754536c631", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x05b37f472aa80398bff12cc74c8ee784c4fc89757292580d3a498bff17e9f114", - "0x07d79da1aab9cfef58a5f3d1c9ec466956a45f8d2af0c1da6dd4c93f720fae6e", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0735e15bb21942f2398161e26bd92c64f0708e72f530fa45528b9abbd78f8aab", - "0x052520bffcb0cd59c308d98088e9c3ea1517eb11223cfe7c5ad88b9e5da835cf", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x00aec750bd9d2d2a931597f521f08096562f8cae0a56d67d9fbf5b6e6d68d446", - "0x066c792f2d850db9196e978addde139621a3692e08c693672b7607601c397e9f", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x00c5eade7fe31dc819332c452883cdcb55a814bcb24deb1e5434e3ca78c2dfbd", - "0x00c7586f5778362cbb756dc43badfd414daf74b51dec8cd6d5f90abfd210f3be", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x025c09b3f1188c562571536202eb0f5fc4b9a7590417b8ea58b4343685d88a63", - "0x003d5b817c73b37e9a1d24ca923351359b42ced2f3cafbcac8c2d6322dc767bb", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x07d22f6db0785817b80603a6b448140b24e8b60f8973353440d5608c30477267", - "0x038eed6b0ed0ba3d3d4dd9ec54de67a1799a5e6cdc266785faff5d398157fd03", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0703c7bd289b40ffa419fecc0b461d2710a8c2db8d5f15e5d6118cd6c0943144", - "0x0640e113cd9fced918bf453eccc4d25185ab31a0f3cd156f570cea6b0ec1ad5b", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x01de3f19c8f92240677a3d1473c128bb6a9ca378cdafde5256bfa9210c3bbefe", - "0x0098cb0af810997ef3efce3a1be29234d0567779ca4a433dbd1959b6a1922166", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x07ef17f36e55b2972b0906192c9e9f51f2205b08a2d84bc78722417aa2934e47", - "0x065fe716d3226bf703c57e46b8ee4e9ccc8ecb8c0be1194d50edde02ea71d8f5", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x02ffbe161e4f3f1d123114d3c34f543b35cb359feeebfb51b504ece303551366", - "0x070e94e4b6398579b1957954f397ee6c618db781e4cff756e731918689738c41", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x038f30aa7efc0788f0c9b954dba2447085c13cfa3d41fa59a81b36d67e89c4f5", - "0x02eea547c4b781721363d18c990c902ef8b60b5b1b6e2f713e0d239d28521f68", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x06eabfd10074b6a162bc6f2cf6f1a9d823b9fb2cfff7138dc439132d863a3bfe", - "0x0580e269cf14c4e9458454a1f4dde74b1eedf62cd9acbdbd7319cbfffe9dc625", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x032e60904e73f9756f71e0a918d302aeca17cad4acacc81bab15702ab5ff78f0", - "0x00bcf4c0204f8275072f98a65b09ac58b87cdc9c70c4edfe99fe18870a3a5459", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x049c35575996c1517d2daed90d2fe4a58e674d6b4aaa7288d0642c8bf59e562f", - "0x057eeee00adea4ca80eeabab57852cbf03f1a57e21872cd44221e0550b9193b8", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x02a071757dfcae601fc55bf9ef21d54ca0aaed0708eb23224d11d7f640d1926b", - "0x0727e0c2c037ca0cabe79e5bc2280982adc4160d3d50a68a4b3de7c1f4a17dd3", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x010e1776b4c2a867bf1b028c6edec224cc6616c747e272f49e69b67b02a893dd", - "0x008d45d62ec8e627b56950f2f7622a0438647f9e9f28e723e4a37cebc039a1b0", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x04529cab559737deecd6382e5b105a83d67f980d938523adcb8ec6fc115c950b", - "0x07171b1bf016e2ff95ea3f5ac02547e7681c616783fb3790c435881910c49a45", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x03bbe92a1db3bd144807d444402db9e7e7648be36ad7244ca71c1cebec018267", - "0x036c0a901ea70034655b4b764f3ceb18dcc22a93aaff950bd6bdb78e94f7fe0f", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x02b51246a5a1ff0c1a107d0d3bad567a722e1ac483baa64047a6e262c2bfb39f", - "0x07b5d0f28a590c171d4c576a5a8cf9f7bdaa72deafe21c15b0bfcfee8d5c9af2", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x079a93a75ecbe943acc964fd39ecfc971dc6555b2bc335e7b53f52f4eb16cd36", - "0x0146132a68ce2ca8b48363612226771ac547eb3cf52b6eb7981718faac08aa3c", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x03841fc7b70be861bae8583fd3932cf5a44626333caa6359c94f43a2e94de833", - "0x06e3d13a8b1f4903aacd63fb62c5d00361358fc0fa2fbc486ba1a7b93ddab421", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x07617d181826191973027c91db8eb8064a05f79d9a50d52ceebdd9f2690ac3fd", - "0x04151da2596b54c737dfb16fd6ce3cb808fe3223394d858e12f768b577e6e7be", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x07f9cebe5cc7e1cfb706781695de8e5bc1424b2db26efef6ba8c7757310ca0ec", - "0x06cbe3e0bb486693d0c74b77338c47be2653403b9f77387f75cc331004473367", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x07a01fb06bd582f996ee217609bb587b88c45238af47903674d37652ca9559b5", - "0x053d9b838e4190d7df56c4310baf4135484639136b5fa41ea0f77565d263199f", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0549df7e2b644ea44f68e9ed17aaa92204533cf0e4d9de83352f6c5ada70bb9b", - "0x023888f267366a05ace872ce02a4af7c80ab39bf694f28ae3ecafcfe7b32747d", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x07d467a3c5b585168333916dbc38b1097b2bc72dad8e45d689f6f8b3d1bf8733", - "0x01b02efdabd079f64f782eba3e60477a0d3e6013962321d814d703fb33d7ebf6", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x06c8772e475819410a619921b89a3d9fe570a22501cdf23f5cce2d69ec90588a", - "0x04bd5ccd30b46b762cab4fcb66cd9612d00e07922005405b5917479ae8898f07", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x06b22d32e0590e169504e7f19864fd646d0994e7ed3e578a5b88f6e095913439", - "0x068c3b22d859fb85e5c8fa0a8aea932285945b230957e603394333e9ad5acd82", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x071ce5ec8286eb8c93b8481c6d19cf0a288ef4da4397e9c80f65023e516bc097", - "0x054470babc742780cd8a05499026e738ccbf81d4170d1731734de68a8e5b402c", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x05b96327f749318df9166d14fc9572ae139bcc4764c6f623a5f3ce09103dd7f9", - "0x03fabb13f2369b0fe7f379687dc828d369310dee8ccfd155135d1c64f55c0717", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0027beb13a43bc6a1f6ce046da438b0beac5899ff4d57962dcfb6476b563f74b", - "0x014074e9e93ee45394dfbe833998b9d1691961f8ba3166224b36404448c61bb3", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x05149febecd552f074abc1beb25d960419076dd5fee10da2eeb9a83d886c3ce4", - "0x003524cfa92a5cec3bc0f05d7366cac9d9754b63d17110607bba9cda73ce1f67", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x057cf7f95c852bb1d17c40c5e19aa4cac2c3a340a34abee5780b397111f90606", - "0x073385d023e114c4daa21d6ad104c293b6b217bc1eaf823d745dfa23b0944b20", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x07a7c0c03fecdc676edfdf47ac1a94c04cd0de8c880a277573c0c5ac2368db3a", - "0x02ccfc48e106d1e89442d7189aa40d2ecc28147483728f50128eab037d4cbe0d", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x06b1de6c8f161aa6509a1dcacf2c0aa1bcf6ee9d9b40e032a9d72f77a6fa298c", - "0x005e9312eb5b59d6cbadd7d3dcbc39f1b5bd9a8346fdcfdf1107bada6f9cc048", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x05a38e4aad48419248adde929c0974dc7b3436afd280aa66c8b41cc1c355f475", - "0x04bc05b4ca8bf44ac90e403a92a29da1faf7b08196e03fcc09ddec6acd8a14d8", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0581b5a3d69da43bac1449c91b6b27509f3a4e6a763292893636306cc98dbdd6", - "0x068ae0c7029855f022112c46c4e682d3ba307530083bf6a14bf18abd658af4af", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x078b2e17b1c846bca93bdfab486f7e2abe368ee05e1d25d5e9a2e10a1d743284", - "0x0392b7396dad0689000fa595c712774caae7051e45431972f9f9ce5b79eae50c", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x034abe1951778ab0f157f1cd087313b24fbdb58c239975db82ab203ccdefbacb", - "0x04d0523c9969b2a11633b81696d0bb09b4d81b422af29cd7bbe17ffd79cd9c18", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x02b40455d3e9b2958e4a4ea8d6c4bdbe619b022fba9a1d7c8b9145a36ada6466", - "0x03809b0e22528e767f1e1e5b48802c962fa492b5e56c759b81e8c8dcbe5a689f", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x047bce37acc3d67ad0296dcded981b8d6a950a2ee3b519aec3f4d0b442436ae4", - "0x0747ea488143285177ca1897eb0461f518363a343dd7b1191671dd8659c44c8f", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x07a3f902e9dd5c400a999868d4e83f6a6e181e21181a9b6c23bd43bdd665941d", - "0x01ff4ec9de43cf82bc919525ba56f7c0e7d6a392069e8a4437c0a23a6e409294", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x032670fc3fa43bf39974ba72ea51f0d045d92d084a81fe5282dfc8309aa900b9", - "0x0518fee521bf1af62356aac3b7e53fdbf57121e030c6e9572b3de69912ca4eb4", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x04b9ca363eabed9c66091a347375f7065cd28f49f914447de7cc1461f1375f1e", - "0x03a1a3a2e5e7e72476befe2571ece708052d740d02cbe6fed58740968ae609c4", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x016c1225b1c6cc302906a12b1f26f57a6eb126e7b86714c32617ca49f239800a", - "0x0642f44feb10ee93f0e29aad0005485c3ef62f9fe6313d88c43f9811bfa06e6d", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x04cc6da42863a3deca62fa218b7a3b50e034eb4bafd393eccba3f4cbe192ef10", - "0x020bfa683c884f203713953b26d2821287ecd305fa2cb70570474533fc07f918", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x018c485d3980760ea411bdfd35702618ce8b8556acaccdf90a5d5301f8a5d978", - "0x029c98a7dea7696c58243b0cfceb64abfed5400d85ea300eccc09a50158241e5", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x06d4bc77784972378aa1d8318ee51e9d22ffc0786c096b1f2fd435ba6e119576", - "0x0306b20b7ad718d9b9e58726b1094544a4509ea0919befa8bde18608a524cb5d", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0081d255039366fb563231adc22bd6d0529bef7ef0dab5ef675c9c9de7f3609a", - "0x06168942144b6d55e30d56e6ad61e252833ce8898384c1b61245c5cad8cbc7dd", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0087705353c44a5ccec8de65cf5433be6b3d9bd21eea49b60e6c907cf1a67a6a", - "0x0112804b13eee56e3b01aff75fa08fa8374c44fc461aed8a30ad54acd09c24eb", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0452ef4a03a3e04f32f5dc9ac0625d09abe0d218f4dae795f3d1718f1a472d33", - "0x0438c4952c3362784ecc892b149908fbcff8c82659d70477a3aa88b8208650de", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0474263d3698e4d5a0c3b1bab9a44405db5ef8816d2018ca566b242824967e91", - "0x008b354c7317212eef649c4b1c80c87a3785816f36485b6cf95eda8998fe4739", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0554b21b2acd97f5bcc3951542e43cf182f42a44f0ece0a60a24ee582b1a35bb", - "0x00e29be586fe4a1a820d79f0b51ef10df8c17eff0758c76ba90838b714d426a6", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x026da1cd7e5812701ec9767ad4f51fe2639d4d4685eaf7fdb75e04dd8e667358", - "0x030343a1d0b278661e4da1033aaa38a8c216f63146659142c09b795ef63c908f", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x003767745e019ee0d217c8ba0cdc177ae4c9ab1fe21e6b1d4515c3cc0a0e2c6d", - "0x06d8eb16a8147e85ff207121a43d05d74cf72344164d96ecd84f97d9973dbb6f", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x04cb477ea8961bd7c8d5d06f84e50625f3ac5ddd9a7d0202d2d61079d20419ae", - "0x02b80b3517e48c4d090091102a2083e6a4588857eb37db8efc5fe0bb0aba9c19", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0181a5e432f119d5116b508baa46fe2bcb68abd6af8fc2dd0e03c819015fb999", - "0x0067c6f6c33fa7873f2a2bde0cb740ba7259d9279e25fc05031065e456f18c85", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x06cf6eeeb9d339c0a05f72fd5af73fc7588e6d957100ee8999109437bc126cae", - "0x054fa257cea22032eac272fcd034dadf2e00d602ef9e519cf7072023c130aad1", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x019b32925048c5519d929650c833661b452ef7be7963fab0b6b328ab7dd7a28a", - "0x01bd0c14a10bf9b88ea61011c0b2e64d07da151c6203800d5a5d12063838a510", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x02d2fc90a73bf94c3db720e23088769696a1e3ea91b78b335dd0f993210979e3", - "0x017e5045368eb0a7a5bff3129bf138677efe519b11a89537720dbec57c4519ac", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x012a5fc5559428bc3b4eff97b21b63668b866e0722807f1db1f19696bacd9b0d", - "0x04c2eb07f0c24047a3d73b560144f3fd32c99d6dbd9fc7cd2fd2a72a6e4b24c7", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x071a01cb4af0a81273641a44c4db4dc0db87c8a066764883df1ae33b2598a089", - "0x07c762d9538a956d09f1db67f86124e3aca9f31aebcbdfac01343281c97743e6", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x02793ec158f20a704fd44e44c488ed4ac19114798a2fe211bdf5029889112c3d", - "0x0255b6308a754e2d729053c89b2166775d25184d44e54483737424846fcec764", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x07468302558c3cd547b79898a16762024ffc496b8a0911364dfe2905e8812e45", - "0x02800043bb1e9f9fc25b9a1544b133ce623e80eea3f2fc786a3560406cc7575b", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x013662b7a7d390aa76eb86a7c3bff6d9913eb28db6bd1a7c42de5cdad2e35ce2", - "0x040626aded7f56f82cc431ae30527b096f57fbfbc04d3e12a5abae3edf301cf1", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x01a35ac47ee8cf1c49eb2701712b63865290d97dbb2407d4f641353aebbacb12", - "0x07b3c36b89ca33bb05059307882a3959a8f5e70e8c1788da48f6db4f9a5d651f", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x03643a9482f90274b45c082cb7c772055359799cddd26672bff7839f6c6edc41", - "0x05f9eb1494afdebb2d03c3122e90af7a85e85d92c4c1a0b744c7dc7316bd0d59", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x01a3288165b3d496b8041d4f36d20459ca5cc49ea7ca274d53e0b67cb82ae586", - "0x07853a238c311ee2f84b869d3b37ea78cac8b84f43d740f91c6fe7a0e2059bcb", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x00f07b4aebd4dbd0f5055f99fd6c39d4adf0d327e1e1119c0732b2475a3b2ad2", - "0x07c56a471744c603a0cee4edcf9cf2190139f28249699582a4b0e417fd3d2190", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x018dcf54c617cf84b10af356d4b194cc87cdd5a205838186acccfc97eb17e7a3", - "0x00a712fc9a189c28ef15710bbf696d58dd420bc28fb657ef447b41c6db5135e7", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x03faa880b13272babbb38dfd022e7946690c33d457b4faf21fa4dc5bff0d225f", - "0x01a44a6c42e68f28a193ab9495b983ae8cb306e1340ef1bfc00a49e73196fc8c", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x06bc656b4b8bd3626705df1333a67ee8e2c0c6de22bd29f9f511ac885636bf97", - "0x06f25f2601d50e455e2ae271656a655deef302f426f2481a7bcf893850cab805", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0255825bd49b8a2cce114360bd9c8fe8c641af64c8e7710107213cfcb006f43d", - "0x03619cce4482335232f9e76a1460be9d296f2d468d26e4f95a78c71524fe59cc", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x07f83009eeed4f12f54d341bbf06066480cfcdf51dda103ac54d4bcecf6b3b31", - "0x04269519d28faafd7fd68bebfd8404d71ba05d62c4bb6d65d24aa6802fb84ab6", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x01c1649a9736486d4c8526528117607c4798a25944b07acd70a2acd0f17c3f91", - "0x023336dc8874c49dbd7a437596b61f022da91f47e35d79a9d5ebf5175958b1e9", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x02f325650eb316646b4eec903fe44828fcb11054f1bd42ca3a77f7e734110b35", - "0x044f976082271016f9048e22c507d97d628722bb431f8d5cc1890524e6c386bf", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0107d2f6ef804c197b9434cdc5ddaf121df4ed1d2c6cbded9b1f656855a1c5c4", - "0x0724363249716ca29d1bef965a5a44fac4254b9882daf6f3e6d8e5e2fea67ecb", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x06a606fd16800b8d796d09abb8c21469050fe1768848b8740e6afbc872fd45d6", - "0x062d61a1fd28555fc562573a9e5ca876f9c6fe0aa04d3c302ae0327ec41b9653", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x03fd2cbfc06b7075f137d90bfaf6462187485ca0209fa9fabb695f823cee6459", - "0x032392241f9f019c14835c76816ae60f2a4be03ea9628e7f0140c3628ca4017f", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0750b166bb6edc0ee80fae39c7c106879036738df2d79fb2294e1c21e9a24d6b", - "0x054f8aa297a1afafe2a17a3254f45861167414327e918d17003c6aad01d0b24c", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x07b659395e8bb7842f125457c385c7e804ef1f9c84f3d3a6d479ade59cd3a41c", - "0x06f2a7b327cab78ab02294da77e791e6647e796ba02308f45561319887949ad2", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0794b669c83ec8d902f433df9d33de8dee9bf27983695efe38f87d3c82d0b907", - "0x07301adece3ad85d9fea3e5f75cbdf875ce3f4cbf6e6f2668acf03b616d1ad70", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x04cadceddb9bf118345942ede154d71a9fe580967f5701a1d7e732c1dc77c24c", - "0x058ede3d64a50e5d727110184f31dd17ca60f77a5c52467152b5a2c84e8b006e", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x016f658d75c01aa46cf319d58b6d5cc1f4d8ddf0bb3aceeded70dd37f979367e", - "0x06a87fa09d3e5919699b6e4c81cf4c9e5a7035c7ab6b73ddb175267e993154fc", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x02088d6deb0b560dde3129cfe9f5e7226ceef7b4bca6970728a44420b5380f7d", - "0x05173bfe27897b347728c6ea8e1161317e6ab8a41aca12fd70adf3e6838cd125", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0766165d778749692c019adf2b9ec0b5f4c7f466bf1ad6cc12d597e23c25ce98", - "0x076053f73d3d1587fc162ebd6ac9bcbe683374faa0a15967e1bbd3065bfc6d0e", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0604203efa6b354c1b43fb22b0e0e3f907b1ee3de0ef034998d0ec9d4d1f861e", - "0x05e7167c9a52c15004e4755fd44c584d279c8afffc419b23b15279584978ae6f", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x03aedb10db9cf3285cdeee375879396fac1fb50dd259e1716f8c01e66f67ca72", - "0x07feb9400f621f58c21601f23b7ec7c94a9b6b193c1cd74a8a60846aedadd359", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x04ab7151702de76faa493e7a0b1ac20ee4d10c33b83fec9477547cb1236973eb", - "0x063f1f122e3ef3acc46b0915ac69c3f5772879799cad889a817f55f5853d1235", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x01fef4984ac71fa5db51e6bda4e4a8e23f41232dfab5a159ad17709554a83732", - "0x07f7017955e6389e21430a8f77f97f1c6bf6154f00a005660c76a94bf11b531a", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x01675ead0d20e5bc3a7a7331999a87ac4c916ae29669e54197bb02aa6364520f", - "0x04d1122da90d49e491922d9b533a6a668e2f65a2737ebb391ebb29fb7c1f8a9d", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0465634cab60985348f196d1602a932a474bcc61989aa09e0725bed9195a9bbd", - "0x06d028e36b701b721fd85896ddfa4c165bd496aa43d4fea71ba006ea4f04627d", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x07ac57d51bf8173a030a6fc9952c3e487369bce7cb1c1400d771f9a1be8c998e", - "0x005539bbea9db994c5ae8ed339e3a3e14136ca8f3f10fdef11e41211ebd1b31b", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0056145b9633aee6dd42b5a7268de3cc6447693e906eff23d9b69314423df828", - "0x0661fde3d9df064099d671ce42e9547c26089f5f47a6931d573e8ae2039a35f6", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x02f7148111ef53c613157aeec12e16a20f13481da4390b6ce18a85d1d8547087", - "0x02eeda779ab395597651d2a0b833ccf53b10280750139916ae2baf4ec57c633d", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x04797ae2b36c492549df4e4502ba763ff59ea0d9307e685b5f9333c894180af2", - "0x0169b2819f8a703427fab0c5e21000cbfe27c54e73e66d81cd3472ca77d9a82f", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x076ee1744d6dd2a5657a0349918b26732481a0ed0dd6806f57b92636c8efd4b6", - "0x0781109085344cea212fcacd660952db070e65278613f5c0a42e6db8f2696519", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x063ccfda68ce7d794aa7c18f56a1698b909a03145fa62579ba72cb754702ec49", - "0x040b3d34fe9f7fe2a8f4acbc57eead33a566d24e9509a7749a141ff7819719b8", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x05408adbb2c572fa635d30f4414435a4aea18d6039bee419c2003902e2b70acf", - "0x0193992ce312a57eba1303f65a8c3517208cb69095d3553071a7cb8ffbe4f3f9", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x04cbecb6c850bb39fd97ab31ccfbd0e62eb8728f84669b1b0957bdbcdab74725", - "0x0537296c9f1c10c2dc28b5a0a3f2b4131460b01ba4fc3638b4b1890637983b1e", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0056a46da8277a3676e6151fa10fe7723924d7b8e51e51193071eca569acae7c", - "0x00fb87ecd90525261340abff73d0cea4132f5d531352ee3dd827b96ebc8be0ab", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x01786ba4a4c2c75b9855fd65d6f861c9f959eeed746823e26d31e657b11094f7", - "0x02daf77cdc6eceba63bae70a65b7e7a54fe80f7e20501438debe490b26dc84fe", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x04439c7810e7b2ba772b701ec3acdca0b80c9df23047710b87f7dc3f13b337d3", - "0x05029cfe704c602a8a4662af0a5860ec03fb88f046d0e3400f2ce7638014c621", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x02248eec40b5732a6a488b681f093643af7937071bc73118acae295a32b51b05", - "0x01577e4aec30a97b648de4d0b19cf8891151b4eb11f8de9c6d7312f091552e19", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x07f0a8523bca4ad80e04f1c207a145f14f50dcfce7c32162b596e1a89abcf0ff", - "0x049a3de68d88983e34a3e5e8682287b2ca6f5098a9a209fcaa1e616922fa270d", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x04738424e558d4e0d87a3124ca02ea24f0adc6b7a9768b0d3945ed2a6104857c", - "0x0033576f92aca3f0c8ae689c3c274c2de6b918940d86a6852e02fc99e35d1614", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0671b3accf49c52dea432655097c72f46d5a350c69172b17a0220f973746875e", - "0x0488cc52af96f67dfd7de7addef8855334f1669587b26e937a574bcbd868f855", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x058f476d9a4408cf9a2607cf4f61b54a1e6689ddc97856098d0264b113933660", - "0x06fa567773dff779e649834f9a6ee057841eeae323c89b4cacb0b18ec304bf90", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x017b2dac6d06143b8c2bb010eeba4f50ec595f8fadb4f7589f39539a5ced0d3b", - "0x006e8b5ee833f19eadfcfd190d7e204ac322583c122997797e74a7f73044d79a", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x07829edd8b866ebf7baaf604ed13d19a9797578f44bbc51b1cd67ca53803e96b", - "0x05559040a6083f2af1f9133ccaf5bc2ce06e56ddfc7dd410e9635c0116b62722", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x077349b499ca9472df70d65db0197881dd25fab03e80be9bd075a8c98448fca7", - "0x0424768b4eb05463d6fa2fcb8dcc44e9b565a8b463847ff360846b110645a995", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0315f8b01b4e7a20113fb035201e8161f6399a5faff23c93ae31415164e51325", - "0x00452d88dd2b40e9c7e3036817b0e8d41bf0ea885c7e3a43d755e25753e1c960", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0780c31a0d31819552e95814ccc571aaac900b3f3ed845635fa05b653662ae66", - "0x030d86dbc3009cb6064ffb234e9fa13a8d143530fece7a89549c519beef75907", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x050051882b947333de55e2ca92a6e4bef4e92103978b9b4cb9a183c210ba7828", - "0x04c3137031700e20c3b5e30f291f61d43e36aab26d670650c845fcf08faa6e55", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x05f6ecb6f978460a6dcdae171904c67105672fae8ca690451f43bc85c6af165c", - "0x06b634d81f1d918066e033e5f73b950f60ca45c35ab2aa664c65fc81b3365a02", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0172ae2b485ee895a76f834aa0c625369239300bac9a00c70338c1a88c2c4451", - "0x06144d2ce25d2df5831a2e919c195f89d8ff75bc56ee57ffa9b7716e53e345f7", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x032738bb1e6083bbb9d11cb5f564dfd256a82ca7a9858de75eff15035049f3f7", - "0x0098f2d576a36ea27f8d76e180e63745a1a3b054d364d2d50f4a25fa4581ed62", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x07f927b881f2cdc05e1a69e40bb714af47b630d1425f08ab5d574ee698f33d51", - "0x026a465288e96572de303203bd38f4a03031e8158da0591cb037c0a5111d1056", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x036a65598552f8753580d1655417d645a140966e10a1e1663015f9fdfae44881", - "0x033d5bbfaebf59eae72b89b1aea12ab2ba3c9617f8c3baed1ec16bdf668381b5", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x01e026aa2e29e1f9232c83217b99bf168fd88d4ed727c90dfa2b136d1abecd92", - "0x07083229783d73bbe9535d3b6f6ba5644f81544248a0654533dbd59c1b766aa9", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x00403becfa545c826782026ff409cc16c9d4fe428f1b5b6e630c92439d2fa5fd", - "0x047bd6f2bf5d74f710ecb479c79b01fb774fbdad590e683a415cdedf33f71dc5", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x044b5e18f31d3fc996144754fc2291088e2373c8600e5e5805d04fb9e6c556d0", - "0x015e0f1deb8cd42dd8aad889ddff55749e7f54968f0b9f0e80406d0aa86d4f70", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0570d408024854a5da1b1d970309a80bb6c71e4d6fd728ef51def6b8a1117413", - "0x0260bfbd3cee508164370e0d8941b3a8e40935fe94bc0bdc489bc3fb174643fa", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x02c49c728366b9e23e3d1de137cf9252765d3d5707e7ba1a03ed76bfb3418486", - "0x012600244503aeef17f2dc0197e4bdb26878dc0e5fd0802b43a6ffe7d0890b27", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x03a747826d241b877d3d56b16e0b810cf088eda4fd6048da174c9991a942a5eb", - "0x02c7ba19b0a3486a2cdb84d4a388d34beb077a0e467ba44590166f93f6a09d2e", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x00387f6ecc9cbdb8586cb02e94817723356ff2e004c476902113d5bb613aae22", - "0x06dca3d3d25ba6f91e3bcbf1748f696e276c7cdd177f9b691dd8870e796b919b", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x02c4fae98326d7db2b7016f7555dee9bfa5cccc89dbfeb46ebcf4349101e59e8", - "0x00c1fba46b7d400fc5a920ec9704db9e89538677604e2358304095534c698712", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0206ad68c08f41ac747005c24c6cff7944f9215663da9afd17cd9ef1db40413b", - "0x04c6bec54e7e23ecec5bac1aad6c6ac7c07d507b40b0b1d082c0e9d4ed962c32", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x038b6374a6e668dd993d69c39e9b911892635800b88ac4338ef922f2df743035", - "0x007143e2c4552d55f99a9d8b17bc87e31341323a4359f2bbffc0568f926117a1", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x02adb8b58d38985c3b8540a3f03c19c8237e0c9eaeca8553adbdae4e0aaee054", - "0x0735a4dcf91c9b05d5cdb37d070a4f0a5e08c45570cdf17be45130f1e7011d9f", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x039f0e83bb7830f481c8707b5d051c360da25c115a8c769d6b965061ca7d9cd3", - "0x05bcbac19bdfdedad3f33c990fc44f8105fc264c4a1a58979b6b2672ac2a5953", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0634969521aba3e55994492a33d9fddc52d637ed498397c72f905d0b826960b5", - "0x011a28c45aa12546379e3f3d7b453d0fcd3d4a6b811e25558a474b9fa71127fd", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x03d60cd375842714b37bda89dd1f13a7e0f3ff133b522209617d031bce05a537", - "0x000f77f216451ab01ad5226844d2162a7f32744688bcb4325445539e2ce5cec4", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0235bf66f67c9100e7f0e22bb299cdfaa603644b240e0770aec7e7fd163e2a65", - "0x037110b3fa83ece3990afca2bea8d5ebb3c7aace60a0147f8e6ab733e2f2b4d5", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x051977d2b2faa62cefffce4e9adfceb2869e77b1cfa0b5bba01551518255d10c", - "0x0560077d670eb1a4d0ed57fd0ffec447c78a1a42a38a5b507ddceefe86910cf1", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x03b796d4eb69a55471fa86108f787b3604874e92b6887a7667a6c2bfbbd9a42b", - "0x04912d6dc0419732ef82cb3278415851d4e2d7ca89e0f4d7128cc9de51b810fe", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x01d792c5ff309816f25af02ceb9685fa262a28c46751ca9e05a0e13e9ef69c8c", - "0x013036bf806d496c5437a005fe8cccd06a51fd6e188a1b339ca7aa226df54934", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x02d1f875f592bffae296527bb3cf451e9003091f33164621a55d0788de772f30", - "0x012448e8f4862542b03ea4f9c5079512d542e8ec34ef96d17bbf43a613d9bd2d", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0703d11de078808301cc89d8fe0bde4d5af05afb8c7613fd2f1945f9296b2012", - "0x079b05c8a9063d1637d045dfbf7607a684f1937885115d771bbb9ebbcf79d121", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x048d53516dd51e49faa7ab46c8c10db1befd10f23c6a9d9bc3640a2f0da44518", - "0x073a2fb3d064adadf21aa1362c04affc660598f38a9e069b3afb74d0a99ae9ee", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x02a9a2661f93ef4062e50b1afded1cacbc5416cacb1ab3a79a931ef558f223d0", - "0x07b1a5c8fed8bb39edab6ad60d681e96a53d675639e4eee29a1cdfae75405e7c", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x073a7aa0a0107ba1eefe2dba9bcea14598b22f2d85c298868dea8ded65f0a790", - "0x043ed5b808a7ae9b3dc6d7acff888a0575f3f8ffb93c1a8d54033272ceb53dd0", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x02ab8a2d6790f00ef24c3799948fd3a5aef9eebeca258f47e5c40815fff07946", - "0x04326921affab9e2b2a6c81cfd05fb67ed3de36acd45e5a246d84564a2f5775c", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x02a5d8f0b141da6c9b6fa22020cbda07ede6b2049841bc8d2c241937fe114313", - "0x07a55fb43b52dabb260e293a98e649a0fcec15d2d49351dec2f783c62331e622", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0066587e4dbcfc34a87513a88525065eaf8d64ae3cf48957eedbac3ac2679593", - "0x0624148e048bc661444d902b6d5f9c037c99259dcf7ffc83a0b777e521290840", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x01a1bb3e0ecaafe959461c569b90a2981ac5bf2fdcf5109e0a992cfc8ba6cf1e", - "0x070d1939ece4d6ca20db1866d2f91515cb2e7e888486dcffb3c88dd787b222d7", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x062b7a3f47d0d770d7f6ad07ff5f67a6b14764d6724128e7af9d62a7a013cd08", - "0x01e8938b2d03f763e95ed17713c49428b19629ad686bd2b94659600aeca01fb1", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x048c32cff161ed145da0d5b73084897647abb777adf65738559ceab6939cf3e0", - "0x03d99308978e828f857c382df32b472bda81e8ec8e30c8844077ba6d6d2ba903", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x02947ff091a8ec9684affbc9a62e09e598841c4a6dc638088492aa47dea57097", - "0x019a2cc97975e547f97a4d02e42f89e6ced6f5a953cfccdec347867d26926541", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x06627cd70c6422a9373157874a1addf2a9d89dc90e950adab0321f2cd202efc5", - "0x0762aa84b65c2e489b6799c8642e69f7368ef08e547d1d7f40927c260832a2e9", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x01960d85f30475615f82484eba0bdafb7ea7cac3809f0518a757d66f02b01676", - "0x036c8f77baabf0cc8805d993bbe62041fcf4e3239cf9d53278a4fbd91e75eeb7", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x00f971b37ed54829095626f539e176a4d2a5a036242bad1a0342a32ded82f887", - "0x04c4c7cfa186c546aaa1ae161433c584365c7a9eb5d1fbc21fb852e013a14390", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0430dc5b38c1267679b86516cef74a30c44a0846acb0faefeadb5910187ef79e", - "0x00f79c5d52f98277851da8504c24e31cac35dae9b56ab86e9ca8b19d9111c767", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x03ad881666c1d4dc3e57121b2909a3f2bbc53f8d73e784208fa47f814285840e", - "0x03fd5915a7bf67da6348806de84af4ca56c284a798a8cc4e898712963f84d073", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x02765f28074d21d5a055340b6d40092d2bbef807e02009fabfa08ec0b9bdf38b", - "0x007fb189e0553d5df52b6843661814824b3f3cbebbd54988f042fb256c6bf30b", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0590695f1f588f848bb9a938ee14493cb53cc3bc86c619c1b091141001b606b5", - "0x025a308d4dc7f847d3307175d895783045373480c4deb2d44d7e6f3149fb5f7a", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x03e44a1811d1e02404a4ab8879d5205007b81d76b1b64743367a1dd54f642eb0", - "0x067806cbc745bc13919004514e8adc8443c211c89386fa66bf601589e859b4ef", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x006d3371394b7db24d5314a8c76a43df406910d6813c20667fe516427167fae0", - "0x0182cf4e97568896ce2e76493be103cf904f4564142443c8724889544733f821", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x070c827f9b919dbd419892a54521e113a612f9a3b8657db4fff7f4f7d705ac46", - "0x032e94382c744dcd499b9bf75ca013a41d697ee02434e0c74da52704418eb102", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x073a0e2eeb9f935a6d2387f4b90305b59e71dc8ad041bac0ac52f29bf81684fb", - "0x00ab144280f09589316a174c60f3f149194cec2e26565859c1eed8a2258bdeec", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0473f80e105bef7a7df83885211514abcbd476ab3de0a36718d1c62b172fc0d4", - "0x0250043ed68e4ecf45e430d787ed85971949b703807e3de3997ce7397f80eb6d", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x010a4447f66513537e6ef0cb0ba852968f322169dd7c2bf4cc62580cf32a05cf", - "0x072e365de6c0cd941d116b6543ac87002e40240c7eb9621fc35209127df213ba", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0348836cb2aaa00212f4b1a4e2d7fc5417f246bf2fe5c9a16ebabda449e2e08a", - "0x03f7276fd7d69e0d55ce5ee1d2d830534a27227fe0b6d8a36c93f9a78b872969", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x07afb9d34b6a42ea8c6d870e4b8191c274201dc1f93a1a2219a2392b7e345a31", - "0x042bbc20dc7115e0758b364a110227b16b64ec58fc535ce5ff1a9ad8b8a09fdd", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x07b3ba6309fc0715974589fd653437c35b97bba522b56be4e0869fca7af92454", - "0x0593ecc14fc8f9f879d5521ab3f35b6b22689bf716384b90f630420fa2123dbf", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x002cae0c2afee1767fd4c66f52e1f176d217e92e89cc19eb36d5a6c1715f641a", - "0x05335efe2d9bc3667d25ea88bf76438a4d6ab9ba5c512f9da7d0529b79b62d83", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x032a4ff978a0233158dd5e1a212916fbbe39f2bbee3cd772477fb8d70cebd593", - "0x067bd970c80f213b685ffb523a32eb9178bcc4020ab48cf8ff3f85f4db99fba8", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x053429b5c5f7c699373ce8afd54c9acf895d45c7a64477ebb232c638ca821779", - "0x07fe278f1f041e10a68eb6b66571444a4f1107642dc74769390b0750d09041f8", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x06703aba9929d65dcbd31d04311b71a8ca48509986ecd2b8f92297828de3316d", - "0x01495b3429131d0500a9320239732b57925615dd18a4b818d8552a744bf259cd", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x01cc5fde334707723c3a06f00c106db88664284a2df47bb6b144d9f960aea3e2", - "0x00dbbf610d100316938bcd8bcd078513512ecb50d4579690dbefaa419c05980d", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0031f765b5a172e29ce82db044678d7c1f93e6fb5fb97bc59ec395fb1fcf4e91", - "0x0208f69e2ea6d31784f727ea4b0e2abfcb724cbcbf2aae8d5b48fcc581e7ebcb", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x022992b8b89b0078f988569854abb40e966185a09fc5486e9ed6ac14048f2507", - "0x0602df5cb61e30434fa8d7ed5be1651dfce46e3d916facf29b15394e5a0f0a45", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0578fed75d426971bf0a85d40043584f33852df77a33ca74a393cb054f1e8990", - "0x0684840ff08fb48c18f09dcb89a804201931aec7e557718ef1251de926e87b23", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x02f952c31a6a768718938c810edac393c56ab5cb9b335b1b3e130711b3b99ed5", - "0x001ffdc88ff32ee24a813a3d7ce1c5a19cbe0180bf4a73defb666259651e46d5", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x04abcbca5c0c47969263ddafe824829754aeb5cd31139225a35d0e09f783f7b4", - "0x05e96dfd0afd90e37cbe517ea2c88762e6f30db963824e14690a08c4e5dae974", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x03a6939fb7b858a59eb4ae6ec6dddf24ad79296a8f4e256f3f001d2b72fce244", - "0x04a428159c6af46ab7d4ccd75da2bb237a7d47dbc65c56794cc177c0a695d163", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x059b5370d88a523609b4bd56eb16ae3f38ff242bf5bda498900692ef4ca56b80", - "0x0219fb058c170d99090a5e66c64f1da391bf8ce7e86075847db66b1ee65fbd49", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x054e90cb8f3a2998d2675c5780679e06c0556b1e618f8fdf07f9a4b2466fbf1e", - "0x016248676b6f06ec5e34994bc3115f85c8147b54f34d8500928f2fdc051e2089", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0525c70a2ba0dbdd68d75640f47f13d0d415ea595f7030f533f4625c2a46523b", - "0x058292c8675e5e1a438f49e0c05648d9a7aa997f2f1fd77d5de1944afe5d7eea", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0287cffbcf14ecee898e12b39f029ccc47c032de11e41bc67cfc16efe06c803c", - "0x04664fcb11a94f40b514cc610a0d6d109072acf683aaad0c7350d4122337585f", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x054726d78d099007393348787a03107ab492e59690a46c87fb02ec554f2353bd", - "0x053b54b77184ba75a3391e0ebfa6d6974db028f3f8e34bbd5460759a5848dd76", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x01872093186916b2e04353cf986515cd42748e374d30a3c346cbcc628069fd90", - "0x05fd4d512dfa06bb6a6f645aa7a85860e467087baf3a9fd4b887d6356f29dd42", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x04da98d9146a6308ed3a27fd96fb043c958c8915ea588f84b3a3197a2c19709f", - "0x0795df00d87a85e59e526d10479e68c96beb400b311c30ca9da4085da4513f70", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x024a916bb35b50413081d837a37cbed9dbeb069c9d2c27b9f35d2eebe54979cb", - "0x00e4ab7e5c842d5f4b4f48ee966d599bed1bb7357efb376a23ac2a73b9814c23", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x04ac81a66903537769d3aac6c483ccc08535cb767b6b5e1ec8017a7393ab70ae", - "0x02cb22b77a8a05d26f11a4dec80eff292633aa05553a889c5ab16b6ac6e2ab17", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x043428751740de1cea31bd3fd72529aa23c918a93d8673b77c4ca307ade9601d", - "0x004976b490b971d548dbdde646c266f8d93de1a6712a53830db7eccb8722daf5", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0086ee0ef8fb30548335b4586faeec4d5afafe1699f0009f98e6e4e4cfa24700", - "0x03fd9ed58b07fa6e37e6385640099cd08988d155b8b9d7e50ea2558225585dea", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x044bdf3bdcf17c0c5e9e3f51422cf2b58aede261a976bc06c4058f561ef2e911", - "0x01cc2a11da7c3096ab66f935a1f6a12f13df95fe2d8056e0ca9fbf4288a80256", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x03c21dab7935839e79774b0693bb0fba4198b56698aa94b75d05d6a5a2895694", - "0x0452d6f82329a437e204f1cb5132c33914d3c7072e01ee20b509cf61aed556dd", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x04c7ce963fcad8d7f23b15b8b07b1907da775c8761607f54438e25cca65ce990", - "0x0283b2367f93779ce0d78f6ba472348d98751b9f1ff4666348eb24eebff7d21e", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x02d5926394d3bfc412571b7cf88ac2646ea76d9fae5e4d602558e4419ce7e42c", - "0x029e6daf8ba52dbb542239f4f2502db13d0157e23634d16d61b1b1beb4797577", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0277259851e9b6724b2f66d72c6a4333cc7aa70badbc2513361b9168bbc77b6c", - "0x03b2aa537ba787ece4cedbace351a40f2d14529624411615b30e2716ddd1dc6a", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0021d0175349e21114988a2930b9a607d43245783cb4a0c984ce27f4c4206708", - "0x059f1f49342cc5496213d3329bf4ca7fb0044337449c579bf53147a1dac9e67c", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0167f821b381f4c8adcc39789475fb55ba639e5124fe75f26dd61be396dd5e66", - "0x022002c87d4cafb47ac9d27286d5cf5ff7a6715d69814118269b0729be9e4b3a", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x06b702d0b32610a252a647801357e6c6eab9028f7cdae4753769c7c8f6cf6120", - "0x017f33cea7775c738b699eb79c9013366907e02f6ab3831345911454b9172a2f", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x031010666c6db83a9f9e4db4c48173afd405783ac53852a6e38a8ff925528843", - "0x01f466dc9b5d9094107c741dbf380f9fd98d8549cd50f67169901516f8cce74c", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x00102ea8e36a3776f43bd1d4d8724bdbff3367f4cce0501956a13c6c46a728c9", - "0x03a8ac494ecdadb57bd0a194ac8d215a61a1551c5da25f94ad0a420562a5f3b0", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x00ba6e7d812e16b28f78e43b5a6e5fe0e60c52a87891298f158e436599ac12e9", - "0x02e8b2499d68782624d936a24f0afe4474cff8ab764fa0a6527eeed388a0edbe", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x030ba4656d2f558920cb56269fc63ac6d2f6abe62f637791f2872106aba3640b", - "0x00d634df472bdbbc1bba4af926b4dafc1c68aea73da521ef5e5005a52d41745f", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x01ad3875769a5053388a86edc85dd80fdffbbda6a456aea497ff81a0f1f6707b", - "0x02de7cdec5e2bad56a71bd2f33a4ae4c874e1ad4210a6ac32b443cfa34e85b1b", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x060d2d2584dfaac05ddbf022260038b7f7100499d9b428a0203c8b3485c91d77", - "0x00bd195cbe56fa8296cb3adca7fe1599167d3765c77aaa8bd3af336a7bef1fd2", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x05fd540087f3b2de5c6a6f1dc25e38d74bc85b4b294cb5fb40a84c89482b7aaa", - "0x06bc9cd019ae06d384a5614765111d0dd8e414d6f8e8559de5f15972cc8eee22", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x04c875322d64d87d1e0e49eda60877fd018476a23141c0fc7731c62968badfe5", - "0x026f509faa07d10d89ba3b02dd59faccc988245df7bbee02c7cf34ac7f3a7bd1", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x04ac1e912d8485f87a4daebd46dac1e23d5b2c39d7939a79812e16b0202e2df6", - "0x034018c5cdaddc8bb1ae3957f119a6718e12a620deaabefe0d7294910c8730ec", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0798ca71b40fdcf33e354124548417ecd057535519da2766776d70fe2a72fa4e", - "0x0241ed056d421ec76460badedd40a2cdb56438aa873c5b10df44fd2710755cee", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x00a8f4c8a4c13dc5c99c2b3500ccff1eb8bb58f14f60f1b6c6f6a285d3628ba8", - "0x02a4b35487672cf5a38991cd4cb468fc528f46d3381a83a883764363880c4a41", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x05e3cc5bf4ff7e41c8c47f62db31d8277ba96acb80c0c0b81664d3d29d5b885e", - "0x036da58362588ba3aa6759da2b55a15d7f9a4b367138f9721363d376dba5ba49", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x00c489650fb7f459ce09cd05a456fc5a46b849b38a671298ed645bcdaab168b0", - "0x045610d092b8af1c43ceed474cd17f7bbee65120aa6fa4d37f949e7e41f25327", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0394256a5ef4d7af5459587a0bd2edb8acaf5ecfef2563c9a04daf34a4abe4c6", - "0x01ebee390dae1403c0c53994e1d064fa64e20fcb45392e209b2b99486a559ffd", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x054dbc841b75941d1d565c7a0e038e311018b184360cd75b86e0fe616bad579f", - "0x0237d3513168529e1447c0b485388115cad47d4536ead738ddf66d325971d0cc", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0410a1511fead6151e9bedb089b9832d0fe01fab76d3f8459929f767525aeb27", - "0x0361f0a5ffe09fcc3ad4eff3f5e89508ac247af80267100b69de3c59df561cfa", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x06caa24a4068708eae78ed0beb77a031f70e6c0eb057c570e3bcce9c3a8db7f2", - "0x0051293fae0ab0d96a1e30a404deeb0ac2320c45972a4897d2f20eac52627700", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x01a17d04f7b05f418caceccb581195f589a12fdd476416d8d134a3e8b3728d64", - "0x04f763f03f036ea0c569136ae53811357826e00c2ee1e3034c4e5862f8f93bb8", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0397f7cf33bd612bb6a0b2850d04b27a0608e224a9170b9c83343eb41c9f4589", - "0x013d6ed39d562730830b0870fcd5682a2418f26370f30237097088046396eadb", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x038cd437c9f659e110a869605c182ee9fdc26de36baf559d9229e258267bb734", - "0x0624b1128ea7739bf1cbd0e423af92a4884323c868d2ba0ee9d362946edee2d1", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0184f94e669e04756f978c9836fe450270aa85c34856b41bd71fed35a2a569f2", - "0x01c81a617471bea2ef8fb9bc26c5c9293e6aa88771d13127bee9345106da7442", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x04ff9ee9bed0a844f3f541a0ac539b283207775aa1bb45327a5b04b1158e03ab", - "0x03d05ba8c806d2589f1584119344b9c97e01988537c9182b811b871585d6dd3a", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x04d0d45d657ce19f224510ca4b555204494870703b3a7ef9e89194971528c8e3", - "0x004fcf2cf9f5e1c85e5fb19e787c939926761cafdd707e58c20ba5f5793deb57", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x027ad5866bb2359b9f59560fbf92dacfb0c915de7ef86a57121cfcfbb2887b6c", - "0x0006c36512cdbd4491d54449718e0b22da3e7e27382bc1f72405a7d2490bfbba", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x024c03accf09c6e90ce17ded6927f308ee5e18c0d27d005454988fd8ec2447f3", - "0x06ff398bcd17fdd1f057801a673b2f694aa7eeadbb085df3270dc91ea4bcde90", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x05e5801d82e2828e2d9b2a7d2d0ce4d39d45d64d9020f4c5d62394998430857f", - "0x0305a84246dad1030452623541c3fe70940cdbfc9147c1d22af5476b44ef0935", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x07e845b16ea50ef9329cd0d91d16bcf64f72f960b24b5e738a0b27fe1c2b9fd6", - "0x00cee1d347da00038b17392bec8c936d3465db8c26af58e9a7ee297accfa28df", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x078b126e50b7042d2a019f95cb87a3213c664ca1bafe345999b1e9e2dac1e608", - "0x019e398196b22f4488cbe854c614ad8c353839abc5ab3a4f3f5c03c16ba8a198", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x06d3a5ce91132f385a91823c5c8046c4b638f5fe63357424410d901457cdb867", - "0x07b80bae16d2d487e122495174f7a70992bc5dafbed72bf84127ead7c57302bb", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x04bd5e66ab98b62e023e53039d0a1c95d5bcd420453205169973b6336dc820c3", - "0x00edd38447467b80fbda3a53ed19df9937eef7bfc5aec6ee97b7c276e4b9b72c", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x032d053a904dc4d88fbe7d0b96e0cbeca22a00aa5c79c753d52b0b60abf31602", - "0x03af6a02e5cae6d6490354ae51185149e3fdb6d0d9caab90e95ff58aa0c40377", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x051dd488fa0cff2b5345ee57de6817d0313017d1d7f1d34f685dafa1c6ea8c2e", - "0x02ae37ad0c25e42f428cccaad36716a740c959cc038fd1fd49478ad7850b008f", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0762df876cd550a0b52b23110031c820f97de1af79dfba0a1c629e7b158464d8", - "0x067b129eac654b12c72de7b489315ae4e880036c371fa4b8cda5a326e90e8984", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x01a703aeddf27cfdf3f34b31add45d888d02ca32969c1c9a4141c18e8e778f3c", - "0x0119b01ae09ed6ef8a6cc4d8dcd2e11dffab1f4cf03404a02f4945180aa9d197", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x049b1fbff5bdb0aa6938b066dde0ed772c0d81f9eff52e7fe038b0ccbd78adb5", - "0x01c6e57834eb14d507eed8b36c81ddf92fa91c242467061927a742fafa82b43d", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x05a2bfda12de400870e87d3f87ef8525ad34f9c411ae9820f08976ac2175ae5d", - "0x019382ba4fc3af304db282bb3406e8dbe5f747cfbc32e95b8e9c7ffccc836e79", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x054ed4775db18b6cbb0b6773438546bc03f791bc661c55ddd8034f21ebeefc18", - "0x068ab8a3eca41f8c94f9061f2498c9abc2247fada2bd779286ea2030511a6c38", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x04223a8ff4857fdd3ab8282e89c6b06353799d8f6d8f5576801510b665f44086", - "0x079507001758befb3817f8199dd5636d38ccd7e4048f935e7c73da920282203b", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x064b90993ce6d3d558ff2ca5553d70f4dc3e2b28830cc1cca95cacecb4b18636", - "0x077e8b51bd296249e0578ea33a923d577ad5417157ad23d29487d67495ee165b", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x04adfc538d1af18246aefde88485f23f89ec8e16649ce25d38cd34b3e7fda5c8", - "0x06b3638015045a14c610e1ab0b7812bce40066ee0ba446a4f3a3e8014a4f5522", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x06ba9b36a30240af617d871bf22e1de74d1166c71d5c701f4f9bff0ec7336153", - "0x03625c7f21a1a4b79d2463b1b22d3a51bedb5f774fed1c1c41dfd089f35e84e7", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x060e2e23f95c86445f8f0620afaa226c3635caa5edc3aa6c711581bdd3a07666", - "0x013e40a164697b163fd8ba428660e551dd4a6c569036fd4383d694836bd2aaae", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x02f28b8994ca6f234d9293d26196b43b9d1d5306844348c4a638102c05de85f5", - "0x0759cfb172eab065d477248b3569f4ff5791055f01e95fe71b94b8e615d73c96", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x03c2ee954ff534f856f59188fa0f29ed8a022aee0cac52d634f6dc58cd514d70", - "0x022bd162e74925f0a876bd8a206b8767dfdd7c898576a73a490f138d9a7f99c6", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x03e7a211677058697135a70c83c7edc59daa48eecadd72dc0f26ce8c2dcc69a7", - "0x02bedb69489bac2f480632b7e338919a90b7409e2f2aba97014122123359f93a", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x005763a7cab001e1aaeabf9ab5b9b2fffe6cc2b299ab04ec4933da74d960e1ab", - "0x0715ee4f8ee93ab5a1dba00f0a6abc4eec47d49b61254cc27fc36a031e32f0f8", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x02243457a1b5925af6eb73537955fd0520493ab73eeda45cc383d22965f97729", - "0x06da7b5fe8c87c775296ebd71be0e9ac319e917d51d55c876b9ecf0229b4354a", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x05e71e52e73df10ddfcddd40e2d860903608574bba5b25783e6a0c266a87975b", - "0x048bb150db7a9c623e5dbfa6f254dce1b70dfec4d9a9800edf05dea2ffdb9cd9", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x015d4b26e873732cba57172ad2205714541f33f5002b3155ff92679d472ceb8f", - "0x01af9273a4ff12aed1dc523fc438c31c4b2cd6ae63ab9dfe25a46131227c414a", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x019976ad8d7b7f47c785408243a227401996b36e47c7a78a7bc7d4256233ba9a", - "0x00896b713c5d7777b0703821a73c1d9a4c3755501042120534ff13990975e1f5", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x058a2f6b6e86c21eca310aebeb7d1b0dcbdd99381f31849112b91a72672d535d", - "0x0575963e5b737775b0fe17d876071a7290630156dd69880e96f3f0b2597d1f2f", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0760f919a5d381d12a8255127d34edf7bb958ccb8c3ebfc3dba0d8539fd8f881", - "0x03f7cbe54778d40b187517aa304bb1e4d3d7828b094594cc0807131cb0b20083", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x02b8e4660169a4fa4bff1520240ceda9c4e9a3dedd6407d0eb69c0a164d18c8f", - "0x02470c6ef5dd219c3cd3a9c685c6154ae844371f92967dc482f2743a4abe712c", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x06537321b9626a90945e4f7bbb3761c0af95a8cdce3453d8af14770ddc2a4cd8", - "0x070d28d2e9030e9a7c0c857069c5e9bfacccbb914170ee768552eb27ea93386d", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x05bfdb032cda93a1e432698b26ac0cd4ac06ef8b2a2d73777bdd358a0cadaf38", - "0x0583e1e660ba5b856cf36ba48b6793ae833cdc25b8acafc5e90610aaaed28fe6", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x06e1d98138b75b6ed62e79cc2f6212dbc35e9d8c66390c71890947625248e57d", - "0x0669e49259b9fbb99c200c3ab9bded17005ffc021ba01eaaaee3f1baddbb6a9f", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x07200845e932230d3cad95e2ca1faf33ef55ccd896904a98e41c401ce2f747dc", - "0x06d7614692d257fca047bf41eeea309c72dc39d31b9abf29d679bf2238379777", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x061674b992c29827186cab5ff454758dbbed8e89bc23d0bd33193afccc3a04bc", - "0x038e1020744c13903809ea30a0662fdb5226ae760cdcf10800faabec452e00f8", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x02ea2d48bcb83c0c9cda4efe11f07165cfcbc9ccd26526e5fb12556316d4b1df", - "0x01d2d68b74ad384c5c4a9c85453104216357bfcdf635680b40215f0f800974cb", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x04df09af52a5ff777d9ac4e23406fa75c2ff004335f665aa60c909e68ec3979e", - "0x059c36bf38716707bfbb4fe6f838e69d81a4467668158e8da1f4f2fd1ed6b22d", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x07881212050264c40c336ed3a15dd2cd868ec9a558f5b728869eab66e8b8ed54", - "0x021aaefcc8ad8a161b8971d6880321781dbd939570c540da4c330922b8c81e9b", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0761afc4619500b4f916c8c87a473efa26431d172cc5d59d3dc59d2a6371dd6d", - "0x0530355f5420a2024cd20e8f74f5467ccf3386e3ec853251a553e9ecf1185692", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x07162b4d0862e287a6484d569bb50ec6e2288038147d78a720434c552ec91725", - "0x06bcaed36b3aef9b8f82f4aed4653fbfd043450cfe3f10a2b067e9b9f872d9bb", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0540e83451162b899da47fb165dfc2751e1982427f3012310978463fdadeca25", - "0x01a89c13e28f9a87db4927bb205ee965b4a1fd375aa53fc09df604cfc20621f2", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x00b6be88ce0461d20f59c5199573cda0170b61decf6e8e69a6d32f1695adc4ed", - "0x05536e4808370716f2bb3423a9a49a38ddbfe91faf3b7a35eb53d3519238b6cf", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x01b65d53cadecf8627bb0927ddfaf4d246a64c18e874adb4223b628b6574cf9c", - "0x02f28f98a243b296f0e5bdefe1c0ac189d484d69c6bde75128f0269d75b811b5", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x078c50f7df96774b12158f7fce97a90d0e70fec175220a9a586d64fc595a3c92", - "0x053d584bb30e14da642c7e68f955ca9c28525695d0de5f286fb3da3c79f5078c", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x05792699a8ac265c6705ca82c51a94b9c420148d0dc1d299cbf908c67d0b71b3", - "0x002d7210d6f60a7a1cd92b27c917c3807d1134c04a0110f7b74a7d65fda055f3", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x032fbf3cca31c91ffd59c5414a6aad1ca6782b7263a603c58eaffc717b600408", - "0x013aeb2000447cb52fd00ccbfa8d9156aa85dc686067a74cc15855f25571b50c", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x02e85840066ac2e58c702b6e591e93d44c18c82439048999af4c60ea215800aa", - "0x06cd7517d893cee1d1a969c4a754f0c1452ed71a033b72cc22945a2e4781937c", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x068199e5649520a9f875a2685748af77ad696fea45f9d64375c378f51f8830cb", - "0x05014b3d4c0b182e63efaf9aaa71ee7b6e067a98325db21f6d650cc9b80eaf2e", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x060e4d8b358e5b3765862433e95d8d8f118ef0fc350c71d9d64ce5b3fece6782", - "0x00c7acf271efd3730e2ef248fbacb0281356b1e51d521b0a19828de9eeeb1322", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x00e5972af1655eb6dde2e8c77cc58044299922441b5ee41ceaf5cafedc765bcc", - "0x0550282f37a4783dd60801c237045992d6fbe82a5902e7d837ea25f6f98c7b3a", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x007efc1aad1f580d8f50274f1c114c40056be19a8c96fa8c4cb5bf85e1e7f3e4", - "0x02689f1c3898b114d668be6413643ee9f879913d40c262541fd0316264c60a4f", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x06182a2a2996c29b1fc675baddfbebbff12b057f927b95556c2b5ddf65b02588", - "0x01bb2fbcca55f330424f89921fdd8ff21097732a238c048d1a1f1584a66e60fa", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x07939db98037f59b0113e9d60051f75ac9c3cfd1a3eb535c73e2d945068c6c5c", - "0x0410914ca8bbf3c65cdf3e9772ca790c19131c50068d34b7346c10260a578a8e", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x030ffcbed9babd48765b3fa36908bfce7e75d79f63686e82575f5e4a28253945", - "0x02d95c65f88c12b8119ecbf9a3c0030d2190a3e27936bfe5b3a488e42524db2f", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x07112c99d2106a694d6d1c2e97107ae06b8cc795dc0339cf49afbe57f253894e", - "0x0741ae7df04837099a5cea8d31bd70b29468c0b79ce0882723862f2a6adeeb78", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x045d9f134d8ae7b17752f12a8012f9dfd42d11b3e5fa3d5fc224c1e28f6956e9", - "0x01d262d16d9b7bf23c0ef668ab06efee158930204ead1123da051c9495a7669d", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0225b77ad00a2b83d26690190b74867326eca4f55bfbc3a13be036225ca3b1b5", - "0x0411faafef89042ce6beb64309fdaff70fa53e9d32d79a21e7f82f80e79ff05e", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x07cb85261173bfc8d38a3b0ff759f834cc4d37694c605557c7b05a89eea35e5a", - "0x0017cdbe0a247cbb4f57448f612ed8d6f8d38756fd06a54d70c682c472cce809", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x06767edde937c06534e8ca071620f57c8dae00977670c333325d578bd7c6697e", - "0x015ff39370e68b5729627930ca82523e3da7ad937c4281eb438e20a2ab04fc86", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x060979645fac49b980dacfe1ee3f10a27ae21a4f59bfa5a881d069205cb2ca19", - "0x02011651dd09a58f0621d8bf30e39767374387cb42cc6e47003c20608484708a", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x057e90543d895732f8c69c8b14500aaf2cd49520c64a1a86bf29bfce4b820063", - "0x04d55a784b4acc720827adf5a47b7f2b52367e3a7fd7a20b4d622a8617a192c3", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0311fb393f127dc1fc40e2d160429245a763310ae587d3d4c9963b75f7ce6d01", - "0x04b2530e6c5ee42425f164e1615c33947f33939066b9e9e6dc7da9c1ea660697", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x079f4de0efc7e9a7733594ef61a5739c3d5105a9dfacf6c48792653680f94891", - "0x02fdbe06646fd33d837caa71460006ed285259d428f827a49f83ab19d6edca12", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0538b773dbf83aece332006e3e0fb7ad4c060c07f9722649b8647721af4931bf", - "0x0051512266c1c349d1a4f1b84f3d941505929709ed8cffe4d2b0c295f4ab9fc6", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x01501e64c99c8b6658b0479f2c05c9142d246eaabfccf2fcec8dc4399539d8e1", - "0x03bab1e3339e42c9ee66c65b0b20236fdd9362d3ce786ad3a9779ab578af50a8", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0059b907b941f24fb8ea2458153e55f07534b388e835af7b69f3c9f54392a335", - "0x01d5438c4f2f68a417f3d56f916d899a6ffe910f5f2989ca31687f1b10f60db8", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x01c12eaa4d72ed191b1dd7241242fca5b8644124031a00b4d3360979d3e50511", - "0x06ba5fbc974818f6a9195b553eb8b2f905444efe320e1a22b334db4a602293ef", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x02887d08a26f484546f360e33abbf7a998b7170a5b30070938b84f072c676bf3", - "0x062a78e8d00e5d3a59e2fc424ffa08961567ba1ef24c8531cd7bceee6074a535", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x06c001082916f31c6564e74323ec0d53259fa4ab8b1022883d6889c5b7a37b1e", - "0x041fc3640ebcae7f29555a0ed57f9a8e8a963c5046e3bac0e9402fa3eae34ec6", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x00d76dd0019e5efc64a7807378fbb0e21de101584e8c11777c90ef0a7c936e9c", - "0x07d7d3a2d9c87b7712eb14dd26d269e929cbc07c71bed9735894637e2b1dcaba", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x039c69e087ddb82c79978d14dcc82b9bc5f885431794366a57a574f9e4387719", - "0x0715ea99f0cc2d4810635a32d2dd4b79c3ba9da9c5c2d6368a1d064e73429185", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x06e3cc8076b3d45377929033af35aab0c6d19ae4fd47c0daf844079ca04c46eb", - "0x07b90f338e4d848aa8f19d0b5c3bca916a2a9024acbf14bddb278bca2aa39e5f", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0496d685b825e26e34c22c1a26edffd712b6582eee335ef9b6bd793055fa43bb", - "0x01c844e5a97c06f4a7ef0b3298df7233b0c0fe636d5bb3cbed1d1f05c2e28952", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0547bf81b8e912577f731fdacdb350a39c9dc6d19c2f5340317af052c7f4c37c", - "0x02eec56d8bd11415fbf0954e1450e6b00d77ada0c0b6b6642e6de08ea1d83e6a", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x021c1f807c822bb3dcb31bbed8f3da7f0e547612f89aedf0b60d89c65193025b", - "0x05c4db1edeb5256d73ec0f68aa46614d497e01aeff7637a0da17da284608ac0c", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x06c8d85dc432ec9e91bea8f4f6afafa786c641d2989757afc724cbd0327294ac", - "0x05e81297b8c8cb2cd33ab5eadfcd378d07ce6107675365ee496d955dbd4e2349", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0646ea047e0a63d633b9b509abc5707d02aec1d5236101aab95fad26e54909a2", - "0x030099d8d0409997fa82aebde60bec2995a64ef6247c4d13dc633398112a562b", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x069b769fa354620ad3a91a1478c0fbfe89b64f4b556c39d071f45e4f6f269bf5", - "0x034e6049d0c6efc1f21148490ede6fc6a708ec9259cafd780a9b14ed1ad15abc", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0195da9337ca0d1cb0ea350c2979292b0dbfaa5fc80aa6ec103007dab23db9cc", - "0x03172c11b10cd26ed3d1a63a41f8a56a2eeeb730909ea0c36391bee0ed2ffe4e", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x034844dacdd3ec54a3af328bb9d67715ab33425e194ac9977ca02ef22e8f7a88", - "0x003c1affc6372f32a1634748124f9e1a03c4f0c993971da0dc28888b0801279d", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0436b192e03a49796cf9bc5e93c88268b71c9c24f9c3a85322bba634ebea309d", - "0x067a8091ef69d62abcb28ce5df4dc7d53f8dc2b9690344f75ecd03a6d9386044", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x007b34e1908fc20f1f6bbf950f40ee14d8b64a4b8e1e04f165d1b6efa6ab8a2e", - "0x00d4fdb816338bd52ea2428faeb0d22d435838e73783eb66e52c3a307710c430", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0592d25b68baff87a6d7fd41ff0dadbddc1bd1316683de3b2d677501c0eb14e4", - "0x027ad1e1099683f54589010faeefb19e38569ace43653be8787a42b0591e7bc5", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x044d2ccc8a2411445d359f0b3d4fa48f63ca93a4003590bb313ab6a5a4c7e300", - "0x0187a25479b2313fc4faa6ab3fb2a96094066ace2ac30efc76584a33c17aece8", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x03283a28ef9d49137040c098b5941e910c8b30565f7393e429de20490063c9af", - "0x01824656c4d7739526b92d527f5b85589553e971437dc6bd8b5e4f43b67fd0f3", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x02b2f2a50c79bb9d6b8ec329c49d74cb486811f4b584027889fd6430f56c3f3f", - "0x02ebd50d1f00e14bde5af28650aa0797e7827b6a54c2619798f5d648412a15cf", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0089a5111ae911512ba62e87b97f643c0219702f235c70f62c6678a129302009", - "0x0557fa3d98e9ce7b83b47545013a4498f3de43787fb66b1a54521222242f7c1b", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x043d625a28ce111cc8a02cb8300b325bd1897e29c880b40fc4e4a51828ae844e", - "0x0104d513d9109c70fa10db4b7a112f5c007b43bc0ce2431c195b90ae40201eca", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0020ee5fda37becd37e0f0e46ffee8dd194bdadf46208e3402bb53d705d2e528", - "0x04c4e442a3e73ac401a114ba91bf1e392076078ca9a3ec602c123cfc5cfdead9", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x039751cc920d45769b846743246ccd4485a8b91976820922f44be1db3ddf5b7d", - "0x07ca836ca40a568f69d93c2b913799e11fb64933ba0f00567c39d6e03fec63ad", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x01eb5b065322067013989a3599658975a21d0ae24d10e19f63453955843dae98", - "0x03ab725194722e3c5e46eb3ec62b9ae2ab21db95e6c23d6c15b3ca3e5203553d", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x024cf41ddfb1e7853b96f855f4af00b29e64664b5965f599b319aa2ae6c8e14e", - "0x0013cf357cb7966b3e46612a8f9399aabff7cda12daa4d1429e1f127fe16e559", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x02ad007a1b6b53f0180991af1d5674445fa4f7f8f29cbb91ec682855c386a31d", - "0x06ea05c6033d24f51aa3cbfc751a3798f767aa600f7e83f720cfe18cebe4cbab", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x008412fc6672556cb4fd6d84cf65daa41a0652532ac44be39eddb9faf1c25616", - "0x0370ee5845b5409e80428232f402c640127d387ade33110bcf860915fde5c6da", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x01c9b5e53377e72da5066cb08566bbf9ec31ec1877f455d932cd9b1aa375d34e", - "0x072f79555a8bc207863f32d482fca54692825449fd8963fcea3de3a8183a739a", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x00574a6e05eb14591729515be239ea8c1fa9e12d4049d42876f76c8ff37bca03", - "0x05f99b3af43ca68c1c73e8190d5f73c8de162ba643d7d5f0cd73cfa8135db6d3", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x04f8f9c8efda878a0c6abe77766bb2199b5aa836811cb3a5ab3c9d8f4a5e0486", - "0x070eed1b076368fb39902a395748d956770afba02f7d9f12e0820d3872804311", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0513fc5c2e16505b2b25a2f284e167d5401194bcac0dc3ecf8b7c9acb560daa1", - "0x0687ee7a1a8954d08d3856e1a16ded808e419e789736d3f55f79f7693bad69f5", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x06576fe870cb5c232048669288090dbbda89ae49fb7739e8870e78b3b6a808d8", - "0x075b82f52dd3c3f9f5f27210344f84cf2f94aa35bd57780062b98f0b2d7c2917", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0677a76c2f29f5403a1c9825601d9038fa1b5eb1ae8025a2d8fbb69b44855dab", - "0x059097f1eade3ec28c8d0d19866d207f8d5ef2a912ca3068d3eb3ecf2f038501", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0519f9db47a14a51aea3cb1dcbc78bfff5050c9fedc71659daaf12d2e8f7de7b", - "0x000e188d695032088b4c40b4b5ee23ff3770e72d1f06f349fbd6dbe3b0bdbd60", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x053d48bd1205274b1c2b0a0ceb3d21c5fcd7c8892a784931603240b288a598b9", - "0x035387abd7ea59c9b956de44d36533cad1f6668c438d666651695ff3862159be", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x01c2c07baab9fc694944eeaba6ef635304e8e1567a2b7a6d61df443677fd60e1", - "0x0707ff9003ade09d0afe86a016375c586864d05111000c88f24aedd17495adbd", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0127718111e66782b8a42d8ffc4a1e97b2d5f944c31afe47627682ff2ee7bd4e", - "0x04ba816ac45175b8125ec6690e8cbd1ebedd8a5c54623fd50b910134faab9f1b", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x042f63fd2e0cf7a1f11a591494f39d6b510a23a8eaabc2bfb8e72acc280abb06", - "0x06ce168a0bc0a5e6cf78a88074e3f82c7f3deec19f674f96f6eafe8fad127509", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x00c168fea1a035a849be27a71bfe1f652867c1399597b15c700843e5f667ce51", - "0x07da8cf4372d2fcf59f9124883dbdb8914e7c93554a352a097e3913c6836f62b", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x040d081431101eaccdf4583f8f7825cc83db0bf18f026f4922870278cab9e5f7", - "0x0661853c80cbee5a2960b19908755822ea9eafb4c55e2d86f36d977089c3772d", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0020a0afb24b2e5da3e87adf069942e079fae3abf04aaca2eff8615c47b7bdab", - "0x005b53f873a748b1a29e23be2e2f4fc0e9d4346c18d2bfe5a0ad9f19079ebbd6", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x06abf47a38dba78e5bf05b096a24bb941526f1a8f629df8eda8b9b995009a3bb", - "0x071c2891bda1d50db4aca94b07218dc31d20817bb6a939041f8874975f7574f7", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0213eb1ea99e08825110dd61094eb6e8145119dc1c507636f068730b1e086d44", - "0x0744f6853f4f02f4f042468d0739e0c9f64df720b87ed77d1979547084ef7a89", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0735ef017d091ca23264ad0aa7bb9b2be3309b4539605e79ed4a652ccb2fbe3c", - "0x07f0ccc7a5747c4e921fff97d431169f690763427e2cfd1ad74d7a0308d7faa9", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x07f556e47e8f4fe796c402dc700cee1d4efb07457494048290bde5aca7acd70e", - "0x032eed2b2e186467e1b7754a97d2dabd3c08ca59c82741e8889641cc36452367", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x03f36babc5a30070b610ed97db44997e6d9115c9c0579ad8f75d295a17130001", - "0x079047908a2474e32d5c712a07bf5c4ad522590bb5d6cefda410d30528e12ca8", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x066d365eb1292b306599a40aa706d0e7e4e7cb1012884e51177876f86e176ea2", - "0x03ad1ed31126c008aeabd279d264bc30f80924c6f79667f5a06b11115270efdb", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x05ef23fe0fc556e2e5cc62241ef4e58fa55c5fd94708f950522aacceed4b30df", - "0x05f1b4c4b3dca554e3b0eab09e9a5074b237236221040653e7649ab59e87d667", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x04c31fda9ea575a2e82ea4d3071e50e8960bfb81ce3bbb04459e0561b09dd279", - "0x0779ccad5cb0d3cb83ae80c963f4323ed7c742bc78531a51a3a421530a6341bf", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x051c04907ae88a5926b242fb2862cb1f2c651a94e6caad5bff8601c079fded74", - "0x010a585a269f460aed43f54c7de13cdf623fc8de5957526997278be939ef32ad", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x065bcc7ae98ee8e619c3bbda9fd4544715fdc5a59b842e6091babdda0bb096f8", - "0x040c653faedac89c27d63a16e4240d14a2d2c927d6d1d22597b6d69b83ba1130", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x02142443335c5958370142c04837289ec46372c2e9b798e3ad324dc85fc840d5", - "0x07667866914c2c41bbb3b6ab31698385cffe0ff4ee2b76cb8e2524d5bc58912d", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x056f2bd9f4c37332c8f0e58a314fa7cdeea3b4689b14214480d3740d9ee0305b", - "0x00d32c7bd71966497d0167d441ef3115fa7afafb88efadcbde303e7b1454002f", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x056c714415f3058375d7955b50d8240fdbb718e5bf427fc4172c02d7890bcfd3", - "0x036c54dae8a824126841282ea36fe5dabc40986eef27598f3df4cb1ba8eedfe2", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x030a11a11b775c0cc080dce4607dbea2e6f3c51108baa282ddba9af9c34d0b2a", - "0x061c16bab165c3795135db7a12a847c9764d60d1d3bad74c0c28856850959cb3", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x01367b40dc7f9a797ee18866afd6acbf14cf7c2952f994971eafe7a80e90f556", - "0x0424a3e513e957bf49892216785ebd90056d77358fef34f6cecbbc960c1e61dc", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x05f6d6994cc25902128fb00c9924a9791e03786e648ec325eda971fd094c7693", - "0x000a9a1b470224b7cc67676b3eb180885e3b3dfa91662b9928ddfe9c0f6d7462", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x00c1e1bd626a735aa2c065831317217ecce68e377eb1f67e54ce2e97bc2ef2dc", - "0x053c5af23a9b482f420be6dfd37b6886154cfd130794098e1f51c1885ac2556a", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x05aff3b30775ae4758e604a4a6262803a545f5ef4e7855fa245ac6a6431a9ece", - "0x039a4799e5519047f29333bee9c86c99bfa8056d4aa381c396c4a44331fe795f", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x07a4eb659c8111d725dd8d6e70b0c391f23b1b43e8a0e238040f7684768e4831", - "0x02f694db4a3a3afe5a72ca01f175913c1f5abb7ea7cd0896347b0901d9fd530b", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x03d753e9723701a8e9d99b91bb93dee2eda7ffa5072fb2cd5c5fd99aebcdb299", - "0x015798bf5c17d6d5880fed1553af32dd8d8baf2888c715a886575448a24c7975", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0105547339511559be2a1b5ed273bb1095ef091272311f63a1b9c27b986f6db4", - "0x032485cf96554c920226bbd8f8e89caf0a03dfecb9382232175b64f8fc45824b", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0736acada308645ab32a4cd64ac993b5e20c2e523b190cea557a6e97016b9761", - "0x0398dff04b73b3381102759bd5c601cdbe149badf280c3020c3dfc58bbf6b2df", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x05a22976f1a563642904203b1cf49000dff0fb5552dfb089d3d0649dee9b3ed0", - "0x0735d59c59e9c832ae2ef4e3a602b01cea5b51f232677943668af56bf0634b77", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x06593e5078466b07a4222d2e544da826d2c583c9cc5f2eaea148b129b00d4aa0", - "0x011b352b08a0a61d3cd67d1dc08069dec3bde907b3da0f56de5011b956bf8744", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x048caf1dfa3a312fee9f8141769af825cb05655a6c11a7f820938cb68ce0bd0c", - "0x016b37bb7a46debd606039edb6fdb8b51f5b72d795020248bc0cc282561f62d7", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x035badc16dd993f0a3ec4555623beff1d65fb088f3f5cd3b82af654c81768970", - "0x0705edf7a7d411b00afa6e0eb1197fd8b50d0a849738dab817febac685cd60c9", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0381c962a749ff95ecd72b2f556f2f3ef61419c3c1d53b5cd47e341337272505", - "0x028e430907cceab2b1673fa145fbda3dd8f8f1c7bddbe2b1d5f44bf35e9c712e", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x02ea867dc03f57fd2c673891c38357aa0009fb0986126b920640620bcc02dd47", - "0x068237b0d3ff317275205c23ad1f41d23771e9e94043a5d956c58d75cf3fc5ae", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x02b37ab18c66e0ee425ecce1251eafa1069e3cda41b7b7c161ff8cc4a3c859a6", - "0x075f1106ce4f67307f8c301cd9ffae5e9c9c7b7be09fe5cf7d445af568b24c26", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0574ab8edb3bef422ef246d9300a1466b9d270815c6d202eaa5127916b6e4a5b", - "0x06dc476e19e79095e62f0a32848b73ca46a62ea5f12b68c2c3ceab028e46bcf4", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x04fec9681f3e9218e3f160f7fc73da5a77709e3bad6c877af106bdf4f642a68f", - "0x0632d0cd3dbb007c8572ca4cd80e5622f5ec0e2ab2b857f98dc0bd3cf31fbe69", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x07a6eb353c5be9ff03fe4a06c01fb71aad2b38144179a291ebcbb2c2417cca65", - "0x03de3ecb12f2fa699b46a9d399abf77ca17bebc3e491bfb2542dd0fba991e2bb", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x002c7ead583d6c32162091034a9eddfa775b4e84b8bdbea939edb2a80dcf64f6", - "0x0461790ce40d9c276d962b2a1e9a74d66e9d7335962e234e8a2fc6963d31722d", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x042dfbde92548f0371e98540e4958c7024dc432a6c5da9997eb4618547c36436", - "0x009123509ad725ffbc9aca591d3eff15fb5c1474b131cdb6279f9d0d72859835", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x034285af023d9b4c2c2b88e8704bf2c05a9b553b00b2e70ff05f8c2970cb134f", - "0x033fe678e7671760a83836107428dbade68c3593fbe568f3f8f1b2c568099c44", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0368ecc587e70537f3c268e92e9ab4cbf6a80e23585911e5aec5cf8c43ac127e", - "0x019957285e7fca1c126d8c47fe3c2ff2276f6cfdbd5ab73a7563551a76758540", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x02861d2d56f876748babf45435cb3becf1e1582b8967568b4ea6eb38330a4a50", - "0x0728b1c907c18c5c27306a99af372faa1e779803330fd07522d5560b94d2d895", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x078fcef3146fe8a19de8819d643350fc86ca3d6b0bdce2a5769000ced0432df0", - "0x000e13cb7fd79355a97b356ee140ef8acd698356c6d715b0750447a635725de6", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x06222f720a24466263db6a11842f117fc4bb78da6705f140e48869db3e087441", - "0x06eff5b9bf3aeedc962bc5a24b66e7bdad2153450ed53a058bf2c8dbf2907693", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x02db18ec981852683c758a939d2363ab61f455489afe88fc85d69de596431ff5", - "0x06d804a5824c8e1c7fefd4e91eabfb8d6fbb03f6a0ca497d7c6349b2b5bc2cc5", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x05b34ecf07469ed26b5719aa9c7deec98cceac3844ec6104085d897c68177eb2", - "0x00b76f8d188a6692816e599285f395eaf249ee203d664879ade372f9c046bcf1", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x003075c040ca2cb40d242df9b933d0d038d670274f874281bd549329b3bd9f44", - "0x066be858643d2f64e30467416851ac0a24740ffe75538e8999bced6157e6742e", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x02f244dc9f349f6b7151de259f7764e9f904f700a8fa6b1eeca14f73b798aefe", - "0x05345d8bca1995a40e818478938b7314cfa45f0677a6d5fd5b22e6141fbc9fff", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x04a9dbd186db6fdac051af404a0c5e7db403cbfa501b459586566582565e0d25", - "0x04d7f12d7661248bd0209ff8028769bb975910525af57af1c27e9865e06b9344", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x00d12792a21a98b772f76eed4d1a753d67b719e9f8b481a8f2443d2b32d2fa7e", - "0x025ffdfd341a3e58879eb76bae4c75ccd9b35041b2d29b4ac904e31d89f1a38a", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x03fd3f8abcc4590870d92ecad30cfcfa593ca21499778dff90607d1f7ac1dae5", - "0x078aa97841d18e1d4687e9ecb6679b54f7f876d8d8e5ebcc78b95b57fac9f201", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x017c6ec5ea206eb97cbf53851e37ce391080e0d2bf1e5395610f79ab0503f7ce", - "0x03adb71ca3523d88ceb1e365f12dfb24895453c14daf0046b2626cddadfdf5f7", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x070859f9771a713e54974ce11cdaf44b0dcc3e9befa0c0834908d877eeaafd27", - "0x00d18f794bf0cc0623b711e7450030424e52326c45ba9b03341883ae4828a5f8", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x065ebd15beb3ed8e0f4069570c97fa9ca964677646cce1cdf683262afbe98305", - "0x00f5ff85545e0afe895477de71260d32f2f8982cb0a2a5aaa2d70008024a83dc", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x02a820cfd0fd4ab0871e7b303cd545a3086caf8fa818c087a4017197da74efbf", - "0x05f992683ff37f6c041b84bfc01503d333ac9763505cc8f69473da01812969d1", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x044681a9eb73031c23d8ef75b12d5df25713df60bc02607daa9866d9149dd744", - "0x0122775da2fd7c94dcb831a2b4bde6128e621383c85645fca0e82314f9b8db2a", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x02d9bc641ccf15bebe6517ed3956ea4586fb098b6c74694f96adc8cb7201cf45", - "0x01bbc53311496cce45322005c77f1d83a067f23c78031fba7a8bbc90850a45d9", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x013ba6bc9a47ec3ea8b384d914e87ec89a314ed45b93bb2de27acefb7d9f48dd", - "0x0143b7129aa9c120cf28984bb365738e37f6fc9642c0f6f6a425b7b00613a5ce", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x05b0526de2c07fe7cd73e3884f642d57a0ac5e13c68590ed03a14e530616e8c1", - "0x00eec69d0cbd92c9fca31ec967dba848bec368e792d6678797946a5e34fe3487", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x015aac85b659e12eff355ef5ffd6f12d364f7b54a862b337f54c8bd27015e05d", - "0x03a7335484bd88aebbe97e2e9b71497cb962f9b10329a8d5989c6f80b5890034", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x008a67e26e06db54d3e4879c6035e18220f8a81850472cd3ccb5503d7b3256d1", - "0x0771ac312423512608b1beebc05f5d8057dd072e4af98198d374786ce9b0aaae", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x018c1c95bfcb49c356302fb6121ff71eab812e3e29dc70538fe1997905b63ee1", - "0x07ddd8295b92cc0db2b71595a21ac4939f6adc3ee14a7818347e6a551d88c89a", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x02efc1b57562f2ae67cea6083596e9c042cbcf9a4035a0fcbd922d7d54fd7700", - "0x0110f2de4efcbb5f44c9af2acd2edc949afe50e06ab91d96b8482f7600b54877", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x077e0ba159b8e19ef7f50d29a4db58c4331d20f22356dd43ff4c7862717d2d44", - "0x04233573fb814ce30ee8d38a408a721087f16835fc8da6b1e4fe56cff93cc6ab", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x05281cad86e0ba84d6f81a2fa2240d8e91b1c36f872919341e5f4feb1381e72e", - "0x021ed005e763cf31ee5237cd84cd36f61b778fbbdbcfff051e7b904f9c31ff34", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x025c35e11083da6d9cae5b3714631ecd406cb403ef277a2e56e516107413b43d", - "0x00d0a74f7195af1fb80ff134de7696fcfa3b394d60bb5917d2f267c47ce44554", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x06cf6b3efee707210cb3a72f1e885c3d0953aefb43e5e148c740aa1641725c61", - "0x00911cb630b898e2c1a9115f9e45bafe3b819edfb1eab6e15612d14289939984", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x074e913de55f1e46143cb2ecfc580f8d3d3908f200281322b84e21c989cda293", - "0x0761d2736c9ac7670ba905bc2629c6c0dbe988820a4454ff415ba68710f7df92", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x07ffac5f286975623ae0f5b4f597cec900b9ea177273c313dbf3d038df4fcceb", - "0x04df4b8618c7a70ac33138d8d06576a44d3220908af4feac20919ae9215a3052", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x044084305e0c911a40b7cbefe5f13cffe9a99375d1a584c4a2200958050af7a9", - "0x0249c83877371564708ea525b64b1e7e12785460d83364446531c9adcacba5f0", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x04c240ab5492265852c4163ccf60732cc95ec1dc1c94ce6a4ae08750fd0a3786", - "0x002ae167b9ecffa4a9ee667ba816fc13e1650389fd1d5a0031289c4ed8315722", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x04ae508f6f5b4d984150791fa9004ea89fe381334c3c49d9bc78b9c32955acbc", - "0x05bf312dca6e656862ece5c05d037e88f18fe3098d7b6c5da0a96b35e5635506", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x047f3fea50bc3285c0180a7d9366b74d2be2ca717737d2fde36f17d7bad014de", - "0x0622d8c822e455807d71a8da46b7a4017079801a9073d16b18ec0986e0907c8d", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x02bf71ad4d1bee1a67fb300477029f54bdb0e09f78bf2ac2e8afc7465a7adbcc", - "0x06244dd6cad282539049be57487bfd9900bb0d5da805d02b535096368fcb4cd5", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x010af803ff65cfb87e1dc0a062fe31c7defa48621660faaf0268e35a137cdd5a", - "0x055467a668f3028b7c82d3052a595c0a836abf2467614c8b29cd8229785b32ef", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x02f5d0a23f8822d62a36a1f253faf0c661231e843f059918fe419f85f159459f", - "0x04fd679bdb0b7b72b05ff4d6c07e04f377d5d4d687814fa2324f9557faba5e42", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x028932642b5ff9f4cb9d36faef4577b4291841678eaa7ef1c1cf2376680293a7", - "0x033cc0aa065b9623115baa4cd55d2dae00007accdb2695dc4b278aa362581e71", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0269a9618cefad2b9370799f8cac232dec9b8588bc54bf97d99b33d3b304020f", - "0x074a10aa1f74c5e2d5cbda1c88b229051938d58fadf0cea5e633c96201880f7b", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0696c5910ce59323c2dd21f38b1cc80dd7b43a3b9e483d8c40c8a2199a7761f6", - "0x044561b2c51658207d9407e90be4680b3976dd9f950e03ec7292fbe67ef61629", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x05cf4d8be1728a291f71b7a9cb7f9aca1084aa2effa98db4f410f61a1c22ff60", - "0x030935a29b92e07d31dbff6a906cbd6d8cabbf1b14bf2ca619105d87cb35709b", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0410368a143b069b31f4bac0cf435378f99b07f8bc7ba67f50818ed211fe337e", - "0x043d574d10d73982a53e0ec48f09663486e0b9ded91c9661c3796ba72df05644", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x003a62d8f763b62def36e4089458046a49c5ecb91b861549530773e0548ff2bb", - "0x06a10a03ba61e6ac657270465c09aa9526cf1ebe96bdecdf0e7000476a47b9eb", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x00284eed3a17c51e0677d4fe897f056abe9def8af07a4630e6ca5723e2aa6677", - "0x0516a06ac1d5626ed03d2eee9de6f60f0311eca703a99b0fb31b9c66b01c27c7", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0091a4972c794a03417c09eb14f44cf8215f9ae25c7ac80733771952422b3efe", - "0x017037f7ed12573eeca739d9d5eceafa96a6530fa200f859dee2dd0ec21ba114", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x02a2c63b16cccd685f731d06fe93ce2cffb358d34d03dda9a7368185c1eb0c32", - "0x07180baca0ba81284809f92eca1654cd76b925a9242e5d5e0f18d0a55d13c6ec", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x005c8a2f644c59045499ecc254e728cd1700949d3a49621ce07a35ef33090ac0", - "0x028d40254932fb842b411d03389168b6d6e1e8a589597f3b30399628f7448e75", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0115a22c63d5346ba2f558c45b20f953757b5c1f346920484aeeed9908cb96fa", - "0x00abf1818b944833a54370703f189d86862bf73794ef59d2004197ab0ed8748b", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x02393930803623c4c46b96a1b097fbc16bc7dd61035220f4e1746602b3a2793c", - "0x00714b487bb2aeaf69b54413c07e4fc82d4aafca6aa170195d88fa8b12b3403a", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x05f9466017ec09769611389ea5370ad68dda936d3f5816c9e928ff9574abf9a7", - "0x06619b5b145bb5f4f29deb7a4cd68ef4da3995312fa6537f0d01684da4267ece", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x01e6ee1075012ef9057a26182edaeee83e0b6764be08bc1dcf0bca5268ffba7b", - "0x0774127fe3365b48d21dc035f43be38cf5a12fd9f5d846611274a9bae569f1b7", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x023a140ae82662aee2a571f29a39689558a2d06d522686a6d0fab22e55bf8b7e", - "0x02822b2da393040069ecf4d026fb801d3bbc8c21bfa81cda3c59af389eb96204", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0772dfb873ecc2aabd917f53f2ee22879cde6ca36f1b578c3cca65679f55e754", - "0x010609d39a302b213c56e6de8fa936a0cc82f98e400bf23deef8b78d22099815", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x033137e3f3cb5f7b679e4a37566ce7542d9e8a79e76a18ee9957e59607137a88", - "0x06cd489253de59762fb3e4ed73c937d64b83e34af7699292644d5213a7aae7f4", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0693103fdd16ae5d3877d0dab78c625e9b33d7f066dbbe92f9ede01f345e53f8", - "0x010657d1f44a201e6696ebddb35c3911ffea242951c857a86bcdbff2582c3762", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x07cffd5305bf42b7ca96b427b7c91367980290767a37416cdb570dec8a581c06", - "0x0734bdb561d81bc7566a7864eb6a6fd58a83ab3a8992b985c5467508f37cf285", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x01fd5e137a43ddddac2e7c89de7dfdb8337320856cbb9fb2acbe9c4a525789bf", - "0x0401b8979f5d15c3e3a4f92442b34d8dedb6591cd096a71e1f1e94aa3fabd118", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x074f229babe01b4962b3307589c1a13019134b1db6822698388bebb55d21c30f", - "0x0156ae857ab3279f754facba0db36398dffec8c31e5e160473198f2f891b7531", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0334b9fe3a5fd99bc966ddd1309698fd32afd1f235062f2c275b6616a185de45", - "0x00221a60053583cc0607f6f2e6966b62fc9dac00538bb7eb1148e007a92116d2", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x06c300a27f89d63ee9755b2ea96328828a9dee18aa344b49f9d902c128868dea", - "0x0532fdec4e0ec3cc2e2632b385d51585417e2c4facb5be357d62a38c9e79dfc4", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x07ad710ba002a67c731efbaba2149d16fec5d2f7aa3d126fd9886172e9f4ea30", - "0x03a10f8e902a7a13aec94d66415347e1314f9bac83a7db176096b809b25ffb86", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x07aa19a47edaed5f8a09a767d4ff4c3f4d225514a06f739488f09cc8460eaa35", - "0x02785a4061ff1058057a0dc1bdbe3f09eb5caa98450bed410d715e8244be5339", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x059481b045bc04b88d6529355f25c4e4beeb3930ff686d5b3d200ff85f482514", - "0x01ed00514dedced569889e1f55ce9da003a41ab578ea8e811df97d210eb70384", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x00b9abe8f72134b4968dd400215bc7f2388312749799dc2b5adfeffcd1be8a09", - "0x04638d68892214e2495b2c97cdf406679e45c7b4ec7c32b00952fa0c6ce1350f", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x04306dd0a184a3283c3097ff8f7434cec80912e9dc04b7df21ba73fda9f8e6d8", - "0x006d42bd3d1a8dbddafd09e872e2aa3891ae79ec939dc1b382196bc21c4ab749", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x022a254c19ec896f55126618ce4dcfb774a5d8aa5ecc6f24b08285a757073754", - "0x07c37ea255904bebe720be3a505bbee4dcfca51cb48f2bb253eeb505772a480c", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0628a3c0a1e907102a9df29030c11b64f919f76d315a6357fe9b25f3b3ca2b1f", - "0x0451bdd071feb4f2a7c48e830122eb35b2a51de0f8e424de16fb3b2382ff0812", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x06380f53aff40fab369e19dc791e10b057b6d26249239b1396d3727c971e8bd2", - "0x03c181e0335ef446b068f6b9323983e3137eda28a96c6b9f2a153532c953082b", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x03d93322514ca66201f1de97b06783bec46306b30bf9aa4ed04e627d2e011102", - "0x03a9ab56ffd41d08a699289335bf112d8180e99df53f52cc16cb3d756d2df340", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x01c349614ecd6aecea874e846b8afc0ad521c5d174867426139d14d068f844ca", - "0x024faa512032a1da2640c8a794193d057b1fa4def9bd638a2ffaa99791018020", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x020c996bae0f44b76c268b30d7143cb70985d6037b46871a4822b7f54cc0798e", - "0x064e17565fdc925b802622ebc9aa0bbeeebde1b5f80e29f055d6fbe40cac9567", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x003e414fb4df29b1eb0ae3de7cf3542453bc3d0ddb8e47b5c85eb9f38479e222", - "0x0482e93b3bf4e79bae26354be0475f91eeadc8181dbebc29e5b1eabeae6ae547", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x01c3f2124e1135c32a426d1d14e471edd9e0f2c7bd703ee123cbbd608e8c4be7", - "0x003cc607a3c3f1ab68dd5fa56c65996002721b8ad8ad4b0dd9e5b1467d316583", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x00294af33272ffcee0b56a436de1b73759cbddebef4c07888b42c2f92b0b68e1", - "0x00d837164311d5dca8d37b99ef9eb22708643c83d1cbdfe852f63ea07b06fbad", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x064f4f01829229361242a45a7e2bc1723413d30f5e7556a64063caab225f62d5", - "0x00fe83577da128e3f95db4185068217149477be155f10c16d83d67d29480621d", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0753bdb5439a19bbffdfa02b1dc24e8368f22d0a8276b109c11e6feb26f56f39", - "0x06ed396231af93647633eab467f1a034f38e76823eb85baf97cae56e2dcd9f75", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0767c032379049e1a5389f2c6020e4f4ad4ba0ad7f83df20c3ba5f9d9ae9660e", - "0x02fb39eb2c2a32fdf867d560f609882400f7f819445f330cbfc2251287f2bc10", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x032f33716fa01f599798dc5e45a2af6defbace1eda8feeabdfb6af8531fb65a0", - "0x06eaae27fdc37f1c53a932e832406abb765d0239bfc794ca9702ed59575d786c", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0218e929a70b88ffc36750d319ece96708fd068e62ec510e23fd21f6c150bb15", - "0x05e1fac562d3755afdcaf2dcee8b221f2bdeecb76d7b6bd7870c9ff1af205d1c", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x05674f0cb892b733fc0b50e121d8679afed0a925c32594cc65ffe83bebe7748e", - "0x07fbf0325dd38dd94905adab2c52758552292a6a103d9edfcb11938828e828c8", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x07f8526f9b86b3f1926bacb2b2996637f0a4570b6c4cb35586c25540f681511c", - "0x01a2a025dd148f3665d8370f8078b0ec65bd6d4175fbf3ebb61ea76527c4ac6c", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x061e6d4a1ad813d8a6f01472b8e91b5d61a4471c174e66fec05010336b4f555c", - "0x018021f27153b69b2b4eb062aff1f455ca85c8d996828af0dea96e140862029a", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0681af52d1da820127d65c65915877b64665e3a9c6d8750998d23a835caa0c77", - "0x014a799e0d0bf61acd05208752e7ed76608f537f3e183dfdc99349cbe930e08c", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0063ad7df133e439ae69a479becb8f5fb5cb6d72507771d91bcae9e6d3b0861f", - "0x074eb8c00e0d1112cc9a91d4ea16ff471f36a1fb95d3952f3e27544fcb27125f", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x02f5bb99c2d30502b9dc0a16b31b9c2f341c1ba040004747e6f9bde66998aef9", - "0x01fb7ea7c629b1e69191d639f98fbf22ffe03ac928cbb873964503a40b1a6a78", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0785bb5dec3af0323f28070cf154f208a54680c5defc317932df978cc308d180", - "0x00fe48a5f6c9d6c45d4f4eb0e7ce8922f3ff5d0656c9671ea75423b05710bfbf", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0097c2d77250d0ccbafe4fde9468e68ca7f5a7202f23b42bb4c0b0543bf55fcf", - "0x06c4c89f5e6648080673c3fabb411e37d8aff4302ea4fd589796652273d1f0c9", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x04a8f053573a0a74251059d0229d89b6660407ba0b491779fd10f87a5117c81f", - "0x021b70112485398bf67ec9d733df24a1df30dea718a93b786f41ed04e3ae3c5e", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0726c01ec4a08df8fc8de173311f50d4f3b97c5a9cf68c1536146f827db95ae8", - "0x015013cafadefa7f1c4e4dfdd70bd4d3979dd18bd7f0332572ce2a3fd8773d12", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x06ea27424e66192d111517cb581b98205f8452681e44bdade703ea7f77ea2e14", - "0x05d5b77d05a9368ee976709c8d6192aaa3d3512181727a5bab9999587b1f91a7", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x038ac0fbfa98937257460db7e6645d7e5112b6fce7234813fc8a704e8ade8da2", - "0x073c0109f86048aad08c443f781ae60ad13b99f7b9cfdf3128fe6d6eeb799a7b", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x077fb5cf98618dd79dacb904176b885a5aba901ee7962ea72486fb53a30c1770", - "0x00ab24917373fa01bea67260e2be38fb6c2869049738d54bbe1f77668dd97946", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x06180ffda23152742bb35d3fce07fe8857877558a33ef2e4c43a2a7bdb47c94e", - "0x008d2f0194ed6b0e6f97b356724a49241a56b51d7727a49f503870aca15333a6", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0035ea942b767b19a479d8c9a5b773efa4d273ef9486fb21fa055f9d725e5e8f", - "0x00561c91da1cb0415877d90f7e31099b8d507161193d75b2012e16cce6e21dd1", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x06f6d3a38621582ace092eb50ecfe9eff265df141ebdcab8653299116fcea291", - "0x04a1bf3f39bc919c8f1b720a0b1ce952cad17f2ba98308ee6b76dd9b6f3d7b75", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x02700a13c6a1870056a00b224b7ea23b176c030a732880c549b93baa1637e47e", - "0x00ad9f1c528777eb7cb5257bd17a03e9a3a516bd4ba1dbe0b7c0c178b72f32bc", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x002fbf9dfafe04cfb6abcf43fba2efee5035f6c33e11497d6946a02fe2c818e8", - "0x04b5bf53516fd846d3ecabe2c3f3bb6953d961f83f97a16fe38b5663ce28464a", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x047d082745dff02a344c0e76ed84c54082bc4583a266f03612e1ad26b57f58e5", - "0x076e1102bd546ff9dbece7ccb5c71c15143eef9dde0e53fd3861a9307d87bd99", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x02624d1f4f49c122349478eeed032b5f506e45b66859f892a65ba1be672da661", - "0x00e21169547a5c1eb9d0f2ba8ac9a1af96d5dc8bb56cadf9828e02be359b0b3c", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x05d5a067c649215f6e08ef147b676689f38303370df7561559b0e7aa86066907", - "0x06fb17040e0c920e7492c6aa5e7e1ad679efefcfd9cb2e929d60ecfe09c3fc4a", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x011f9f7a2b7c113c957192e590909608c531574e8536777939c1785ff7f60905", - "0x057659297edf957dd0b96a98c44b094656378e840930483a6ecb5cb53409e013", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x034ac72d93631c091aee7e831fdac1ac298e766a71dd34f0e5bd6fdefb271641", - "0x04b5a5f110182c651cc051bc3e6ea664b0001204c6379f8da98eaa51fddb7a19", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x06a307fc28e1df8d9ad01766419e097797d65cb674436fa1c8f012d3de2c2a1f", - "0x026911a635ba824db004875d79dd84834a97ac12643e42829015bf88c1fd6f05", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x02a74860e3336d6db916555894cc8028f41508812925db1925457afe40257155", - "0x05f8da573f4c39816ce2dba8a20224223a7cfec53117ec78973930c0e9b60244", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x026c0fa942970f2a2781d1a95ae782f13a5ef0380a01f4fbb4520a660b1498c2", - "0x03bb5594e2eedbfec9f1575526ca86f9be5990a681aefa5a69122cb5a4940e4f", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x04d2b49e1ed0799f719b8269f092cb489a466a645bc0ccabafdc678864c176d7", - "0x05410083df7d256f18cbf5697ae5e52c31e075d8a3b27e21d6f5177ca882f6c1", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x046291c56b16d5ade6e075736e8ea4fc258cc92e8d8b3ae087c47b731a12948d", - "0x052405c4a55e978b49b7555c8955e6cf3fa3eb6d14d492754b1e6990c8fa2bb7", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x04186fc02816091642948079ca033be7c1f6477f734ff8a0dc46400f952039af", - "0x03e4e732362d563429087f176b243d6f7e69e358e2d165304dbaf39afc0e968c", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x05b9e5eb2eb540583d887f1d7ee02965351e8015a9f8b29310f25e379f4eb752", - "0x07493e636ed3ceeacaf4d5aaf246c8feef46e45799800c358b768a3088c42160", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0110ecb9fbf6c333d168cee473cc5ad98809b6cb9eb5d1f6cd28ab5fab504fd3", - "0x07e3c54d7533d9f8c3310f219dab0cc3ea4d39b418a748eeffd6bae2b8637a43", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0097316b88fc88e048ea7e0313ca84c09b5994d766729deac7c01897dfbe6b28", - "0x03258bfb171435fc9f8487ba95c99419ec0742b1c2884704201d3d5fbde6d015", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x06dc30a8e9c2f659215036ad6fa368f648ff3fbd8c74212c263cebf12c9edd65", - "0x019f02da1639ca6a86470229aeb33a32dc13bf76e779356a0a5416952a1c41f0", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x07e1c73259b587b4113b911be0b5142b600f0558e9cd3e34427d25aa4b6a8265", - "0x03597920bbed1b6e963144bb42b1605306859a0770b76d6799f0f7255eeaf264", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x059802c51324036314471e33c6342a65f4737dd53e88183a7c9211a4dc6506c4", - "0x07e2b256ba1ffb790f6931cc37c2e9a7a2c3264e3c0d3f3f3c2e581fcf04ccac", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x022f998bad499bc94a72855b43b07ccda3ed1225976c2385accaec5323cd840e", - "0x06efd0615e05ce0d25af73c6384e461c2fad62d6c781305ccc7889552f68b6a5", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x03ad3caa8af2cc03b74b36a3561a5faf853f396725f586486b541bf0d77465a6", - "0x04c189f1c7783b424775d6ecc19e41c90133f9ede5d78179b6edf727ac761c65", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0195277d2b3aab717855be7ca829bdf8343d7588d654bf9e00f86fa6d4d375a4", - "0x01a01694e0108c982a2fb26709aebd10fe5cea3238ed41f1eb5b30900776bc80", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x05be4d711b80da70e6d3ac493250bbfd16f20b25f31919b3a91cf14ffbac1096", - "0x07f55a0919f082e8885f1515e83c5b39b6022404503507498e1b4422d79c43e2", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x02605125b95ca4ba93a21cbbba5762898a7cf9e988f07ab9e64cb3868e3b139d", - "0x062f0ccf55b9fc0eaf9736fc8ee484e2acdbe259813af9803cf815829a5e9d3b", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0152d0f558c392294fafc66ee37734441265afb524c357261bd2914b1ce95e13", - "0x051372ea8979979ee27713a2b9e0645d17de772b1096d65b7e4b8f966ae19832", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x01092bbbf206f2a3068167c3dd99a72de31e206f6c504c071c8214d105ff814d", - "0x0309f489f68a62089f53b96df5d4fbc3ecc5a1a42eb7ece0e49bad17ad490ff4", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x052b6275330dbb176400ff1141e101fe6b8cd03ce9a9e2cf83c6ef9d5ff68243", - "0x04fdf066a090c89a86f868f1a254dd5e732dc83ce20d0d69a1405599b18886b1", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x01b1751727e2f67fd93a9d8c4a27d564b09e4beb4de2b3aa70f973ae30e06205", - "0x07822725cc556fdc79d01f60458f126699333f72c85ad980f05e7224f5ddd285", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x006fe73b22de12675a6d0cdd1ed16f0a90b0c94f42d3ab00ec51257acdcc1fa5", - "0x06377be34f74b709d92105d6df906c09299ce3a2b9169e05ad3f5fc687c8314c", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x02abdee9409d9c92559ca3f4e6bddd649c31aa09b90bfcb4a612af491241e18d", - "0x03ffa8eac180a29de3f8a69efca84bac046f921f5725e96a6ff0530be1436aaf", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0678a46d83bae6169163dd84af3d05de9688be3695620a3e496501bb529f36f5", - "0x00e6d1f41cd449c064ff71a1ec128be6447ed6ee52a03b43582975877b09a6c2", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x07a6b0dca8e7d7f7ee32183f46f796de5d543dfe2a5428e06c40a815372eddb5", - "0x00bb53113a8464e93de7c3b0f8a9e3bfb32c49f53fbf1ef9e5860acd82ef2415", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0773cc68a7d80cabc25a5000f6445c4e06b012804e0eea2e8177cd37d1e84865", - "0x02f8b5fa66e1aa16933d18b9c7621cd1aa639c6f7655cf392e512f2e0594d8b1", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x024b007862a191dc88844e852b6963087840fc1b72fe11002c77022243d4888e", - "0x050a7c81e0897e8e804d405e139eb4ff1b8d33a855f5a02f39e5197671919483", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x01e25793355a7c264b72a76f4c20939ec8a6725c610b7f5999b605451d325fe7", - "0x03aefb6927c0f62dbffab880486f2a71cdd555c16e8b2ef36f401f2285862b47", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0684f9041f17388fff796fb3f57b5ed9705b8d4aa3bad58e24a0a1045f530f9f", - "0x0395d115fdd8d5d35166e7ebf31c7523d0eec45a65dd9032f0577ad0a2f09fc5", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x07895d9967744712a9cd861e38618f8da67d17cd1d78b4cce6b860f6ea1b2d3d", - "0x01ae4e4f890b0f3ea412257c6056f37646018f301b33ee608401b75aa501ef26", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0376313f27d00bb1aae7ec991745efe6ee28c6b50de0c6cd9845cc4bb4f83543", - "0x06a8e0a9389ba528b156fa94ac090a895d7b795818d4941c29415d9e2984c547", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x00a80380c71bd466a696b3f0fbf02817c9459d9798f4f3899cf32edf647fe066", - "0x06a09805e814e7cdfc76eba4b79f1df5ae559e0f0aba9f728d3cba4ea5c57471", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x07d83445f0f78b41b4067a3fcd14f90dbae07b3bb347daa7b6f445e824a863e1", - "0x0397ccbb659be69ce74ad88fdf9ad180179217b839137627d7454f93edcacc9a", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0223694b921d247d989a79b9b2b2f07496036c40cb043eab074a9d6a2cd2ffed", - "0x00c247217f1b1df35e30d9e15fdaadf42d6fb0edd3a5a7e265d4cdc426c120aa", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0142a3df8be4e1eb4c83eefcbbb337ec4f546c0c285bcce6a7c49ae0f183cd82", - "0x00156ec9736364dd8b0af4bdc75f26a72dfc10dc3abbc7b849a66d33dde85eef", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x01e077f82822eaf18acc39bb4b5160e0e26ebba7a6a1ca12f4ecf955004fa861", - "0x04288219a47286bca7c3b14264870f25e853562756afd05c4a7b8469f92f433d", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x00eb141980706624ada5e79c105a6d768c2da5292f08f6bdeeb768f50f9b79d1", - "0x0239ef0bf0e450fa6e6ed302ad3a4daa900894af273748c46b3c7de13aa3cb82", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0102333620df278c6714bbc880fc087db58c1b9b4d77ed4d61b32a74bfc7c3e2", - "0x06a77d37727ccf71c2caeb151faf4404d4b94e9047f9f0a7c3966367f3b53c65", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x029ebbb9c4c259059955dd95cdf957d5656764769e57b8592e1a7eedaebafb35", - "0x0153d9d2dece6117202fc0e60ef0668cd519815ebf1b0f4b1aa8fa53bdd71d18", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x06880245b13e9e584f0ab92c5d49d05606f8b3699433650e130408397b4eca0d", - "0x034e409a698ee50bb1f2aa7faad94e2407693c7130dc6da9750542229b62d06c", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x01dc97aa51567f0b142db18d1bd6d36d0ded26d45b0bbb5ef1e0e9826259d07a", - "0x07b0dff1adcf7cad388ef7630effae8567ed5678e7ba2830ae19921c50ce8953", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x05cd4bbd716f36338c131f1338cff9ad5b5fe829ae0d9096d5ba42606b9e3e94", - "0x044cee813ce858e76a48b55e95c1f3c30be681b2b6ce6692b4c3d3918bce27bf", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x02eb44c0bc24a14f69c357653d68de76400ed4d3cc124daf1fcc29bea0cdc8a7", - "0x04d4f21c3132b54a032f499890a16796be93903cc81f275651f1233ee71fe63a", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x035fb9f7f9953b229c458b16ad65a9d0edd4b7ab3161f70aaf1553a156f48d87", - "0x07f600967a3250660d2b19e02eed88528b6bacbf2650e0004ff2cf00f3248fbf", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0003d6d14faab89c39e08cda4d3f8491c3d7cba7803557d37a83f7ac3faedd89", - "0x06fa536f469981e237e20898b6fd7536eaac249ae67386fcb0d0f62a97ddce62", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x00891626f466536929ee7eadcd18b41925706dedab7528ed5f0f7abf039eb9d2", - "0x05f73d11c141c933a35b2d0d06e5cbae614a20d17dc3b439f8bcdc3413c5ea37", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0215c23fd3f073f870e5e80303967391bf173f8adcdbeec72d131c557babc203", - "0x010634332e9d9439a321597dc5b0fac9ff478834c3d6e281735f21a4a5e13266", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0698f29425628eaabc3a4ef3780d43cd230d3ef0eaff8964eef505d959aeef90", - "0x02072fc2e9f1c283c0fd131b8198fea44e41d79884086a6be6510dae22475177", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x021ea0bdc1332bc36e6aeb43be9071651c27e4ea2eadec636c8d818d4af72a36", - "0x03a523d9643dccc6bb9c7c58413312caa3e60ba9c7c7f0177e0f3f469a3241e3", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x07348a14b527af6e4ad8f5e706f7eb69f280899169fea261ffeee1d25efef0bb", - "0x05e6599a430ef13cd9d04167ad20086a1f650a00c5f71dca7a2501fb40d400de", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x01a1aef5ddfca017179cc483c40bcce2c58c05e18dfee1f37aa662c6d8453476", - "0x04f959c61289dcb4a29b5e39f3d74a4ff0c9135fa3c83f4e175c6f392a16d614", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x015104dd2249322cbe7e01744c30b804c51bb1aad71b88b6f1d3928d91d58c8e", - "0x0269e6543d36e448b79ac85f9fc4013df675b38e41210eaf4d47f07f1856e2ad", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x060deaed1bffb6190beed40caaf2bfab5e43d3707aff7ad3f278d571aa247eae", - "0x00e41f71ff254c1418e6a66992af307789fe04d6606fb2670900bb1a089fd879", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x02d1a26e2e133a7ccbe8e3484cc73d19acd7acad3502d3dc06215afc61b8910b", - "0x07ed72de5f803abf62be96c84e50cc25433cfbf450439d7140edbfcfe31982eb", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x07c67de0144ffa919c8035e9959dd9eeb7e298af0b83461f66f18b31f71cbb05", - "0x032f16e957f1a6ed5eb683575851bfebc0f2a207fd5a929646af7c8c8deb65a4", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0728af9c327b86851d639f8df92adfae61b255f082c8f77e6234acac9eaae44e", - "0x04c3e6b7383fd386d7961c3e8bf8b3bca5cd32a990f17774fcc63032c4b346d2", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x00121121aa2867bbcb260d035a5388ae4bcaaaa68a99e82d2aa58a4cbc631b5d", - "0x065611da2bd673b9d1bde2004586d3c308427825602be53b4d44f4680280c532", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x017f4b0609b8b376f283f8683afd66abc94c5f06fcc4e9cd9f49508f5a7cf0a8", - "0x019ccbd820dbf69b50c7b524b380d2ba96e68fddcd71a57193c99a8536f2af0e", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0550e5ad076f4b9ef07336c5e5d2807d04598a62b74dfc5943fe71032fc1b4ec", - "0x024932ae07ee6996ab9c277b748e6158b6bb0a2e12aeb1262d81ed0bf85c3f56", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x03b4a38a1b12a2ea9aab01e1cd698cc14a0f3f07635138a731c40da8db8ea2bf", - "0x07e7c92ebc93a9e355f70dd7dd4f5a13001249daf80ce2083496fc364d6ed72a", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x01e1fac4a1646253fb1332fadc21fbdd3e3a24a840d129400f520ae4116a4cf5", - "0x069c406f9f46576afad68808de0ab7e8922b6226af748e721d9097e21f1800f3", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x05db0ddcdf79ffe74d6454c12d2bc60b06776db03c75dc413f5be42ea9a91b5e", - "0x0134c3d6c699841f17306835bb193785228ffe7ab212a01a861c56b086a18cec", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x053f2dded21057b8d6715c0fbe18fb7295b9c3795fb5267ecb1a7345faef498c", - "0x05d098198a7ed3b31aa978e43d227edf64175b8f4111b5c8825140d309b3a592", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0626814e320fb5bea505b248fd1c1389ad586c1cfe04923fe2f83173e915f4f8", - "0x07ae407a926e887206a8b85cf485f1f327c9bb8ccbb6897024e2d122877d8ee0", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x022481d73d948702b1d11f662cbefe5005c2e17fc3ec9419c8e881b867626d9d", - "0x0420d41966fd0c27edb72d1c5eb9097694fec7d18039098b0e87e5fbb158e24f", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x02688070f09c9725c2578819c5a49b47cc024cb45e63f2d4cabc5d926038fcc2", - "0x017beab7c707c79b047441389fdff6d4872e74359bc1da247140de9fc03375a9", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x016a69a7cf1f65594afadb02879b3e794748daccd696a1735b2d34122af395d6", - "0x04dd0e24a1432070a39ebc7fd27260b6ea21468e9a90a3116103c4ece6e777d9", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0023186237dc7d3b570cea645282ad4c359731bbfa54e7f036426bf6493812cd", - "0x07d1fbab7e61a22d3b00993290d9f4cd5d820061573e787f66c2cff9a18e1eaf", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x054442669b321b67eecc04ac0d44eb106e94eec55f884743853da112306db35d", - "0x079e135fc490bde123cfb435a5623d5a91267bb78af42e7c36bb7b4f1e8738a6", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0208ada8917dbbaa4fd47860342d88e2ea48f9ad8496245e823195c503c9e591", - "0x043449048fcb48958593754d47444c8006ade78bb3653620184e587357b0876b", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x02e34ab0e42003b89aa2eddc291984690086e2b2ccd9652d7a3a963031d27cbd", - "0x034112f9bed212363cb62d8dabcb64d42e07239aef78be28cc4b6df8df33952b", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x02d7d88f58ff5a901f89e893920f3fafa5ed0bad3e26c981be99f7297c7364a3", - "0x0527edc567207eb33cb94b0a28ae240cc9e482f8282f635e15400706a72dd283", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x04a17894569ea9902cd0a985f500120364bddb3a34642b1429a9fff6d8ed7f77", - "0x02234e91e011d378154fe23ce94700b19bca1ccb6b2f979d302ad72eef531bc1", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x04c0615b6ccfe7165a1ff4990bb52a5ac593a93b7f7d5523ed9e78d1cfeb1983", - "0x0230006751153f0a50c375d114dff3a493a8c09f524687a7abbbc6457c918bef", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x00053a33f7ab1686308771229bc41f2e8da1bf6ce7262f73a95fcc4d7570543e", - "0x02843c0ad3ca1580d0d26099e81222946e8d1a0551dcfff5b097db60483ebcf3", - ), - ] -} - -pub const fn points_p4() -> [ShortWeierstrassProjectivePoint; 15] { - [ - StarkCurve::from_affine_hex_string_unchecked( - "0x054302dcb0e6cc1c6e44cca8f61a63bb2ca65048d53fb325d36ff12c49a58202", - "0x01b77b3e37d13504b348046268d8ae25ce98ad783c25561a879dcc77e99c2426", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x013961b56b9fc0e412e468c385c22bd0680a25624ec211ffbb6bc877b2a6926c", - "0x062f7f7792c77cd981fad13cb6863fe099c4d971c1374109185eae99943f16e9", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x075d976d5ff2a1d52c24a9ec8ed35ba60bbf630716b3bfbce6954f3fcb9dfa87", - "0x0250632783922466251dd6abef2544ea22f5a89b2e97d0c53408a0ff0f45c595", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x047abd7308c70659af3f00fafe6837298af3cb530b6c2ba710ffd07a6bc1ae98", - "0x075d0c8a7377aa9f0663d0c124a5659750847afabc29e39893fd27534a4a03cb", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x079e6dd2bb4e7ca86d651d69277c8ba45dcb0081e28946933c1624f33b43c297", - "0x03da084b3dd2e894cbd3a95cfd0d3ce98dd3d679bbafbeef0359b192a658b782", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x04b0af87863a15652fe3e0c7a9318f91ccac4dd1d7eb766be4769093cd5ef21a", - "0x0644cc5a94960f1aefd34fe3a75c19b5df4d690bff6e76f1122ac429b29e8e5a", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0576bd6a9723bcdac5ae9423c82a3a7306dd8ee518b585886c31dbc5cbc7ee73", - "0x00a711ed74dde7ecd3ef8f81909ea1c1072af38e2729cd088c2e286f65e85d2d", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x02c6276b764fb398fa555857dbe0ce0ec18fab7a233bf23851295739801f0585", - "0x05d8f4897ce44007ec5bfcb9aeb78b8f6e1d40a514f72d213c9300d2770d2b8c", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x012d65f3000d0a98cc3d172fb031a07aa63f391709b211e5c38147dfacfe71c5", - "0x05e94d3f940a0601f57fbe70ac424b62a6aa6af065d82b9394cd909a39630fa3", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x009628a2d5078181628e2a6e7214f432ab6ad06d7446568e1d0e1f7eeafdad15", - "0x03812de62dcbf40d0fc38b3c22da6978132840c3336d9724dace26a91c61467b", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x05fef03c9ba206f38c70829708e17a04052a699db2bbed270d3b9f641044b3e6", - "0x034a81caa74de5e03554f0feee581fbec57f3cb99b336a6b0b7121998947770d", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x062423050232c4818bd34bbdb4a8127899655dfb1dae2172bccb278a7c3a0b11", - "0x065b076c15d5b8f7f24eb25f46b8147e6384f9a28a957e77a5bc1e55bcd31c37", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x04aa4c2a719175731431d916ceaf28d3386a4cd73a8fcad057c0fa95b5803580", - "0x037ba35dc893cd7909f28c9c0965e0029ecb3dfcaa2bc1509e5eebf984112c7f", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x0356d8851b470a0b475f5e637bd7435cb794acf1856dc37f61fc80a540861229", - "0x0001be1102267114ad5e85097e7e282a459707d4f1a7969587b59b692aa64fa6", - ), - StarkCurve::from_affine_hex_string_unchecked( - "0x049d7273e1dc085a9a502054d8c627268faad04bee9d554e8bc826487c0ee020", - "0x0283007adf98c5aa81d8dc112ec0e71cf97cc63a83887ac46cfa56bcdc6454bc", - ), - ] -} diff --git a/crates/crypto/src/hash/pedersen/mod.rs b/crates/crypto/src/hash/pedersen/mod.rs deleted file mode 100644 index e615e158f..000000000 --- a/crates/crypto/src/hash/pedersen/mod.rs +++ /dev/null @@ -1,104 +0,0 @@ -use lambdaworks_math::{ - elliptic_curve::short_weierstrass::point::ShortWeierstrassProjectivePoint as Point, - field::{ - element::FieldElement as FE, - fields::fft_friendly::stark_252_prime_field::Stark252PrimeField, - }, -}; - -mod constants; -mod parameters; -use parameters::PedersenParameters; -pub use parameters::PedersenStarkCurve; - -mod private { - use super::*; - - pub trait Sealed {} - - impl Sealed for P {} -} - -pub trait Pedersen: PedersenParameters + self::private::Sealed { - /// Implements Starkware version of Pedersen hash of x and y. - /// Divides each of x and y into 4-bit chunks, and uses lookup tables to accumulate pre-calculated - /// points corresponding to a given chunk. - /// Accumulation starts from a "shift_point" whose points are derived from digits of pi. - /// Pre-calculated points are multiples by powers of 2 of the "shift_point". - /// - /// Find specification at https://docs.starkware.co/starkex/crypto/pedersen-hash-function.html - fn hash(x: &FE, y: &FE) -> FE; - - /// Performs lookup to find the constant point corresponding to 4-bit chunks of given input. - /// Keeps adding up those points to the given accumulation point. - fn lookup_and_accumulate(acc: &mut Point, bits: &[bool], prep: &[Point]); -} - -// FIXME: currently we make some assumptions that apply to `Stark252PrimeField`, so only mark the -// implementation when that's the field. -impl> Pedersen for P { - // Taken from Jonathan Lei's starknet-rs - // https://github.com/xJonathanLEI/starknet-rs/blob/4ab2f36872435ce57b1d8f55856702a6a30f270a/starknet-crypto/src/pedersen_hash.rs - - fn hash(x: &FE, y: &FE) -> FE { - let x = x.to_bits_le(); - let y = y.to_bits_le(); - let mut acc = P::SHIFT_POINT.clone(); - - Self::lookup_and_accumulate(&mut acc, &x[..248], &P::POINTS_P1); // Add a_low * P1 - Self::lookup_and_accumulate(&mut acc, &x[248..252], &P::POINTS_P2); // Add a_high * P2 - Self::lookup_and_accumulate(&mut acc, &y[..248], &P::POINTS_P3); // Add b_low * P3 - Self::lookup_and_accumulate(&mut acc, &y[248..252], &P::POINTS_P4); // Add b_high * P4 - - *acc.to_affine().x() - } - - fn lookup_and_accumulate(acc: &mut Point, bits: &[bool], prep: &[Point]) { - bits.chunks(P::CURVE_CONST_BITS) - .enumerate() - .for_each(|(i, v)| { - let offset = bools_to_usize_le(v); - if offset > 0 { - // Table lookup at 'offset-1' in table for chunk 'i' - *acc = acc.operate_with_affine(&prep[i * P::TABLE_SIZE + offset - 1]); - } - }) - } -} - -#[inline] -fn bools_to_usize_le(bools: &[bool]) -> usize { - let mut result: usize = 0; - for (ind, bit) in bools.iter().enumerate() { - if *bit { - result += 1 << ind; - } - } - result -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::hash::pedersen::parameters::PedersenStarkCurve; - - // Test case ported from: - // https://github.com/starkware-libs/crypto-cpp/blob/95864fbe11d5287e345432dbe1e80dea3c35fc58/src/starkware/crypto/ffi/crypto_lib_test.go - - #[test] - fn test_stark_curve() { - let x = FE::from_hex_unchecked( - "03d937c035c878245caf64531a5756109c53068da139362728feb561405371cb", - ); - let y = FE::from_hex_unchecked( - "0208a0a10250e382e1e4bbe2880906c2791bf6275695e02fbbc6aeff9cd8b31a", - ); - let hash = PedersenStarkCurve::hash(&x, &y); - assert_eq!( - hash, - FE::from_hex_unchecked( - "030e480bed5fe53fa909cc0f8c4d99b8f9f2c016be4c41e13a4848797979c662" - ) - ); - } -} diff --git a/crates/crypto/src/hash/pedersen/parameters.rs b/crates/crypto/src/hash/pedersen/parameters.rs deleted file mode 100644 index 7d302b128..000000000 --- a/crates/crypto/src/hash/pedersen/parameters.rs +++ /dev/null @@ -1,49 +0,0 @@ -use lambdaworks_math::elliptic_curve::short_weierstrass::traits::IsShortWeierstrass; -use lambdaworks_math::elliptic_curve::short_weierstrass::{ - curves::stark_curve::StarkCurve, point::ShortWeierstrassProjectivePoint as Point, -}; -use lambdaworks_math::elliptic_curve::traits::IsEllipticCurve; -use lambdaworks_math::field::fields::fft_friendly::stark_252_prime_field::Stark252PrimeField; -use lambdaworks_math::field::traits::IsPrimeField; - -use crate::hash::pedersen::constants::*; - -pub trait PedersenParameters { - type F: IsPrimeField + Clone; - type EC: IsEllipticCurve + IsShortWeierstrass + Clone; - - const CURVE_CONST_BITS: usize; - const TABLE_SIZE: usize; - const SHIFT_POINT: Point; - const POINTS_P1: [Point; 930]; - const POINTS_P2: [Point; 15]; - const POINTS_P3: [Point; 930]; - const POINTS_P4: [Point; 15]; -} - -pub struct PedersenStarkCurve; - -impl Default for PedersenStarkCurve { - fn default() -> Self { - Self::new() - } -} - -impl PedersenStarkCurve { - pub fn new() -> Self { - Self {} - } -} - -impl PedersenParameters for PedersenStarkCurve { - type F = Stark252PrimeField; - type EC = StarkCurve; - - const CURVE_CONST_BITS: usize = 4; - const TABLE_SIZE: usize = (1 << Self::CURVE_CONST_BITS) - 1; - const SHIFT_POINT: Point = shift_point(); - const POINTS_P1: [Point; 930] = points_p1(); - const POINTS_P2: [Point; 15] = points_p2(); - const POINTS_P3: [Point; 930] = points_p3(); - const POINTS_P4: [Point; 15] = points_p4(); -} diff --git a/crates/crypto/src/hash/poseidon/mod.rs b/crates/crypto/src/hash/poseidon/mod.rs deleted file mode 100644 index 67748081e..000000000 --- a/crates/crypto/src/hash/poseidon/mod.rs +++ /dev/null @@ -1,219 +0,0 @@ -use alloc::{borrow::ToOwned, vec::Vec}; -use lambdaworks_math::field::element::FieldElement as FE; - -pub mod parameters; -pub mod starknet; - -use parameters::PermutationParameters; - -mod private { - use super::*; - - pub trait Sealed {} - - impl Sealed for P {} -} - -pub trait Poseidon: PermutationParameters + self::private::Sealed { - fn hades_permutation(state: &mut [FE]); - fn full_round(state: &mut [FE], rindex: usize); - fn partial_round(state: &mut [FE], index: usize); - fn hash(x: &FE, y: &FE) -> FE; - fn hash_single(x: &FE) -> FE; - fn hash_many(inputs: &[FE]) -> FE; -} - -impl Poseidon for P { - fn hades_permutation(state: &mut [FE]) { - let mut index = 0; - for _ in 0..P::N_FULL_ROUNDS / 2 { - Self::full_round(state, index); - index += P::N_ROUND_CONSTANTS_COLS; - } - for _ in 0..P::N_PARTIAL_ROUNDS { - Self::partial_round(state, index); - index += 1; - } - for _ in 0..P::N_FULL_ROUNDS / 2 { - Self::full_round(state, index); - index += P::N_ROUND_CONSTANTS_COLS; - } - } - - #[inline] - fn full_round(state: &mut [FE], index: usize) { - for (i, value) in state.iter_mut().enumerate() { - *value = &(*value) + &P::ROUND_CONSTANTS[index + i]; - *value = &(*value).square() * &*value; - } - Self::mix(state); - } - - #[inline] - fn partial_round(state: &mut [FE], index: usize) { - state[2] = &state[2] + &P::ROUND_CONSTANTS[index]; - state[2] = &state[2].square() * &state[2]; - Self::mix(state); - } - - fn hash(x: &FE, y: &FE) -> FE { - let mut state: Vec> = vec![x.clone(), y.clone(), FE::from(2)]; - Self::hades_permutation(&mut state); - let x = &state[0]; - x.clone() - } - - fn hash_single(x: &FE) -> FE { - let mut state: Vec> = vec![x.clone(), FE::zero(), FE::from(1)]; - Self::hades_permutation(&mut state); - let x = &state[0]; - x.clone() - } - - fn hash_many(inputs: &[FE]) -> FE { - let r = P::RATE; // chunk size - let m = P::STATE_SIZE; // state size - - // Pad input with 1 followed by 0's (if necessary). - let mut values = inputs.to_owned(); - values.push(FE::from(1)); - values.resize(values.len().div_ceil(r) * r, FE::zero()); - - assert!(values.len() % r == 0); - let mut state: Vec> = vec![FE::zero(); m]; - - // Process each block - for block in values.chunks(r) { - let mut block_state: Vec> = - state[0..r].iter().zip(block).map(|(s, b)| s + b).collect(); - block_state.extend_from_slice(&state[r..]); - - Self::hades_permutation(&mut block_state); - state = block_state; - } - - state[0].clone() - } -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::hash::poseidon::starknet::PoseidonCairoStark252; - use lambdaworks_math::field::{ - element::FieldElement, fields::fft_friendly::stark_252_prime_field::Stark252PrimeField, - }; - - #[test] - fn test_hades_permutation() { - // Initialize a state to test. The exact contents will depend on your specific use case. - let mut state: Vec> = vec![ - FieldElement::::from_hex("0x9").unwrap(), - FieldElement::::from_hex("0xb").unwrap(), - FieldElement::::from_hex("0x2").unwrap(), - ]; - - PoseidonCairoStark252::hades_permutation(&mut state); - - // Compare the result to the expected output. You will need to know the expected output for your specific test case. - let expected_state0 = FieldElement::::from_hex( - "0x510f3a3faf4084e3b1e95fd44c30746271b48723f7ea9c8be6a9b6b5408e7e6", - ) - .unwrap(); - let expected_state1 = FieldElement::::from_hex( - "0x4f511749bd4101266904288021211333fb0a514cb15381af087462fa46e6bd9", - ) - .unwrap(); - let expected_state2 = FieldElement::::from_hex( - "0x186f6dd1a6e79cb1b66d505574c349272cd35c07c223351a0990410798bb9d8", - ) - .unwrap(); - - assert_eq!(state[0], expected_state0); - assert_eq!(state[1], expected_state1); - assert_eq!(state[2], expected_state2); - } - #[test] - fn test_hash() { - let x = FieldElement::::from_hex("0x123456").unwrap(); - let y = FieldElement::::from_hex("0x789101").unwrap(); - - let z = PoseidonCairoStark252::hash(&x, &y); - - // Compare the result to the expected output. You will need to know the expected output for your specific test case. - let expected_state0 = FieldElement::::from_hex( - "0x2fb6e1e8838d4b850877944f0a13340dd5810f01f5d4361c54b22b4abda3248", - ) - .unwrap(); - - assert_eq!(z, expected_state0); - } - - #[test] - fn test_hash_single() { - let x = FieldElement::::from_hex("0x9").unwrap(); - - let z = PoseidonCairoStark252::hash_single(&x); - - // Compare the result to the expected output. You will need to know the expected output for your specific test case. - let expected_state0 = FieldElement::::from_hex( - "0x3bb3b91c714cb47003947f36dadc98326176963c434cd0a10320b8146c948b3", - ) - .unwrap(); - - assert_eq!(z, expected_state0); - } - - #[test] - fn test_hash_many() { - let a = FieldElement::::from_hex("0x1").unwrap(); - let b = FieldElement::::from_hex("0x2").unwrap(); - let c = FieldElement::::from_hex("0x3").unwrap(); - let d = FieldElement::::from_hex("0x4").unwrap(); - let e = FieldElement::::from_hex("0x5").unwrap(); - let f = FieldElement::::from_hex("0x6").unwrap(); - - let ins = vec![a, b, c, d, e, f]; - let z = PoseidonCairoStark252::hash_many(&ins); - - // Compare the result to the expected output. You will need to know the expected output for your specific test case. - let expected_state0 = FieldElement::::from_hex( - "0xf50993f0797e4cc05734a47daeb214fde2d444ef6619a7c1f7c8e0924feb0b", - ) - .unwrap(); - assert_eq!(z, expected_state0); - - let ins = vec![a]; - let z = PoseidonCairoStark252::hash_many(&ins); - let expected_state0 = FieldElement::::from_hex( - "0x579e8877c7755365d5ec1ec7d3a94a457eff5d1f40482bbe9729c064cdead2", - ) - .unwrap(); - assert_eq!(z, expected_state0); - - let ins = vec![a, b]; - let z = PoseidonCairoStark252::hash_many(&ins); - let expected_state0 = FieldElement::::from_hex( - "0x371cb6995ea5e7effcd2e174de264b5b407027a75a231a70c2c8d196107f0e7", - ) - .unwrap(); - assert_eq!(z, expected_state0); - - let ins = vec![a, b, c]; - let z = PoseidonCairoStark252::hash_many(&ins); - let expected_state0 = FieldElement::::from_hex( - "0x2f0d8840bcf3bc629598d8a6cc80cb7c0d9e52d93dab244bbf9cd0dca0ad082", - ) - .unwrap(); - assert_eq!(z, expected_state0); - - let ins = vec![a, b, c, d]; - let z = PoseidonCairoStark252::hash_many(&ins); - let expected_state0 = FieldElement::::from_hex( - "0x26e3ad8b876e02bc8a4fc43dad40a8f81a6384083cabffa190bcf40d512ae1d", - ) - .unwrap(); - - assert_eq!(z, expected_state0); - } -} diff --git a/crates/crypto/src/hash/poseidon/parameters.rs b/crates/crypto/src/hash/poseidon/parameters.rs deleted file mode 100644 index 2991d7b4e..000000000 --- a/crates/crypto/src/hash/poseidon/parameters.rs +++ /dev/null @@ -1,45 +0,0 @@ -use alloc::vec::Vec; -use lambdaworks_math::field::{element::FieldElement as FE, traits::IsPrimeField}; - -/// Parameters for Poseidon -/// MDS constants and rounds constants are stored as references to slices -/// representing matrices of `N_MDS_MATRIX_ROWS * N_MDS_MATRIX_COLS` and -/// `N_ROUND_CONSTANTS_ROWS * N_ROUND_CONSTANTS_COLS` respectively. -/// We use this representation rather than an array because we can't use the -/// associated constants for dimension, requiring many generic parameters -/// otherwise. -pub trait PermutationParameters { - type F: IsPrimeField + 'static; - - const RATE: usize; - const CAPACITY: usize; - const ALPHA: u32; - const N_FULL_ROUNDS: usize; - const N_PARTIAL_ROUNDS: usize; - const STATE_SIZE: usize = Self::RATE + Self::CAPACITY; - - const MDS_MATRIX: &'static [FE]; - const N_MDS_MATRIX_ROWS: usize; - const N_MDS_MATRIX_COLS: usize; - - const ROUND_CONSTANTS: &'static [FE]; - const N_ROUND_CONSTANTS_ROWS: usize; - const N_ROUND_CONSTANTS_COLS: usize; - - /// This is the mix function that operates with the MDS matrix - /// Round Constants are sometimes picked to simplify this function, - /// so it can be redefined by each set of permutation parameters if a simplification can be made to make it faster. Notice in that case, MDS constants may not be used. - fn mix(state: &mut [FE]) { - let mut new_state: Vec> = Vec::with_capacity(Self::STATE_SIZE); - for i in 0..Self::STATE_SIZE { - let mut new_e = FE::zero(); - for (j, current_state) in state.iter().enumerate() { - let mut mij = Self::MDS_MATRIX[i * Self::N_MDS_MATRIX_COLS + j].clone(); - mij *= current_state; - new_e += mij; - } - new_state.push(new_e); - } - state.clone_from_slice(&new_state[0..Self::STATE_SIZE]); - } -} diff --git a/crates/crypto/src/hash/poseidon/starknet/mod.rs b/crates/crypto/src/hash/poseidon/starknet/mod.rs deleted file mode 100644 index 0aba34572..000000000 --- a/crates/crypto/src/hash/poseidon/starknet/mod.rs +++ /dev/null @@ -1,2 +0,0 @@ -pub mod parameters; -pub use parameters::PoseidonCairoStark252; diff --git a/crates/crypto/src/hash/poseidon/starknet/parameters.rs b/crates/crypto/src/hash/poseidon/starknet/parameters.rs deleted file mode 100644 index 0e6e9bd24..000000000 --- a/crates/crypto/src/hash/poseidon/starknet/parameters.rs +++ /dev/null @@ -1,494 +0,0 @@ -use crate::hash::poseidon::PermutationParameters; -use lambdaworks_math::field::{ - element::FieldElement as FE, fields::fft_friendly::stark_252_prime_field::Stark252PrimeField, -}; - -impl PermutationParameters for PoseidonCairoStark252 { - type F = Stark252PrimeField; - const RATE: usize = 2; - const CAPACITY: usize = 1; - const ALPHA: u32 = 3; - const N_FULL_ROUNDS: usize = 8; - const N_PARTIAL_ROUNDS: usize = 83; - - const MDS_MATRIX: &'static [FE] = &PoseidonCairoStark252::MDS_MATRIX; - const N_MDS_MATRIX_ROWS: usize = 3; - const N_MDS_MATRIX_COLS: usize = 3; - - const ROUND_CONSTANTS: &'static [FE] = - &PoseidonCairoStark252::OPTIMIZED_ROUND_CONSTANTS; - - const N_ROUND_CONSTANTS_ROWS: usize = 91; - const N_ROUND_CONSTANTS_COLS: usize = 3; - /// Redefined mix function for optimization purposes - #[inline(always)] - fn mix(state: &mut [FE]) { - let t = &state[0] + &state[1] + &state[2]; - state[0] = &t + &state[0].double(); - state[1] = &t - &state[1].double(); - state[2] = &t - (&state[2] + &state[2] + &state[2]); - } -} - -#[derive(Clone, Default)] -pub struct PoseidonCairoStark252; - -impl PoseidonCairoStark252 { - /// Since the mix function was redefined, these constants are not used, but are still valid. - const MDS_MATRIX: [FE; 3 * 3] = [ - FE::from_hex_unchecked("3"), - FE::from_hex_unchecked("1"), - FE::from_hex_unchecked("1"), - FE::from_hex_unchecked("1"), - FE::from_hex_unchecked("800000000000011000000000000000000000000000000000000000000000000"), - FE::from_hex_unchecked("1"), - FE::from_hex_unchecked("1"), - FE::from_hex_unchecked("1"), - FE::from_hex_unchecked("800000000000010ffffffffffffffffffffffffffffffffffffffffffffffff"), - ]; - // These constants can be found both in Jonathan's implementation - // https://github.com/xJonathanLEI/starknet-rs/blob/35c287e1a06e6ab68447f5f0b9df53f910960f57/starknet-crypto-codegen/src/poseidon/params.rs - // And the round 0 ones matches the one used - // in Cairo Lang - // https://github.com/starkware-libs/cairo-lang/blob/c98fc0b50529185b7018208cb3460191eeb53e0d/src/starkware/cairo/stark_verifier/air/layouts/starknet/autogenerated.cairo#L1574-L1596 - // These constants can be used for an unoptimized implementation of Poseidon, in which three additions need to be performed - // in each round, regardless if it is a full round or partial round. - #[allow(unused)] - const UNOPTIMIZED_ROUND_CONSTANTS: [FE; 3 * 91] = [ - FE::from_hex_unchecked("6861759ea556a2339dd92f9562a30b9e58e2ad98109ae4780b7fd8eac77fe6f"), - FE::from_hex_unchecked("3827681995d5af9ffc8397a3d00425a3da43f76abf28a64e4ab1a22f27508c4"), - FE::from_hex_unchecked("3a3956d2fad44d0e7f760a2277dc7cb2cac75dc279b2d687a0dbe17704a8309"), - FE::from_hex_unchecked("626c47a7d421fe1f13c4282214aa759291c78f926a2d1c6882031afe67ef4cd"), - FE::from_hex_unchecked("78985f8e16505035bd6df5518cfd41f2d327fcc948d772cadfe17baca05d6a6"), - FE::from_hex_unchecked("5427f10867514a3204c659875341243c6e26a68b456dc1d142dcf34341696ff"), - FE::from_hex_unchecked("5af083f36e4c729454361733f0883c5847cd2c5d9d4cb8b0465e60edce699d7"), - FE::from_hex_unchecked("7d71701bde3d06d54fa3f74f7b352a52d3975f92ff84b1ac77e709bfd388882"), - FE::from_hex_unchecked("603da06882019009c26f8a6320a1c5eac1b64f699ffea44e39584467a6b1d3e"), - FE::from_hex_unchecked("4332a6f6bde2f288e79ce13f47ad1cdeebd8870fd13a36b613b9721f6453a5d"), - FE::from_hex_unchecked("53d0ebf61664c685310a04c4dec2e7e4b9a813aaeff60d6c9e8caeb5cba78e7"), - FE::from_hex_unchecked("5346a68894845835ae5ebcb88028d2a6c82f99f928494ee1bfc2d15eaabfebc"), - FE::from_hex_unchecked("550a9e24176509ea7631ccaecb7a4ab8694ab61f238797098147e69dd91e5a3"), - FE::from_hex_unchecked("219dcccb783b1cbaa62773fedd3570e0f48ad3ed77c8b262b5794daa2687000"), - FE::from_hex_unchecked("4b085eb1df4258c3453cc97445954bf3433b6ab9dd5a99592864c00f54a3f9a"), - FE::from_hex_unchecked("53e8a8e8a404c503af2bf3c03e420ea5a465939d04b6c72e2da084e5aabb78d"), - FE::from_hex_unchecked("5ca045c1312c09d1bd14d2537fe5c19fb4049cb137faf5df4f9ada962be8ca8"), - FE::from_hex_unchecked("7c74922a456802c44997e959f27a5b06820b1ed97596a969939c46c162517f4"), - FE::from_hex_unchecked("c0bba6880d2e686bf5088614b9684ff2526a20f91670435dc6f519bb7ab83f"), - FE::from_hex_unchecked("4526bcaec43e8ebd708dd07234c1b2dc1a6203741decd72843849cd0f87934a"), - FE::from_hex_unchecked("1cc9a17b00d3607d81efaea5a75a434bef44d92edc6d5b0bfe1ec7f01d613ed"), - FE::from_hex_unchecked("28b1e269b84c4012aa8cdbead0bc1ce1eb7284e2b28ed90bc7b4a4fde8f01f"), - FE::from_hex_unchecked("62af2f41d76c4ad1d9a2482fbdaf6590c19656bcb945b58bb724dc7a994498d"), - FE::from_hex_unchecked("5cfd7e44946daa6b2618213b0d1bf4a2269bed2dc0d4dbf59e285eee627df1a"), - FE::from_hex_unchecked("7ff2afb40f3300856fdd1b94da8d3bbcf0312ab9f16ac9bc31955dc8386a747"), - FE::from_hex_unchecked("5cd236bdc15b54183e90bab8ae37f8aab40efae6fa9cd919b3248ee326e929c"), - FE::from_hex_unchecked("5463841390e22d60c946418bf0e5822bd999084e30688e741a90bbd53a698a"), - FE::from_hex_unchecked("24c940fff3fe8c8b2021f13eb4d71747efd44a4e51890ae8226e7406144f805"), - FE::from_hex_unchecked("4e50cb07b3873268dc88f05393d9d03153ca4c02172dd1d7fc77d45e1b04555"), - FE::from_hex_unchecked("62ca053e4da0fc87b430e53238d2bab1d9b499c35f375d7d0b32e1189b6dcb5"), - FE::from_hex_unchecked("719f20ac59d1ebcaaf37fe0b851bc2419cd89100adff965951bff3d3d7e1191"), - FE::from_hex_unchecked("7645ca5e87a9f916a82fe5bb90807f44050ac92ca52f5c798935cf47d55a8fd"), - FE::from_hex_unchecked("15b8aeaca96ab53200eed38d248ecda23d4b71d17133438015391ca63663767"), - FE::from_hex_unchecked("53d94dbbca7cb2aa8252f106292ac3b98799e908f928c196c1b658bf10b2e2"), - FE::from_hex_unchecked("28f90b403e240f1c6f4c0a3b70edbb3942b447c615c0f033913831c34de2d1e"), - FE::from_hex_unchecked("2485167dc233ba6e1161c4d0bf025159699dd2feb36e3e5b70ae6e770e22081"), - FE::from_hex_unchecked("1c8b08a90d6ee46ff7de548541dd26988f7fdaacdd58698e938607a5feca6e8"), - FE::from_hex_unchecked("105c3bf5cba256466b75e79d146f9880c7c4df5ecdad643ce05b16901c4881e"), - FE::from_hex_unchecked("238019787f4cc0b627a65a21bef2106d5015b85dfbd77b2965418b02dbc6bd7"), - FE::from_hex_unchecked("15e624d7698fdf9b73dce29a5f24c465c15b52dec8172923a6ebc99a6ddc5e1"), - FE::from_hex_unchecked("5d3688ba56f34fdf56bc056ad8bf740ca0c2efef23b04a479f612fde5800a0a"), - FE::from_hex_unchecked("229abdef3fef7ae9e67ed336e82dc6c2e26d872d98b3cce811c69ae363b444d"), - FE::from_hex_unchecked("3e8096ecfcbcde2ee400801a56f236db2c43d1e33c92b57ac58daf2d3fc44db"), - FE::from_hex_unchecked("3ad5fec670d7039108d605aae834c7ce6a7cd4e1b47bf6a02265352c57db9bd"), - FE::from_hex_unchecked("7cf4598c0cf143875877afdbb4df6794ef597fff1f98557adca32046aeaef0a"), - FE::from_hex_unchecked("58aecc0081b55134a4d1c4c8f27932e4170c37841fef49aca0ec7a123c00ad6"), - FE::from_hex_unchecked("757b4b7ee98e0a15460b71995790396e4ef3c859db5b714ec09308d65d2ca61"), - FE::from_hex_unchecked("6b82800937f8981f3cd974f43322169963d2b54fd2b7ed348dc6cc226718b5d"), - FE::from_hex_unchecked("3a915b1814707273427e34ab8fbb7ca044f14088fedae9606b34a60b1e9c64"), - FE::from_hex_unchecked("54afbf1bd990043f9bc01028ff44195c0bb609d367b76269a627689547bfbef"), - FE::from_hex_unchecked("5e1ceb846fe1422b9524c7d014931072c3852df2d991470b08375edf6e762bb"), - FE::from_hex_unchecked("7f751f98968212ebe5dff3ce06e8cb916709e0c48e3020c6b2b01c1bec0814b"), - FE::from_hex_unchecked("36f6b64463f7c29fc3180616e340536bea7f01d226b68b6d45cd6dfbff811e4"), - FE::from_hex_unchecked("61135c9846faf39b4511d74fe8de8b48dd4d0e469d6703d7ed4fe4fe8e0dbac"), - FE::from_hex_unchecked("b58921a3fbdbb559b78f6acfca9a21a4ba83cc6e0ae3527fbaad907fc912b8"), - FE::from_hex_unchecked("22a4f8a5cdc7474b9d16b61c2973847211d84eb2fb27b816e52821c2e2b1b1e"), - FE::from_hex_unchecked("41cf6db5d6145edfeccbbc9a50b2ceedeb1765c61516ffcb112f810ad67036f"), - FE::from_hex_unchecked("be44689973db2b1cfc05fa8f4aec6fac6a0ff2fdfab744ade9de11416b6831"), - FE::from_hex_unchecked("39bf209c4e117e16489cda45128096d6d148a237142dc4951df0b8239be148b"), - FE::from_hex_unchecked("209cf541e5f74fc2b93310b8ce37b092a58282643860b5707c7eb980ea03a06"), - FE::from_hex_unchecked("6b562e6005f34ee0bdc218ba681b6ba7232e122287036d18c22dd5afa95326d"), - FE::from_hex_unchecked("e8103a23902be5dc6d5f59253a627a2a39c8aca11a914670e7a35dea38c8f"), - FE::from_hex_unchecked("6a3725548c664fd06bdc1b4d5f9bed83ef8ca7468d68f4fbbf345de2d552f72"), - FE::from_hex_unchecked("67fcd6997472e8e605d0f01a8eccc5f11a45c0aa21eb4ebb447b4af006a4a37"), - FE::from_hex_unchecked("26144c95c8de3634075784d28c06c162a44366f77792d4064c95db6ecb5cff0"), - FE::from_hex_unchecked("5b173c8b0eb7e9c4b3a874eb6307cda6fd875e3725061df895dc1466f350239"), - FE::from_hex_unchecked("7e1c2d6fde8ac9f87bae06ad491d391c448f877e53298b6370f2165c3d54ddb"), - FE::from_hex_unchecked("4db779f3e5b7424996f451b156fe4e28f74d61e7771f9e3fa433b57ca6627a9"), - FE::from_hex_unchecked("bb930d8a6c6583713435ec06b6fed7825c3f71114acb93e240eed6970993dd"), - FE::from_hex_unchecked("4472d73b2830565d708467e9296fb5599d3a08814c31c4189e9579c046e878f"), - FE::from_hex_unchecked("7ba9c303dfee2d89e10e3c883ca5ce5614d23739b7cb2052cc23612b11170e2"), - FE::from_hex_unchecked("21c0e3319ede47f0425dc9b2c1ed30e6356cb133e97579b822548eb9c4dc4b7"), - FE::from_hex_unchecked("2cfd61139e50ddd37b09933816e2a0932e53b7dc4f4947565c1d41e877eb191"), - FE::from_hex_unchecked("5abea18941a4976844544d92ee0eca65bdd10b3f170b0dc2f30acd37e26d8e7"), - FE::from_hex_unchecked("77088fdb015c7947a6265e44fef6f724ea28ae28b26e6eee5a751b7ce6bcc21"), - FE::from_hex_unchecked("3abdc9d677231325b3e3c43cfd443076b4ce33cddbc8446120dce84e6122b73"), - FE::from_hex_unchecked("2250f430b7fe7d12e5d00b6b83e52a52ca94879ccfab81a7a602662c2d62c4d"), - FE::from_hex_unchecked("5c92ef479c11bb51fb24ef76d57912b12660e7bd156d6cabbb1efb79a25861b"), - FE::from_hex_unchecked("235ec597391648b510f616fa8b87900fd08fd4208a785cffcf784a63a0fd5c6"), - FE::from_hex_unchecked("4ed4e872eb7e736207be77e9d11e38f396b5c0ba3376e855523c00b372cc668"), - FE::from_hex_unchecked("5f9406febca3879b756ef3f6331890b3d46afa705908f68fb7d861c4f275a1b"), - FE::from_hex_unchecked("1d9c501d9ff1fba621a9f61b68873c05f17b0384661f06d97edf441abdaa49d"), - FE::from_hex_unchecked("4b0de22bbd0a58534982c8e28d2f6e169e37ba694774c4dfa530f41c535952e"), - FE::from_hex_unchecked("1b4d48bd38a3f8602186aabb291eca0d319f0e3648b2574c49d6fd1b033d903"), - FE::from_hex_unchecked("7558bbea55584bf1725d8aa67ddba626b6596bbd2f4e65719702cefcead4bab"), - FE::from_hex_unchecked("1108f1a9500a52f561ea174600e266a70b157d56ece95b60a44cf7a3eef17be"), - FE::from_hex_unchecked("8913d96a4f36b12becb92b4b6ae3f8c209fb90caab6668567289b67087bf60"), - FE::from_hex_unchecked("6502262c51ad8f616926346857dec8cca2e99f5742b6bf223f4d8a6f32867a6"), - FE::from_hex_unchecked("7cb5fcdc00892812889280505c915bde962ea034378b343cd3a5931d2ec0e52"), - FE::from_hex_unchecked("2eb919524a89a26f90be9781a1515145baea3bc96b8cd1f01b221c4d2a1ce87"), - FE::from_hex_unchecked("58efb6272921bc5eada46635e3567dced0662c0161223e3c1c63e8de3ec3d73"), - FE::from_hex_unchecked("62fcd49ca9c7587b436d205ffc2a39594254a1ac34acd46d6955e7844d4f88e"), - FE::from_hex_unchecked("635895330838846e62d9acce0b625f885e5941e54bd3a2106fcf837aef5313b"), - FE::from_hex_unchecked("7da445b81e9b3d36d47a5f4d23b92a378a17f119d5e6e70629f8b41fefb12e3"), - FE::from_hex_unchecked("2b22dab62f0817e9fc5737e189d5096a9027882bef1738943b7016256118343"), - FE::from_hex_unchecked("1af01472348f395bacdfed1d27664d0d5bdea769be8fcb8fbef432b790e50d5"), - FE::from_hex_unchecked("76b172dbbeec5a31de313b9390f79ec9284163c8e4986bc5b682e5ac6360309"), - FE::from_hex_unchecked("70efaeae36f6af0f362f6cb423d2009b30ddb4178d46def0bdb2905b3e0862"), - FE::from_hex_unchecked("6cb99b36e521ac0a39872686b84ee1d28c4942b8036a1c25a0e4117ccaeedf"), - FE::from_hex_unchecked("29fd44305a5a9a70bbf9674e544bda0fb3d0fe5bb3aa743fd1b8a4fc1dc6055"), - FE::from_hex_unchecked("6b447ded1046e83629b184d8c36db3a11a6778d8848142aa6363d6619f9764"), - FE::from_hex_unchecked("642a8b4be4ba812cbfcf55a77339b5d357cceb6946fdc51c14b58f5b8989b59"), - FE::from_hex_unchecked("489e0a26f65a1eecc6cc6aa5b6e775cbc51a73700bd794a7acd79ae1d95882a"), - FE::from_hex_unchecked("3b19d4ef195975bbf78ab5dc2fd1d24816428f45a06293c1b9d57b9a02e9200"), - FE::from_hex_unchecked("7d2dd994756eacba576b74790b2194971596f9cd59e55ad2884c52039013df5"), - FE::from_hex_unchecked("1922810cc08f50bf300df869823b9f18b3327e29e9e765002970ef0f2e8c5f3"), - FE::from_hex_unchecked("52f3afaf7c9102f1d46e1d79a70745b39c04376aafff05771cbd4a88ed418ac"), - FE::from_hex_unchecked("7ccfc88e44a0507a95260f44203086e89552bbe53dcc46b376c5bcab6ea788e"), - FE::from_hex_unchecked("2949125939e6ad94100228beff83823f5157dd8e067bc8819e40a1ab008dd9c"), - FE::from_hex_unchecked("6cb64e3a0d37a6a4273ce4ee6929ba372d6811dde135af4078ba6e1912e1014"), - FE::from_hex_unchecked("d63b53707acf8962f05f688129bf30ad43714257949cd9ded4bf5953837fae"), - FE::from_hex_unchecked("bcb1549c9cabb5d13bb968b4ea22d0bb7d7460a6965702942092b32ef152d4"), - FE::from_hex_unchecked("3d1c5233657ce31f5ead698fe76f6492792a7205ba0531a0ca25b8d8fe798c1"), - FE::from_hex_unchecked("2240b9755182ee9066c2808b1e16ea448e26a83074558d9279f450b79f97516"), - FE::from_hex_unchecked("cc203d8b0f90e30fe8e54f343cef59fe8d70882137de70c9b43ab6615a646c"), - FE::from_hex_unchecked("310c6cc475d9346e061bacdc175ea9e119e937dea9d2100fa68e03c1f77910b"), - FE::from_hex_unchecked("7f84b639f52e57420bc947defced0d8cbdbe033f578699397b83667049106c7"), - FE::from_hex_unchecked("584ca7f01262c5bd89c4562f57139f47e9f038cb32ec35abe4e1da8de3e164a"), - FE::from_hex_unchecked("1135eefaf69b6e4af7d02f562868be3e02fdc72e01e9510531f9afa78abbbde"), - FE::from_hex_unchecked("372082b8a6c07100a50a3d33805827ad350c88b56f62c6d36a0d876856a99e8"), - FE::from_hex_unchecked("7c3c12b819a8aad87499bac1a143fc59674f132e33898f0c119e3d12462dfe6"), - FE::from_hex_unchecked("4f1354c51e8f6905b84157cfeff6822c056ce9e29d602eb46bd9b75a23836cf"), - FE::from_hex_unchecked("2da9f26a8271659075739ba206507a08ac360150e849950ef3973548fbd2fca"), - FE::from_hex_unchecked("287173956a2beb111b5ec29195e38cc3f6a65ff50801aa75fd78dd550702843"), - FE::from_hex_unchecked("7273101c190ff64212420095a51c8411c7f3227f6a7a4a64ae6ba7f9201e126"), - FE::from_hex_unchecked("2dbf2a6b56b26d23ebeb61e500687de749b03d3d349169699258ee4c98005fc"), - FE::from_hex_unchecked("85b6cbb29739a6808e67f00ab89b52ab89ef8d92530394e4b910efd706c7fb"), - FE::from_hex_unchecked("3d55b5f1171efda1dacbcbadfd5b910b493fa9589fd937e3e06ce26b08925a3"), - FE::from_hex_unchecked("aaedaa6ef2fa707d16b3b295410c0e44f7a2f8135c207824f6ae2a9b16e90c"), - FE::from_hex_unchecked("6aca6ebf70b1cb46c6331e9f1a5c4cc89b80f8adc5d18915c1cd0d496ccf5e1"), - FE::from_hex_unchecked("1678602af36c28abb010f831d403d94d5e90003e6d37c677e9dd157fb27761"), - FE::from_hex_unchecked("2022036bdf687f041b547fefdf36d4c2cd3f4b0526a88aafe60a0a8f508bad2"), - FE::from_hex_unchecked("7bfc350957c968ca664397414bdfb8f9b8dfe49fb63e32353d4e2e8d1d4af6"), - FE::from_hex_unchecked("2d639cbd418cb9fc24ea29ccd1d15ab81f43a499b27a06d3c5e2176f7ad79af"), - FE::from_hex_unchecked("ecdea7f959a4d488403d5b39687a1fe0dee3369e5fbc0f4779569f64506e0c"), - FE::from_hex_unchecked("3f656bdc4fefd92b70658e2f1992ef9f22e5f2d28c490e21d4e34357154b558"), - FE::from_hex_unchecked("d1b8cb1561eed32319638ccab9033dfec47596f8a6f4ce6594e19fddd59254"), - FE::from_hex_unchecked("758ffc77c62e3e0f86ef6ea01545ad76f281ec2941da7222d1e8b4e2ec1f192"), - FE::from_hex_unchecked("20315ca079570df995386e96aeaa1b4596aacd28f83c32f29a591c95e6fcac5"), - FE::from_hex_unchecked("3e55cf341e7c280cb05f3d6ff9c8d9f2cfe76b84a9d1b0f54884b316b740d8d"), - FE::from_hex_unchecked("4d56feb32cde74feede9749739be452e92c029007a06f6e67c81203bf650c68"), - FE::from_hex_unchecked("4ee807aa678a9a433b6171eaa6a2544497f7599fb8145d7e8089f465403c89b"), - FE::from_hex_unchecked("25d2bacc8f1ee7548cb5f394de2cb6e1f365e56a1bc579d0f9a8ad2ef2b3821"), - FE::from_hex_unchecked("5f573de597ce1709fc20051f6501268cd4b278811924af1f237d15feb17bd49"), - FE::from_hex_unchecked("30297c3c54a505f5826a280e053cf7a3c1e84a1dcf8b33c682cf85ddac86deb"), - FE::from_hex_unchecked("2f5e9c47c9a86e043c7526a59783f03c6bc79b69b8709fe6a052b93a8339ae8"), - FE::from_hex_unchecked("1bf75c7a739da8d29f9c23065ff8ccb1da7deec83e130bcd4a27a416c72b84b"), - FE::from_hex_unchecked("60563d5f852ae875989017bd5c4cfdc29cd27fc4e91eeabdb8e864df3c3c675"), - FE::from_hex_unchecked("7a4b1d70885aa820969635468daec94f8156c20e3131bd71005be1cd16ccf9e"), - FE::from_hex_unchecked("347bb025695e497f1e201cd62aa4600b8b85cf718cd1d400f39c10e59cc5852"), - FE::from_hex_unchecked("6783ab1e1ef97bb9e7f9381eb6ab0de2c4c9c2de413691ba8aa666292e9e217"), - FE::from_hex_unchecked("133e0280c6de90e7b3870a07823c081fd9c4cb99d534debd6a7bfb4e5b0dd46"), - FE::from_hex_unchecked("865d450ce29dc42fb5db72460b3560a2f093695573dff94fd0216eb925beec"), - FE::from_hex_unchecked("1de023f840e054a35526dabacf0dee948efba06bcbb414ecd81a6b301664e57"), - FE::from_hex_unchecked("55fc1e341bfdf7805015a96f724c5ac7cc7b892a292d38190631ab1a5388c4"), - FE::from_hex_unchecked("2df6557bfd4a4e7e7b27bf51552d2b5162706a3e624faca01a307ef8d532858"), - FE::from_hex_unchecked("113a8a66962ce08d92a6bd3e9c1d55ef8f226da95e4d629046d73d0507f6271"), - FE::from_hex_unchecked("271577d6ee9fa377f2c889874ba5b44ca1076033db5c2de4f3367b08c008e53"), - FE::from_hex_unchecked("3396b33911219b6b0365c09348a561ef1ccb956fc673bc5291d311866538574"), - FE::from_hex_unchecked("1e1392f2da08549c8a7d89e899189306170baa3c3436e6a5398f69c8f321636"), - FE::from_hex_unchecked("661545081032013df118e1d6e7c61a333e313b1a9a5b6d69c876bd2e7d694ca"), - FE::from_hex_unchecked("6b14294e71cd7fb776edbd432d20eb8f66d00533574e46573516f0cacdeec88"), - FE::from_hex_unchecked("7252fbbb06c2848338b1c41df31e4e51fe2a18e2406c671915cab6eb1a1d4f2"), - FE::from_hex_unchecked("3ccf71be7cc2a9abcf5a09807c69679430c03645747621b7f5327cb00ff99da"), - FE::from_hex_unchecked("29778dc707504fa6a9f7c97b4ceef0a9b39001d034441617757cd816dac919a"), - FE::from_hex_unchecked("39473f6f06bb99e33590d34e3bae36e491f7bbf86a26aa55a8f5b27bb98d4c5"), - FE::from_hex_unchecked("7ba7c32f875b71b895caa0215f996fd4ad92bab187e81417063dde91c08c027"), - FE::from_hex_unchecked("37c1367e49cbfc403b22aac82abf83b0ed083148a5f4c92839e5d769bdab6b6"), - FE::from_hex_unchecked("5c9eb899931d2f4b53ffcf833cdfa05c2068375ff933eb37ae34157c0b2d951"), - FE::from_hex_unchecked("5f6054a4d48698ec27772fb50a7d2e5c1557ffdc1ffd07331f2ca26c6e3b661"), - FE::from_hex_unchecked("20e6d62a2fe0fe9b0fab83e8c7d1e8bfd0fec827960e40a91df64664dcd7774"), - FE::from_hex_unchecked("6290a56a489ad52120c426fe0e409c2ff17adf51f528cafb0d026d14ffd6aac"), - FE::from_hex_unchecked("3703f16f990342c2267a6f7ece342705a32ca4c101417286279f6fc315edc7c"), - FE::from_hex_unchecked("5194962daf6679b9a0c32b5a9a307ba92e2c630f70e439195b680dd296df3fd"), - FE::from_hex_unchecked("e8eae20a79a7c1242c34617b01340fb5fd4bea2aa58b98d2400d9b515ee5e2"), - FE::from_hex_unchecked("369058169d63091ae28bfb28def7cd8d00dd7c2894fae4ffec65242afa5cd45"), - FE::from_hex_unchecked("418c963bc97195a74077503ee472f22cfdff0973190ab189c7b93103fd78167"), - FE::from_hex_unchecked("68d07a3eefc78dc5b28b3f4dc93167fb8c97112d14a25b4d4db559720156386"), - FE::from_hex_unchecked("517e892228df2d4f15a3c4241c98ba25ba0b5557375003f8748583a61836372"), - FE::from_hex_unchecked("5cc0f0f6cf9be94a150116e7932f8fe74ac20ad8100c41dc9c99538792e279b"), - FE::from_hex_unchecked("53d5d7863434c6629bdb1f8a648e4820883543e821f0f5c1668884c0be41ec8"), - FE::from_hex_unchecked("a158126b89e6b0a600bf53f8101707b072218912dd0d9df2528f67de24fdf5"), - FE::from_hex_unchecked("6b53b807265387ee582069a698323d44c204bed60672b8d8d073bed2fede503"), - FE::from_hex_unchecked("1097fb448406b7a6de0877efd58c01be53be83bde9601a9acc9e0ca2091fda0"), - FE::from_hex_unchecked("cbc0ff7239d3763902396389d67b3049ce1fefde66333ce37ca441f5a31bec"), - FE::from_hex_unchecked("79a3d91dd8a309c632eb43d57b5c5d838ceebd64603f68a8141ebef84280e72"), - FE::from_hex_unchecked("23fb472fe575135300f74e8f6de8fe1185078218eceb938900e7598a368db9"), - FE::from_hex_unchecked("7ac73134016d2a8a4c63a6b9494c0bd7a6ba87cc33e8a8e23ebda18bfb67c2a"), - FE::from_hex_unchecked("19a16068c3eac9c03f1b5c5ee2485ccc163d9ab17bb035d5df6e31c3dcf8f14"), - FE::from_hex_unchecked("1f24b4356a6bbfd4d4ef9fd1634752820ee86a925725ac392134d90def073ea"), - FE::from_hex_unchecked("3e44e7f7aeea6add59b6b4d11c60a528fb70727f35d817305971592333d36"), - FE::from_hex_unchecked("5f93b02f826741414535a511ed3eb4fe85987ae57bc9807cbd94cd7513d394e"), - FE::from_hex_unchecked("f0a0a88db99247d71c3d51d4197fa3fd1cc76e670607e35ca2d3bada29523a"), - FE::from_hex_unchecked("3432226916d31f3acac1e211431fd4cd2b6f2e80626af6564bdde3e77608db0"), - FE::from_hex_unchecked("55625941bfea6f48175192845a7ad74b0b82940ef5f393ca3830528d59cf919"), - FE::from_hex_unchecked("ddf48695b204477dfe4f8cb3ef1b39783e9b92f9276b858e2e585e318e20a4"), - FE::from_hex_unchecked("260730a657ff8f38851a679ab2a1490434ee50d4953e7c5d3194578b08ae8e3"), - FE::from_hex_unchecked("4cfd231373aa46d96283840bdb79ba6d7132775b398d324bcd206842b961aa9"), - FE::from_hex_unchecked("3203843c41cd453f14fa0bc0b2191a27ebc659e74fd48f981e963de57eff25d"), - FE::from_hex_unchecked("2c2f6ae5624d1fb8435d1c86bf76c260f5e77a54b006293705872e647cc46"), - FE::from_hex_unchecked("780225456e63903b3e561384ef2e73a85b0e142b69752381535022014765f06"), - FE::from_hex_unchecked("7f602ec1a80a051fd21b07f8e2960613082fc954b9a9ff641cc432a75c81887"), - FE::from_hex_unchecked("62561b0a0a72239b60f6aaf7022b7d323fe77cd7c1ab432f0c8c118ca7e6bca"), - FE::from_hex_unchecked("604fe5a6a22344aa69b05dea16b1cf22450c186d093754cb9b84a8a03b70bc8"), - FE::from_hex_unchecked("1cf9987a4044716d3dc140bf5f9b76f6eada5995905189f8682eaf88aef2b7b"), - FE::from_hex_unchecked("6bc0b2487c1eece3db47a4bdd60cf69debee233e91b50e9ee42ce22cbfbacbf"), - FE::from_hex_unchecked("2f5dbb5055eb749a11403b93e90338b7620c51356d2c6adcbf87ab7ea0792e6"), - FE::from_hex_unchecked("446328f4dddae6529743c43883d59c45f63b8a623a9cf318489e5fc4a550f61"), - FE::from_hex_unchecked("4ba30c5240cde5bca6c4010fb4b481a25817b43d358399958584d2c48f5af25"), - FE::from_hex_unchecked("5f5275f76425b15c89209117734ae85708351d2cf19af5fe39a32f89c2c8a89"), - FE::from_hex_unchecked("576f3b5156f4763e18c7f98df3b2f7b993cdda4eb8cb92415e1be8e6af2fc17"), - FE::from_hex_unchecked("11dc3f15cba928aed5a44b55a5b026df84a61719ed5adbb93c0e8e12d35ef3d"), - FE::from_hex_unchecked("44c40e6bd52e91ad9896403ae4f543ae1c1d9ea047d75f8a6442b8feda04dca"), - FE::from_hex_unchecked("1836d733a54013ebd0ccbf4974e80ac1954bf90fe9ea4e2c914ad01166026d8"), - FE::from_hex_unchecked("3c553be9776b628a8159d306ef084727611df8037761f00f84ca02ce731b3ac"), - FE::from_hex_unchecked("6ce94781c1a23fda1c7b87e0436b1b401ae11a6d757843e342f5017076a059"), - FE::from_hex_unchecked("381ec71fbdef3160253be9f00f4e6b9e107f457812effb7371cc2daa0acd0ed"), - FE::from_hex_unchecked("1844da9cc0eeadc6490d847320d9f3cd4fb574aa687bafdfe0ffa7bf2a8f1a1"), - FE::from_hex_unchecked("7a8bf471f902d5abb27fea5b401483dedf97101047459682acfd7f9b65a812f"), - FE::from_hex_unchecked("633b6fb004de62441915fb51ac174456f5a9cdff7aecb6e6b0d063839e56327"), - FE::from_hex_unchecked("179ee5cec496194771200382bfc6d17bbe546ba88fed8b17535fd70fbc50ab6"), - FE::from_hex_unchecked("2806c0786185986ea9891b42d565256b0312446f07435ac2cae194330bf8c42"), - FE::from_hex_unchecked("438703d948708ae90c7a6b8af194b8b603bb2cdfd26bfa356ac9bb6ee041393"), - FE::from_hex_unchecked("24446628f56029d7153bd3a482b7f6e1c56f4e02225c628a585d58a920035af"), - FE::from_hex_unchecked("4c2a76e5ce832e8b0685cdeeea3a253ae48f6606790d817bd96025e5435e259"), - FE::from_hex_unchecked("78a23323520994592933c079b148aed57d5e4ce1ab122d370983b8caa0e0300"), - FE::from_hex_unchecked("79ca6c5e1025b2151144ea5937dd07cadce1aa691b19e6db87070ba51ec22c0"), - FE::from_hex_unchecked("6b2e4a46e37af3cf952d9d34f8d6bd84a442ebfd1ac5d17314e48922af79c5d"), - FE::from_hex_unchecked("305d6cd95cc2eab6805d93d3d8d74e1ca7d443f11e34a18e3529e0d03435c2"), - FE::from_hex_unchecked("6097b4b8b90db14b39743ed23f8956cabb7aea70cc624a415c7c17b37fbf9a9"), - FE::from_hex_unchecked("64e1b3f16c26c8845bdb98373e77dad3bdcc90865b0f0af96288707c18893f"), - FE::from_hex_unchecked("649fafe673f21e623384d841221b73421c56014af2ffdf57f1579ae911fd335"), - FE::from_hex_unchecked("7d806dccbf1a2696b294404e849722f2baa2f4d19005a49d1ba288a77fefe30"), - FE::from_hex_unchecked("5951a37da53e3bbc0b3e2db1a9a235d7a03f48f443be6d659119c44aafc7522"), - FE::from_hex_unchecked("6d87fa479fb59524d1912c3554ae3d010496a31bdacb542c816a1607a907731"), - FE::from_hex_unchecked("1451cccd4200fa9d473ad73466b4e8c0a712a0b12bb6fc9462a3ac892acc9b2"), - FE::from_hex_unchecked("3ca1b6400b3e51007642535f1ca9b03832ca0faa15e1c4ed82dd1efdc0763da"), - FE::from_hex_unchecked("52c55735b2f0a6560ad1516a8f13592b0dd024ff4162539f993a99c7a1a4d95"), - FE::from_hex_unchecked("7e04de60aa80132f0149d1dee29617de750bd5ce3e9fa5e62951d65f6b924cd"), - FE::from_hex_unchecked("271784e6920a68e47c4c8fab71c8f8303ef29e26f289223edf63291c0a5495"), - FE::from_hex_unchecked("5c7c19061a84d5960a04b8f0adaa603c8afe93f17b7f0e56b49514af43d0c69"), - FE::from_hex_unchecked("172db5affe783af419da337cb79061e090943c2959dea1b38e4436f5482eafe"), - FE::from_hex_unchecked("518b7975a6d8d310eac9fe4082916f021a7ecbadf18809746a9e061a2cb9456"), - FE::from_hex_unchecked("20c5539dc45dd56d4bbc2440a9f5061d74b8ae5e37b34e8755a0315f1e196db"), - FE::from_hex_unchecked("1ea6f5fb309fa4a08bc7d516e80efc3a977b47208283cf35a9d8bc213b90b14"), - FE::from_hex_unchecked("50ce323c5128dc7fdd8ddd8ba9cfe2efd424b5de167c7257d1f766541e29ded"), - FE::from_hex_unchecked("401e37d0e276547695538b41d3c28215b865f5b7d1b497a8919284c613cb7d8"), - FE::from_hex_unchecked("645a0de30acc3117f2893056fc5880255daa12cc61261cc0fab9cf57c57397b"), - FE::from_hex_unchecked("69bc3841eb0a310d9e988d75f09f698d4fdc9d0d69219f676b66ae7fa3d495b"), - FE::from_hex_unchecked("2684bbe315ad2c4bdd47c38fe72db47cf0ae0c455cda5484baf523f136bdc6"), - FE::from_hex_unchecked("11e0f83c547ca5c68202e8d34e5595a88858c2afa664365e4acb821fd8a13ee"), - FE::from_hex_unchecked("4af4a7635f8c7515966567ceec34315d0f86ac66c1e5a5ecac945f1097b82ef"), - FE::from_hex_unchecked("4fba58cf8aaf4893cb7158908ccc18b1dc48894d2bb46225c72b11f4c74b271"), - FE::from_hex_unchecked("397c4c169115b468cc90da2e664f8c29a7f89be0ead679a38b0f44c8a2a0e20"), - FE::from_hex_unchecked("6563b9ebb6450dbad397fa5dd13c501f326dd7f32be22e20998f59ec7bacff"), - FE::from_hex_unchecked("376edb238f7b630ea81d307f4c79f9afec48562076dd09c36cd79e9cb817165"), - FE::from_hex_unchecked("60d4208bb50eb15f29ed22addcd50a1b337504039690eb858584cda96e2e061"), - FE::from_hex_unchecked("6a37d569d2fbc73dbff1019dc3465ec0f30da46918ab020344a52f1df9a9210"), - FE::from_hex_unchecked("d3b174c7290c6bf412083ff35d23821dc512f1df073c1b429130371ac63b1a"), - FE::from_hex_unchecked("226ed3d763477454b46eb2a5c3b814634d974919689fb489fe55e525b980373"), - FE::from_hex_unchecked("5f3997e7dafcb2de0e7a23d33d2fd9ef06f4d79bd7ffa1930e8b0080d218513"), - FE::from_hex_unchecked("7c5eec716d94634434df335a10bbac504f886f7f9d3c1648348c3fae8fdf14d"), - FE::from_hex_unchecked("53cc30d7fe0f84e7e24fd22c0f9ad68a89da85553f871ef63d2f55f57e1a7c"), - FE::from_hex_unchecked("368821ee335d71819b95769f47418569474a24f6e83b268fefa4cd58c4ec8fa"), - FE::from_hex_unchecked("5334f75b052c0235119816883040da72c6d0a61538bdfff46d6a242bfeb7a1"), - FE::from_hex_unchecked("5d0af4fcbd9e056c1020cca9d871ae68f80ee4af2ec6547cd49d6dca50aa431"), - FE::from_hex_unchecked("30131bce2fba5694114a19c46d24e00b4699dc00f1d53ba5ab99537901b1e65"), - FE::from_hex_unchecked("5646a95a7c1ae86b34c0750ed2e641c538f93f13161be3c4957660f2e788965"), - FE::from_hex_unchecked("4b9f291d7b430c79fac36230a11f43e78581f5259692b52c90df47b7d4ec01a"), - FE::from_hex_unchecked("5006d393d3480f41a98f19127072dc83e00becf6ceb4d73d890e74abae01a13"), - FE::from_hex_unchecked("62c9d42199f3b260e7cb8a115143106acf4f702e6b346fd202dc3b26a679d80"), - FE::from_hex_unchecked("51274d092db5099f180b1a8a13b7f2c7606836eabd8af54bf1d9ac2dc5717a5"), - FE::from_hex_unchecked("61fc552b8eb75e17ad0fb7aaa4ca528f415e14f0d9cdbed861a8db0bfff0c5b"), - ]; - - // The following constants are used for an optimized version of Poseidon hash, as suggested in Appendix B from - // the Poseidon paper (available at https://eprint.iacr.org/2019/458.pdf). - // In partial rounds, instead of adding constants to all the state, we add a constant just to the state - // to which the S box is applied (non-linear). The constants for the other positions are "moved forward" and - // added at the end. - // To get this constants you can use the rust code written at the end of the file - pub const OPTIMIZED_ROUND_CONSTANTS: [FE; 107] = [ - FE::from_hex_unchecked("6861759ea556a2339dd92f9562a30b9e58e2ad98109ae4780b7fd8eac77fe6f"), - FE::from_hex_unchecked("3827681995d5af9ffc8397a3d00425a3da43f76abf28a64e4ab1a22f27508c4"), - FE::from_hex_unchecked("3a3956d2fad44d0e7f760a2277dc7cb2cac75dc279b2d687a0dbe17704a8309"), - FE::from_hex_unchecked("626c47a7d421fe1f13c4282214aa759291c78f926a2d1c6882031afe67ef4cd"), - FE::from_hex_unchecked("78985f8e16505035bd6df5518cfd41f2d327fcc948d772cadfe17baca05d6a6"), - FE::from_hex_unchecked("5427f10867514a3204c659875341243c6e26a68b456dc1d142dcf34341696ff"), - FE::from_hex_unchecked("5af083f36e4c729454361733f0883c5847cd2c5d9d4cb8b0465e60edce699d7"), - FE::from_hex_unchecked("7d71701bde3d06d54fa3f74f7b352a52d3975f92ff84b1ac77e709bfd388882"), - FE::from_hex_unchecked("603da06882019009c26f8a6320a1c5eac1b64f699ffea44e39584467a6b1d3e"), - FE::from_hex_unchecked("4332a6f6bde2f288e79ce13f47ad1cdeebd8870fd13a36b613b9721f6453a5d"), - FE::from_hex_unchecked("53d0ebf61664c685310a04c4dec2e7e4b9a813aaeff60d6c9e8caeb5cba78e7"), - FE::from_hex_unchecked("5346a68894845835ae5ebcb88028d2a6c82f99f928494ee1bfc2d15eaabfebc"), - FE::from_hex_unchecked("4B085EB1DF4258C3453CC97445954BF3433B6AB9DD5A99592864C00F54A3F9A"), - FE::from_hex_unchecked("731CFD19D508285965F12A079B2A169FDFE0A8E610E6F2D5CA5D7B0961F6D96"), - FE::from_hex_unchecked("217D08B5339852BCC6F7A774936B3E72ECD9E1F9A73D743F8079C1E3587EEAA"), - FE::from_hex_unchecked("C935DD633B0FD63599B13C850DAB3CB966BA510C81B20959E267008518C6E"), - FE::from_hex_unchecked("52AF8D378DD6772EE187ED23F79A7D98CF5A0A387103971467FE940E7B8B2BE"), - FE::from_hex_unchecked("294851C98B2682F1EC9918B9F12FCCEAA6E28A7B79B2E506362CDA595F8AB75"), - FE::from_hex_unchecked("11B59990BACC280824D1021418D4F589DA8C30063471494C204B169AB086064"), - FE::from_hex_unchecked("4B4DF56E3D7753F91960D59AE099B9BEB2CE690E6BBDCD0B599D49CEB2ACD6A"), - FE::from_hex_unchecked("5EECFA15A757DC3ECAE9FBD8FF06E466243534F30629FC5F1CF09EB5161AC4"), - FE::from_hex_unchecked("680BFDD8B9680E04659227634A1EC5282E5A7CEF81B15677F8448BDA4279059"), - FE::from_hex_unchecked("1D0BF8FAB0A1A7A14E2930794F7A3065C17E10B1CEDD791B8877D97ACD85053"), - FE::from_hex_unchecked("2C2C8C79F808ACE54BA207053C0D412C0FC11A610F14C48876701A37E32F464"), - FE::from_hex_unchecked("354EC9ED01D20EC52AAE19A9B858D3474D8234C11AD7BCE630AD56C54AFA562"), - FE::from_hex_unchecked("30DF20FCF6427BAC38BB5D1A42287F4E4136AC5892340E994E6EA28DEEC1E55"), - FE::from_hex_unchecked("528CF329C64E7EE3040BAFBDEFF61E241D99B424091E31472EDA296FC9C6778"), - FE::from_hex_unchecked("40416F24F623534634789660DF5435EBF0C3E0C69E6C5B5FF6E757930BD1960"), - FE::from_hex_unchecked("380C8F936E2ED9FD488AE3BAC7DCE315BA21B11E88339CD5444435CCC9EA38"), - FE::from_hex_unchecked("1CC4F5D5603D176F1A8E344392EFD2D03AD0541832829D245E0E2291F255B75"), - FE::from_hex_unchecked("5728917AF5DA91F9539310D99F5D142E011D6C8E015EA5423C502AA99C09752"), - FE::from_hex_unchecked("EFB450A9E86E1A46E295A348F0F23590925107D17C56D7C788FECC17219AA1"), - FE::from_hex_unchecked("2020D74D36C421AE1A025616B342D0784B8FCD977DE6C53A6C26693774DCA99"), - FE::from_hex_unchecked("7CFB309B75FD3BF2705558AE511DC82335050969F4BF84FA2B7B4F583989287"), - FE::from_hex_unchecked("4651E48B2E9349A5365E009ECE626809D7B7D02A617EB98C785A784812D75E9"), - FE::from_hex_unchecked("D77627B270F65122D0269719DA923CCAE822D9AAD0F0947A3B5C8F71C0DCC7"), - FE::from_hex_unchecked("199AD3D641B54C4D571B3FE37773A8B82B003377F0DD8B7D3B7758C32908EA8"), - FE::from_hex_unchecked("44F33640A8ECFD3973E2E9172A7333482B2D297BE2DA289319E72D137CDFE6E"), - FE::from_hex_unchecked("7E4ADF9894D964189D00A02DCF1E6BE7F801234F5216EAB6B6F366B6701ABF7"), - FE::from_hex_unchecked("3641FA5B3C90452F5FF808F8A9817EDA7C6AECFB5471DFDCA559FB4E711EE90"), - FE::from_hex_unchecked("3DE5729EFD2FCBD897A49A78FA923FC306DF32E6E2F0E02D0EEE2C2CC3F3533"), - FE::from_hex_unchecked("62691891A3FC1E27F622966CA0BE20C06563500C8F06C9BDB77BD2882D6C994"), - FE::from_hex_unchecked("6608D3BF11C18E4688739F72205763D1590CC4F9885AE1D86E96E0604BAA0BE"), - FE::from_hex_unchecked("11C9C9B39CAC71E3419726CE779116D07249F51CBDDA4FD98C25CBBF593A316"), - FE::from_hex_unchecked("61E23B58203269CAEF0850F74DA27B9748E3312EA40C6844DD68C557C462AD7"), - FE::from_hex_unchecked("4182CD9AB1D9488F870A572010BC2A3D9878440B25951E4CE010855CF83BDC8"), - FE::from_hex_unchecked("520FE6C4A096793F9055E6823116D15F1DF2FE89D306F9965F6A59F4F3ECB71"), - FE::from_hex_unchecked("346B2B2D6E5810129E093093DCD3DFA99ED6D71F47723EA3FBE4D4E2FD4AFA1"), - FE::from_hex_unchecked("1359CA923E7F1448EC1DD2A3684BEE4E8B682C8E8E973ACEA72877CE9F7E6CF"), - FE::from_hex_unchecked("47C655F55CF307800DFEFDAD24DE86FDE9DEADAB145A1B392420F37B95D9675"), - FE::from_hex_unchecked("4AB291F16555FA8A968CD7C9C285A9598EFD925F2D58B7AA38AD87DCA8441A8"), - FE::from_hex_unchecked("39F409C7C782101223D1F6F7D86C21A22C44EF959510E392C9C7C5D17C629C5"), - FE::from_hex_unchecked("44BE36B782F882AD86EECB0CD6BEB02E1A2F9FB5587A3BABFACEAD0CAFB6052"), - FE::from_hex_unchecked("50A1DFDE9B504AD2906DB6EB5B507203CD1CEB394C52CE7107679A53A0D538B"), - FE::from_hex_unchecked("5C753C14DA89E287B181C0DD11AC6C3680BDD7F1017DAE083E7AEBBEAB183AB"), - FE::from_hex_unchecked("2CF6306ED32232106C8015A3B180F386EEE93E15F7B4F4FA57746525FC0520C"), - FE::from_hex_unchecked("2C2014634D52E27420873CF347429091DFC6380689BD4F54D7D8E502C1C3A09"), - FE::from_hex_unchecked("3CFB9C5BD93E02B2FDACDE2058E33E5975C446345F010D850FC09CDF86ED8A1"), - FE::from_hex_unchecked("363FA71A383CF3897933F1411FC5F806E311E84F72CB50A9EA4E1281F6B0299"), - FE::from_hex_unchecked("728199657067EE16947B3FC76271676B4901B2A3686CFFEBCB960DA91B05DF8"), - FE::from_hex_unchecked("3FDFBD47D27F3D34F0723B728E8921DC9BDE34A9872DF5A652A078D7E4EE021"), - FE::from_hex_unchecked("7F241379440CACD7DC0EFBE7858EB7DE53CC02CA7D24197945C453398EFF449"), - FE::from_hex_unchecked("5B2E8771EA9A0004E3BF056F3727797CBB457A27574D5F104354E52A5C25F0B"), - FE::from_hex_unchecked("A8DDBCE708DE44A7E0B3B0333146E1E910245BE6BF822EA057A081BDA2E23E"), - FE::from_hex_unchecked("2D521E0DACA24E431AA47CD90A0F551C12270E533835613EDCE2E19AA9B0F61"), - FE::from_hex_unchecked("6CDBC0F2AA54D2CF7D5AC3B93F855AF03EEF7B07AAEE00341A6266C30E08AE6"), - FE::from_hex_unchecked("3DD96A17111EC8F4C5DA3AD6794C0961CEEE452CBE92C7A0941112B36ED9BF3"), - FE::from_hex_unchecked("5EAFB1EDEEDC5C07AC07FDD06159344A2CFB92196A65D9EC0C5E732C36687DC"), - FE::from_hex_unchecked("4AB038D7B09EDA9324577B260FEAEBDBCEC5A7B7C7F449B312CFCD065C207E6"), - FE::from_hex_unchecked("4CA71981E4DF6B505D2B0D94E235608463C58052570F68E495FC80C7FDEF220"), - FE::from_hex_unchecked("6DEE9C6DA4617E32AA419899C8EA8137E9B59D7E2759FFE573C15B77E413D2F"), - FE::from_hex_unchecked("58F9E60B34DDAB84DCBE2396065A4305B4A795A4770E4541E625D0460C6F186"), - FE::from_hex_unchecked("47B7B4A802A10C1E6C9C735DB6C34042D290906F274BEA8FCECEF17FC9AF632"), - FE::from_hex_unchecked("1849BCDB9AD7171096ECC936A186774084A074BE0BFC0FBB9463A06A2BD430C"), - FE::from_hex_unchecked("41870FBE04438348AF5767BDDAECD8AEA3B49B4217547DEC4D699B1466736CC"), - FE::from_hex_unchecked("226C04E598076A9FA02AA64557DAF28C0EC42E3D4DA68D1965029D284738B07"), - FE::from_hex_unchecked("1F0E971F0485A5B42EB92D6655C3DDB475CEC4371F269A95335B2A7D6DAC0FB"), - FE::from_hex_unchecked("9F31CC2907DCCBF994D35AA47EE3F4EBDF3703F795047A7B40DD3926431563"), - FE::from_hex_unchecked("4B40CCE78F3B641E31CE4DF58CE5A42C22CFBC198C84451FFE8CCA4C64BD7D2"), - FE::from_hex_unchecked("191660489E4BD8A3E4563173DE4A226F3AC736962FDFB70F72CB93CE50F8B9F"), - FE::from_hex_unchecked("18C0919618DB971F74EB01F293F2DAEA814B475103373DC7ED8DD4C7B467410"), - FE::from_hex_unchecked("35B60253848530E845C8753121577D0EF37002E941C3DC1FB240BD57EADC803"), - FE::from_hex_unchecked("1AE99DB1575AE91C8B43A9F71A5F362581AD9B413D97FA6FD029134957451D5"), - FE::from_hex_unchecked("3E6E1D0F3F8A0F728148EBCBD5D7D337D7CB8FEB58A37D2D1DFB357E172647B"), - FE::from_hex_unchecked("18BC36DFFA8F96A659E1A171B55D2706EE3E9AD619E16F5C38DD1F4A209B8F3"), - FE::from_hex_unchecked("2C7A3EF1AFB6A302B54AFC3A107FF9199A16EFE9A1CC3AB83FA5B64893DE4ED"), - FE::from_hex_unchecked("53A7BD889BED07BF5E27DD8E92F6AE85E4FE4E84B0C6DDE9856E94469DE4BD7"), - FE::from_hex_unchecked("4D383FF7FFC6318FDA704ACA35995F86BEC5A02CE9A0BF9D3CC0CC2F03CCEA9"), - FE::from_hex_unchecked("4667B6762FB8AD53D07EF7E8A65B21CA96E0B3503037710D1292519C326F5CD"), - FE::from_hex_unchecked("2CC8B43E75CF0B42A93C39EA98BCD46055DCCC9589F02EB7FB536422E5921F"), - FE::from_hex_unchecked("6B32EE98680871D38751447BFD76086BA4DF0E7BE59C55F4B2CE25582BF9C60"), - FE::from_hex_unchecked("3E907927C7182FAAA3B3C81358B82E734EFAC1F0609F0862D635CB1387102A3"), - FE::from_hex_unchecked("3F3A5057B3A08975F0253728E512AF78D2F437973F6A93793EA5E8424FBC6EA"), - FE::from_hex_unchecked("14B491D73724779F8AA74B3FD8AA5821C21E1017224726A7A946BB6CA68D8F5"), - FE::from_hex_unchecked("5C8278C7BBFC30AE7F60E514FE3B9367ACA84C54AD1373861695EA4ABB814EF"), - FE::from_hex_unchecked("64851937F9836EE5A08A7DDE65E44B467018A82BA3BF99BBA0B4502755C8074"), - FE::from_hex_unchecked("6A9AC84251294769ECA450FFB52B441882BE77CB85F422FF9EA5E73F1D971DC"), - FE::from_hex_unchecked("37EC35B710B0D04C9A2B71F2F7BD098C6A81D991D27F0FC1884F5CA545064DE"), - FE::from_hex_unchecked("5334f75b052c0235119816883040da72c6d0a61538bdfff46d6a242bfeb7a1"), - FE::from_hex_unchecked("5d0af4fcbd9e056c1020cca9d871ae68f80ee4af2ec6547cd49d6dca50aa431"), - FE::from_hex_unchecked("30131bce2fba5694114a19c46d24e00b4699dc00f1d53ba5ab99537901b1e65"), - FE::from_hex_unchecked("5646a95a7c1ae86b34c0750ed2e641c538f93f13161be3c4957660f2e788965"), - FE::from_hex_unchecked("4b9f291d7b430c79fac36230a11f43e78581f5259692b52c90df47b7d4ec01a"), - FE::from_hex_unchecked("5006d393d3480f41a98f19127072dc83e00becf6ceb4d73d890e74abae01a13"), - FE::from_hex_unchecked("62c9d42199f3b260e7cb8a115143106acf4f702e6b346fd202dc3b26a679d80"), - FE::from_hex_unchecked("51274d092db5099f180b1a8a13b7f2c7606836eabd8af54bf1d9ac2dc5717a5"), - FE::from_hex_unchecked("61fc552b8eb75e17ad0fb7aaa4ca528f415e14f0d9cdbed861a8db0bfff0c5b"), - ]; - - // The following function allows to compute the optimized partial round constants. - // How to move forward the constants: - // M*S(s+c) where s is the state, c the first partial-round constant. We decompose c = c'+c'' where c' has all zeros - // except for the non-linear position, c'' has a zero in the non-linear position. - // M*S(s+c) = M*S(s+c')+M*c'' - // We repeat this process as many times as partial rounds we have. - // Notice that the first constant for the next full round will change - - /* pub fn compute_new_constants() -> String { - let mut res = String::from(""); - let three: FE = FE::from_hex_unchecked("3"); - - let mut i = 4; - let mut c_next: [FE; 3] = [FE::zero(), FE::zero(), FE::zero()]; - let mut c_current: [FE; 3]; - - while i <= 86 { - let index = 3 * i; - c_current = [ - Self::UNOPTIMIZED_ROUND_CONSTANTS[index] + c_next[0], - Self::UNOPTIMIZED_ROUND_CONSTANTS[index + 1] + c_next[1], - Self::UNOPTIMIZED_ROUND_CONSTANTS[index + 2] + c_next[2], - ]; - - c_next = [ - three * c_current[0] + c_current[1], - c_current[0] - c_current[1], - c_current[0] + c_current[1], - ]; - res = format!( - "{}, \n FE::from_hex_unchecked(\"{}\")", - res, - c_current[2].representative().to_hex() - ); - if i == 86 { - c_next[0] += Self::UNOPTIMIZED_ROUND_CONSTANTS[3 * 87]; - c_next[1] += Self::UNOPTIMIZED_ROUND_CONSTANTS[3 * 87 + 1]; - c_next[2] += Self::UNOPTIMIZED_ROUND_CONSTANTS[3 * 87 + 2]; - res = format!("{}, Last constant: \n FE::from_hex_unchecked(\"{}\"), \n FE::from_hex_unchecked(\"{}\"), \n FE::from_hex_unchecked(\"{}\")", res, c_next[0].representative().to_hex(), c_next[1].representative().to_hex(),c_next[2].representative().to_hex()); - } - i += 1; - } - res - } - */ -} diff --git a/crates/crypto/src/hash/rescue_prime/mod.rs b/crates/crypto/src/hash/rescue_prime/mod.rs deleted file mode 100644 index c68be4b8a..000000000 --- a/crates/crypto/src/hash/rescue_prime/mod.rs +++ /dev/null @@ -1,11 +0,0 @@ -mod parameters; -mod rescue_prime_optimized; -mod utils; - -pub use rescue_prime_optimized::MdsMethod; -pub use rescue_prime_optimized::RescuePrimeOptimized; - -use lambdaworks_math::field::element::FieldElement; -use lambdaworks_math::field::fields::u64_goldilocks_field::Goldilocks64Field; - -pub type Fp = FieldElement; diff --git a/crates/crypto/src/hash/rescue_prime/parameters.rs b/crates/crypto/src/hash/rescue_prime/parameters.rs deleted file mode 100644 index d091d8962..000000000 --- a/crates/crypto/src/hash/rescue_prime/parameters.rs +++ /dev/null @@ -1,963 +0,0 @@ -use super::Fp; -pub const ALPHA: u64 = 7; -pub const ALPHA_INV: u64 = 10540996611094048183; - -// Constants obtained using the paper implementation in Sage -// https://github.com/ASDiscreteMathematics/rpo/tree/master/reference_implementation - -// Constants for the 128-bit security level -pub const MDS_VECTOR_128: [Fp; 12] = [ - Fp::const_from_raw(7), - Fp::const_from_raw(23), - Fp::const_from_raw(8), - Fp::const_from_raw(26), - Fp::const_from_raw(13), - Fp::const_from_raw(10), - Fp::const_from_raw(9), - Fp::const_from_raw(7), - Fp::const_from_raw(6), - Fp::const_from_raw(22), - Fp::const_from_raw(21), - Fp::const_from_raw(8), -]; -pub const ROUND_CONSTANTS_128: [Fp; 168] = [ - Fp::const_from_raw(5789762306288267392), - Fp::const_from_raw(6522564764413701783), - Fp::const_from_raw(17809893479458208203), - Fp::const_from_raw(107145243989736508), - Fp::const_from_raw(6388978042437517382), - Fp::const_from_raw(15844067734406016715), - Fp::const_from_raw(9975000513555218239), - Fp::const_from_raw(3344984123768313364), - Fp::const_from_raw(9959189626657347191), - Fp::const_from_raw(12960773468763563665), - Fp::const_from_raw(9602914297752488475), - Fp::const_from_raw(16657542370200465908), - Fp::const_from_raw(6077062762357204287), - Fp::const_from_raw(15277620170502011191), - Fp::const_from_raw(5358738125714196705), - Fp::const_from_raw(14233283787297595718), - Fp::const_from_raw(13792579614346651365), - Fp::const_from_raw(11614812331536767105), - Fp::const_from_raw(14871063686742261166), - Fp::const_from_raw(10148237148793043499), - Fp::const_from_raw(4457428952329675767), - Fp::const_from_raw(15590786458219172475), - Fp::const_from_raw(10063319113072092615), - Fp::const_from_raw(14200078843431360086), - Fp::const_from_raw(12987190162843096997), - Fp::const_from_raw(653957632802705281), - Fp::const_from_raw(4441654670647621225), - Fp::const_from_raw(4038207883745915761), - Fp::const_from_raw(5613464648874830118), - Fp::const_from_raw(13222989726778338773), - Fp::const_from_raw(3037761201230264149), - Fp::const_from_raw(16683759727265180203), - Fp::const_from_raw(8337364536491240715), - Fp::const_from_raw(3227397518293416448), - Fp::const_from_raw(8110510111539674682), - Fp::const_from_raw(2872078294163232137), - Fp::const_from_raw(6202948458916099932), - Fp::const_from_raw(17690140365333231091), - Fp::const_from_raw(3595001575307484651), - Fp::const_from_raw(373995945117666487), - Fp::const_from_raw(1235734395091296013), - Fp::const_from_raw(14172757457833931602), - Fp::const_from_raw(707573103686350224), - Fp::const_from_raw(15453217512188187135), - Fp::const_from_raw(219777875004506018), - Fp::const_from_raw(17876696346199469008), - Fp::const_from_raw(17731621626449383378), - Fp::const_from_raw(2897136237748376248), - Fp::const_from_raw(18072785500942327487), - Fp::const_from_raw(6200974112677013481), - Fp::const_from_raw(17682092219085884187), - Fp::const_from_raw(10599526828986756440), - Fp::const_from_raw(975003873302957338), - Fp::const_from_raw(8264241093196931281), - Fp::const_from_raw(10065763900435475170), - Fp::const_from_raw(2181131744534710197), - Fp::const_from_raw(6317303992309418647), - Fp::const_from_raw(1401440938888741532), - Fp::const_from_raw(8884468225181997494), - Fp::const_from_raw(13066900325715521532), - Fp::const_from_raw(8023374565629191455), - Fp::const_from_raw(15013690343205953430), - Fp::const_from_raw(4485500052507912973), - Fp::const_from_raw(12489737547229155153), - Fp::const_from_raw(9500452585969030576), - Fp::const_from_raw(2054001340201038870), - Fp::const_from_raw(12420704059284934186), - Fp::const_from_raw(355990932618543755), - Fp::const_from_raw(9071225051243523860), - Fp::const_from_raw(12766199826003448536), - Fp::const_from_raw(9045979173463556963), - Fp::const_from_raw(12934431667190679898), - Fp::const_from_raw(5674685213610121970), - Fp::const_from_raw(5759084860419474071), - Fp::const_from_raw(13943282657648897737), - Fp::const_from_raw(1352748651966375394), - Fp::const_from_raw(17110913224029905221), - Fp::const_from_raw(1003883795902368422), - Fp::const_from_raw(4141870621881018291), - Fp::const_from_raw(8121410972417424656), - Fp::const_from_raw(14300518605864919529), - Fp::const_from_raw(13712227150607670181), - Fp::const_from_raw(17021852944633065291), - Fp::const_from_raw(6252096473787587650), - Fp::const_from_raw(18389244934624494276), - Fp::const_from_raw(16731736864863925227), - Fp::const_from_raw(4440209734760478192), - Fp::const_from_raw(17208448209698888938), - Fp::const_from_raw(8739495587021565984), - Fp::const_from_raw(17000774922218161967), - Fp::const_from_raw(13533282547195532087), - Fp::const_from_raw(525402848358706231), - Fp::const_from_raw(16987541523062161972), - Fp::const_from_raw(5466806524462797102), - Fp::const_from_raw(14512769585918244983), - Fp::const_from_raw(10973956031244051118), - Fp::const_from_raw(4887609836208846458), - Fp::const_from_raw(3027115137917284492), - Fp::const_from_raw(9595098600469470675), - Fp::const_from_raw(10528569829048484079), - Fp::const_from_raw(7864689113198939815), - Fp::const_from_raw(17533723827845969040), - Fp::const_from_raw(5781638039037710951), - Fp::const_from_raw(17024078752430719006), - Fp::const_from_raw(109659393484013511), - Fp::const_from_raw(7158933660534805869), - Fp::const_from_raw(2955076958026921730), - Fp::const_from_raw(7433723648458773977), - Fp::const_from_raw(6982293561042362913), - Fp::const_from_raw(14065426295947720331), - Fp::const_from_raw(16451845770444974180), - Fp::const_from_raw(7139138592091306727), - Fp::const_from_raw(9012006439959783127), - Fp::const_from_raw(14619614108529063361), - Fp::const_from_raw(1394813199588124371), - Fp::const_from_raw(4635111139507788575), - Fp::const_from_raw(16217473952264203365), - Fp::const_from_raw(10782018226466330683), - Fp::const_from_raw(6844229992533662050), - Fp::const_from_raw(7446486531695178711), - Fp::const_from_raw(16308865189192447297), - Fp::const_from_raw(11977192855656444890), - Fp::const_from_raw(12532242556065780287), - Fp::const_from_raw(14594890931430968898), - Fp::const_from_raw(7291784239689209784), - Fp::const_from_raw(5514718540551361949), - Fp::const_from_raw(10025733853830934803), - Fp::const_from_raw(7293794580341021693), - Fp::const_from_raw(6728552937464861756), - Fp::const_from_raw(6332385040983343262), - Fp::const_from_raw(13277683694236792804), - Fp::const_from_raw(2600778905124452676), - Fp::const_from_raw(3736792340494631448), - Fp::const_from_raw(577852220195055341), - Fp::const_from_raw(6689998335515779805), - Fp::const_from_raw(13886063479078013492), - Fp::const_from_raw(14358505101923202168), - Fp::const_from_raw(7744142531772274164), - Fp::const_from_raw(16135070735728404443), - Fp::const_from_raw(12290902521256031137), - Fp::const_from_raw(12059913662657709804), - Fp::const_from_raw(16456018495793751911), - Fp::const_from_raw(4571485474751953524), - Fp::const_from_raw(17200392109565783176), - Fp::const_from_raw(7123075680859040534), - Fp::const_from_raw(1034205548717903090), - Fp::const_from_raw(7717824418247931797), - Fp::const_from_raw(3019070937878604058), - Fp::const_from_raw(11403792746066867460), - Fp::const_from_raw(10280580802233112374), - Fp::const_from_raw(337153209462421218), - Fp::const_from_raw(13333398568519923717), - Fp::const_from_raw(3596153696935337464), - Fp::const_from_raw(8104208463525993784), - Fp::const_from_raw(14345062289456085693), - Fp::const_from_raw(17036731477169661256), - Fp::const_from_raw(17130398059294018733), - Fp::const_from_raw(519782857322261988), - Fp::const_from_raw(9625384390925085478), - Fp::const_from_raw(1664893052631119222), - Fp::const_from_raw(7629576092524553570), - Fp::const_from_raw(3485239601103661425), - Fp::const_from_raw(9755891797164033838), - Fp::const_from_raw(15218148195153269027), - Fp::const_from_raw(16460604813734957368), - Fp::const_from_raw(9643968136937729763), - Fp::const_from_raw(3611348709641382851), - Fp::const_from_raw(18256379591337759196), -]; - -pub const MDS_MATRIX_128: [[Fp; 12]; 12] = [ - [ - Fp::const_from_raw(7), - Fp::const_from_raw(23), - Fp::const_from_raw(8), - Fp::const_from_raw(26), - Fp::const_from_raw(13), - Fp::const_from_raw(10), - Fp::const_from_raw(9), - Fp::const_from_raw(7), - Fp::const_from_raw(6), - Fp::const_from_raw(22), - Fp::const_from_raw(21), - Fp::const_from_raw(8), - ], - [ - Fp::const_from_raw(8), - Fp::const_from_raw(7), - Fp::const_from_raw(23), - Fp::const_from_raw(8), - Fp::const_from_raw(26), - Fp::const_from_raw(13), - Fp::const_from_raw(10), - Fp::const_from_raw(9), - Fp::const_from_raw(7), - Fp::const_from_raw(6), - Fp::const_from_raw(22), - Fp::const_from_raw(21), - ], - [ - Fp::const_from_raw(21), - Fp::const_from_raw(8), - Fp::const_from_raw(7), - Fp::const_from_raw(23), - Fp::const_from_raw(8), - Fp::const_from_raw(26), - Fp::const_from_raw(13), - Fp::const_from_raw(10), - Fp::const_from_raw(9), - Fp::const_from_raw(7), - Fp::const_from_raw(6), - Fp::const_from_raw(22), - ], - [ - Fp::const_from_raw(22), - Fp::const_from_raw(21), - Fp::const_from_raw(8), - Fp::const_from_raw(7), - Fp::const_from_raw(23), - Fp::const_from_raw(8), - Fp::const_from_raw(26), - Fp::const_from_raw(13), - Fp::const_from_raw(10), - Fp::const_from_raw(9), - Fp::const_from_raw(7), - Fp::const_from_raw(6), - ], - [ - Fp::const_from_raw(6), - Fp::const_from_raw(22), - Fp::const_from_raw(21), - Fp::const_from_raw(8), - Fp::const_from_raw(7), - Fp::const_from_raw(23), - Fp::const_from_raw(8), - Fp::const_from_raw(26), - Fp::const_from_raw(13), - Fp::const_from_raw(10), - Fp::const_from_raw(9), - Fp::const_from_raw(7), - ], - [ - Fp::const_from_raw(7), - Fp::const_from_raw(6), - Fp::const_from_raw(22), - Fp::const_from_raw(21), - Fp::const_from_raw(8), - Fp::const_from_raw(7), - Fp::const_from_raw(23), - Fp::const_from_raw(8), - Fp::const_from_raw(26), - Fp::const_from_raw(13), - Fp::const_from_raw(10), - Fp::const_from_raw(9), - ], - [ - Fp::const_from_raw(9), - Fp::const_from_raw(7), - Fp::const_from_raw(6), - Fp::const_from_raw(22), - Fp::const_from_raw(21), - Fp::const_from_raw(8), - Fp::const_from_raw(7), - Fp::const_from_raw(23), - Fp::const_from_raw(8), - Fp::const_from_raw(26), - Fp::const_from_raw(13), - Fp::const_from_raw(10), - ], - [ - Fp::const_from_raw(10), - Fp::const_from_raw(9), - Fp::const_from_raw(7), - Fp::const_from_raw(6), - Fp::const_from_raw(22), - Fp::const_from_raw(21), - Fp::const_from_raw(8), - Fp::const_from_raw(7), - Fp::const_from_raw(23), - Fp::const_from_raw(8), - Fp::const_from_raw(26), - Fp::const_from_raw(13), - ], - [ - Fp::const_from_raw(13), - Fp::const_from_raw(10), - Fp::const_from_raw(9), - Fp::const_from_raw(7), - Fp::const_from_raw(6), - Fp::const_from_raw(22), - Fp::const_from_raw(21), - Fp::const_from_raw(8), - Fp::const_from_raw(7), - Fp::const_from_raw(23), - Fp::const_from_raw(8), - Fp::const_from_raw(26), - ], - [ - Fp::const_from_raw(26), - Fp::const_from_raw(13), - Fp::const_from_raw(10), - Fp::const_from_raw(9), - Fp::const_from_raw(7), - Fp::const_from_raw(6), - Fp::const_from_raw(22), - Fp::const_from_raw(21), - Fp::const_from_raw(8), - Fp::const_from_raw(7), - Fp::const_from_raw(23), - Fp::const_from_raw(8), - ], - [ - Fp::const_from_raw(8), - Fp::const_from_raw(26), - Fp::const_from_raw(13), - Fp::const_from_raw(10), - Fp::const_from_raw(9), - Fp::const_from_raw(7), - Fp::const_from_raw(6), - Fp::const_from_raw(22), - Fp::const_from_raw(21), - Fp::const_from_raw(8), - Fp::const_from_raw(7), - Fp::const_from_raw(23), - ], - [ - Fp::const_from_raw(23), - Fp::const_from_raw(8), - Fp::const_from_raw(26), - Fp::const_from_raw(13), - Fp::const_from_raw(10), - Fp::const_from_raw(9), - Fp::const_from_raw(7), - Fp::const_from_raw(6), - Fp::const_from_raw(22), - Fp::const_from_raw(21), - Fp::const_from_raw(8), - Fp::const_from_raw(7), - ], -]; - -// Constants for the 160-bit security level - -pub const MDS_VECTOR_160: [Fp; 16] = [ - Fp::const_from_raw(256), - Fp::const_from_raw(2), - Fp::const_from_raw(1073741824), - Fp::const_from_raw(2048), - Fp::const_from_raw(16777216), - Fp::const_from_raw(128), - Fp::const_from_raw(8), - Fp::const_from_raw(16), - Fp::const_from_raw(524288), - Fp::const_from_raw(4194304), - Fp::const_from_raw(1), - Fp::const_from_raw(268435456), - Fp::const_from_raw(1), - Fp::const_from_raw(1024), - Fp::const_from_raw(2), - Fp::const_from_raw(8192), -]; - -pub const ROUND_CONSTANTS_160: [Fp; 224] = [ - Fp::const_from_raw(1965335827333385572), - Fp::const_from_raw(13386940263093285890), - Fp::const_from_raw(2676433512518024499), - Fp::const_from_raw(3265387569419834752), - Fp::const_from_raw(1983410871005483133), - Fp::const_from_raw(9697282293408698131), - Fp::const_from_raw(1272774544215511539), - Fp::const_from_raw(8206289606243220511), - Fp::const_from_raw(1290391036756663400), - Fp::const_from_raw(18219831014774660739), - Fp::const_from_raw(9691367064095402927), - Fp::const_from_raw(1323942862844130786), - Fp::const_from_raw(15151407902520044968), - Fp::const_from_raw(3367241195349533752), - Fp::const_from_raw(4045613938354522492), - Fp::const_from_raw(8414577515806306591), - Fp::const_from_raw(12735791373473705278), - Fp::const_from_raw(3301196190123345788), - Fp::const_from_raw(4934538150586227609), - Fp::const_from_raw(3817643842607407527), - Fp::const_from_raw(13416431558822898318), - Fp::const_from_raw(5832629091408730901), - Fp::const_from_raw(3362368740314001033), - Fp::const_from_raw(11092906639494490385), - Fp::const_from_raw(6071859273097876791), - Fp::const_from_raw(10161425034618716356), - Fp::const_from_raw(7152209120756903545), - Fp::const_from_raw(16380870469663741149), - Fp::const_from_raw(3952136951542576078), - Fp::const_from_raw(17537441052343611097), - Fp::const_from_raw(11551242553047556263), - Fp::const_from_raw(10106900133850428740), - Fp::const_from_raw(11416650542216810040), - Fp::const_from_raw(11422270812969046329), - Fp::const_from_raw(8866991719313052084), - Fp::const_from_raw(11055863001411088108), - Fp::const_from_raw(6180770262849183127), - Fp::const_from_raw(15065904341621422463), - Fp::const_from_raw(6379231142859676194), - Fp::const_from_raw(12898133478008807755), - Fp::const_from_raw(17022976567648776965), - Fp::const_from_raw(9092326911543756291), - Fp::const_from_raw(6030122978628466915), - Fp::const_from_raw(9597034755157312926), - Fp::const_from_raw(994741965321505508), - Fp::const_from_raw(7556490651023083151), - Fp::const_from_raw(13471961853484783473), - Fp::const_from_raw(5530500298270693480), - Fp::const_from_raw(3138602747749119790), - Fp::const_from_raw(14959768162492908516), - Fp::const_from_raw(9134218270579160311), - Fp::const_from_raw(11526344086740032769), - Fp::const_from_raw(18056157006815181954), - Fp::const_from_raw(6800589288408907691), - Fp::const_from_raw(15936640138392473876), - Fp::const_from_raw(2300163192580995689), - Fp::const_from_raw(4526841916921293676), - Fp::const_from_raw(7195881155996340935), - Fp::const_from_raw(2785483023916634674), - Fp::const_from_raw(15081468567893261932), - Fp::const_from_raw(6614707290651872269), - Fp::const_from_raw(13681365294828420351), - Fp::const_from_raw(10664658542323360702), - Fp::const_from_raw(10084964797450915045), - Fp::const_from_raw(4845198022119750202), - Fp::const_from_raw(2607866667643628253), - Fp::const_from_raw(5208104371714885253), - Fp::const_from_raw(12959011109386888563), - Fp::const_from_raw(4000466944391262442), - Fp::const_from_raw(17728719744160665330), - Fp::const_from_raw(7150641948246037689), - Fp::const_from_raw(9776810486328380322), - Fp::const_from_raw(8402715679168885485), - Fp::const_from_raw(3121448252217290414), - Fp::const_from_raw(17436789549778885163), - Fp::const_from_raw(15165907014487612788), - Fp::const_from_raw(11269595316481578714), - Fp::const_from_raw(9914651255870961898), - Fp::const_from_raw(12689101348845299684), - Fp::const_from_raw(11975655653136929369), - Fp::const_from_raw(7372192115875804252), - Fp::const_from_raw(374526648312709133), - Fp::const_from_raw(5985220408386061330), - Fp::const_from_raw(7185802228951619536), - Fp::const_from_raw(1399294693953396201), - Fp::const_from_raw(3261364014951657316), - Fp::const_from_raw(12077409443637692420), - Fp::const_from_raw(9673650825325087603), - Fp::const_from_raw(5569045552142119082), - Fp::const_from_raw(17617312550416673451), - Fp::const_from_raw(6211450796053144311), - Fp::const_from_raw(11274862073326008409), - Fp::const_from_raw(18367233290057731659), - Fp::const_from_raw(13198876392118957255), - Fp::const_from_raw(13272050586507026767), - Fp::const_from_raw(13010781901687851463), - Fp::const_from_raw(11176896862794321170), - Fp::const_from_raw(6638609153583434674), - Fp::const_from_raw(14505835809704498565), - Fp::const_from_raw(17581684280975726513), - Fp::const_from_raw(699795237352602006), - Fp::const_from_raw(9944038704239459812), - Fp::const_from_raw(8047212797227008956), - Fp::const_from_raw(1395744870455664103), - Fp::const_from_raw(18357515964980248812), - Fp::const_from_raw(9097466431298056431), - Fp::const_from_raw(14710664890151992774), - Fp::const_from_raw(6629781383077611287), - Fp::const_from_raw(17573797615501516970), - Fp::const_from_raw(12347664633647440814), - Fp::const_from_raw(11021709264172808686), - Fp::const_from_raw(10955032358008028206), - Fp::const_from_raw(12827014260928926472), - Fp::const_from_raw(14274600229400487385), - Fp::const_from_raw(12031986599882032134), - Fp::const_from_raw(16154104676212634613), - Fp::const_from_raw(18132152994017433356), - Fp::const_from_raw(15441239634310983499), - Fp::const_from_raw(10976597099491887044), - Fp::const_from_raw(3707145841124002094), - Fp::const_from_raw(8720928559638383045), - Fp::const_from_raw(16336200500310468906), - Fp::const_from_raw(6210805750383775651), - Fp::const_from_raw(7719884621977079797), - Fp::const_from_raw(11449042012956416425), - Fp::const_from_raw(9075619080551251971), - Fp::const_from_raw(617668424765806231), - Fp::const_from_raw(12270348236411784037), - Fp::const_from_raw(6186113401837024523), - Fp::const_from_raw(15458192282022704662), - Fp::const_from_raw(3533646002027882636), - Fp::const_from_raw(7323750725122298699), - Fp::const_from_raw(17370102587019252090), - Fp::const_from_raw(1740987243995377904), - Fp::const_from_raw(10219908189144498973), - Fp::const_from_raw(1822464913426161699), - Fp::const_from_raw(13340330593340428766), - Fp::const_from_raw(11476413915876641735), - Fp::const_from_raw(10301877462024259119), - Fp::const_from_raw(17003473479205724655), - Fp::const_from_raw(10899885430087119072), - Fp::const_from_raw(2161571014943847810), - Fp::const_from_raw(10337649388059569402), - Fp::const_from_raw(1627927149280118935), - Fp::const_from_raw(981019442244479500), - Fp::const_from_raw(8080861373146567887), - Fp::const_from_raw(8033636340692269807), - Fp::const_from_raw(1747076424940820198), - Fp::const_from_raw(15430102639810276278), - Fp::const_from_raw(9286420248392647962), - Fp::const_from_raw(11497964697936588530), - Fp::const_from_raw(17639509337065865628), - Fp::const_from_raw(2160917583540985983), - Fp::const_from_raw(6735220140815683510), - Fp::const_from_raw(6183237619116523957), - Fp::const_from_raw(13347893983048485379), - Fp::const_from_raw(4087545433624195113), - Fp::const_from_raw(11701648626105993864), - Fp::const_from_raw(11913677089736238784), - Fp::const_from_raw(271004950317860287), - Fp::const_from_raw(11794070108002091165), - Fp::const_from_raw(15639064309077629849), - Fp::const_from_raw(16481734838884572560), - Fp::const_from_raw(3932918848577657311), - Fp::const_from_raw(16327200574281469287), - Fp::const_from_raw(7060041503065075033), - Fp::const_from_raw(4892761442718320741), - Fp::const_from_raw(8255275116206368067), - Fp::const_from_raw(14957838536671021552), - Fp::const_from_raw(14493715972468567436), - Fp::const_from_raw(7463718209809697261), - Fp::const_from_raw(3440982266989812843), - Fp::const_from_raw(2354199421703013492), - Fp::const_from_raw(2321628279578256047), - Fp::const_from_raw(3746041501354899488), - Fp::const_from_raw(11186576936873825301), - Fp::const_from_raw(15218587616061641074), - Fp::const_from_raw(11844784525417523222), - Fp::const_from_raw(7998727848169056055), - Fp::const_from_raw(7948968711630609066), - Fp::const_from_raw(11805042600408037937), - Fp::const_from_raw(18172588443872800894), - Fp::const_from_raw(13092373363317372568), - Fp::const_from_raw(2169983441195298580), - Fp::const_from_raw(1499680808057735775), - Fp::const_from_raw(7077486803310915643), - Fp::const_from_raw(743612288630452727), - Fp::const_from_raw(11665426394426065172), - Fp::const_from_raw(15533499373769144802), - Fp::const_from_raw(14249183160150274240), - Fp::const_from_raw(13792290235996127743), - Fp::const_from_raw(4995017088228886738), - Fp::const_from_raw(9763845271226970122), - Fp::const_from_raw(1727820159257625458), - Fp::const_from_raw(9681902124347643227), - Fp::const_from_raw(11327574568051933160), - Fp::const_from_raw(10627429556158481577), - Fp::const_from_raw(13984143774797145216), - Fp::const_from_raw(17082059622058840713), - Fp::const_from_raw(16264233536802058333), - Fp::const_from_raw(10077962488096645822), - Fp::const_from_raw(5057253598123536060), - Fp::const_from_raw(2301672207952647376), - Fp::const_from_raw(17506877517896521554), - Fp::const_from_raw(14583366393971011156), - Fp::const_from_raw(6226877164823354372), - Fp::const_from_raw(2260055134098203623), - Fp::const_from_raw(12945296184826522120), - Fp::const_from_raw(15417698598606677168), - Fp::const_from_raw(7447949755934804788), - Fp::const_from_raw(8017843736725863212), - Fp::const_from_raw(1003688007091182795), - Fp::const_from_raw(8935767355090348282), - Fp::const_from_raw(793319158990348431), - Fp::const_from_raw(4437923789992338287), - Fp::const_from_raw(7869978205237541489), - Fp::const_from_raw(9039403419111053092), - Fp::const_from_raw(3845065612997771849), - Fp::const_from_raw(15179573672801872590), - Fp::const_from_raw(2879645310341005490), - Fp::const_from_raw(4421001170561580576), - Fp::const_from_raw(7614461260369642079), - Fp::const_from_raw(10869617590371203777), - Fp::const_from_raw(4582902440098948914), -]; - -pub const MDS_MATRIX_160: [[Fp; 16]; 16] = [ - [ - Fp::const_from_raw(256), - Fp::const_from_raw(2), - Fp::const_from_raw(1073741824), - Fp::const_from_raw(2048), - Fp::const_from_raw(16777216), - Fp::const_from_raw(128), - Fp::const_from_raw(8), - Fp::const_from_raw(16), - Fp::const_from_raw(524288), - Fp::const_from_raw(4194304), - Fp::const_from_raw(1), - Fp::const_from_raw(268435456), - Fp::const_from_raw(1), - Fp::const_from_raw(1024), - Fp::const_from_raw(2), - Fp::const_from_raw(8192), - ], - [ - Fp::const_from_raw(8192), - Fp::const_from_raw(256), - Fp::const_from_raw(2), - Fp::const_from_raw(1073741824), - Fp::const_from_raw(2048), - Fp::const_from_raw(16777216), - Fp::const_from_raw(128), - Fp::const_from_raw(8), - Fp::const_from_raw(16), - Fp::const_from_raw(524288), - Fp::const_from_raw(4194304), - Fp::const_from_raw(1), - Fp::const_from_raw(268435456), - Fp::const_from_raw(1), - Fp::const_from_raw(1024), - Fp::const_from_raw(2), - ], - [ - Fp::const_from_raw(2), - Fp::const_from_raw(8192), - Fp::const_from_raw(256), - Fp::const_from_raw(2), - Fp::const_from_raw(1073741824), - Fp::const_from_raw(2048), - Fp::const_from_raw(16777216), - Fp::const_from_raw(128), - Fp::const_from_raw(8), - Fp::const_from_raw(16), - Fp::const_from_raw(524288), - Fp::const_from_raw(4194304), - Fp::const_from_raw(1), - Fp::const_from_raw(268435456), - Fp::const_from_raw(1), - Fp::const_from_raw(1024), - ], - [ - Fp::const_from_raw(1024), - Fp::const_from_raw(2), - Fp::const_from_raw(8192), - Fp::const_from_raw(256), - Fp::const_from_raw(2), - Fp::const_from_raw(1073741824), - Fp::const_from_raw(2048), - Fp::const_from_raw(16777216), - Fp::const_from_raw(128), - Fp::const_from_raw(8), - Fp::const_from_raw(16), - Fp::const_from_raw(524288), - Fp::const_from_raw(4194304), - Fp::const_from_raw(1), - Fp::const_from_raw(268435456), - Fp::const_from_raw(1), - ], - [ - Fp::const_from_raw(1), - Fp::const_from_raw(1024), - Fp::const_from_raw(2), - Fp::const_from_raw(8192), - Fp::const_from_raw(256), - Fp::const_from_raw(2), - Fp::const_from_raw(1073741824), - Fp::const_from_raw(2048), - Fp::const_from_raw(16777216), - Fp::const_from_raw(128), - Fp::const_from_raw(8), - Fp::const_from_raw(16), - Fp::const_from_raw(524288), - Fp::const_from_raw(4194304), - Fp::const_from_raw(1), - Fp::const_from_raw(268435456), - ], - [ - Fp::const_from_raw(268435456), - Fp::const_from_raw(1), - Fp::const_from_raw(1024), - Fp::const_from_raw(2), - Fp::const_from_raw(8192), - Fp::const_from_raw(256), - Fp::const_from_raw(2), - Fp::const_from_raw(1073741824), - Fp::const_from_raw(2048), - Fp::const_from_raw(16777216), - Fp::const_from_raw(128), - Fp::const_from_raw(8), - Fp::const_from_raw(16), - Fp::const_from_raw(524288), - Fp::const_from_raw(4194304), - Fp::const_from_raw(1), - ], - [ - Fp::const_from_raw(1), - Fp::const_from_raw(268435456), - Fp::const_from_raw(1), - Fp::const_from_raw(1024), - Fp::const_from_raw(2), - Fp::const_from_raw(8192), - Fp::const_from_raw(256), - Fp::const_from_raw(2), - Fp::const_from_raw(1073741824), - Fp::const_from_raw(2048), - Fp::const_from_raw(16777216), - Fp::const_from_raw(128), - Fp::const_from_raw(8), - Fp::const_from_raw(16), - Fp::const_from_raw(524288), - Fp::const_from_raw(4194304), - ], - [ - Fp::const_from_raw(4194304), - Fp::const_from_raw(1), - Fp::const_from_raw(268435456), - Fp::const_from_raw(1), - Fp::const_from_raw(1024), - Fp::const_from_raw(2), - Fp::const_from_raw(8192), - Fp::const_from_raw(256), - Fp::const_from_raw(2), - Fp::const_from_raw(1073741824), - Fp::const_from_raw(2048), - Fp::const_from_raw(16777216), - Fp::const_from_raw(128), - Fp::const_from_raw(8), - Fp::const_from_raw(16), - Fp::const_from_raw(524288), - ], - [ - Fp::const_from_raw(524288), - Fp::const_from_raw(4194304), - Fp::const_from_raw(1), - Fp::const_from_raw(268435456), - Fp::const_from_raw(1), - Fp::const_from_raw(1024), - Fp::const_from_raw(2), - Fp::const_from_raw(8192), - Fp::const_from_raw(256), - Fp::const_from_raw(2), - Fp::const_from_raw(1073741824), - Fp::const_from_raw(2048), - Fp::const_from_raw(16777216), - Fp::const_from_raw(128), - Fp::const_from_raw(8), - Fp::const_from_raw(16), - ], - [ - Fp::const_from_raw(16), - Fp::const_from_raw(524288), - Fp::const_from_raw(4194304), - Fp::const_from_raw(1), - Fp::const_from_raw(268435456), - Fp::const_from_raw(1), - Fp::const_from_raw(1024), - Fp::const_from_raw(2), - Fp::const_from_raw(8192), - Fp::const_from_raw(256), - Fp::const_from_raw(2), - Fp::const_from_raw(1073741824), - Fp::const_from_raw(2048), - Fp::const_from_raw(16777216), - Fp::const_from_raw(128), - Fp::const_from_raw(8), - ], - [ - Fp::const_from_raw(8), - Fp::const_from_raw(16), - Fp::const_from_raw(524288), - Fp::const_from_raw(4194304), - Fp::const_from_raw(1), - Fp::const_from_raw(268435456), - Fp::const_from_raw(1), - Fp::const_from_raw(1024), - Fp::const_from_raw(2), - Fp::const_from_raw(8192), - Fp::const_from_raw(256), - Fp::const_from_raw(2), - Fp::const_from_raw(1073741824), - Fp::const_from_raw(2048), - Fp::const_from_raw(16777216), - Fp::const_from_raw(128), - ], - [ - Fp::const_from_raw(128), - Fp::const_from_raw(8), - Fp::const_from_raw(16), - Fp::const_from_raw(524288), - Fp::const_from_raw(4194304), - Fp::const_from_raw(1), - Fp::const_from_raw(268435456), - Fp::const_from_raw(1), - Fp::const_from_raw(1024), - Fp::const_from_raw(2), - Fp::const_from_raw(8192), - Fp::const_from_raw(256), - Fp::const_from_raw(2), - Fp::const_from_raw(1073741824), - Fp::const_from_raw(2048), - Fp::const_from_raw(16777216), - ], - [ - Fp::const_from_raw(16777216), - Fp::const_from_raw(128), - Fp::const_from_raw(8), - Fp::const_from_raw(16), - Fp::const_from_raw(524288), - Fp::const_from_raw(4194304), - Fp::const_from_raw(1), - Fp::const_from_raw(268435456), - Fp::const_from_raw(1), - Fp::const_from_raw(1024), - Fp::const_from_raw(2), - Fp::const_from_raw(8192), - Fp::const_from_raw(256), - Fp::const_from_raw(2), - Fp::const_from_raw(1073741824), - Fp::const_from_raw(2048), - ], - [ - Fp::const_from_raw(2048), - Fp::const_from_raw(16777216), - Fp::const_from_raw(128), - Fp::const_from_raw(8), - Fp::const_from_raw(16), - Fp::const_from_raw(524288), - Fp::const_from_raw(4194304), - Fp::const_from_raw(1), - Fp::const_from_raw(268435456), - Fp::const_from_raw(1), - Fp::const_from_raw(1024), - Fp::const_from_raw(2), - Fp::const_from_raw(8192), - Fp::const_from_raw(256), - Fp::const_from_raw(2), - Fp::const_from_raw(1073741824), - ], - [ - Fp::const_from_raw(1073741824), - Fp::const_from_raw(2048), - Fp::const_from_raw(16777216), - Fp::const_from_raw(128), - Fp::const_from_raw(8), - Fp::const_from_raw(16), - Fp::const_from_raw(524288), - Fp::const_from_raw(4194304), - Fp::const_from_raw(1), - Fp::const_from_raw(268435456), - Fp::const_from_raw(1), - Fp::const_from_raw(1024), - Fp::const_from_raw(2), - Fp::const_from_raw(8192), - Fp::const_from_raw(256), - Fp::const_from_raw(2), - ], - [ - Fp::const_from_raw(2), - Fp::const_from_raw(1073741824), - Fp::const_from_raw(2048), - Fp::const_from_raw(16777216), - Fp::const_from_raw(128), - Fp::const_from_raw(8), - Fp::const_from_raw(16), - Fp::const_from_raw(524288), - Fp::const_from_raw(4194304), - Fp::const_from_raw(1), - Fp::const_from_raw(268435456), - Fp::const_from_raw(1), - Fp::const_from_raw(1024), - Fp::const_from_raw(2), - Fp::const_from_raw(8192), - Fp::const_from_raw(256), - ], -]; - -#[derive(Clone)] -pub enum SecurityLevel { - Sec128, - Sec160, -} - -pub fn get_state_size(security_level: &SecurityLevel) -> usize { - match security_level { - SecurityLevel::Sec128 => 12, - SecurityLevel::Sec160 => 16, - } -} - -pub fn get_capacity(security_level: &SecurityLevel) -> usize { - match security_level { - SecurityLevel::Sec128 => 4, - SecurityLevel::Sec160 => 6, - } -} - -pub fn get_round_constants(level: &SecurityLevel) -> &'static [Fp] { - match level { - SecurityLevel::Sec128 => &ROUND_CONSTANTS_128, - SecurityLevel::Sec160 => &ROUND_CONSTANTS_160, - } -} - -pub enum MdsVector { - Mds128([Fp; 12]), - Mds160([Fp; 16]), -} - -pub fn get_mds_vector(level: SecurityLevel) -> MdsVector { - match level { - SecurityLevel::Sec128 => MdsVector::Mds128(MDS_VECTOR_128), - SecurityLevel::Sec160 => MdsVector::Mds160(MDS_VECTOR_160), - } -} - -#[allow(clippy::large_enum_variant)] -pub enum MdsMatrix { - Mds128([[Fp; 12]; 12]), - Mds160([[Fp; 16]; 16]), -} - -pub fn get_mds_matrix(level: &SecurityLevel) -> MdsMatrix { - match level { - SecurityLevel::Sec128 => MdsMatrix::Mds128(MDS_MATRIX_128), - SecurityLevel::Sec160 => MdsMatrix::Mds160(MDS_MATRIX_160), - } -} - -impl MdsVector { - pub fn as_slice(&self) -> &[Fp] { - match self { - MdsVector::Mds128(ref vec) => vec, - MdsVector::Mds160(ref vec) => vec, - } - } -} diff --git a/crates/crypto/src/hash/rescue_prime/rescue_prime_optimized.rs b/crates/crypto/src/hash/rescue_prime/rescue_prime_optimized.rs deleted file mode 100644 index a8956c5c9..000000000 --- a/crates/crypto/src/hash/rescue_prime/rescue_prime_optimized.rs +++ /dev/null @@ -1,815 +0,0 @@ -use super::parameters::*; -use super::utils::*; -use super::Fp; -use crate::alloc::vec::Vec; -use core::iter; -use lambdaworks_math::field::errors::FieldError; - -// Implementation of the Rescue Prime Optimized hash function. -// https://eprint.iacr.org/2022/1577 -// https://github.com/ASDiscreteMathematics/rpo/tree/master/reference_implementation -// It supports two security levels: 128-bit and 160-bit. Depending on the security level chosen -// the integer parameters are set accordingly. - -// For the Security level (λ) of 128 bits we have: -// Number of rounds (N): 7 -// State size (m): 12 -// Rate (r): 8 -// Capacity (c): 4 - -// For the Security level (λ) of 160 bits we have: -// Number of rounds (N): 7 -// State size (m): 16 -// Rate (r): 10 -// Capacity (c): 6 - -// In the paper, the authors use a number of rounds equal to 7 as a trade-off between security and performance. -// The number of rounds can be increased to 8 or 9 to achieve a higher level of security at the cost of performance. -const NUM_FULL_ROUNDS: usize = 7; - -pub struct RescuePrimeOptimized { - /// State width of the hash function. - m: usize, - /// Capacity of the sponge. - capacity: usize, - /// Rate of the sponge. - rate: usize, - /// Precomputed round constants used in the permutation. - round_constants: &'static [Fp], - /// MDS matrix used in the permutation. - mds_matrix: Vec>, - /// MDS vector used for optimizing matrix multiplication. - mds_vector: MdsVector, - /// Method used for applying the MDS matrix. - mds_method: MdsMethod, -} - -impl Default for RescuePrimeOptimized { - fn default() -> Self { - Self::new(SecurityLevel::Sec128, MdsMethod::MatrixMultiplication).unwrap() - } -} - -impl RescuePrimeOptimized { - /// Creates a new instance of `RescuePrimeOptimized` with corresponding Security level and the specified MDS method. - pub fn new(security_level: SecurityLevel, mds_method: MdsMethod) -> Result { - let m = get_state_size(&security_level); - let capacity = get_capacity(&security_level); - let rate = m - capacity; - let mds_matrix = get_mds_matrix(&security_level); - let round_constants = get_round_constants(&security_level); - let mds_vector = get_mds_vector(security_level); - Ok(Self { - m, - capacity, - rate, - round_constants, - mds_matrix: match mds_matrix { - MdsMatrix::Mds128(matrix) => matrix.iter().map(|&row| row.to_vec()).collect(), - MdsMatrix::Mds160(matrix) => matrix.iter().map(|&row| row.to_vec()).collect(), - }, - mds_vector, - mds_method, - }) - } - - /// Applies the inverse S-box to the state. - pub fn apply_inverse_sbox(state: &mut [Fp]) { - for x in state.iter_mut() { - *x = x.pow(ALPHA_INV); - } - } - - /// Applies the S-box to the state. - pub fn apply_sbox(state: &mut [Fp]) { - for x in state.iter_mut() { - *x = x.pow(ALPHA); - } - } - - /// Performs MDS matrix-vector multiplication. - fn mds_matrix_vector_multiplication(&self, state: &[Fp]) -> Vec { - let m = state.len(); - let mut new_state = vec![Fp::zero(); m]; - - for (i, new_value) in new_state.iter_mut().enumerate() { - for (j, state_value) in state.iter().enumerate() { - *new_value += self.mds_matrix[i][j] * state_value; - } - } - - new_state - } - - /// Performs MDS using Number Theoretic Transform. - fn mds_ntt(&self, state: &[Fp]) -> Result, FieldError> { - let m = state.len(); - let omega = if m == 12 { - Fp::from(281474976645120u64) - } else { - Fp::from(17293822564807737345u64) - }; - let mds_vector = self.mds_vector.as_slice(); - - let mds_ntt = ntt(mds_vector, omega); - let state_rev: Vec = iter::once(state[0]) - .chain(state[1..].iter().rev().cloned()) - .collect(); - let state_ntt = ntt(&state_rev, omega); - - let mut product_ntt = vec![Fp::zero(); m]; - for i in 0..m { - product_ntt[i] = mds_ntt[i] * state_ntt[i]; - } - - let omega_inv = omega.inv()?; - let result = intt(&product_ntt, omega_inv)?; - - Ok(iter::once(result[0]) - .chain(result[1..].iter().rev().cloned()) - .collect()) - } - - /// Performs MDS using the Karatsuba algorithm. - fn mds_karatsuba(&self, state: &[Fp]) -> Vec { - let m = state.len(); - let mds_vector = self.mds_vector.as_slice(); - let mds_rev: Vec = iter::once(mds_vector[0]) - .chain(mds_vector[1..].iter().rev().cloned()) - .collect(); - - let conv = karatsuba(&mds_rev, state); - - let mut result = vec![Fp::zero(); m]; - result[..m].copy_from_slice(&conv[..m]); - for i in m..conv.len() { - result[i - m] += conv[i]; - } - - result - } - - /// Applies the MDS transformation to the state. - fn apply_mds(&self, state: &mut [Fp]) -> Result<(), FieldError> { - let new_state = match self.mds_method { - MdsMethod::MatrixMultiplication => self.mds_matrix_vector_multiplication(state), - MdsMethod::Ntt => self.mds_ntt(state)?, - MdsMethod::Karatsuba => self.mds_karatsuba(state), - }; - state.copy_from_slice(&new_state); - Ok(()) - } - - /// Adds the round constants to the state. - fn add_round_constants(&self, state: &mut [Fp], round: usize) { - let m = self.m; - let round_constants = &self.round_constants[round * 2 * m..]; - - state - .iter_mut() - .zip(round_constants.iter()) - .take(m) - .for_each(|(state_elem, &constant)| { - *state_elem += constant; - }); - } - - /// Adds the second set of round constants to the state. - fn add_round_constants_second(&self, state: &mut [Fp], round: usize) { - let m = self.m; - let round_constants = &self.round_constants[round * 2 * m + m..]; - - state - .iter_mut() - .zip(round_constants.iter()) - .take(m) - .for_each(|(state_elem, &constant)| { - *state_elem += constant; - }); - } - - /// Performs the full permutation on the state. - pub fn permutation(&self, state: &mut [Fp]) { - let num_rounds = NUM_FULL_ROUNDS; - for round in 0..num_rounds { - let _ = self.apply_mds(state); - self.add_round_constants(state, round); - Self::apply_sbox(state); - let _ = self.apply_mds(state); - self.add_round_constants_second(state, round); - Self::apply_inverse_sbox(state); - } - } - - /// Hashes an input sequence of field elements. - pub fn hash(&self, input_sequence: &[Fp]) -> Vec { - let mut state = vec![Fp::zero(); self.m]; - let input_len = input_sequence.len(); - if !input_len.is_multiple_of(self.rate) { - state[0] = Fp::one(); - } - let num_full_chunks = input_len / self.rate; - for i in 0..num_full_chunks { - let chunk = &input_sequence[i * self.rate..(i + 1) * self.rate]; - state[self.capacity..(self.rate + self.capacity)].copy_from_slice(&chunk[..self.rate]); - self.permutation(&mut state); - } - let last_chunk_size = input_len % self.rate; - if last_chunk_size != 0 { - let mut last_chunk = vec![Fp::zero(); self.rate]; - for j in 0..last_chunk_size { - last_chunk[j] = input_sequence[num_full_chunks * self.rate + j]; - } - last_chunk[last_chunk_size] = Fp::one(); - state[self.capacity..(self.rate + self.capacity)] - .copy_from_slice(&last_chunk[..self.rate]); - self.permutation(&mut state); - } - - state[self.capacity..self.capacity + self.rate / 2].to_vec() - } - - /// Hashes an input sequence of bytes. - pub fn hash_bytes(&self, input: &[u8]) -> Vec { - let field_elements = bytes_to_field_elements(input); - self.hash(&field_elements) - } -} -#[derive(Clone)] -pub enum MdsMethod { - /// Use standard matrix multiplication. - MatrixMultiplication, - /// Use Number Theoretic Transform for multiplication. - Ntt, - /// Use Karatsuba algorithm for multiplication. - Karatsuba, -} -#[cfg(test)] -mod tests { - use super::*; - use rand::rngs::StdRng; - use rand::{Rng, SeedableRng}; - - // Values obtained from the Sage implemenstation in - // https://github.com/ASDiscreteMathematics/rpo/tree/master/reference_implementation - pub const EXPECTED_128: [[Fp; 4]; 19] = [ - [ - Fp::const_from_raw(1502364727743950833u64), - Fp::const_from_raw(5880949717274681448u64), - Fp::const_from_raw(162790463902224431u64), - Fp::const_from_raw(6901340476773664264u64), - ], - [ - Fp::const_from_raw(7478710183745780580u64), - Fp::const_from_raw(3308077307559720969u64), - Fp::const_from_raw(3383561985796182409u64), - Fp::const_from_raw(17205078494700259815u64), - ], - [ - Fp::const_from_raw(17439912364295172999u64), - Fp::const_from_raw(17979156346142712171u64), - Fp::const_from_raw(8280795511427637894u64), - Fp::const_from_raw(9349844417834368814u64), - ], - [ - Fp::const_from_raw(5105868198472766874u64), - Fp::const_from_raw(13090564195691924742u64), - Fp::const_from_raw(1058904296915798891u64), - Fp::const_from_raw(18379501748825152268u64), - ], - [ - Fp::const_from_raw(9133662113608941286u64), - Fp::const_from_raw(12096627591905525991u64), - Fp::const_from_raw(14963426595993304047u64), - Fp::const_from_raw(13290205840019973377u64), - ], - [ - Fp::const_from_raw(3134262397541159485u64), - Fp::const_from_raw(10106105871979362399u64), - Fp::const_from_raw(138768814855329459u64), - Fp::const_from_raw(15044809212457404677u64), - ], - [ - Fp::const_from_raw(162696376578462826u64), - Fp::const_from_raw(4991300494838863586u64), - Fp::const_from_raw(660346084748120605u64), - Fp::const_from_raw(13179389528641752698u64), - ], - [ - Fp::const_from_raw(2242391899857912644u64), - Fp::const_from_raw(12689382052053305418u64), - Fp::const_from_raw(235236990017815546u64), - Fp::const_from_raw(5046143039268215739u64), - ], - [ - Fp::const_from_raw(9585630502158073976u64), - Fp::const_from_raw(1310051013427303477u64), - Fp::const_from_raw(7491921222636097758u64), - Fp::const_from_raw(9417501558995216762u64), - ], - [ - Fp::const_from_raw(1994394001720334744u64), - Fp::const_from_raw(10866209900885216467u64), - Fp::const_from_raw(13836092831163031683u64), - Fp::const_from_raw(10814636682252756697u64), - ], - [ - Fp::const_from_raw(17486854790732826405u64), - Fp::const_from_raw(17376549265955727562u64), - Fp::const_from_raw(2371059831956435003u64), - Fp::const_from_raw(17585704935858006533u64), - ], - [ - Fp::const_from_raw(11368277489137713825u64), - Fp::const_from_raw(3906270146963049287u64), - Fp::const_from_raw(10236262408213059745u64), - Fp::const_from_raw(78552867005814007u64), - ], - [ - Fp::const_from_raw(17899847381280262181u64), - Fp::const_from_raw(14717912805498651446u64), - Fp::const_from_raw(10769146203951775298u64), - Fp::const_from_raw(2774289833490417856u64), - ], - [ - Fp::const_from_raw(3794717687462954368u64), - Fp::const_from_raw(4386865643074822822u64), - Fp::const_from_raw(8854162840275334305u64), - Fp::const_from_raw(7129983987107225269u64), - ], - [ - Fp::const_from_raw(7244773535611633983u64), - Fp::const_from_raw(19359923075859320u64), - Fp::const_from_raw(10898655967774994333u64), - Fp::const_from_raw(9319339563065736480u64), - ], - [ - Fp::const_from_raw(4935426252518736883u64), - Fp::const_from_raw(12584230452580950419u64), - Fp::const_from_raw(8762518969632303998u64), - Fp::const_from_raw(18159875708229758073u64), - ], - [ - Fp::const_from_raw(14871230873837295931u64), - Fp::const_from_raw(11225255908868362971u64), - Fp::const_from_raw(18100987641405432308u64), - Fp::const_from_raw(1559244340089644233u64), - ], - [ - Fp::const_from_raw(8348203744950016968u64), - Fp::const_from_raw(4041411241960726733u64), - Fp::const_from_raw(17584743399305468057u64), - Fp::const_from_raw(16836952610803537051u64), - ], - [ - Fp::const_from_raw(16139797453633030050u64), - Fp::const_from_raw(1090233424040889412u64), - Fp::const_from_raw(10770255347785669036u64), - Fp::const_from_raw(16982398877290254028u64), - ], - ]; - - pub const EXPECTED_160: [[Fp; 5]; 19] = [ - [ - Fp::const_from_raw(4766737105427868572), - Fp::const_from_raw(7538777753317835226), - Fp::const_from_raw(13644171984579649606), - Fp::const_from_raw(6748107971891460622), - Fp::const_from_raw(3480072938342119934), - ], - [ - Fp::const_from_raw(6277287777617382937), - Fp::const_from_raw(5688033921803605355), - Fp::const_from_raw(1104978478612014217), - Fp::const_from_raw(973672476085279574), - Fp::const_from_raw(7883652116413797779), - ], - [ - Fp::const_from_raw(3071553803427093579), - Fp::const_from_raw(12239501990998925662), - Fp::const_from_raw(14411295652479845526), - Fp::const_from_raw(5735407824213194294), - Fp::const_from_raw(6714816738691504270), - ], - [ - Fp::const_from_raw(4455998568145007624), - Fp::const_from_raw(18218360213084301612), - Fp::const_from_raw(8963555484142424669), - Fp::const_from_raw(13451196299356019287), - Fp::const_from_raw(660967320761434775), - ], - [ - Fp::const_from_raw(7894041400531553560), - Fp::const_from_raw(3138084719322472990), - Fp::const_from_raw(15017675162298246509), - Fp::const_from_raw(12340633143623038238), - Fp::const_from_raw(3710158928968726190), - ], - [ - Fp::const_from_raw(18345924309197503617), - Fp::const_from_raw(6448668044176965096), - Fp::const_from_raw(5891298758878861437), - Fp::const_from_raw(18404292940273103487), - Fp::const_from_raw(399715742058360811), - ], - [ - Fp::const_from_raw(4293522863608749708), - Fp::const_from_raw(11352999694211746044), - Fp::const_from_raw(15850245073570756600), - Fp::const_from_raw(1206950096837096206), - Fp::const_from_raw(6945598368659615878), - ], - [ - Fp::const_from_raw(1339949574743034442), - Fp::const_from_raw(5967452101017112419), - Fp::const_from_raw(824612579975542151), - Fp::const_from_raw(3327557828938393394), - Fp::const_from_raw(14113149399665697150), - ], - [ - Fp::const_from_raw(3540904694808418824), - Fp::const_from_raw(5951416386790014715), - Fp::const_from_raw(13859113410786779774), - Fp::const_from_raw(17205554479494520251), - Fp::const_from_raw(7359323608260195110), - ], - [ - Fp::const_from_raw(7504301802792161339), - Fp::const_from_raw(12879743137663115497), - Fp::const_from_raw(17245986604042562042), - Fp::const_from_raw(8175050867418132561), - Fp::const_from_raw(1063965910664731268), - ], - [ - Fp::const_from_raw(18267475461736255602), - Fp::const_from_raw(4481864641736940956), - Fp::const_from_raw(11260039501101148638), - Fp::const_from_raw(7529970948767692955), - Fp::const_from_raw(4177810888704753150), - ], - [ - Fp::const_from_raw(16604116128892623566), - Fp::const_from_raw(1520851983040290492), - Fp::const_from_raw(9361704524730297620), - Fp::const_from_raw(7447748879766268839), - Fp::const_from_raw(10834422028571028806), - ], - [ - Fp::const_from_raw(243957224918814907), - Fp::const_from_raw(9966149007214472697), - Fp::const_from_raw(18130816682404489504), - Fp::const_from_raw(3814760895598122151), - Fp::const_from_raw(862573500652233787), - ], - [ - Fp::const_from_raw(13414343823130474877), - Fp::const_from_raw(1002887112060795246), - Fp::const_from_raw(16685735965176892618), - Fp::const_from_raw(16172309857128312555), - Fp::const_from_raw(5158081519803147178), - ], - [ - Fp::const_from_raw(14614132925482133961), - Fp::const_from_raw(7618082792229868740), - Fp::const_from_raw(1881720834768448253), - Fp::const_from_raw(11508391877383996679), - Fp::const_from_raw(5348386073072413261), - ], - [ - Fp::const_from_raw(6268111131988518030), - Fp::const_from_raw(17920308297240232909), - Fp::const_from_raw(17719152474870950965), - Fp::const_from_raw(14857432101092580778), - Fp::const_from_raw(5708937553833180778), - ], - [ - Fp::const_from_raw(11597726741964198121), - Fp::const_from_raw(1568026444559423552), - Fp::const_from_raw(3233218961458461983), - Fp::const_from_raw(9700509409081014876), - Fp::const_from_raw(7989061413164577390), - ], - [ - Fp::const_from_raw(11180580619692834182), - Fp::const_from_raw(16871004730930134181), - Fp::const_from_raw(17810700669516829599), - Fp::const_from_raw(13679692060051982328), - Fp::const_from_raw(10386085719330760064), - ], - [ - Fp::const_from_raw(6222872143719551583), - Fp::const_from_raw(3842704143974291265), - Fp::const_from_raw(18311432727968603639), - Fp::const_from_raw(12278517700025439333), - Fp::const_from_raw(7011953052853282225), - ], - ]; - fn rand_field_element(rng: &mut R) -> Fp { - Fp::from(rng.gen::()) - } - - #[test] - fn test_apply_sbox() { - let mut rng = StdRng::seed_from_u64(1); - let rescue = - RescuePrimeOptimized::new(SecurityLevel::Sec128, MdsMethod::MatrixMultiplication) - .unwrap(); - let mut state: Vec = (0..rescue.m) - .map(|_| rand_field_element(&mut rng)) - .collect(); - - let mut expected = state.clone(); - expected.iter_mut().for_each(|v| *v = v.pow(ALPHA)); - - RescuePrimeOptimized::apply_sbox(&mut state); - assert_eq!(expected, state); - } - - #[test] - fn test_apply_inverse_sbox() { - let mut rng = StdRng::seed_from_u64(2); - let rescue = - RescuePrimeOptimized::new(SecurityLevel::Sec128, MdsMethod::MatrixMultiplication) - .unwrap(); - let mut state: Vec = (0..rescue.m) - .map(|_| rand_field_element(&mut rng)) - .collect(); - - let mut expected = state.clone(); - expected.iter_mut().for_each(|v| *v = v.pow(ALPHA_INV)); - - RescuePrimeOptimized::apply_inverse_sbox(&mut state); - assert_eq!(expected, state); - } - - #[test] - fn test_mds_matrix_multiplication() { - let mut rng = StdRng::seed_from_u64(3); - let rescue = - RescuePrimeOptimized::new(SecurityLevel::Sec128, MdsMethod::MatrixMultiplication) - .unwrap(); - let state: Vec = (0..rescue.m) - .map(|_| rand_field_element(&mut rng)) - .collect(); - - let expected_state = rescue.mds_matrix_vector_multiplication(&state); - let mut computed_state = state.clone(); - let _ = rescue.apply_mds(&mut computed_state); - - assert_eq!(expected_state, computed_state); - } - - #[test] - fn test_mds_ntt() { - let mut rng = StdRng::seed_from_u64(4); - let rescue_ntt = RescuePrimeOptimized::new(SecurityLevel::Sec128, MdsMethod::Ntt).unwrap(); - let state: Vec = (0..rescue_ntt.m) - .map(|_| rand_field_element(&mut rng)) - .collect(); - - let expected_state = rescue_ntt.mds_ntt(&state).unwrap(); - let mut computed_state = state.clone(); - let _ = rescue_ntt.apply_mds(&mut computed_state); - - assert_eq!(expected_state, computed_state); - } - - #[test] - fn test_mds_karatsuba() { - let mut rng = StdRng::seed_from_u64(5); - let rescue_karatsuba = - RescuePrimeOptimized::new(SecurityLevel::Sec128, MdsMethod::Karatsuba).unwrap(); - let state: Vec = (0..rescue_karatsuba.m) - .map(|_| rand_field_element(&mut rng)) - .collect(); - - let expected_state = rescue_karatsuba.mds_karatsuba(&state); - let mut computed_state = state.clone(); - let _ = rescue_karatsuba.apply_mds(&mut computed_state); - - assert_eq!(expected_state, computed_state); - } - - #[test] - fn test_add_round_constants() { - let mut rng = StdRng::seed_from_u64(6); - let rescue = - RescuePrimeOptimized::new(SecurityLevel::Sec128, MdsMethod::MatrixMultiplication) - .unwrap(); - let mut state: Vec = (0..rescue.m) - .map(|_| rand_field_element(&mut rng)) - .collect(); - - let round = 0; - let expected_state = state - .iter() - .enumerate() - .map(|(i, &x)| x + rescue.round_constants[round * 2 * rescue.m + i]) - .collect::>(); - - rescue.add_round_constants(&mut state, round); - - assert_eq!(expected_state, state); - } - - #[test] - fn test_permutation() { - let mut rng = StdRng::seed_from_u64(7); - let rescue = - RescuePrimeOptimized::new(SecurityLevel::Sec128, MdsMethod::MatrixMultiplication) - .unwrap(); - let mut state: Vec = (0..rescue.m) - .map(|_| rand_field_element(&mut rng)) - .collect(); - - let expected_state = { - let mut temp_state = state.clone(); - for round in 0..7 { - let _ = rescue.apply_mds(&mut temp_state); - rescue.add_round_constants(&mut temp_state, round); - RescuePrimeOptimized::apply_sbox(&mut temp_state); - let _ = rescue.apply_mds(&mut temp_state); - rescue.add_round_constants_second(&mut temp_state, round); - RescuePrimeOptimized::apply_inverse_sbox(&mut temp_state); - } - temp_state - }; - - rescue.permutation(&mut state); - - assert_eq!(expected_state, state); - } - - #[test] - fn test_hash_single_chunk() { - let rescue = - RescuePrimeOptimized::new(SecurityLevel::Sec128, MdsMethod::MatrixMultiplication) - .unwrap(); - let input_sequence: Vec = (0..8).map(Fp::from).collect(); - let hash_output = rescue.hash(&input_sequence); - - assert_eq!(hash_output.len(), 4); - } - - #[test] - fn test_hash_multiple_chunks() { - let rescue = - RescuePrimeOptimized::new(SecurityLevel::Sec128, MdsMethod::MatrixMultiplication) - .unwrap(); - let input_sequence: Vec = (0..16).map(Fp::from).collect(); // Two chunks of size 8 - let hash_output = rescue.hash(&input_sequence); - - assert_eq!(hash_output.len(), 4); - } - - #[test] - fn test_hash_with_padding() { - let rescue = - RescuePrimeOptimized::new(SecurityLevel::Sec128, MdsMethod::MatrixMultiplication) - .unwrap(); - let input_sequence: Vec = (0..5).map(Fp::from).collect(); - let hash_output = rescue.hash(&input_sequence); - assert_eq!(hash_output.len(), 4); - } - #[test] - // test ported from https://github.com/0xPolygonMiden/crypto/blob/main/src/hash/rescue/rpo/tests.rs - fn hash_padding() { - let rescue = - RescuePrimeOptimized::new(SecurityLevel::Sec128, MdsMethod::MatrixMultiplication) - .unwrap(); - - let input1 = vec![1u8, 2, 3]; - let input2 = vec![1u8, 2, 3, 0]; - let hash1 = rescue.hash_bytes(&input1); - let hash2 = rescue.hash_bytes(&input2); - assert_ne!(hash1, hash2); - - let input1 = vec![1_u8, 2, 3, 4, 5, 6]; - let input2 = vec![1_u8, 2, 3, 4, 5, 6, 0]; - let hash1 = rescue.hash_bytes(&input1); - let hash2 = rescue.hash_bytes(&input2); - assert_ne!(hash1, hash2); - - let input1 = vec![1_u8, 2, 3, 4, 5, 6, 7, 0, 0]; - let input2 = vec![1_u8, 2, 3, 4, 5, 6, 7, 0, 0, 0, 0]; - let hash1 = rescue.hash_bytes(&input1); - let hash2 = rescue.hash_bytes(&input2); - assert_ne!(hash1, hash2); - } - #[cfg(feature = "std")] - #[test] - fn sponge_zeroes_collision() { - let rescue = - RescuePrimeOptimized::new(SecurityLevel::Sec128, MdsMethod::MatrixMultiplication) - .unwrap(); - - let mut zeroes = Vec::new(); - let mut hashes = std::collections::HashSet::new(); - - for _ in 0..255 { - let hash = rescue.hash(&zeroes); - assert!(hashes.insert(hash)); - zeroes.push(Fp::zero()); - } - } - #[test] - fn test_hash_bytes() { - let rescue = - RescuePrimeOptimized::new(SecurityLevel::Sec128, MdsMethod::MatrixMultiplication) - .unwrap(); - let input_bytes = b"Rescue Prime Optimized"; - let hash_output = rescue.hash_bytes(input_bytes); - - assert_eq!(hash_output.len(), 4); - } - - #[test] - fn test_mds_methods_consistency() { - let rescue_matrix = - RescuePrimeOptimized::new(SecurityLevel::Sec128, MdsMethod::MatrixMultiplication) - .unwrap(); - let rescue_ntt = RescuePrimeOptimized::new(SecurityLevel::Sec128, MdsMethod::Ntt).unwrap(); - let rescue_karatsuba = - RescuePrimeOptimized::new(SecurityLevel::Sec128, MdsMethod::Karatsuba).unwrap(); - - let input = vec![ - Fp::from(1u64), - Fp::from(2u64), - Fp::from(3u64), - Fp::from(4u64), - Fp::from(5u64), - Fp::from(6u64), - Fp::from(7u64), - Fp::from(8u64), - Fp::from(9u64), - ]; - - let hash_matrix = rescue_matrix.hash(&input); - let hash_ntt = rescue_ntt.hash(&input); - let hash_karatsuba = rescue_karatsuba.hash(&input); - - assert_eq!(hash_matrix, hash_ntt); - assert_eq!(hash_ntt, hash_karatsuba); - } - - #[test] - fn test_hash_vectors_128() { - let rescue = - RescuePrimeOptimized::new(SecurityLevel::Sec128, MdsMethod::MatrixMultiplication) - .unwrap(); - let elements: Vec = (0..19).map(Fp::from).collect(); - - EXPECTED_128.iter().enumerate().for_each(|(i, expected)| { - let input = elements.iter().take(i + 1); - let hash_output = rescue.hash(input.cloned().collect::>().as_slice()); - - assert_eq!( - hash_output, - *expected, - "Hash mismatch for input length {}", - i + 1 - ); - }); - } - #[test] - fn test_hash_vector_160() { - let rescue = - RescuePrimeOptimized::new(SecurityLevel::Sec160, MdsMethod::MatrixMultiplication) - .unwrap(); - let elements: Vec = (0..19).map(Fp::from).collect(); - - EXPECTED_160.iter().enumerate().for_each(|(i, expected)| { - let input = elements.iter().take(i + 1); - let hash_output = rescue.hash(input.cloned().collect::>().as_slice()); - - assert_eq!( - hash_output, - *expected, - "Hash mismatch for input length {}", - i + 1 - ); - }); - } - #[cfg(feature = "std")] - #[test] - fn test_hash_example_and_print() { - let rescue = RescuePrimeOptimized::new(SecurityLevel::Sec128, MdsMethod::Ntt).unwrap(); - - let input = b"Hello there"; - - let hash_result = rescue.hash_bytes(input); - - println!("Input: {input:?}"); - println!("Hash result:"); - for (i, value) in hash_result.iter().enumerate() { - println!(" {i}: {value}"); - } - - println!("Hash as u64 values:"); - for value in hash_result.iter() { - print!("{}, ", value.value()); - } - println!(); - assert_eq!(hash_result.len(), 4); - } -} diff --git a/crates/crypto/src/hash/rescue_prime/utils.rs b/crates/crypto/src/hash/rescue_prime/utils.rs deleted file mode 100644 index ba17341bc..000000000 --- a/crates/crypto/src/hash/rescue_prime/utils.rs +++ /dev/null @@ -1,78 +0,0 @@ -use super::Fp; -use alloc::vec::Vec; -use lambdaworks_math::field::errors::FieldError; - -// Auxiliary algorithms based on the reference implementation in Sage -// https://github.com/ASDiscreteMathematics/rpo/tree/master/reference_implementation - -pub fn bytes_to_field_elements(input: &[u8]) -> Vec { - input - .chunks(7) - .map(|chunk| { - let mut buf = [0u8; 8]; - buf[..chunk.len()].copy_from_slice(chunk); - if chunk.len() < 7 { - buf[chunk.len()] = 1; - } - let value = u64::from_le_bytes(buf); - Fp::from(value) - }) - .collect() -} - -pub fn ntt(input: &[Fp], omega: Fp) -> Vec { - (0..input.len()) - .map(|i| { - input.iter().enumerate().fold(Fp::zero(), |acc, (j, val)| { - acc + *val * omega.pow((i * j) as u64) - }) - }) - .collect() -} - -pub fn intt(input: &[Fp], omega_inv: Fp) -> Result, FieldError> { - let n = input.len() as u64; - let inv_n = Fp::from(n).inv()?; - let transformed = ntt(input, omega_inv); - Ok(transformed.into_iter().map(|val| val * inv_n).collect()) -} - -pub fn karatsuba(lhs: &[Fp], rhs: &[Fp]) -> Vec { - let n = lhs.len(); - if n <= 32 { - let mut result = vec![Fp::zero(); 2 * n - 1]; - lhs.iter().enumerate().for_each(|(i, &lhs_val)| { - rhs.iter().enumerate().for_each(|(j, &rhs_val)| { - result[i + j] += lhs_val * rhs_val; - }); - }); - return result; - } - - let half = n / 2; - let (lhs_low, lhs_high) = lhs.split_at(half); - let (rhs_low, rhs_high) = rhs.split_at(half); - - let z0 = karatsuba(lhs_low, rhs_low); - let z2 = karatsuba(lhs_high, rhs_high); - - let lhs_sum: Vec = lhs_low.iter().zip(lhs_high).map(|(a, b)| *a + *b).collect(); - let rhs_sum: Vec = rhs_low.iter().zip(rhs_high).map(|(a, b)| *a + *b).collect(); - - let z1 = karatsuba(&lhs_sum, &rhs_sum); - - let mut result = vec![Fp::zero(); 2 * n - 1]; - - z0.iter().enumerate().for_each(|(i, &val)| result[i] = val); - z2.iter() - .enumerate() - .for_each(|(i, &val)| result[i + 2 * half] = val); - - z1.iter().enumerate().for_each(|(i, &val)| { - result[i + half] += val - - z0.get(i).cloned().unwrap_or(Fp::zero()) - - z2.get(i).cloned().unwrap_or(Fp::zero()); - }); - - result -} diff --git a/crates/crypto/src/hash/sha3/mod.rs b/crates/crypto/src/hash/sha3/mod.rs deleted file mode 100644 index 9404e78f3..000000000 --- a/crates/crypto/src/hash/sha3/mod.rs +++ /dev/null @@ -1,74 +0,0 @@ -use alloc::{ - string::{String, ToString}, - vec::Vec, -}; -use sha3::{Digest, Sha3_256}; - -pub struct Sha3Hasher; - -/// Sha3 Hasher used over fields -/// Notice while it's generic over F, it's only generates enough randomness for fields of at most 256 bits -impl Sha3Hasher { - pub const fn new() -> Self { - Self - } - - pub fn expand_message(msg: &[u8], dst: &[u8], len_in_bytes: u64) -> Result, String> { - let b_in_bytes = Sha3_256::output_size() as u64; - - let ell = len_in_bytes.div_ceil(b_in_bytes); - if ell > 255 { - return Err("Abort".to_string()); - } - - let dst_prime: Vec = [dst, &Self::i2osp(dst.len() as u64, 1)].concat(); - let z_pad = Self::i2osp(0, 64); - let l_i_b_str = Self::i2osp(len_in_bytes, 2); - let msg_prime = [ - z_pad, - msg.to_vec(), - l_i_b_str, - Self::i2osp(0, 1), - dst_prime.clone(), - ] - .concat(); - let b_0: Vec = Sha3_256::digest(msg_prime).to_vec(); - let a = [b_0.clone(), Self::i2osp(1, 1), dst_prime.clone()].concat(); - let b_1 = Sha3_256::digest(a).to_vec(); - - let mut b_vals = Vec::>::with_capacity(ell as usize); - b_vals.push(b_1); - for idx in 1..ell { - let aux = Self::strxor(&b_0, &b_vals[idx as usize - 1]); - let b_i = [aux, Self::i2osp(idx, 1), dst_prime.clone()].concat(); - b_vals.push(Sha3_256::digest(b_i).to_vec()); - } - - let mut b_vals = b_vals.concat(); - b_vals.truncate(len_in_bytes as usize); - - Ok(b_vals) - } - - fn i2osp(x: u64, length: u64) -> Vec { - let mut x_aux = x; - let mut digits = Vec::new(); - while x_aux != 0 { - digits.push((x_aux % 256) as u8); - x_aux /= 256; - } - digits.resize(length as usize, 0); - digits.reverse(); - digits - } - - fn strxor(a: &[u8], b: &[u8]) -> Vec { - a.iter().zip(b).map(|(a, b)| a ^ b).collect() - } -} - -impl Default for Sha3Hasher { - fn default() -> Self { - Self::new() - } -} diff --git a/crates/crypto/src/lib.rs b/crates/crypto/src/lib.rs deleted file mode 100644 index 5e3d81162..000000000 --- a/crates/crypto/src/lib.rs +++ /dev/null @@ -1,11 +0,0 @@ -#![allow(clippy::op_ref)] -#![cfg_attr(not(feature = "std"), no_std)] -#[macro_use] -extern crate alloc; - -pub mod commitments; -#[cfg(feature = "std")] -pub mod errors; -pub mod fiat_shamir; -pub mod hash; -pub mod merkle_tree; diff --git a/crates/crypto/src/merkle_tree/README.md b/crates/crypto/src/merkle_tree/README.md deleted file mode 100644 index 9617d7a4c..000000000 --- a/crates/crypto/src/merkle_tree/README.md +++ /dev/null @@ -1,231 +0,0 @@ -# Merkle Trees - -A Merkle tree is a binary tree data structure where each leaf node contains the hash of a data block, and each non-leaf node contains the hash of its two child nodes. This structure allows for efficient and secure verification of content in large data structures. - -## What is a Merkle Tree? - -Merkle trees provide a way to efficiently verify the integrity of data. Here's how they work: - -1. **Leaf Nodes**: Start by hashing each piece of data (e.g., files, transactions) to create the leaf nodes -2. **Internal Nodes**: Each internal node is created by hashing the concatenation of its two child nodes -3. **Root Hash**: The hash at the top of the tree (root) represents a cryptographic summary of all the data - -For example, with 8 files (f₁, f₂, ..., f₈): -- First, hash each file: h₁ = H(f₁), h₂ = H(f₂), ..., h₈ = H(f₈) -- Then hash pairs: h₁₂ = H(h₁, h₂), h₃₄ = H(h₃, h₄), etc. -- Continue until you reach the root: h₁₋₈ = H(h₁₋₄, h₅₋₈) - -The power of Merkle trees comes from their ability to generate compact proofs. A **Merkle proof** for a specific piece of data consists of the minimal set of hashes needed to recompute the root hash. This allows verification that a piece of data belongs to the original set without needing all the data. - -## Overview - -The Merkle tree implementation in Lambdaworks provides: - -- A generic `MerkleTree` structure that can work with different hash functions and data types -- Support for generating and verifying inclusion proofs -- Multiple backend implementations for different use cases -- Serialization and deserialization of proofs -- Optional parallel processing for improved performance - -## Implementation - -The implementation in this codebase includes: - -- `MerkleTree`: The main Merkle tree data structure -- `Proof`: Represents a Merkle proof for verifying inclusion of data -- `IsMerkleTreeBackend`: A trait for implementing different backend strategies -- Several backend implementations: - - `FieldElementBackend`: For hashing field elements using various hash functions - - `FieldElementVectorBackend`: For hashing vectors of field elements - - `BatchPoseidonTree`: For batch hashing with Poseidon - -## API Usage - -### Creating a Merkle Tree - -Here's a basic example of creating a Merkle tree with field elements: - -```rust -use lambdaworks_crypto::merkle_tree::{ - merkle::MerkleTree, - backends::field_element::FieldElementBackend, -}; -use lambdaworks_math::field::{ - element::FieldElement, - fields::fft_friendly::stark_252_prime_field::Stark252PrimeField, -}; -use sha3::Keccak256; - -// Define the types we'll use -type F = Stark252PrimeField; -type FE = FieldElement; - -// Create some data -let values: Vec = (1..6).map(FE::from).collect(); - -// Build the Merkle tree using Keccak256 as the hash function -let merkle_tree = MerkleTree::>::build(&values).unwrap(); -``` - -### Using BatchPoseidonTree for Efficient Hashing - -The `BatchPoseidonTree` backend is specifically designed for efficient batch hashing using the Poseidon hash function, which is particularly useful in zero-knowledge proof systems. This backend provides optimized performance for vectors of field elements. - -```rust -use lambdaworks_crypto::merkle_tree::{ - merkle::MerkleTree, - backends::field_element_vector::BatchPoseidonTree, -}; -use lambdaworks_crypto::hash::poseidon::starknet::PoseidonCairoStark252; -use lambdaworks_math::field::{ - element::FieldElement, - fields::fft_friendly::stark_252_prime_field::Stark252PrimeField, -}; - -// Define the types we'll use -type F = Stark252PrimeField; -type FE = FieldElement; - -// Create some data (vectors of field elements) -let values: Vec> = vec![ - vec![FE::from(1), FE::from(2)], - vec![FE::from(3), FE::from(4)], - vec![FE::from(5), FE::from(6)], - vec![FE::from(7), FE::from(8)], -]; - -// Build the Merkle tree using Poseidon hash function -let merkle_tree = MerkleTree::>::build(&values).unwrap(); - -// Generate a proof for a specific element -let proof = merkle_tree.get_proof_by_pos(1).unwrap(); - -// Verify the proof -let is_valid = proof.verify::>( - &merkle_tree.root, - 1, - &values[1] -); - -assert!(is_valid, "Proof verification failed"); -``` - -Key features of `BatchPoseidonTree`: - -1. **Optimized for ZK Systems**: Poseidon is designed to be efficient in zero-knowledge proof systems, making this backend ideal for ZK applications. - -2. **Batch Hashing**: The `hash_many` function efficiently processes multiple field elements at once. - -3. **Field Element Compatibility**: Works with vectors of field elements, which is common in cryptographic protocols. - -4. **Performance**: Poseidon offers better performance than traditional hash functions when working with field elements in ZK contexts. - -### Generating Proofs - -To generate a proof for a specific leaf: - -```rust -// Generate a proof for the first element (index 0) -let proof = merkle_tree.get_proof_by_pos(0).unwrap(); -``` - -### Verifying Proofs - -To verify that a value is included in the tree: - -```rust -// Verify the proof -let is_valid = proof.verify::>( - &merkle_tree.root, // The Merkle root - 0, // The position of the leaf - &values[0] // The value to verify -); - -assert!(is_valid, "Proof verification failed"); -``` - -### Working with Vectors of Field Elements - -If you need to hash vectors of field elements: - -```rust -use lambdaworks_crypto::merkle_tree::{ - merkle::MerkleTree, - backends::field_element_vector::FieldElementVectorBackend, -}; -use lambdaworks_math::field::{ - element::FieldElement, - fields::fft_friendly::stark_252_prime_field::Stark252PrimeField, -}; -use sha3::Keccak256; - -// Define the types we'll use -type F = Stark252PrimeField; -type FE = FieldElement; - -// Create some data (vectors of field elements) -let values: Vec> = vec![ - vec![FE::from(1), FE::from(2)], - vec![FE::from(3), FE::from(4)], - vec![FE::from(5), FE::from(6)], -]; - -// Build the Merkle tree -let merkle_tree = MerkleTree::>::build(&values).unwrap(); - -// Generate a proof -let proof = merkle_tree.get_proof_by_pos(0).unwrap(); - -// Verify the proof -let is_valid = proof.verify::>( - &merkle_tree.root, - 0, - &values[0] -); - -assert!(is_valid, "Proof verification failed"); -``` - -## Serialization and Deserialization - -Proofs can be serialized and deserialized for storage or transmission. Note that serialization requires the `alloc` feature to be enabled: - -```rust -// This requires the 'alloc' feature to be enabled -use lambdaworks_crypto::merkle_tree::{ - merkle::MerkleTree, - proof::Proof, - // For testing, you might use a simpler backend like TestBackend -}; -use lambdaworks_math::traits::{Deserializable, Serializable}; - -// Serialize the proof -let serialized_proof = proof.serialize(); - -// Deserialize the proof -let deserialized_proof = Proof::deserialize(&serialized_proof).unwrap(); - -// Verify the deserialized proof -let is_valid = deserialized_proof.verify( - &merkle_tree.root, - 0, - &values[0] -); - -assert!(is_valid, "Deserialized proof verification failed"); -``` - -Note: The serialization example assumes that the type used for the Merkle tree nodes implements both `Serializable` and `Deserializable` traits. - -## Performance Considerations - -- The Merkle tree implementation automatically pads the input data to the next power of 2, which is required for a balanced binary tree -- For large datasets, enable the `parallel` feature to use parallel processing for improved performance -- Choose an appropriate backend based on your security and performance requirements: - - Standard cryptographic hash functions (SHA-3, Keccak) provide strong security guarantees - - -## References - -- [Merkle Tree - Wikipedia](https://en.wikipedia.org/wiki/Merkle_tree) -- [What is a Merkle Tree?](https://decentralizedthoughts.github.io/2020-12-22-what-is-a-merkle-tree/) - A comprehensive explanation of Merkle trees, proofs, and applications diff --git a/crates/crypto/src/merkle_tree/backends/field_element.rs b/crates/crypto/src/merkle_tree/backends/field_element.rs deleted file mode 100644 index 68fed0b6d..000000000 --- a/crates/crypto/src/merkle_tree/backends/field_element.rs +++ /dev/null @@ -1,142 +0,0 @@ -use crate::hash::poseidon::Poseidon; - -use crate::merkle_tree::traits::IsMerkleTreeBackend; -use core::marker::PhantomData; -use lambdaworks_math::{ - field::{element::FieldElement, traits::IsField}, - traits::AsBytes, -}; -use sha3::{ - digest::{generic_array::GenericArray, OutputSizeUser}, - Digest, -}; - -#[derive(Clone)] -pub struct FieldElementBackend { - phantom1: PhantomData, - phantom2: PhantomData, -} - -impl Default for FieldElementBackend { - fn default() -> Self { - Self { - phantom1: PhantomData, - phantom2: PhantomData, - } - } -} - -impl IsMerkleTreeBackend - for FieldElementBackend -where - F: IsField, - FieldElement: AsBytes + Sync + Send, - [u8; NUM_BYTES]: From::OutputSize>>, -{ - type Node = [u8; NUM_BYTES]; - type Data = FieldElement; - - fn hash_data(input: &FieldElement) -> [u8; NUM_BYTES] { - let mut hasher = D::new(); - hasher.update(input.as_bytes()); - hasher.finalize().into() - } - - fn hash_new_parent(left: &[u8; NUM_BYTES], right: &[u8; NUM_BYTES]) -> [u8; NUM_BYTES] { - let mut hasher = D::new(); - hasher.update(left); - hasher.update(right); - hasher.finalize().into() - } -} - -#[derive(Clone, Default)] -pub struct TreePoseidon { - _poseidon: PhantomData

, -} - -impl

IsMerkleTreeBackend for TreePoseidon

-where - P: Poseidon + Default, - FieldElement: Sync + Send, -{ - type Node = FieldElement; - type Data = FieldElement; - - fn hash_data(input: &FieldElement) -> FieldElement { - P::hash_single(input) - } - - fn hash_new_parent( - left: &FieldElement, - right: &FieldElement, - ) -> FieldElement { - P::hash(left, right) - } -} - -#[cfg(test)] -mod tests { - use alloc::vec::Vec; - use lambdaworks_math::field::{ - element::FieldElement, fields::fft_friendly::stark_252_prime_field::Stark252PrimeField, - }; - use sha3::{Keccak256, Keccak512, Sha3_256, Sha3_512}; - - use crate::merkle_tree::{backends::field_element::FieldElementBackend, merkle::MerkleTree}; - - type F = Stark252PrimeField; - type FE = FieldElement; - - #[test] - fn hash_data_field_element_backend_works_with_keccak_256() { - let values: Vec = (1..6).map(FE::from).collect(); - let merkle_tree = - MerkleTree::>::build(&values).unwrap(); - let proof = merkle_tree.get_proof_by_pos(0).unwrap(); - assert!(proof.verify::>( - &merkle_tree.root, - 0, - &values[0] - )); - } - - #[test] - fn hash_data_field_element_backend_works_with_sha3_256() { - let values: Vec = (1..6).map(FE::from).collect(); - let merkle_tree = - MerkleTree::>::build(&values).unwrap(); - let proof = merkle_tree.get_proof_by_pos(0).unwrap(); - assert!(proof.verify::>( - &merkle_tree.root, - 0, - &values[0] - )); - } - - #[test] - fn hash_data_field_element_backend_works_with_keccak_512() { - let values: Vec = (1..6).map(FE::from).collect(); - let merkle_tree = - MerkleTree::>::build(&values).unwrap(); - let proof = merkle_tree.get_proof_by_pos(0).unwrap(); - assert!(proof.verify::>( - &merkle_tree.root, - 0, - &values[0] - )); - } - - #[test] - fn hash_data_field_element_backend_works_with_sha3_512() { - let values: Vec = (1..6).map(FE::from).collect(); - let merkle_tree = - MerkleTree::>::build(&values).unwrap(); - let proof = merkle_tree.get_proof_by_pos(0).unwrap(); - assert!(proof.verify::>( - &merkle_tree.root, - 0, - &values[0] - )); - } -} diff --git a/crates/crypto/src/merkle_tree/backends/field_element_vector.rs b/crates/crypto/src/merkle_tree/backends/field_element_vector.rs deleted file mode 100644 index 8d5f132dd..000000000 --- a/crates/crypto/src/merkle_tree/backends/field_element_vector.rs +++ /dev/null @@ -1,211 +0,0 @@ -use core::marker::PhantomData; - -use crate::hash::poseidon::Poseidon; -use crate::merkle_tree::traits::IsMerkleTreeBackend; -use alloc::vec::Vec; -use lambdaworks_math::{ - field::{element::FieldElement, traits::IsField}, - traits::AsBytes, -}; -use sha3::{ - digest::{generic_array::GenericArray, OutputSizeUser}, - Digest, -}; - -#[derive(Clone)] -pub struct FieldElementVectorBackend { - phantom1: PhantomData, - phantom2: PhantomData, -} - -impl Default for FieldElementVectorBackend { - fn default() -> Self { - Self { - phantom1: PhantomData, - phantom2: PhantomData, - } - } -} - -impl IsMerkleTreeBackend - for FieldElementVectorBackend -where - F: IsField, - FieldElement: AsBytes, - [u8; NUM_BYTES]: From::OutputSize>>, - Vec>: Sync + Send, -{ - type Node = [u8; NUM_BYTES]; - type Data = Vec>; - - fn hash_data(input: &Vec>) -> [u8; NUM_BYTES] { - let mut hasher = D::new(); - for element in input.iter() { - hasher.update(element.as_bytes()); - } - let mut result_hash = [0_u8; NUM_BYTES]; - result_hash.copy_from_slice(&hasher.finalize()); - result_hash - } - - fn hash_new_parent(left: &[u8; NUM_BYTES], right: &[u8; NUM_BYTES]) -> [u8; NUM_BYTES] { - let mut hasher = D::new(); - hasher.update(left); - hasher.update(right); - let mut result_hash = [0_u8; NUM_BYTES]; - result_hash.copy_from_slice(&hasher.finalize()); - result_hash - } -} - -#[derive(Clone, Default)] -pub struct BatchPoseidonTree { - _poseidon: PhantomData

, -} - -impl

IsMerkleTreeBackend for BatchPoseidonTree

-where - P: Poseidon + Default, - Vec>: Sync + Send, - FieldElement: Sync + Send, -{ - type Node = FieldElement; - type Data = Vec>; - - fn hash_data(input: &Vec>) -> FieldElement { - P::hash_many(input) - } - - fn hash_new_parent( - left: &FieldElement, - right: &FieldElement, - ) -> FieldElement { - P::hash(left, right) - } -} - -#[cfg(test)] -mod tests { - use lambdaworks_math::field::{ - element::FieldElement, fields::fft_friendly::stark_252_prime_field::Stark252PrimeField, - }; - use sha2::Sha512; - use sha3::{Keccak256, Keccak512, Sha3_256, Sha3_512}; - - use crate::merkle_tree::{ - backends::field_element_vector::FieldElementVectorBackend, merkle::MerkleTree, - }; - - type F = Stark252PrimeField; - type FE = FieldElement; - - #[test] - fn hash_data_field_element_backend_works_with_sha3_256() { - let values = [ - vec![FE::from(2u64), FE::from(11u64)], - vec![FE::from(3u64), FE::from(14u64)], - vec![FE::from(4u64), FE::from(7u64)], - vec![FE::from(5u64), FE::from(3u64)], - vec![FE::from(6u64), FE::from(5u64)], - vec![FE::from(7u64), FE::from(16u64)], - vec![FE::from(8u64), FE::from(19u64)], - vec![FE::from(9u64), FE::from(21u64)], - ]; - let merkle_tree = - MerkleTree::>::build(&values).unwrap(); - let proof = merkle_tree.get_proof_by_pos(0).unwrap(); - assert!(proof.verify::>( - &merkle_tree.root, - 0, - &values[0] - )); - } - - #[test] - fn hash_data_field_element_backend_works_with_keccak256() { - let values = [ - vec![FE::from(2u64), FE::from(11u64)], - vec![FE::from(3u64), FE::from(14u64)], - vec![FE::from(4u64), FE::from(7u64)], - vec![FE::from(5u64), FE::from(3u64)], - vec![FE::from(6u64), FE::from(5u64)], - vec![FE::from(7u64), FE::from(16u64)], - vec![FE::from(8u64), FE::from(19u64)], - vec![FE::from(9u64), FE::from(21u64)], - ]; - let merkle_tree = - MerkleTree::>::build(&values).unwrap(); - let proof = merkle_tree.get_proof_by_pos(0).unwrap(); - assert!(proof.verify::>( - &merkle_tree.root, - 0, - &values[0] - )); - } - - #[test] - fn hash_data_field_element_backend_works_with_sha3_512() { - let values = [ - vec![FE::from(2u64), FE::from(11u64)], - vec![FE::from(3u64), FE::from(14u64)], - vec![FE::from(4u64), FE::from(7u64)], - vec![FE::from(5u64), FE::from(3u64)], - vec![FE::from(6u64), FE::from(5u64)], - vec![FE::from(7u64), FE::from(16u64)], - vec![FE::from(8u64), FE::from(19u64)], - vec![FE::from(9u64), FE::from(21u64)], - ]; - let merkle_tree = - MerkleTree::>::build(&values).unwrap(); - let proof = merkle_tree.get_proof_by_pos(0).unwrap(); - assert!(proof.verify::>( - &merkle_tree.root, - 0, - &values[0] - )); - } - - #[test] - fn hash_data_field_element_backend_works_with_keccak512() { - let values = [ - vec![FE::from(2u64), FE::from(11u64)], - vec![FE::from(3u64), FE::from(14u64)], - vec![FE::from(4u64), FE::from(7u64)], - vec![FE::from(5u64), FE::from(3u64)], - vec![FE::from(6u64), FE::from(5u64)], - vec![FE::from(7u64), FE::from(16u64)], - vec![FE::from(8u64), FE::from(19u64)], - vec![FE::from(9u64), FE::from(21u64)], - ]; - let merkle_tree = - MerkleTree::>::build(&values).unwrap(); - let proof = merkle_tree.get_proof_by_pos(0).unwrap(); - assert!(proof.verify::>( - &merkle_tree.root, - 0, - &values[0] - )); - } - - #[test] - fn hash_data_field_element_backend_works_with_sha2_512() { - let values = [ - vec![FE::from(2u64), FE::from(11u64)], - vec![FE::from(3u64), FE::from(14u64)], - vec![FE::from(4u64), FE::from(7u64)], - vec![FE::from(5u64), FE::from(3u64)], - vec![FE::from(6u64), FE::from(5u64)], - vec![FE::from(7u64), FE::from(16u64)], - vec![FE::from(8u64), FE::from(19u64)], - vec![FE::from(9u64), FE::from(21u64)], - ]; - let merkle_tree = - MerkleTree::>::build(&values).unwrap(); - let proof = merkle_tree.get_proof_by_pos(0).unwrap(); - assert!(proof.verify::>( - &merkle_tree.root, - 0, - &values[0] - )); - } -} diff --git a/crates/crypto/src/merkle_tree/backends/mod.rs b/crates/crypto/src/merkle_tree/backends/mod.rs deleted file mode 100644 index 431e6597b..000000000 --- a/crates/crypto/src/merkle_tree/backends/mod.rs +++ /dev/null @@ -1,5 +0,0 @@ -pub mod field_element; -pub mod field_element_vector; -/// Configurations for merkle trees -/// Setting generics to some value -pub mod types; diff --git a/crates/crypto/src/merkle_tree/backends/types.rs b/crates/crypto/src/merkle_tree/backends/types.rs deleted file mode 100644 index 0c0c5e952..000000000 --- a/crates/crypto/src/merkle_tree/backends/types.rs +++ /dev/null @@ -1,28 +0,0 @@ -use sha2::{Sha256, Sha512}; -use sha3::{Keccak256, Keccak512, Sha3_256, Sha3_512}; - -use super::{field_element::FieldElementBackend, field_element_vector::FieldElementVectorBackend}; - -// Field element backend definitions - -// - With 256 bit -pub type Sha3_256Backend = FieldElementBackend; -pub type Keccak256Backend = FieldElementBackend; -pub type Sha2_256Backend = FieldElementBackend; - -// - With 512 bit -pub type Sha3_512Backend = FieldElementBackend; -pub type Keccak512Backend = FieldElementBackend; -pub type Sha2_512Backend = FieldElementBackend; - -// Vector of field elements backend definitions - -// - With 256 bit -pub type BatchSha3_256Backend = FieldElementVectorBackend; -pub type BatchKeccak256Backend = FieldElementVectorBackend; -pub type BatchSha2_256Backend = FieldElementVectorBackend; - -// - With 512 bit -pub type BatchSha3_512Backend = FieldElementVectorBackend; -pub type BatchKeccak512Backend = FieldElementVectorBackend; -pub type BatchSha2_512Backend = FieldElementVectorBackend; diff --git a/crates/crypto/src/merkle_tree/merkle.rs b/crates/crypto/src/merkle_tree/merkle.rs deleted file mode 100644 index 76e7718a3..000000000 --- a/crates/crypto/src/merkle_tree/merkle.rs +++ /dev/null @@ -1,148 +0,0 @@ -use core::fmt::Display; - -use alloc::vec::Vec; - -use super::{proof::Proof, traits::IsMerkleTreeBackend, utils::*}; - -#[derive(Debug)] -pub enum Error { - OutOfBounds, -} -impl Display for Error { - fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { - write!(f, "Accessed node was out of bound") - } -} - -#[cfg(feature = "std")] -impl std::error::Error for Error {} - -/// The struct for the Merkle tree, consisting of the root and the nodes. -/// A typical tree would look like this -/// root -/// / \ -/// leaf 12 leaf 34 -/// / \ / \ -/// leaf 1 leaf 2 leaf 3 leaf 4 -/// The bottom leafs correspond to the hashes of the elements, while each upper -/// layer contains the hash of the concatenation of the daughter nodes. -#[derive(Clone)] -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -pub struct MerkleTree { - pub root: B::Node, - nodes: Vec, -} - -const ROOT: usize = 0; - -impl MerkleTree -where - B: IsMerkleTreeBackend, -{ - /// Create a Merkle tree from a slice of data - pub fn build(unhashed_leaves: &[B::Data]) -> Option { - if unhashed_leaves.is_empty() { - return None; - } - - let hashed_leaves: Vec = B::hash_leaves(unhashed_leaves); - - //The leaf must be a power of 2 set - let hashed_leaves = complete_until_power_of_two(hashed_leaves); - let leaves_len = hashed_leaves.len(); - - //The length of leaves minus one inner node in the merkle tree - //The first elements are overwritten by build function, it doesn't matter what it's there - let mut nodes = vec![hashed_leaves[0].clone(); leaves_len - 1]; - nodes.extend(hashed_leaves); - - //Build the inner nodes of the tree - build::(&mut nodes, leaves_len); - - Some(MerkleTree { - root: nodes[ROOT].clone(), - nodes, - }) - } - - /// Returns a Merkle proof for the element/s at position pos - /// For example, give me an inclusion proof for the 3rd element in the - /// Merkle tree - pub fn get_proof_by_pos(&self, pos: usize) -> Option> { - let pos = pos + self.nodes.len() / 2; - let Ok(merkle_path) = self.build_merkle_path(pos) else { - return None; - }; - - self.create_proof(merkle_path) - } - - /// Creates a proof from a Merkle pasth - fn create_proof(&self, merkle_path: Vec) -> Option> { - Some(Proof { merkle_path }) - } - - /// Returns the Merkle path for the element/s for the leaf at position pos - fn build_merkle_path(&self, pos: usize) -> Result, Error> { - let mut merkle_path = Vec::new(); - let mut pos = pos; - - while pos != ROOT { - let Some(node) = self.nodes.get(sibling_index(pos)) else { - // out of bounds, exit returning the current merkle_path - return Err(Error::OutOfBounds); - }; - merkle_path.push(node.clone()); - - pos = parent_index(pos); - } - - Ok(merkle_path) - } -} -#[cfg(test)] -mod tests { - use super::*; - use lambdaworks_math::field::{element::FieldElement, fields::u64_prime_field::U64PrimeField}; - - use crate::merkle_tree::{merkle::MerkleTree, test_merkle::TestBackend}; - - const MODULUS: u64 = 13; - type U64PF = U64PrimeField; - type FE = FieldElement; - - #[test] - fn build_merkle_tree_from_a_power_of_two_list_of_values() { - let values: Vec = (1..5).map(FE::new).collect(); - let merkle_tree = MerkleTree::>::build(&values).unwrap(); - assert_eq!(merkle_tree.root, FE::new(7)); // Adjusted expected value - } - - #[test] - // expected | 8 | 7 | 1 | 6 | 1 | 7 | 7 | 2 | 4 | 6 | 8 | 10 | 10 | 10 | 10 | - fn build_merkle_tree_from_an_odd_set_of_leaves() { - const MODULUS: u64 = 13; - type U64PF = U64PrimeField; - type FE = FieldElement; - - let values: Vec = (1..6).map(FE::new).collect(); - let merkle_tree = MerkleTree::>::build(&values).unwrap(); - assert_eq!(merkle_tree.root, FE::new(8)); // Adjusted expected value - } - - #[test] - fn build_merkle_tree_from_a_single_value() { - const MODULUS: u64 = 13; - type U64PF = U64PrimeField; - type FE = FieldElement; - - let values: Vec = vec![FE::new(1)]; // Single element - let merkle_tree = MerkleTree::>::build(&values).unwrap(); - assert_eq!(merkle_tree.root, FE::new(2)); // Adjusted expected value - } - - #[test] - fn build_empty_tree_should_not_panic() { - assert!(MerkleTree::>::build(&[]).is_none()); - } -} diff --git a/crates/crypto/src/merkle_tree/mod.rs b/crates/crypto/src/merkle_tree/mod.rs deleted file mode 100644 index 5fee9b7c8..000000000 --- a/crates/crypto/src/merkle_tree/mod.rs +++ /dev/null @@ -1,7 +0,0 @@ -pub mod backends; -pub mod merkle; -pub mod proof; -#[cfg(test)] -pub mod test_merkle; -pub mod traits; -mod utils; diff --git a/crates/crypto/src/merkle_tree/proof.rs b/crates/crypto/src/merkle_tree/proof.rs deleted file mode 100644 index 9b77539b6..000000000 --- a/crates/crypto/src/merkle_tree/proof.rs +++ /dev/null @@ -1,181 +0,0 @@ -use alloc::vec::Vec; -#[cfg(feature = "alloc")] -use lambdaworks_math::traits::Serializable; -use lambdaworks_math::{errors::DeserializationError, traits::Deserializable}; - -use super::traits::IsMerkleTreeBackend; - -/// Stores a merkle path to some leaf. -/// Internally, the necessary hashes are stored from root to leaf in the -/// `merkle_path` field, in such a way that, if the merkle tree is of height `n`, the -/// `i`-th element of `merkle_path` is the sibling node in the `n - 1 - i`-th check -/// when verifying. -#[derive(Debug, Clone)] -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -pub struct Proof { - pub merkle_path: Vec, -} - -impl Proof { - /// Verifies a Merkle inclusion proof for the value contained at leaf index. - pub fn verify(&self, root_hash: &B::Node, mut index: usize, value: &B::Data) -> bool - where - B: IsMerkleTreeBackend, - { - let mut hashed_value = B::hash_data(value); - - for sibling_node in self.merkle_path.iter() { - if index.is_multiple_of(2) { - hashed_value = B::hash_new_parent(&hashed_value, sibling_node); - } else { - hashed_value = B::hash_new_parent(sibling_node, &hashed_value); - } - - index >>= 1; - } - - root_hash == &hashed_value - } -} - -#[cfg(feature = "alloc")] -impl Serializable for Proof -where - T: Serializable + PartialEq + Eq, -{ - fn serialize(&self) -> Vec { - self.merkle_path - .iter() - .flat_map(|node| node.serialize()) - .collect() - } -} - -impl Deserializable for Proof -where - T: Deserializable + PartialEq + Eq, -{ - fn deserialize(bytes: &[u8]) -> Result - where - Self: Sized, - { - let mut merkle_path = Vec::new(); - for elem in bytes[0..].chunks(8) { - let node = T::deserialize(elem)?; - merkle_path.push(node); - } - Ok(Self { merkle_path }) - } -} -#[cfg(test)] -mod tests { - - #[cfg(feature = "alloc")] - use super::Proof; - use alloc::vec::Vec; - use lambdaworks_math::field::{element::FieldElement, fields::u64_prime_field::U64PrimeField}; - #[cfg(feature = "alloc")] - use lambdaworks_math::traits::{Deserializable, Serializable}; - - use crate::merkle_tree::{merkle::MerkleTree, test_merkle::TestBackend}; - - /// Small field useful for starks, sometimes called min i goldilocks - /// Used in miden and winterfell - // This field shouldn't be defined inside the merkle tree module - pub type Ecgfp5 = U64PrimeField<0xFFFF_FFFF_0000_0001_u64>; - pub type Ecgfp5FE = FieldElement; - pub type TestMerkleTreeEcgfp = MerkleTree>; - #[cfg(feature = "alloc")] - pub type TestProofEcgfp5 = Proof; - - const MODULUS: u64 = 13; - type U64PF = U64PrimeField; - type FE = FieldElement; - - #[test] - #[cfg(feature = "alloc")] - fn serialize_proof_and_deserialize_using_be_it_get_a_consistent_proof() { - let merkle_path = [Ecgfp5FE::new(2), Ecgfp5FE::new(1), Ecgfp5FE::new(1)].to_vec(); - let original_proof = TestProofEcgfp5 { merkle_path }; - let serialize_proof = original_proof.serialize(); - let proof: TestProofEcgfp5 = Proof::deserialize(&serialize_proof).unwrap(); - - for (o_node, node) in original_proof.merkle_path.iter().zip(proof.merkle_path) { - assert_eq!(*o_node, node); - } - } - - #[test] - #[cfg(feature = "alloc")] - fn serialize_proof_and_deserialize_using_le_it_get_a_consistent_proof() { - let merkle_path = [Ecgfp5FE::new(2), Ecgfp5FE::new(1), Ecgfp5FE::new(1)].to_vec(); - let original_proof = TestProofEcgfp5 { merkle_path }; - let serialize_proof = original_proof.serialize(); - let proof: TestProofEcgfp5 = Proof::deserialize(&serialize_proof).unwrap(); - - for (o_node, node) in original_proof.merkle_path.iter().zip(proof.merkle_path) { - assert_eq!(*o_node, node); - } - } - - #[test] - // expected | 8 | 7 | 1 | 6 | 1 | 7 | 7 | 2 | 4 | 6 | 8 | 10 | 10 | 10 | 10 | - fn create_a_proof_over_value_that_belongs_to_a_given_merkle_tree_when_given_the_leaf_position() - { - let values: Vec = (1..6).map(FE::new).collect(); - let merkle_tree = MerkleTree::>::build(&values).unwrap(); - let proof = &merkle_tree.get_proof_by_pos(1).unwrap(); - assert_merkle_path(&proof.merkle_path, &[FE::new(2), FE::new(1), FE::new(1)]); - assert!(proof.verify::>(&merkle_tree.root, 1, &FE::new(2))); - } - - #[test] - #[cfg(feature = "alloc")] - fn merkle_proof_verifies_after_serialization_and_deserialization() { - let values: Vec = (1..6).map(Ecgfp5FE::new).collect(); - let merkle_tree = TestMerkleTreeEcgfp::build(&values).unwrap(); - let proof = merkle_tree.get_proof_by_pos(1).unwrap(); - let serialize_proof = proof.serialize(); - let proof: TestProofEcgfp5 = Proof::deserialize(&serialize_proof).unwrap(); - assert!(proof.verify::>(&merkle_tree.root, 1, &Ecgfp5FE::new(2))); - } - - #[test] - fn create_a_merkle_tree_with_10000_elements_and_verify_that_an_element_is_part_of_it() { - let values: Vec = (1..10000).map(Ecgfp5FE::new).collect(); - let merkle_tree = TestMerkleTreeEcgfp::build(&values).unwrap(); - let proof = merkle_tree.get_proof_by_pos(9349).unwrap(); - assert!(proof.verify::>(&merkle_tree.root, 9349, &Ecgfp5FE::new(9350))); - } - - fn assert_merkle_path(values: &[FE], expected_values: &[FE]) { - for (node, expected_node) in values.iter().zip(expected_values) { - assert_eq!(node, expected_node); - } - } - - #[test] - fn verify_merkle_proof_for_single_value() { - const MODULUS: u64 = 13; - type U64PF = U64PrimeField; - type FE = FieldElement; - - let values: Vec = vec![FE::new(1)]; // Single element - let merkle_tree = MerkleTree::>::build(&values).unwrap(); - - // Update the expected root value based on the actual logic of TestBackend - // For example, in this case hashing a single `1` results in `2` - let expected_root = FE::new(2); // Assuming hashing a `1`s results in `2` - assert_eq!( - merkle_tree.root, expected_root, - "The root of the Merkle tree does not match the expected value." - ); - - // Verify the proof for the single element - let proof = merkle_tree.get_proof_by_pos(0).unwrap(); - assert!( - proof.verify::>(&merkle_tree.root, 0, &values[0]), - "The proof verification failed for the element at position 0." - ); - } -} diff --git a/crates/crypto/src/merkle_tree/test_merkle.rs b/crates/crypto/src/merkle_tree/test_merkle.rs deleted file mode 100644 index 746ff6a83..000000000 --- a/crates/crypto/src/merkle_tree/test_merkle.rs +++ /dev/null @@ -1,40 +0,0 @@ -use core::marker::PhantomData; - -use lambdaworks_math::field::{element::FieldElement, traits::IsField}; - -use super::{merkle::MerkleTree, traits::IsMerkleTreeBackend}; - -pub type TestMerkleTree = MerkleTree>; - -#[derive(Debug, Clone)] - -/// This hasher is for testing purposes -/// It adds the fields -/// Under no circunstance it can be used in production -pub struct TestBackend { - phantom: PhantomData, -} - -impl Default for TestBackend { - fn default() -> Self { - Self { - phantom: Default::default(), - } - } -} - -impl IsMerkleTreeBackend for TestBackend -where - FieldElement: Sync + Send, -{ - type Node = FieldElement; - type Data = FieldElement; - - fn hash_data(input: &Self::Data) -> Self::Node { - input + input - } - - fn hash_new_parent(left: &Self::Node, right: &Self::Node) -> Self::Node { - left + right - } -} diff --git a/crates/crypto/src/merkle_tree/traits.rs b/crates/crypto/src/merkle_tree/traits.rs deleted file mode 100644 index c09cff9d0..000000000 --- a/crates/crypto/src/merkle_tree/traits.rs +++ /dev/null @@ -1,29 +0,0 @@ -use alloc::vec::Vec; -#[cfg(feature = "parallel")] -use rayon::prelude::{IntoParallelRefIterator, ParallelIterator}; - -/// A backend for Merkle trees. This defines raw `Data` from which the Merkle -/// tree is built from. It also defines the `Node` type and the hash function -/// used to build parent nodes from children nodes. -pub trait IsMerkleTreeBackend { - type Node: PartialEq + Eq + Clone + Sync + Send; - type Data: Sync + Send; - - /// This function takes a single variable `Data` and converts it to a node. - fn hash_data(leaf: &Self::Data) -> Self::Node; - - /// This function takes the list of data from which the Merkle - /// tree will be built from and converts it to a list of leaf nodes. - fn hash_leaves(unhashed_leaves: &[Self::Data]) -> Vec { - #[cfg(feature = "parallel")] - let iter = unhashed_leaves.par_iter(); - #[cfg(not(feature = "parallel"))] - let iter = unhashed_leaves.iter(); - - iter.map(|leaf| Self::hash_data(leaf)).collect() - } - - /// This function takes to children nodes and builds a new parent node. - /// It will be used in the construction of the Merkle tree. - fn hash_new_parent(child_1: &Self::Node, child_2: &Self::Node) -> Self::Node; -} diff --git a/crates/crypto/src/merkle_tree/utils.rs b/crates/crypto/src/merkle_tree/utils.rs deleted file mode 100644 index a52535bde..000000000 --- a/crates/crypto/src/merkle_tree/utils.rs +++ /dev/null @@ -1,144 +0,0 @@ -use alloc::vec::Vec; - -use super::traits::IsMerkleTreeBackend; -#[cfg(feature = "parallel")] -use rayon::prelude::*; - -pub fn sibling_index(node_index: usize) -> usize { - if node_index.is_multiple_of(2) { - node_index - 1 - } else { - node_index + 1 - } -} - -pub fn parent_index(node_index: usize) -> usize { - if node_index.is_multiple_of(2) { - (node_index - 1) / 2 - } else { - node_index / 2 - } -} - -// The list of values is completed repeating the last value to a power of two length -pub fn complete_until_power_of_two(mut values: Vec) -> Vec { - while !is_power_of_two(values.len()) { - values.push(values[values.len() - 1].clone()); - } - values -} - -// ! NOTE ! -// In this function we say 2^0 = 1 is a power of two. -// In turn, this makes the smallest tree of one leaf, possible. -// The function is private and is only used to ensure the tree -// has a power of 2 number of leaves. -fn is_power_of_two(x: usize) -> bool { - (x & (x - 1)) == 0 -} - -// ! CAUTION ! -// Make sure n=nodes.len()+1 is a power of two, and the last n/2 elements (leaves) are populated with hashes. -// This function takes no precautions for other cases. -pub fn build(nodes: &mut [B::Node], leaves_len: usize) -where - B::Node: Clone, -{ - let mut level_begin_index = leaves_len - 1; - let mut level_end_index = 2 * level_begin_index; - while level_begin_index != level_end_index { - let new_level_begin_index = level_begin_index / 2; - let new_level_length = level_begin_index - new_level_begin_index; - - let (new_level_iter, children_iter) = - nodes[new_level_begin_index..level_end_index + 1].split_at_mut(new_level_length); - - #[cfg(feature = "parallel")] - let parent_and_children_zipped_iter = new_level_iter - .into_par_iter() - .zip(children_iter.par_chunks_exact(2)); - #[cfg(not(feature = "parallel"))] - let parent_and_children_zipped_iter = - new_level_iter.iter_mut().zip(children_iter.chunks_exact(2)); - - parent_and_children_zipped_iter.for_each(|(new_parent, children)| { - *new_parent = B::hash_new_parent(&children[0], &children[1]); - }); - - level_end_index = level_begin_index - 1; - level_begin_index = new_level_begin_index; - } -} - -#[cfg(test)] -mod tests { - use alloc::vec::Vec; - use lambdaworks_math::field::{element::FieldElement, fields::u64_prime_field::U64PrimeField}; - - use crate::merkle_tree::{test_merkle::TestBackend, traits::IsMerkleTreeBackend}; - - use super::{build, complete_until_power_of_two}; - - const MODULUS: u64 = 13; - type U64PF = U64PrimeField; - type FE = FieldElement; - - #[test] - fn build_merkle_tree_one_element_must_succeed() { - let mut nodes = [FE::zero()]; - - build::>(&mut nodes, 1); - } - - #[test] - // expected |2|4|6|8| - fn hash_leaves_from_a_list_of_field_elemnts() { - let values: Vec = (1..5).map(FE::new).collect(); - let hashed_leaves = TestBackend::hash_leaves(&values); - let list_of_nodes = &[FE::new(2), FE::new(4), FE::new(6), FE::new(8)]; - for (leaf, expected_leaf) in hashed_leaves.iter().zip(list_of_nodes) { - assert_eq!(leaf, expected_leaf); - } - } - - #[test] - // expected |1|2|3|4|5|5|5|5| - fn complete_the_length_of_a_list_of_fields_elements_to_be_a_power_of_two() { - let values: Vec = (1..6).map(FE::new).collect(); - let hashed_leaves = complete_until_power_of_two(values); - - let mut expected_leaves = (1..6).map(FE::new).collect::>(); - expected_leaves.extend([FE::new(5); 3]); - - for (leaf, expected_leaves) in hashed_leaves.iter().zip(expected_leaves) { - assert_eq!(*leaf, expected_leaves); - } - } - - #[test] - // expected |2|2| - fn complete_the_length_of_one_field_element_to_be_a_power_of_two() { - let values: Vec = vec![FE::new(2)]; - let hashed_leaves = complete_until_power_of_two(values); - - let mut expected_leaves = vec![FE::new(2)]; - expected_leaves.extend([FE::new(2)]); - assert_eq!(hashed_leaves.len(), 1); - assert_eq!(hashed_leaves[0], expected_leaves[0]); - } - - const ROOT: usize = 0; - - #[test] - // expected |10|10|13|3|7|11|2|1|2|3|4|5|6|7|8| - fn complete_a_merkle_tree_from_a_set_of_leaves() { - let leaves: Vec = (1..9).map(FE::new).collect(); - let leaves_len = leaves.len(); - - let mut nodes = vec![FE::zero(); leaves.len() - 1]; - nodes.extend(leaves); - - build::>(&mut nodes, leaves_len); - assert_eq!(nodes[ROOT], FE::new(10)); - } -} diff --git a/crates/gpu/Cargo.toml b/crates/gpu/Cargo.toml deleted file mode 100644 index ee51ebec9..000000000 --- a/crates/gpu/Cargo.toml +++ /dev/null @@ -1,29 +0,0 @@ -[package] -name = "lambdaworks-gpu" -description = "Modular math library for cryptography - GPU implementation" -version.workspace = true -edition.workspace = true -license.workspace = true - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] -thiserror = "1.0.38" - -[dev-dependencies] -proptest = "1.1.0" -rand = "0.8.5" - -[build-dependencies] -walkdir = { version = "2.3.3", optional = true } - -[features] -cuda = ["dep:walkdir"] - -# Some features activate compilation of code which isn't -# supported in all machines (e.g. metal, cuda), so we won't -# use `--all-features` in any case, instead every feature -# that should compile in all cases will require to be added -# as default. If you don't want to compile with all of these -# use `--no-default-features`. -default = [] diff --git a/crates/gpu/build.rs b/crates/gpu/build.rs deleted file mode 100644 index 0271b6e96..000000000 --- a/crates/gpu/build.rs +++ /dev/null @@ -1,56 +0,0 @@ -#[cfg(feature = "cuda")] -fn compile_cuda_shaders() { - use std::process::Command; - use walkdir::WalkDir; - let source_dir = "../math/src/gpu/cuda/shaders"; - - // Tell cargo to invalidate the built crate whenever the source changes - println!("cargo:rerun-if-changed={source_dir}"); - - let children: Vec<_> = WalkDir::new(source_dir) - .into_iter() - .map(Result::unwrap) - .filter(|entry| { - entry - .path() - .extension() - .map(|x| x == "cu") - .unwrap_or_default() - }) - .map(|entry| { - let mut out_path = entry.path().to_owned(); - out_path.set_extension("ptx"); - - println!( - "cargo:warning=compiling:'{}'->'{}'", - entry.path().display(), - out_path.display(), - ); - - Command::new("nvcc") - .arg("-ptx") - .arg(entry.path()) - .arg("-o") - .arg(out_path) - .spawn() - .unwrap() - }) - .collect(); - - children.into_iter().for_each(|child| { - let res = child.wait_with_output().unwrap(); - if !res.status.success() { - println!(); - println!("{}", String::from_utf8(res.stdout).unwrap()); - println!(); - eprintln!("{}", String::from_utf8(res.stderr).unwrap()); - println!(); - panic!("Compilation failed"); - } - }); -} - -fn main() { - #[cfg(feature = "cuda")] - compile_cuda_shaders(); -} diff --git a/crates/gpu/src/cuda/abstractions/errors.rs b/crates/gpu/src/cuda/abstractions/errors.rs deleted file mode 100644 index d73caf855..000000000 --- a/crates/gpu/src/cuda/abstractions/errors.rs +++ /dev/null @@ -1,21 +0,0 @@ -use thiserror::Error; - -#[derive(Debug, Error)] -pub enum CudaError { - #[error("The order of polynomial + 1 should a be power of 2. Got: {0}")] - InvalidOrder(usize), - #[error("Couldn't load compiled PTX: {0}")] - PtxError(String), - #[error("Couldn't get CUDA function: {0}")] - FunctionError(String), - #[error("Couldn't find a CUDA device: {0}")] - DeviceNotFound(String), - #[error("Couldn't allocate memory for copying: {0}")] - AllocateMemory(String), - #[error("Couldn't retrieve information from GPU: {0}")] - RetrieveMemory(String), - #[error("Couldn't launch CUDA function: {0}")] - Launch(String), - #[error("Index out of bounds: {0}. Length of buffer is {0}")] - IndexOutOfBounds(usize, usize), -} diff --git a/crates/gpu/src/cuda/abstractions/mod.rs b/crates/gpu/src/cuda/abstractions/mod.rs deleted file mode 100644 index 629e98fbf..000000000 --- a/crates/gpu/src/cuda/abstractions/mod.rs +++ /dev/null @@ -1 +0,0 @@ -pub mod errors; diff --git a/crates/gpu/src/cuda/mod.rs b/crates/gpu/src/cuda/mod.rs deleted file mode 100644 index 94429c924..000000000 --- a/crates/gpu/src/cuda/mod.rs +++ /dev/null @@ -1 +0,0 @@ -pub mod abstractions; diff --git a/crates/gpu/src/lib.rs b/crates/gpu/src/lib.rs deleted file mode 100644 index 572db957b..000000000 --- a/crates/gpu/src/lib.rs +++ /dev/null @@ -1,2 +0,0 @@ -#[cfg(feature = "cuda")] -pub mod cuda; diff --git a/crates/math/Cargo.toml b/crates/math/Cargo.toml deleted file mode 100644 index b017a15b2..000000000 --- a/crates/math/Cargo.toml +++ /dev/null @@ -1,96 +0,0 @@ -[package] -name = "lambdaworks-math" -description = "Modular math library for cryptography" -version.workspace = true -edition.workspace = true -license.workspace = true - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] -serde = { version = "1.0", default-features = false, features = [ - "derive", -], optional = true } -serde_json = { version = "1.0", default-features = false, features = [ - "alloc", -], optional = true } -proptest = { version = "1.1.0", optional = true } -winter-math = { package = "winter-math", version = "0.6.4", default-features = false, optional = true } -miden-core = { package = "miden-core", version = "0.7", default-features = false, optional = true } -rand = { version = "0.8.5", default-features = false } - -# rayon -rayon = { version = "1.7", optional = true } - -# cuda -cudarc = { version = "0.9.7", optional = true } - -lambdaworks-gpu = { workspace = true, optional = true } -num-bigint = { version = "0.4.6", default-features = false } -num-traits = { version = "0.2.19", default-features = false } - -[dev-dependencies] -rand_chacha = "0.3.1" -criterion = "0.5.1" -const-random = "0.1.15" -iai-callgrind.workspace = true -proptest = "1.1.0" -pprof = { version = "0.13.0", features = ["criterion", "flamegraph"] } -p3-baby-bear = { git = "https://github.com/Plonky3/Plonky3" } -p3-field = { git = "https://github.com/Plonky3/Plonky3" } -rand = { version = "0.8.5", features = ["std"] } - -[features] -default = ["parallel", "std"] -std = ["alloc", "serde?/std", "serde_json?/std"] -alloc = [] -parallel = ["dep:rayon"] -lambdaworks-serde-binary = ["dep:serde", "alloc"] -lambdaworks-serde-string = ["dep:serde", "dep:serde_json", "alloc"] -proptest = ["dep:proptest"] -winter_compatibility = ["winter-math", "miden-core"] -instruments = [] - - -# gpu -cuda = ["dep:cudarc", "dep:lambdaworks-gpu"] - -[target.wasm32-unknown-unknown.dependencies] -getrandom = { version = "0.2.15", features = ["js"] } - -[[bench]] -name = "criterion_elliptic_curve" -harness = false - -[[bench]] -name = "iai_elliptic_curve" -harness = false - -[[bench]] -name = "criterion_polynomial" -harness = false - -[[bench]] -name = "iai_polynomial" -harness = false - -[[bench]] -name = "criterion_field" -harness = false - -[[bench]] -name = "iai_field" -harness = false - -[[bench]] -name = "criterion_msm" -harness = false -required-features = ["parallel"] - -[[bench]] -name = "criterion_fft" -harness = false - -[[bench]] -name = "iai_fft" -harness = false diff --git a/crates/math/README.md b/crates/math/README.md deleted file mode 100644 index cc2fd133b..000000000 --- a/crates/math/README.md +++ /dev/null @@ -1,23 +0,0 @@ -# lambdaworks-math [![Latest Version]][crates.io] - -[Latest Version]: https://img.shields.io/crates/v/lambdaworks-math.svg -[crates.io]: https://crates.io/crates/lambdaworks-math - - -## Usage -Add this to your `Cargo.toml` -```toml -[dependencies] -lambdaworks-math = "0.13.0" -``` - -## Structure -This crate contains all the relevant mathematical building blocks needed for proof systems and cryptography. The main parts are: -- [Finite Fields](./src/field/README.md) -- [Elliptic curves](./src/elliptic_curve/README.md) -- [Polynomials - univariate and multivariate](./src/polynomial/README.md) -- [CircleFFT](./src/circle/README.md) -- [Large unsigned integers](./src/unsigned_integer/) -- [Fast Fourier Transform](./src/fft/README.md) -- [Optimized Multiscalar Multiplication](./src/msm/) -- [Cyclic Group](./src/cyclic_group.rs) diff --git a/crates/math/benches/criterion_elliptic_curve.rs b/crates/math/benches/criterion_elliptic_curve.rs deleted file mode 100644 index 85db985fc..000000000 --- a/crates/math/benches/criterion_elliptic_curve.rs +++ /dev/null @@ -1,15 +0,0 @@ -use criterion::{criterion_group, criterion_main, Criterion}; -use pprof::criterion::{Output, PProfProfiler}; - -mod elliptic_curves; -use elliptic_curves::{ - bls12_377::bls12_377_elliptic_curve_benchmarks, bls12_381::bls12_381_elliptic_curve_benchmarks, - bn_254::bn_254_elliptic_curve_benchmarks, -}; - -criterion_group!( - name = elliptic_curve_benches; - config = Criterion::default().with_profiler(PProfProfiler::new(100, Output::Flamegraph(None))); - targets = bn_254_elliptic_curve_benchmarks, bls12_377_elliptic_curve_benchmarks, bls12_381_elliptic_curve_benchmarks -); -criterion_main!(elliptic_curve_benches); diff --git a/crates/math/benches/criterion_fft.rs b/crates/math/benches/criterion_fft.rs deleted file mode 100644 index 1ecf225d1..000000000 --- a/crates/math/benches/criterion_fft.rs +++ /dev/null @@ -1,165 +0,0 @@ -#![allow(dead_code)] // clippy has false positive in benchmarks -use criterion::{criterion_group, criterion_main, BatchSize, Criterion}; -use lambdaworks_math::field::traits::RootsConfig; -use utils::fft_functions; -use utils::stark252_utils; - -mod utils; - -const SIZE_ORDERS: [u64; 5] = [20, 21, 22, 23, 24]; - -pub fn fft_benchmarks(c: &mut Criterion) { - let mut group = c.benchmark_group("Ordered FFT"); - - for order in SIZE_ORDERS { - group.throughput(criterion::Throughput::Elements(1 << order)); - - let input_nat = stark252_utils::rand_field_elements(order); - let twiddles_nat = stark252_utils::twiddles(order, RootsConfig::Natural); - let mut input_bitrev = input_nat.clone(); - stark252_utils::bitrev_permute(&mut input_bitrev); - let twiddles_bitrev = stark252_utils::twiddles(order, RootsConfig::BitReverse); - - group.bench_with_input( - "Sequential from NR radix2", - &(input_nat.clone(), twiddles_bitrev.clone()), - |bench, (input, twiddles)| { - bench.iter_batched( - || input.clone(), - |mut input| { - fft_functions::ordered_fft_nr(&mut input, twiddles); - }, - BatchSize::LargeInput, - ); - }, - ); - group.bench_with_input( - "Sequential from RN radix2", - &(input_bitrev, twiddles_nat), - |bench, (input, twiddles)| { - bench.iter_batched( - || input.clone(), - |mut input| { - fft_functions::ordered_fft_rn(&mut input, twiddles); - }, - BatchSize::LargeInput, - ); - }, - ); - if order % 2 == 0 { - group.bench_with_input( - "Sequential from NR radix4", - &(input_nat, twiddles_bitrev), - |bench, (input, twiddles)| { - bench.iter_batched( - || input.clone(), - |mut input| { - fft_functions::ordered_fft_nr4(&mut input, twiddles); - }, - BatchSize::LargeInput, - ); - }, - ); - } - } - - group.finish(); -} - -fn twiddles_generation_benchmarks(c: &mut Criterion) { - let mut group = c.benchmark_group("FFT twiddles generation"); - const CONFIGS: [(&str, RootsConfig); 4] = [ - ("natural", RootsConfig::Natural), - ("natural inversed", RootsConfig::NaturalInversed), - ("bit-reversed", RootsConfig::BitReverse), - ("bit-reversed inversed", RootsConfig::BitReverseInversed), - ]; - - for order in SIZE_ORDERS { - group.throughput(criterion::Throughput::Elements(1 << (order - 1))); - for (name, config) in CONFIGS { - group.bench_with_input(name, &(order, config), |bench, (order, config)| { - bench.iter_with_large_drop(|| { - fft_functions::twiddles_generation(*order, *config); - }); - }); - } - } - - group.finish(); -} - -fn bitrev_permutation_benchmarks(c: &mut Criterion) { - let mut group = c.benchmark_group("Bit-reverse permutation"); - - for input in SIZE_ORDERS.map(stark252_utils::rand_field_elements) { - group.throughput(criterion::Throughput::Elements(input.len() as u64)); - group.bench_with_input("Sequential", &input, |bench, input| { - bench.iter_batched( - || input.clone(), - |mut input| { - stark252_utils::bitrev_permute(&mut input); - }, - BatchSize::LargeInput, - ); - }); - } - - group.finish(); -} - -fn poly_evaluation_benchmarks(c: &mut Criterion) { - let mut group = c.benchmark_group("Polynomial evaluation"); - - for poly in SIZE_ORDERS.map(stark252_utils::rand_poly) { - group.throughput(criterion::Throughput::Elements( - poly.coefficients().len() as u64 - )); - group.bench_with_input("Sequential FFT", &poly, |bench, poly| { - bench.iter_with_large_drop(|| { - fft_functions::poly_evaluate_fft(poly); - }); - }); - } - - group.finish(); -} - -fn poly_interpolation_benchmarks(c: &mut Criterion) { - let mut group = c.benchmark_group("Polynomial interpolation"); - - for evals in SIZE_ORDERS.map(stark252_utils::rand_field_elements) { - group.throughput(criterion::Throughput::Elements(evals.len() as u64)); - group.bench_with_input("Sequential FFT", &evals, |bench, evals| { - bench.iter_with_large_drop(|| { - fft_functions::poly_interpolate_fft(evals); - }); - }); - } - - group.finish(); -} - -#[cfg(not(feature = "cuda"))] -criterion_group!( - name = seq_fft; - config = Criterion::default().sample_size(10); - targets = - fft_benchmarks, - twiddles_generation_benchmarks, - bitrev_permutation_benchmarks, - poly_evaluation_benchmarks, - poly_interpolation_benchmarks, -); - -#[cfg(feature = "cuda")] -criterion_group!( - name = seq_fft; - config = Criterion::default().sample_size(10); - targets = - fft_benchmarks, - twiddles_generation_benchmarks, - bitrev_permutation_benchmarks, -); - -criterion_main!(seq_fft); diff --git a/crates/math/benches/criterion_field.rs b/crates/math/benches/criterion_field.rs deleted file mode 100644 index 352985b53..000000000 --- a/crates/math/benches/criterion_field.rs +++ /dev/null @@ -1,37 +0,0 @@ -use criterion::{criterion_group, criterion_main, Criterion}; -use pprof::criterion::{Output, PProfProfiler}; - -mod fields; -use fields::mersenne31::{mersenne31_extension_ops_benchmarks, mersenne31_ops_benchmarks}; -use fields::mersenne31_montgomery::mersenne31_mont_ops_benchmarks; -use fields::{ - baby_bear::{ - babybear_extension_ops_benchmarks_p3, babybear_p3_ops_benchmarks, - babybear_u32_extension_ops_benchmarks, babybear_u32_ops_benchmarks, - babybear_u64_extension_ops_benchmarks, babybear_u64_ops_benchmarks, - }, - binary::binary_ops_benchmarks, - stark252::starkfield_ops_benchmarks, - u64_goldilocks::u64_goldilocks_ops_benchmarks, - u64_goldilocks_montgomery::u64_goldilocks_montgomery_ops_benchmarks, -}; - -criterion_group!( - name = field_benches; - config = Criterion::default().with_profiler(PProfProfiler::new(100, Output::Flamegraph(None))); - - targets = babybear_u32_ops_benchmarks, - babybear_u32_extension_ops_benchmarks, - babybear_u64_ops_benchmarks, - babybear_u64_extension_ops_benchmarks, - babybear_p3_ops_benchmarks, - babybear_extension_ops_benchmarks_p3, - binary_ops_benchmarks, - mersenne31_ops_benchmarks, - mersenne31_extension_ops_benchmarks, - mersenne31_mont_ops_benchmarks, - starkfield_ops_benchmarks, - u64_goldilocks_ops_benchmarks, - u64_goldilocks_montgomery_ops_benchmarks, -); -criterion_main!(field_benches); diff --git a/crates/math/benches/criterion_msm.rs b/crates/math/benches/criterion_msm.rs deleted file mode 100644 index c6c26b7af..000000000 --- a/crates/math/benches/criterion_msm.rs +++ /dev/null @@ -1,79 +0,0 @@ -use criterion::{black_box, criterion_group, criterion_main, BenchmarkId, Criterion}; -use lambdaworks_math::{ - cyclic_group::IsGroup, - elliptic_curve::{ - short_weierstrass::curves::bls12_381::curve::BLS12381Curve, traits::IsEllipticCurve, - }, - field::traits::IsField, - msm::{naive, pippenger}, - unsigned_integer::element::UnsignedInteger, -}; -use rand::{rngs::StdRng, Rng, SeedableRng}; - -type F = ::BaseField; -type FP = ::PointRepresentation; -type UI = UnsignedInteger<6>; - -pub fn generate_cs_and_points(msm_size: usize) -> (Vec, Vec) { - // We use a seeded rng so the benchmarks are reproducible. - let mut rng = StdRng::seed_from_u64(42); - - let g = BLS12381Curve::generator(); - - let cs: Vec<_> = (0..msm_size) - .map(|_| F::from_base_type(UI::from_limbs(rng.gen()))) - .collect(); - - let points: Vec<_> = (0..msm_size) - .map(|_| g.operate_with_self(F::from_base_type(UI::from_limbs(rng.gen())))) - .collect(); - - (cs, points) -} - -pub fn msm_benchmarks_with_size( - c: &mut Criterion, - cs: &[UI], - points: &[FP], - window_sizes: &[usize], -) { - assert_eq!(cs.len(), points.len()); - let msm_size = cs.len(); - - let mut group = c.benchmark_group(format!("MSM benchmarks with size {msm_size}")); - - group.bench_function("Naive", |bench| { - bench.iter(|| black_box(naive::msm(cs, points))); - }); - - for &window_size in window_sizes { - group.bench_function( - BenchmarkId::new("Sequential Pippenger", window_size), - |bench| { - bench.iter(|| black_box(pippenger::msm_with(cs, points, window_size))); - }, - ); - - group.bench_function( - BenchmarkId::new("Parallel Pippenger", window_size), - |bench| { - bench.iter(|| black_box(pippenger::parallel_msm_with(cs, points, window_size))); - }, - ); - } -} - -pub fn run_benchmarks(c: &mut Criterion) { - let exponents = 1..=10; - let window_sizes = vec![1, 2, 4, 8, 12]; - - for exp in exponents { - let msm_size = 1 << exp; - let (cs, points) = generate_cs_and_points(msm_size); - - msm_benchmarks_with_size(c, &cs, &points, &window_sizes); - } -} - -criterion_group!(msm, run_benchmarks); -criterion_main!(msm); diff --git a/crates/math/benches/criterion_polynomial.rs b/crates/math/benches/criterion_polynomial.rs deleted file mode 100644 index b84a58d8f..000000000 --- a/crates/math/benches/criterion_polynomial.rs +++ /dev/null @@ -1,14 +0,0 @@ -mod polynomials; -use criterion::{criterion_group, criterion_main, Criterion}; -use polynomials::{ - dense_multilinear_poly::dense_multilinear_polynomial_benchmarks, - polynomial::polynomial_benchmarks, - sparse_multilinear_poly::sparse_multilinear_polynomial_benchmarks, -}; -use pprof::criterion::{Output, PProfProfiler}; - -criterion_group!( - name = polynomial; - config = Criterion::default().with_profiler(PProfProfiler::new(100, Output::Flamegraph(None))); - targets = polynomial_benchmarks, dense_multilinear_polynomial_benchmarks, sparse_multilinear_polynomial_benchmarks); -criterion_main!(polynomial); diff --git a/crates/math/benches/elliptic_curves/bls12_377.rs b/crates/math/benches/elliptic_curves/bls12_377.rs deleted file mode 100644 index b304597c5..000000000 --- a/crates/math/benches/elliptic_curves/bls12_377.rs +++ /dev/null @@ -1,41 +0,0 @@ -use criterion::{black_box, Criterion}; -use lambdaworks_math::{ - cyclic_group::IsGroup, - elliptic_curve::{ - short_weierstrass::curves::bls12_377::curve::BLS12377Curve, traits::IsEllipticCurve, - }, -}; -use rand::{rngs::StdRng, Rng, SeedableRng}; - -#[allow(dead_code)] -pub fn bls12_377_elliptic_curve_benchmarks(c: &mut Criterion) { - let mut rng = StdRng::seed_from_u64(42); - let a_val: u128 = rng.gen(); - let b_val: u128 = rng.gen(); - let a = BLS12377Curve::generator().operate_with_self(a_val); - let b = BLS12377Curve::generator().operate_with_self(b_val); - - let mut group = c.benchmark_group("BLS12-377 Ops"); - group.significance_level(0.1).sample_size(10000); - group.throughput(criterion::Throughput::Elements(1)); - - // Operate_with G1 - group.bench_function("Operate_with_G1", |bencher| { - bencher.iter(|| black_box(black_box(&a).operate_with(black_box(&b)))); - }); - - // Operate_with_self G1 - group.bench_function("Operate_with_self_G1", |bencher| { - bencher.iter(|| black_box(black_box(&a).operate_with_self(black_box(b_val)))); - }); - - // Double G1 - group.bench_function("Double G1 {:?}", |bencher| { - bencher.iter(|| black_box(black_box(&a).operate_with_self(black_box(2u64)))); - }); - - // Neg G1 - group.bench_function("Neg G1 {:?}", |bencher| { - bencher.iter(|| black_box(black_box(&a).neg())); - }); -} diff --git a/crates/math/benches/elliptic_curves/bls12_381.rs b/crates/math/benches/elliptic_curves/bls12_381.rs deleted file mode 100644 index fbc9e87e0..000000000 --- a/crates/math/benches/elliptic_curves/bls12_381.rs +++ /dev/null @@ -1,110 +0,0 @@ -use criterion::{black_box, Criterion}; -use lambdaworks_math::{ - cyclic_group::IsGroup, - elliptic_curve::{ - short_weierstrass::{ - curves::bls12_381::{ - curve::BLS12381Curve, - pairing::{final_exponentiation, miller, BLS12381AtePairing}, - twist::BLS12381TwistCurve, - }, - traits::Compress, - }, - traits::{IsEllipticCurve, IsPairing}, - }, -}; -use rand::{rngs::StdRng, Rng, SeedableRng}; - -#[allow(dead_code)] -pub fn bls12_381_elliptic_curve_benchmarks(c: &mut Criterion) { - let mut rng = StdRng::seed_from_u64(42); - let a_val: u128 = rng.gen(); - let b_val: u128 = rng.gen(); - let a_g1 = BLS12381Curve::generator().operate_with_self(a_val); - let b_g1 = BLS12381Curve::generator().operate_with_self(b_val); - - let a_g2 = BLS12381TwistCurve::generator(); - let b_g2 = BLS12381TwistCurve::generator(); - - let miller_loop_output = miller(&a_g2, &a_g1); - - let mut group = c.benchmark_group("BLS12-381 Ops"); - group.significance_level(0.1).sample_size(10000); - group.throughput(criterion::Throughput::Elements(1)); - - // Operate_with G1 - group.bench_function("Operate_with_G1", |bencher| { - bencher.iter(|| black_box(black_box(&a_g1).operate_with(black_box(&b_g1)))); - }); - - // Operate_with G2 - group.bench_function("Operate_with_G2 {:?}", |bencher| { - bencher.iter(|| black_box(black_box(&a_g2).operate_with(black_box(&b_g2)))); - }); - - // Operate_with_self G1 - group.bench_function("Operate_with_self_G1", |bencher| { - bencher.iter(|| black_box(black_box(&a_g1).operate_with_self(black_box(b_val)))); - }); - - // Operate_with_self G2 - group.bench_function("Operate_with_self_G2", |bencher| { - bencher.iter(|| black_box(black_box(&a_g2).operate_with_self(black_box(b_val)))); - }); - - // Double G1 - group.bench_function("Double G1", |bencher| { - bencher.iter(|| black_box(black_box(&a_g1).operate_with_self(black_box(2u64)))); - }); - - // Double G2 - group.bench_function("Double G2 {:?}", |bencher| { - bencher.iter(|| black_box(black_box(&a_g2).operate_with_self(black_box(2u64)))); - }); - - // Neg G1 - group.bench_function("Neg G1", |bencher| { - bencher.iter(|| black_box(black_box(&a_g1).neg())); - }); - - // Neg G2 - group.bench_function("Neg G2", |bencher| { - bencher.iter(|| black_box(black_box(&a_g2).neg())); - }); - - // Compress_G1_point - group.bench_function("Compress G1 point", |bencher| { - bencher.iter(|| black_box(BLS12381Curve::compress_g1_point(black_box(&a_g1)))); - }); - - // Decompress_G1_point - group.bench_function("Decompress G1 Point", |bencher| { - let a: [u8; 48] = BLS12381Curve::compress_g1_point(&a_g1); - bencher.iter(|| black_box(BLS12381Curve::decompress_g1_point(&mut black_box(a))).unwrap()); - }); - - // Subgroup Check G1 - group.bench_function("Subgroup Check G1", |bencher| { - bencher.iter(|| black_box(a_g1.is_in_subgroup())); - }); - - // Ate Pairing - group.bench_function("Ate Pairing", |bencher| { - bencher.iter(|| { - black_box(BLS12381AtePairing::compute( - black_box(&a_g1), - black_box(&a_g2), - )) - }); - }); - - // Miller - group.bench_function("Miller", |bencher| { - bencher.iter(|| black_box(miller(black_box(&a_g2), black_box(&a_g1)))) - }); - - // Final Exponentiation Optimized - group.bench_function("Final Exponentiation", |bencher| { - bencher.iter(|| black_box(final_exponentiation(black_box(&miller_loop_output)))) - }); -} diff --git a/crates/math/benches/elliptic_curves/bn_254.rs b/crates/math/benches/elliptic_curves/bn_254.rs deleted file mode 100644 index db4c62463..000000000 --- a/crates/math/benches/elliptic_curves/bn_254.rs +++ /dev/null @@ -1,196 +0,0 @@ -use criterion::{black_box, Criterion}; -use lambdaworks_math::{ - cyclic_group::IsGroup, - elliptic_curve::{ - short_weierstrass::curves::bn_254::{ - curve::BN254Curve, - field_extension::{BN254PrimeField, Degree12ExtensionField, Degree2ExtensionField}, - pairing::{ - cyclotomic_pow_x, cyclotomic_square, final_exponentiation_naive, - final_exponentiation_optimized, miller_naive, miller_optimized, BN254AtePairing, X, - }, - twist::BN254TwistCurve, - }, - short_weierstrass::point::ShortWeierstrassProjectivePoint, - traits::{IsEllipticCurve, IsPairing}, - }, - field::element::FieldElement, -}; -use rand::{rngs::StdRng, Rng, SeedableRng}; - -type FpE = FieldElement; -type Fp2E = FieldElement; -type Fp12E = FieldElement; -type G1 = ShortWeierstrassProjectivePoint; -type G2 = ShortWeierstrassProjectivePoint; -#[allow(dead_code)] -pub fn bn_254_elliptic_curve_benchmarks(c: &mut Criterion) { - let mut rng = StdRng::seed_from_u64(42); - let a_val: u128 = rng.gen(); - let b_val: u128 = rng.gen(); - let a_g1 = BN254Curve::generator().operate_with_self(a_val); - let b_g1 = BN254Curve::generator().operate_with_self(b_val); - - let a_g2 = BN254TwistCurve::generator().operate_with_self(b_val); - let b_g2 = BN254TwistCurve::generator().operate_with_self(b_val); - let f_12 = Fp12E::from_coefficients(&[ - "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", - ]); - let f_2 = Fp2E::new([FpE::from(a_val as u64), FpE::from(b_val as u64)]); - - let miller_loop_output = miller_optimized(&a_g1.to_affine(), &a_g2.to_affine()); - - let mut group = c.benchmark_group("BN254 Ops"); - - // To Affine G1 - group.bench_function("To Affine G1", |bencher| { - bencher.iter(|| black_box(black_box(&a_g1).to_affine())); - }); - - // To Affine G2 - group.bench_function("To Affine G2", |bencher| { - bencher.iter(|| black_box(black_box(&a_g2).to_affine())); - }); - - // Operate_with G1 - group.bench_function("Operate_with_G1", |bencher| { - bencher.iter(|| black_box(black_box(&a_g1).operate_with(black_box(&b_g1)))); - }); - - // Operate_with G2 - group.bench_function("Operate_with G2", |bencher| { - bencher.iter(|| black_box(black_box(&a_g2).operate_with(black_box(&b_g2)))); - }); - - // Operate_with_self G1 - group.bench_function("Operate_with_self G1", |bencher| { - bencher.iter(|| black_box(black_box(&a_g1).operate_with_self(black_box(b_val)))); - }); - - // Operate_with_self G2 - group.bench_function("Operate_with_self G2", |bencher| { - bencher.iter(|| black_box(black_box(&a_g2).operate_with_self(black_box(b_val)))); - }); - - // Double G1 - group.bench_function("Double G1", |bencher| { - bencher.iter(|| black_box(black_box(&a_g1).operate_with_self(black_box(2u64)))); - }); - - // Double G2 - group.bench_function("Double G2", |bencher| { - bencher.iter(|| black_box(black_box(&a_g2).double())); - }); - - // Operate_with Neg G1 (Substraction) - group.bench_function("Operate_with Neg G1 (Substraction)", |bencher| { - bencher - .iter(|| black_box(black_box(&a_g1).operate_with(black_box(&black_box(&b_g1).neg())))); - }); - - // Operate_with Neg G2 (Substraction) - group.bench_function("Operate_with Neg G2 (Substraction)", |bencher| { - bencher - .iter(|| black_box(black_box(&a_g2).operate_with(black_box(&black_box(&b_g2).neg())))); - }); - - // Neg G1 - group.bench_function("Neg G1", |bencher| { - bencher.iter(|| black_box(black_box(&a_g1).neg())); - }); - - // Neg G2 - group.bench_function("Neg G2", |bencher| { - bencher.iter(|| black_box(black_box(&a_g2).neg())); - }); - - // Subgroup Check G2 - group.bench_function("Subgroup Check G2", |bencher| { - bencher.iter(|| black_box(a_g2.is_in_subgroup())); - }); - - // Ate Pairing - group.bench_function("Ate Pairing", |bencher| { - bencher.iter(|| { - black_box(BN254AtePairing::compute_batch(&[( - black_box(&a_g1), - black_box(&a_g2), - )])) - }); - }); - - // Batch Pairing - for num_pairs in 1..=10 { - group.bench_function(format!("Ate Pairing ({num_pairs} pairs)"), |bencher| { - let mut rng = StdRng::seed_from_u64(42); - let mut g1_points: Vec = Vec::new(); - let mut g2_points: Vec = Vec::new(); - - for _ in 0..num_pairs { - let a_val: u128 = rng.gen(); - let g1 = BN254Curve::generator().operate_with_self(a_val); - let g2 = BN254TwistCurve::generator().operate_with_self(a_val); - g1_points.push(g1); - g2_points.push(g2); - } - - let pairs: Vec<(&G1, &G2)> = g1_points.iter().zip(g2_points.iter()).collect(); - - bencher.iter(|| black_box(BN254AtePairing::compute_batch(black_box(&pairs)))); - }); - } - - // Miller Naive - group.bench_function("Miller Naive", |bencher| { - bencher.iter(|| black_box(miller_naive(black_box(&a_g1), black_box(&a_g2)))) - }); - - // Miller Optimized - group.bench_function("Miller Optimized", |bencher| { - bencher.iter(|| black_box(miller_optimized(black_box(&a_g1), black_box(&a_g2)))) - }); - - // Final Exponentiation Naive - group.bench_function("Final Exponentiation Naive", |bencher| { - bencher.iter(|| black_box(final_exponentiation_naive(black_box(&miller_loop_output)))) - }); - - // Final Exponentiation Optimized - group.bench_function("Final Exponentiation Optimized", |bencher| { - bencher.iter(|| { - black_box(final_exponentiation_optimized(black_box( - &miller_loop_output, - ))) - }) - }); - - // Fp12 Multiplication - group.bench_function("Fp12 Multiplication", |bencher| { - bencher.iter(|| black_box(black_box(&f_12) * black_box(&f_12))); - }); - - // Fp2 Multiplication - group.bench_function("Fp2 Multiplication", |bencher| { - bencher.iter(|| black_box(black_box(&f_2) * black_box(&f_2))); - }); - - // Fp12 Inverse - group.bench_function("Fp12 Inverse", |bencher| { - bencher.iter(|| black_box(black_box(&f_12).inv())); - }); - - // Cyclotomic Pow x - group.bench_function("Cyclotomic Pow x", |bencher| { - bencher.iter(|| black_box(cyclotomic_pow_x(black_box(&f_12)))); - }); - - // Fp12 Pow x - group.bench_function("Fp12 Pow x", |bencher| { - bencher.iter(|| black_box(black_box(&f_12).pow(X))); - }); - - // Cyclotomic Square - group.bench_function("Cyclotomic Square", |bencher| { - bencher.iter(|| black_box(cyclotomic_square(black_box(&f_12)))); - }); -} diff --git a/crates/math/benches/elliptic_curves/iai_bls12_377.rs b/crates/math/benches/elliptic_curves/iai_bls12_377.rs deleted file mode 100644 index 988131c4a..000000000 --- a/crates/math/benches/elliptic_curves/iai_bls12_377.rs +++ /dev/null @@ -1,47 +0,0 @@ -use criterion::black_box; -use lambdaworks_math::{ - cyclic_group::IsGroup, - elliptic_curve::{ - short_weierstrass::{ - curves::bls12_377::curve::BLS12377Curve, point::ShortWeierstrassProjectivePoint, - }, - traits::IsEllipticCurve, - }, -}; -use rand::{rngs::StdRng, Rng, SeedableRng}; -#[allow(dead_code)] -type G1 = ShortWeierstrassProjectivePoint; - -#[allow(dead_code)] -pub fn rand_points_g1() -> (G1, G1, u128, u128) { - let mut rng = StdRng::seed_from_u64(42); - let a_val = rng.gen(); - let b_val = rng.gen(); - let a = BLS12377Curve::generator().operate_with_self(a_val); - let b = BLS12377Curve::generator().operate_with_self(b_val); - (a, b, a_val, b_val) -} - -#[allow(dead_code)] -pub fn bls12_377_operate_with_g1() { - let (a, b, _, _) = rand_points_g1(); - let _ = black_box(black_box(&a).operate_with(black_box(&b))); -} - -#[allow(dead_code)] -pub fn bls12_377_operate_with_self_g1() { - let (a, _, _, b_val) = rand_points_g1(); - let _ = black_box(black_box(&a).operate_with_self(black_box(b_val))); -} - -#[allow(dead_code)] -pub fn bls12_377_double_g1() { - let (a, _, _, _) = rand_points_g1(); - let _ = black_box(black_box(&a).operate_with_self(black_box(2u64))); -} - -#[allow(dead_code)] -pub fn bls12_377_neg_g1() { - let (a, _, _, _) = rand_points_g1(); - let _ = black_box(black_box(&a).neg()); -} diff --git a/crates/math/benches/elliptic_curves/iai_bls12_381.rs b/crates/math/benches/elliptic_curves/iai_bls12_381.rs deleted file mode 100644 index 88f1f6a84..000000000 --- a/crates/math/benches/elliptic_curves/iai_bls12_381.rs +++ /dev/null @@ -1,114 +0,0 @@ -use criterion::black_box; -use lambdaworks_math::{ - cyclic_group::IsGroup, - elliptic_curve::{ - short_weierstrass::{ - curves::bls12_381::{ - curve::BLS12381Curve, pairing::BLS12381AtePairing, twist::BLS12381TwistCurve, - }, - point::ShortWeierstrassProjectivePoint, - traits::Compress, - }, - traits::{IsEllipticCurve, IsPairing}, - }, -}; - -use rand::{rngs::StdRng, Rng, SeedableRng}; -#[allow(dead_code)] -type G1 = ShortWeierstrassProjectivePoint; -#[allow(dead_code)] -type G2 = ShortWeierstrassProjectivePoint; - -#[allow(dead_code)] -pub fn rand_points_g1() -> (G1, G1, u128, u128) { - let mut rng = StdRng::seed_from_u64(42); - let a_val = rng.gen(); - let b_val = rng.gen(); - let a = BLS12381Curve::generator().operate_with_self(a_val); - let b = BLS12381Curve::generator().operate_with_self(b_val); - (a, b, a_val, b_val) -} - -#[allow(dead_code)] -pub fn rand_points_g2() -> (G2, G2, u128, u128) { - let mut rng = StdRng::seed_from_u64(42); - let a_val = rng.gen(); - let b_val = rng.gen(); - let a = BLS12381TwistCurve::generator().operate_with_self(a_val); - let b = BLS12381TwistCurve::generator().operate_with_self(b_val); - (a, b, a_val, b_val) -} - -#[allow(dead_code)] -pub fn bls12_381_operate_with_g1() { - let (a, b, _, _) = rand_points_g1(); - let _ = black_box(black_box(&a).operate_with(black_box(&b))); -} - -#[allow(dead_code)] -pub fn bls12_381_operate_with_g2() { - let (a, b, _, _) = rand_points_g1(); - let _ = black_box(black_box(&a).operate_with(black_box(&b))); -} - -#[allow(dead_code)] -pub fn bls12_381_operate_with_self_g1() { - let (a, _, _, b_val) = rand_points_g1(); - let _ = black_box(black_box(&a).operate_with_self(black_box(b_val))); -} - -#[allow(dead_code)] -pub fn bls12_381_operate_with_self_g2() { - let (a, _, _, b_val) = rand_points_g2(); - let _ = black_box(black_box(&a).operate_with_self(black_box(b_val))); -} - -#[allow(dead_code)] -pub fn bls12_381_double_g1() { - let (a, _, _, _) = rand_points_g1(); - let _ = black_box(black_box(&a).operate_with_self(black_box(2u64))); -} - -#[allow(dead_code)] -pub fn bls12_381_double_g2() { - let (a, _, _, _) = rand_points_g2(); - let _ = black_box(black_box(&a).operate_with_self(black_box(2u64))); -} - -#[allow(dead_code)] -pub fn bls12_381_neg_g1() { - let (a, _, _, _) = rand_points_g1(); - let _ = black_box(black_box(&a).neg()); -} - -#[allow(dead_code)] -pub fn bls12_381_neg_g2() { - let (a, _, _, _) = rand_points_g2(); - let _ = black_box(black_box(&a).neg()); -} - -#[allow(dead_code)] -pub fn bls12_381_compress_g1() { - let (a, _, _, _) = rand_points_g1(); - let _ = black_box(BLS12381Curve::compress_g1_point(black_box(&a))); -} - -#[allow(dead_code)] -pub fn bls12_381_decompress_g1() { - let (a, _, _, _) = rand_points_g1(); - let a: [u8; 48] = BLS12381Curve::compress_g1_point(&a); - let _ = black_box(BLS12381Curve::decompress_g1_point(&mut black_box(a))).unwrap(); -} - -#[allow(dead_code)] -pub fn bls12_381_subgroup_check_g1() { - let (a, _, _, _) = rand_points_g1(); - let _ = black_box(black_box(&a.is_in_subgroup())); -} - -#[allow(dead_code)] -pub fn bls12_381_ate_pairing() { - let (a, _, _, _) = rand_points_g1(); - let (_, b, _, _) = rand_points_g2(); - let _ = black_box(BLS12381AtePairing::compute(black_box(&a), black_box(&b))); -} diff --git a/crates/math/benches/elliptic_curves/mod.rs b/crates/math/benches/elliptic_curves/mod.rs deleted file mode 100644 index 8bc80885f..000000000 --- a/crates/math/benches/elliptic_curves/mod.rs +++ /dev/null @@ -1,5 +0,0 @@ -pub mod bls12_377; -pub mod bls12_381; -pub mod bn_254; -pub mod iai_bls12_377; -pub mod iai_bls12_381; diff --git a/crates/math/benches/fields/baby_bear.rs b/crates/math/benches/fields/baby_bear.rs deleted file mode 100644 index 5047c9de2..000000000 --- a/crates/math/benches/fields/baby_bear.rs +++ /dev/null @@ -1,466 +0,0 @@ -use criterion::Criterion; -use std::hint::black_box; - -use lambdaworks_math::field::fields::fft_friendly::{ - quartic_babybear::Degree4BabyBearExtensionField, - quartic_babybear_u32::Degree4BabyBearU32ExtensionField, -}; -use lambdaworks_math::field::{ - element::FieldElement, - fields::{ - fft_friendly::babybear::Babybear31PrimeField, - u32_montgomery_backend_prime_field::U32MontgomeryBackendPrimeField, - }, -}; - -use p3_baby_bear::BabyBear; -use p3_field::extension::BinomialExtensionField; -use p3_field::{Field, PrimeCharacteristicRing}; - -use rand::random; -use rand::Rng; - -pub type U32Babybear31PrimeField = U32MontgomeryBackendPrimeField<2013265921>; -pub type F = FieldElement; -pub type F64 = FieldElement; -pub type Fp4Eu32 = FieldElement; -pub type Fp4E = FieldElement; -type EF4 = BinomialExtensionField; - -pub fn rand_field_elements_u32(num: usize) -> Vec<(F, F)> { - let mut result = Vec::with_capacity(num); - for _ in 0..num { - result.push((F::from(random::()), F::from(random::()))); - } - result -} - -pub fn rand_field_elements_u64(num: usize) -> Vec<(F64, F64)> { - let mut result = Vec::with_capacity(num); - for _ in 0..num { - result.push((F64::from(random::()), F64::from(random::()))); - } - result -} - -pub fn rand_babybear_u64_fp4_elements(num: usize) -> Vec<(Fp4E, Fp4E)> { - let mut result = Vec::with_capacity(num); - for _ in 0..num { - result.push(( - Fp4E::new([ - F64::from(random::()), - F64::from(random::()), - F64::from(random::()), - F64::from(random::()), - ]), - Fp4E::new([ - F64::from(random::()), - F64::from(random::()), - F64::from(random::()), - F64::from(random::()), - ]), - )); - } - result -} -pub fn rand_babybear_u32_fp4_elements(num: usize) -> Vec<(Fp4Eu32, Fp4Eu32)> { - let mut result = Vec::with_capacity(num); - for _ in 0..num { - result.push(( - Fp4Eu32::new([ - F::from(random::()), - F::from(random::()), - F::from(random::()), - F::from(random::()), - ]), - Fp4Eu32::new([ - F::from(random::()), - F::from(random::()), - F::from(random::()), - F::from(random::()), - ]), - )); - } - result -} -fn random_baby_bear(rng: &mut R) -> BabyBear { - BabyBear::new(rng.gen::()) -} -fn rand_babybear_elements_p3(num: usize) -> Vec<(BabyBear, BabyBear)> { - let mut rng = rand::thread_rng(); - (0..num) - .map(|_| (random_baby_bear(&mut rng), random_baby_bear(&mut rng))) - .collect() -} - -fn rand_babybear_fp4_elements_p3(num: usize) -> Vec<(EF4, EF4)> { - let mut rng = rand::thread_rng(); - (0..num) - .map(|_| { - ( - EF4::from(random_baby_bear(&mut rng)), - EF4::from(random_baby_bear(&mut rng)), - ) - }) - .collect() -} - -pub fn babybear_u32_ops_benchmarks(c: &mut Criterion) { - let input: Vec> = [1000000] - .into_iter() - .map(rand_field_elements_u32) - .collect::>(); - let mut group = c.benchmark_group("BabyBear operations using Lambdaworks u32"); - - for i in input.clone().into_iter() { - group.bench_with_input(format!("Addition {:?}", &i.len()), &i, |bench, i| { - bench.iter(|| { - for (x, y) in i { - black_box(black_box(x) + black_box(y)); - } - }); - }); - } - - for i in input.clone().into_iter() { - group.bench_with_input(format!("Multiplication {:?}", &i.len()), &i, |bench, i| { - bench.iter(|| { - for (x, y) in i { - black_box(black_box(x) * black_box(y)); - } - }); - }); - } - - for i in input.clone().into_iter() { - group.bench_with_input(format!("Square {:?}", &i.len()), &i, |bench, i| { - bench.iter(|| { - for (x, _) in i { - black_box(black_box(x).square()); - } - }); - }); - } - - for i in input.clone().into_iter() { - group.bench_with_input(format!("Inverse {:?}", &i.len()), &i, |bench, i| { - bench.iter(|| { - for (x, _) in i { - black_box(black_box(x).inv().unwrap()); - } - }); - }); - } - - for i in input.clone().into_iter() { - group.bench_with_input(format!("Division {:?}", &i.len()), &i, |bench, i| { - bench.iter(|| { - for (x, y) in i { - black_box(black_box(x) / black_box(y)).unwrap(); - } - }); - }); - } -} - -pub fn babybear_u64_ops_benchmarks(c: &mut Criterion) { - let input: Vec> = [1000000] - .into_iter() - .map(rand_field_elements_u64) - .collect::>(); - let mut group = c.benchmark_group("BabyBear operations using Lambdaworks u64"); - - for i in input.clone().into_iter() { - group.bench_with_input(format!("Addition {:?}", &i.len()), &i, |bench, i| { - bench.iter(|| { - for (x, y) in i { - black_box(black_box(x) + black_box(y)); - } - }); - }); - } - - for i in input.clone().into_iter() { - group.bench_with_input(format!("Multiplication {:?}", &i.len()), &i, |bench, i| { - bench.iter(|| { - for (x, y) in i { - black_box(black_box(x) * black_box(y)); - } - }); - }); - } - - for i in input.clone().into_iter() { - group.bench_with_input(format!("Square {:?}", &i.len()), &i, |bench, i| { - bench.iter(|| { - for (x, _) in i { - black_box(black_box(x).square()); - } - }); - }); - } - - for i in input.clone().into_iter() { - group.bench_with_input(format!("Inverse {:?}", &i.len()), &i, |bench, i| { - bench.iter(|| { - for (x, _) in i { - black_box(black_box(x).inv().unwrap()); - } - }); - }); - } - - for i in input.clone().into_iter() { - group.bench_with_input(format!("Division {:?}", &i.len()), &i, |bench, i| { - bench.iter(|| { - for (x, y) in i { - black_box(black_box(x) / black_box(y)).unwrap(); - } - }); - }); - } -} -pub fn babybear_u32_extension_ops_benchmarks(c: &mut Criterion) { - let input: Vec> = [1000000] - .into_iter() - .map(rand_babybear_u32_fp4_elements) - .collect::>(); - - let mut group = c.benchmark_group("BabyBear u32 Fp4 operations"); - - for i in input.clone().into_iter() { - group.bench_with_input(format!("Addition of Fp4 {:?}", &i.len()), &i, |bench, i| { - bench.iter(|| { - for (x, y) in i { - black_box(black_box(x) + black_box(y)); - } - }); - }); - } - - for i in input.clone().into_iter() { - group.bench_with_input( - format!("Multiplication of Fp4 {:?}", &i.len()), - &i, - |bench, i| { - bench.iter(|| { - for (x, y) in i { - black_box(black_box(x) * black_box(y)); - } - }); - }, - ); - } - - for i in input.clone().into_iter() { - group.bench_with_input(format!("Square of Fp4 {:?}", &i.len()), &i, |bench, i| { - bench.iter(|| { - for (x, _) in i { - black_box(black_box(x).square()); - } - }); - }); - } - - for i in input.clone().into_iter() { - group.bench_with_input(format!("Inverse of Fp4 {:?}", &i.len()), &i, |bench, i| { - bench.iter(|| { - for (x, y) in i { - black_box(black_box(x) / black_box(y)).unwrap(); - } - }); - }); - } - - for i in input.clone().into_iter() { - group.bench_with_input(format!("Division of Fp4 {:?}", &i.len()), &i, |bench, i| { - bench.iter(|| { - for (x, _) in i { - black_box(black_box(x).inv().unwrap()); - } - }); - }); - } -} -pub fn babybear_u64_extension_ops_benchmarks(c: &mut Criterion) { - let input: Vec> = [1000000] - .into_iter() - .map(rand_babybear_u64_fp4_elements) - .collect::>(); - - let mut group = c.benchmark_group("BabyBear u64 Fp4 operations"); - - for i in input.clone().into_iter() { - group.bench_with_input(format!("Addition of Fp4 {:?}", &i.len()), &i, |bench, i| { - bench.iter(|| { - for (x, y) in i { - black_box(black_box(x) + black_box(y)); - } - }); - }); - } - - for i in input.clone().into_iter() { - group.bench_with_input( - format!("Multiplication of Fp4 {:?}", &i.len()), - &i, - |bench, i| { - bench.iter(|| { - for (x, y) in i { - black_box(black_box(x) * black_box(y)); - } - }); - }, - ); - } - - for i in input.clone().into_iter() { - group.bench_with_input(format!("Square of Fp4 {:?}", &i.len()), &i, |bench, i| { - bench.iter(|| { - for (x, _) in i { - black_box(black_box(x).square()); - } - }); - }); - } - - for i in input.clone().into_iter() { - group.bench_with_input(format!("Inverse of Fp4 {:?}", &i.len()), &i, |bench, i| { - bench.iter(|| { - for (x, y) in i { - black_box(black_box(x) / black_box(y)).unwrap(); - } - }); - }); - } - - for i in input.clone().into_iter() { - group.bench_with_input(format!("Division of Fp4 {:?}", &i.len()), &i, |bench, i| { - bench.iter(|| { - for (x, _) in i { - black_box(black_box(x).inv().unwrap()); - } - }); - }); - } -} - -pub fn babybear_p3_ops_benchmarks(c: &mut Criterion) { - let input: Vec> = [1000000] - .into_iter() - .map(rand_babybear_elements_p3) - .collect::>(); - - let mut group = c.benchmark_group("BabyBear operations using Plonky3"); - - for i in input.clone().into_iter() { - group.bench_with_input(format!("Addition {:?}", &i.len()), &i, |bench, i| { - bench.iter(|| { - for (x, y) in i { - let _ = black_box(black_box(*x) + black_box(*y)); - } - }); - }); - } - - for i in input.clone().into_iter() { - group.bench_with_input(format!("Multiplication {:?}", &i.len()), &i, |bench, i| { - bench.iter(|| { - for (x, y) in i { - let _ = black_box(black_box(*x) * black_box(*y)); - } - }); - }); - } - - for i in input.clone().into_iter() { - group.bench_with_input(format!("Square {:?}", &i.len()), &i, |bench, i| { - bench.iter(|| { - for (x, _) in i { - let _ = black_box(black_box(x).square()); - } - }); - }); - } - for i in input.clone().into_iter() { - group.bench_with_input(format!("Inverse {:?}", &i.len()), &i, |bench, i| { - bench.iter(|| { - for (x, _) in i { - let _ = black_box(black_box(x).inverse()); - } - }); - }); - } - - for i in input.clone().into_iter() { - group.bench_with_input(format!("Division {:?}", &i.len()), &i, |bench, i| { - bench.iter(|| { - for (x, y) in i { - let _ = black_box(black_box(*x) / black_box(*y)); - } - }); - }); - } -} - -pub fn babybear_extension_ops_benchmarks_p3(c: &mut Criterion) { - let input_sizes = [1000000]; - let input: Vec> = input_sizes - .into_iter() - .map(rand_babybear_fp4_elements_p3) - .collect::>(); - - let mut group = c.benchmark_group("BabyBear Fp4 operations using Plonky3"); - - for i in input.clone().into_iter() { - group.bench_with_input(format!("Addition of Fp4 {:?}", &i.len()), &i, |bench, i| { - bench.iter(|| { - for (x, y) in i { - let _ = black_box(black_box(*x) + black_box(*y)); - } - }); - }); - } - for i in input.clone().into_iter() { - group.bench_with_input( - format!("Multiplication of Fp4 {:?}", &i.len()), - &i, - |bench, i| { - bench.iter(|| { - for (x, y) in i { - let _ = black_box(black_box(*x) * black_box(*y)); - } - }); - }, - ); - } - for i in input.clone().into_iter() { - group.bench_with_input(format!("Square of Fp4 {:?}", &i.len()), &i, |bench, i| { - bench.iter(|| { - for (x, _) in i { - let _ = black_box(black_box(x).square()); - } - }); - }); - } - - for i in input.clone().into_iter() { - group.bench_with_input(format!("Inverse of Fp4 {:?}", &i.len()), &i, |bench, i| { - bench.iter(|| { - for (x, _) in i { - let _ = black_box(black_box(x).inverse()); - } - }); - }); - } - - for i in input.clone().into_iter() { - group.bench_with_input(format!("Division of Fp4 {:?}", &i.len()), &i, |bench, i| { - bench.iter(|| { - for (x, y) in i { - let _ = black_box(black_box(*x) / black_box(*y)); - } - }); - }); - } -} diff --git a/crates/math/benches/fields/binary.rs b/crates/math/benches/fields/binary.rs deleted file mode 100644 index d64d4377f..000000000 --- a/crates/math/benches/fields/binary.rs +++ /dev/null @@ -1,144 +0,0 @@ -use std::hint::black_box; - -use criterion::{BenchmarkId, Criterion}; -use lambdaworks_math::field::fields::binary::field::{BinaryFieldError, TowerFieldElement}; -use rand::Rng; - -pub fn rand_element(num_level: usize) -> TowerFieldElement { - let mut rng = rand::thread_rng(); - let value = rng.gen::(); - TowerFieldElement::new(value, num_level) -} - -fn binary_add_bench(c: &mut Criterion, num_levels_a: usize, num_levels_b: usize) { - let mut group = c.benchmark_group("Binary TowerField add"); - let samples = 1000; - let a_values: Vec<_> = (0..samples).map(|_| rand_element(num_levels_a)).collect(); - let b_values: Vec<_> = (0..samples).map(|_| rand_element(num_levels_b)).collect(); - - group.bench_with_input( - BenchmarkId::from_parameter(format!("F{}×F{}", 1 << num_levels_a, 1 << num_levels_b)), - &(num_levels_a, num_levels_b), - |bencher, _params| { - bencher.iter(|| { - for (a, b) in a_values.iter().zip(b_values.iter()) { - black_box(black_box(a) + black_box(b)); - } - }); - }, - ); - group.finish(); -} - -fn binary_mul_bench(c: &mut Criterion, num_levels_a: usize, num_levels_b: usize) { - let mut group = c.benchmark_group("Binary TowerField mul"); - let samples = 1000; - let a_values: Vec<_> = (0..samples).map(|_| rand_element(num_levels_a)).collect(); - let b_values: Vec<_> = (0..samples).map(|_| rand_element(num_levels_b)).collect(); - - group.bench_with_input( - BenchmarkId::from_parameter(format!("F{}×F{}", 1 << num_levels_a, 1 << num_levels_b)), - &(num_levels_a, num_levels_b), - |bencher, _params| { - bencher.iter(|| { - for (a, b) in a_values.iter().zip(b_values.iter()) { - black_box(black_box(a) * black_box(b)); - } - }); - }, - ); - group.finish(); -} - -fn binary_pow_bench(c: &mut Criterion, num_levels: usize, exponent: u32) { - if num_levels == 0 { - return; - } - let mut group = c.benchmark_group("Binary TowerField pow"); - let samples = 1000; - let a_values: Vec<_> = (0..samples).map(|_| rand_element(num_levels)).collect(); - - group.bench_with_input( - BenchmarkId::from_parameter(format!("F{} ^ {}", 1 << num_levels, exponent)), - &(num_levels, exponent), - |bencher, _params| { - bencher.iter(|| { - for a in a_values.iter() { - black_box(black_box(a).pow(exponent)); - } - }); - }, - ); - group.finish(); -} - -fn binary_inv_bench(c: &mut Criterion, num_levels: usize) { - if num_levels == 0 { - return; - } - - let mut group = c.benchmark_group("Binary TowerField inv"); - - let samples = 1000; - - // Generate non-zero element - let mut rng = rand::thread_rng(); - let values: Vec = (0..samples) - .map(|_| { - let non_zero_val = rng.gen::() | 1; - let a = TowerFieldElement::new(non_zero_val, num_levels); - assert!( - !a.is_zero(), - "Failed to generate non-zero element for inversion benchmark" - ); - a - }) - .collect(); - - group.bench_with_input( - BenchmarkId::from_parameter(format!("F{}", 1 << num_levels)), - &num_levels, - |bencher, _params| { - bencher.iter(|| { - for a in &values { - match black_box(a).inv() { - Ok(inv) => black_box(inv), - Err(BinaryFieldError::InverseOfZero) => { - panic!("Attempted to invert zero element") - } - }; - } - }); - }, - ); - group.finish(); -} - -// Main benchmarking function that runs all benches -pub fn binary_ops_benchmarks(c: &mut Criterion) { - let levels = [0, 1, 2, 3, 4, 5, 6, 7]; - - for &level in &levels { - binary_add_bench(c, level, level); - binary_mul_bench(c, level, level); - binary_pow_bench(c, level, 5); - binary_inv_bench(c, level); - } - - // Benchmarks for operations between different levels - binary_add_bench(c, 0, 1); // F₁ + F₂ (1-bit field + 2-bit field) - binary_add_bench(c, 0, 4); // F₁ + F₁₆ (1-bit field + 16-bit field) - binary_add_bench(c, 1, 4); // F₂ + F₁₆ (2-bit field + 16-bit field) - binary_add_bench(c, 2, 6); // F₄ + F₆₄ (4-bit field + 64-bit field) - binary_add_bench(c, 0, 7); // F₁ + F₁₂₈ (1-bit field + 128-bit field) - binary_add_bench(c, 4, 7); // F₁₆ + F₁₂₈ (16-bit field + 128-bit field) - binary_add_bench(c, 6, 7); // F₆₄ + F₁₂₈ (64-bit field + 128-bit field) - - binary_mul_bench(c, 0, 1); // F₁ × F₂ (1-bit field × 2-bit field) - binary_mul_bench(c, 0, 4); // F₁ × F₁₆ (1-bit field × 16-bit field) - binary_mul_bench(c, 1, 4); // F₂ × F₁₆ (2-bit field × 16-bit field) - binary_mul_bench(c, 2, 6); // F₄ × F₆₄ (4-bit field × 64-bit field) - binary_mul_bench(c, 0, 7); // F₁ × F₁₂₈ (1-bit field × 128-bit field) - binary_mul_bench(c, 4, 7); // F₁₆ × F₁₂₈ (16-bit field × 128-bit field) - binary_mul_bench(c, 6, 7); // F₆₄ × F₁₂₈ (64-bit field × 128-bit field) -} diff --git a/crates/math/benches/fields/mersenne31.rs b/crates/math/benches/fields/mersenne31.rs deleted file mode 100644 index 0601d5e95..000000000 --- a/crates/math/benches/fields/mersenne31.rs +++ /dev/null @@ -1,256 +0,0 @@ -use std::hint::black_box; - -use criterion::Criterion; -use lambdaworks_math::field::{ - element::FieldElement, - fields::mersenne31::{ - extensions::{Degree2ExtensionField, Degree4ExtensionField}, - field::Mersenne31Field, - }, -}; -use rand::random; - -pub type F = FieldElement; -pub type Fp2E = FieldElement; -pub type Fp4E = FieldElement; - -#[inline(never)] -#[export_name = "util::rand_mersenne31_field_elements"] -pub fn rand_field_elements(num: usize) -> Vec<(F, F)> { - let mut result = Vec::with_capacity(num); - for _ in 0..result.capacity() { - result.push((F::new(random()), F::new(random()))); - } - result -} - -//TODO: Check if this is the correct way to bench. -pub fn rand_fp4e(num: usize) -> Vec<(Fp4E, Fp4E)> { - let mut result = Vec::with_capacity(num); - for _ in 0..result.capacity() { - result.push(( - Fp4E::new([ - Fp2E::new([F::new(random()), F::new(random())]), - Fp2E::new([F::new(random()), F::new(random())]), - ]), - Fp4E::new([ - Fp2E::new([F::new(random()), F::new(random())]), - Fp2E::new([F::new(random()), F::new(random())]), - ]), - )); - } - result -} - -pub fn mersenne31_extension_ops_benchmarks(c: &mut Criterion) { - let input: Vec> = [1000000].into_iter().map(rand_fp4e).collect::>(); - - let mut group = c.benchmark_group("Mersenne31 Fp4 operations"); - - for i in input.clone().into_iter() { - group.bench_with_input(format!("Mul of Fp4 {:?}", &i.len()), &i, |bench, i| { - bench.iter(|| { - for (x, y) in i { - black_box(black_box(x) * black_box(y)); - } - }); - }); - } - - for i in input.clone().into_iter() { - group.bench_with_input(format!("Square of Fp4 {:?}", &i.len()), &i, |bench, i| { - bench.iter(|| { - for (x, _) in i { - black_box(black_box(x).square()); - } - }); - }); - } - - for i in input.clone().into_iter() { - group.bench_with_input(format!("Inv of Fp4 {:?}", &i.len()), &i, |bench, i| { - bench.iter(|| { - for (x, _) in i { - black_box(black_box(x).inv().unwrap()); - } - }); - }); - } -} - -pub fn mersenne31_ops_benchmarks(c: &mut Criterion) { - let input: Vec> = [1, 10, 100, 1000, 10000, 100000, 1000000] - .into_iter() - .map(rand_field_elements) - .collect::>(); - let mut group = c.benchmark_group("Mersenne31 operations"); - - for i in input.clone().into_iter() { - group.bench_with_input(format!("add {:?}", &i.len()), &i, |bench, i| { - bench.iter(|| { - for (x, y) in i { - black_box(black_box(x) + black_box(y)); - } - }); - }); - } - - for i in input.clone().into_iter() { - group.bench_with_input(format!("mul {:?}", &i.len()), &i, |bench, i| { - bench.iter(|| { - for (x, y) in i { - black_box(black_box(x) * black_box(y)); - } - }); - }); - } - - for i in input.clone().into_iter() { - group.bench_with_input(format!("pow by 1 {:?}", &i.len()), &i, |bench, i| { - bench.iter(|| { - for (x, _) in i { - black_box(black_box(x).pow(1_u64)); - } - }); - }); - } - - for i in input.clone().into_iter() { - group.bench_with_input(format!("square {:?}", &i.len()), &i, |bench, i| { - bench.iter(|| { - for (x, _) in i { - black_box(black_box(x).square()); - } - }); - }); - } - - for i in input.clone().into_iter() { - group.bench_with_input(format!("square with pow {:?}", &i.len()), &i, |bench, i| { - bench.iter(|| { - for (x, _) in i { - black_box(black_box(x).pow(2_u64)); - } - }); - }); - } - - for i in input.clone().into_iter() { - group.bench_with_input(format!("square with mul {:?}", &i.len()), &i, |bench, i| { - bench.iter(|| { - for (x, _) in i { - black_box(black_box(x) * black_box(x)); - } - }); - }); - } - - for i in input.clone().into_iter() { - group.bench_with_input( - format!("pow {:?}", &i.len()), - &(i, 5u64), - |bench, (i, a)| { - bench.iter(|| { - for (x, _) in i { - black_box(black_box(x).pow(*a)); - } - }); - }, - ); - } - - for i in input.clone().into_iter() { - group.bench_with_input(format!("sub {:?}", &i.len()), &i, |bench, i| { - bench.iter(|| { - for (x, y) in i { - black_box(black_box(x) - black_box(y)); - } - }); - }); - } - - for i in input.clone().into_iter() { - group.bench_with_input(format!("inv {:?}", &i.len()), &i, |bench, i| { - bench.iter(|| { - for (x, _) in i { - black_box(black_box(x).inv().unwrap()); - } - }); - }); - } - - for i in input.clone().into_iter() { - group.bench_with_input(format!("div {:?}", &i.len()), &i, |bench, i| { - bench.iter(|| { - for (x, y) in i { - black_box(black_box(x) / black_box(y)).unwrap(); - } - }); - }); - } - - for i in input.clone().into_iter() { - group.bench_with_input(format!("eq {:?}", &i.len()), &i, |bench, i| { - bench.iter(|| { - for (x, y) in i { - black_box(black_box(x) == black_box(y)); - } - }); - }); - } - - for i in input.clone().into_iter() { - group.bench_with_input(format!("sqrt {:?}", &i.len()), &i, |bench, i| { - bench.iter(|| { - for (x, _) in i { - black_box(black_box(x).sqrt()); - } - }); - }); - } - - for i in input.clone().into_iter() { - group.bench_with_input(format!("sqrt squared {:?}", &i.len()), &i, |bench, i| { - let i: Vec = i.iter().map(|(x, _)| x * x).collect(); - bench.iter(|| { - for x in &i { - black_box(black_box(x).sqrt()); - } - }); - }); - } - - for i in input.clone().into_iter() { - group.bench_with_input(format!("bitand {:?}", &i.len()), &i, |bench, i| { - // Note: we should strive to have the number of limbs be generic... ideally this benchmark group itself should have a generic type that we call into from the main runner. - let i: Vec<(u32, u32)> = i.iter().map(|(x, y)| (*x.value(), *y.value())).collect(); - bench.iter(|| { - for (x, y) in &i { - black_box(black_box(*x) & black_box(*y)); - } - }); - }); - } - - for i in input.clone().into_iter() { - group.bench_with_input(format!("bitor {:?}", &i.len()), &i, |bench, i| { - let i: Vec<(u32, u32)> = i.iter().map(|(x, y)| (*x.value(), *y.value())).collect(); - bench.iter(|| { - for (x, y) in &i { - black_box(black_box(*x) | black_box(*y)); - } - }); - }); - } - - for i in input.clone().into_iter() { - group.bench_with_input(format!("bitxor {:?}", &i.len()), &i, |bench, i| { - let i: Vec<(u32, u32)> = i.iter().map(|(x, y)| (*x.value(), *y.value())).collect(); - bench.iter(|| { - for (x, y) in &i { - black_box(black_box(*x) ^ black_box(*y)); - } - }); - }); - } -} diff --git a/crates/math/benches/fields/mersenne31_montgomery.rs b/crates/math/benches/fields/mersenne31_montgomery.rs deleted file mode 100644 index d228b7517..000000000 --- a/crates/math/benches/fields/mersenne31_montgomery.rs +++ /dev/null @@ -1,230 +0,0 @@ -use std::hint::black_box; - -use criterion::Criterion; -use lambdaworks_math::{ - field::{ - element::FieldElement, - fields::{ - fft_friendly::u64_mersenne_montgomery_field::{ - Mersenne31MontgomeryPrimeField, MontgomeryConfigMersenne31PrimeField, - }, - montgomery_backed_prime_fields::IsModulus, - }, - }, - unsigned_integer::{ - element::{UnsignedInteger, U64}, - montgomery::MontgomeryAlgorithms, - }, -}; -use rand::random; - -pub type F = FieldElement; -const NUM_LIMBS: usize = 1; - -#[inline(never)] -#[export_name = "util::rand_mersenne31_mont_field_elements"] -pub fn rand_field_elements(num: usize) -> Vec<(F, F)> { - let mut result = Vec::with_capacity(num); - for _ in 0..result.capacity() { - let rand_a = UnsignedInteger { limbs: random() }; - let rand_b = UnsignedInteger { limbs: random() }; - result.push((F::new(rand_a), F::new(rand_b))); - } - result -} - -pub fn mersenne31_mont_ops_benchmarks(c: &mut Criterion) { - let input: Vec> = [1, 10, 100, 1000, 10000, 100000, 1000000] - .into_iter() - .map(rand_field_elements) - .collect::>(); - let mut group = c.benchmark_group("Mersenne31 Mont operations"); - - for i in input.clone().into_iter() { - group.bench_with_input(format!("add {:?}", &i.len()), &i, |bench, i| { - bench.iter(|| { - for (x, y) in i { - black_box(black_box(x) + black_box(y)); - } - }); - }); - } - - for i in input.clone().into_iter() { - group.bench_with_input(format!("mul {:?}", &i.len()), &i, |bench, i| { - bench.iter(|| { - for (x, y) in i { - black_box(black_box(x) * black_box(y)); - } - }); - }); - } - - for i in input.clone().into_iter() { - group.bench_with_input(format!("pow by 1 {:?}", &i.len()), &i, |bench, i| { - bench.iter(|| { - for (x, _) in i { - black_box(black_box(x).pow(1_u64)); - } - }); - }); - } - - for i in input.clone().into_iter() { - group.bench_with_input(format!("square {:?}", &i.len()), &i, |bench, i| { - bench.iter(|| { - for (x, _) in i { - black_box(black_box(x).square()); - } - }); - }); - } - - for i in input.clone().into_iter() { - group.bench_with_input(format!("square with pow {:?}", &i.len()), &i, |bench, i| { - bench.iter(|| { - for (x, _) in i { - black_box(black_box(x).pow(2_u64)); - } - }); - }); - } - - // The non-boxed constants are intentional as they are - // normally computed at compile time. - for i in input.clone().into_iter() { - group.bench_with_input(format!("sos_square {:?}", &i.len()), &i, |bench, i| { - bench.iter(|| { - for (x, _) in i { - MontgomeryAlgorithms::sos_square( - black_box(black_box(x.value())), - &>::MODULUS, - &Mersenne31MontgomeryPrimeField::MU, - ); - } - }); - }); - } - - for i in input.clone().into_iter() { - group.bench_with_input(format!("square with mul {:?}", &i.len()), &i, |bench, i| { - bench.iter(|| { - for (x, _) in i { - black_box(black_box(x) * black_box(x)); - } - }); - }); - } - - for i in input.clone().into_iter() { - group.bench_with_input( - format!("pow {:?}", &i.len()), - &(i, 5u64), - |bench, (i, a)| { - bench.iter(|| { - for (x, _) in i { - black_box(black_box(x).pow(*a)); - } - }); - }, - ); - } - - for i in input.clone().into_iter() { - group.bench_with_input(format!("sub {:?}", &i.len()), &i, |bench, i| { - bench.iter(|| { - for (x, y) in i { - black_box(black_box(x) - black_box(y)); - } - }); - }); - } - - for i in input.clone().into_iter() { - group.bench_with_input(format!("inv {:?}", &i.len()), &i, |bench, i| { - bench.iter(|| { - for (x, _) in i { - black_box(black_box(x).inv().unwrap()); - } - }); - }); - } - - for i in input.clone().into_iter() { - group.bench_with_input(format!("div {:?}", &i.len()), &i, |bench, i| { - bench.iter(|| { - for (x, y) in i { - black_box(black_box(x) / black_box(y)).unwrap(); - } - }); - }); - } - - for i in input.clone().into_iter() { - group.bench_with_input(format!("eq {:?}", &i.len()), &i, |bench, i| { - bench.iter(|| { - for (x, y) in i { - black_box(black_box(x) == black_box(y)); - } - }); - }); - } - - for i in input.clone().into_iter() { - group.bench_with_input(format!("sqrt {:?}", &i.len()), &i, |bench, i| { - bench.iter(|| { - for (x, _) in i { - black_box(black_box(x).sqrt()); - } - }); - }); - } - - for i in input.clone().into_iter() { - group.bench_with_input(format!("sqrt squared {:?}", &i.len()), &i, |bench, i| { - let i: Vec = i.iter().map(|(x, _)| x * x).collect(); - bench.iter(|| { - for x in &i { - black_box(black_box(x).sqrt()); - } - }); - }); - } - - for i in input.clone().into_iter() { - group.bench_with_input(format!("bitand {:?}", &i.len()), &i, |bench, i| { - // Note: we should strive to have the number of limbs be generic... ideally this benchmark group itself should have a generic type that we call into from the main runner. - let i: Vec<(UnsignedInteger, UnsignedInteger)> = - i.iter().map(|(x, y)| (*x.value(), *y.value())).collect(); - bench.iter(|| { - for (x, y) in &i { - black_box(black_box(*x) & black_box(*y)); - } - }); - }); - } - - for i in input.clone().into_iter() { - group.bench_with_input(format!("bitor {:?}", &i.len()), &i, |bench, i| { - let i: Vec<(UnsignedInteger, UnsignedInteger)> = - i.iter().map(|(x, y)| (*x.value(), *y.value())).collect(); - bench.iter(|| { - for (x, y) in &i { - black_box(black_box(*x) | black_box(*y)); - } - }); - }); - } - - for i in input.clone().into_iter() { - group.bench_with_input(format!("bitxor {:?}", &i.len()), &i, |bench, i| { - let i: Vec<(UnsignedInteger, UnsignedInteger)> = - i.iter().map(|(x, y)| (*x.value(), *y.value())).collect(); - bench.iter(|| { - for (x, y) in &i { - black_box(black_box(*x) ^ black_box(*y)); - } - }); - }); - } -} diff --git a/crates/math/benches/fields/mod.rs b/crates/math/benches/fields/mod.rs deleted file mode 100644 index acf0a3cfa..000000000 --- a/crates/math/benches/fields/mod.rs +++ /dev/null @@ -1,7 +0,0 @@ -pub mod baby_bear; -pub mod binary; -pub mod mersenne31; -pub mod mersenne31_montgomery; -pub mod stark252; -pub mod u64_goldilocks; -pub mod u64_goldilocks_montgomery; diff --git a/crates/math/benches/fields/stark252.rs b/crates/math/benches/fields/stark252.rs deleted file mode 100644 index 55c111171..000000000 --- a/crates/math/benches/fields/stark252.rs +++ /dev/null @@ -1,229 +0,0 @@ -use std::hint::black_box; - -use criterion::Criterion; -use lambdaworks_math::{ - field::{ - element::FieldElement, - fields::{ - fft_friendly::stark_252_prime_field::{ - MontgomeryConfigStark252PrimeField, Stark252PrimeField, - }, - montgomery_backed_prime_fields::IsModulus, - }, - }, - unsigned_integer::{ - element::{UnsignedInteger, U256}, - montgomery::MontgomeryAlgorithms, - }, -}; -use rand::random; - -pub type F = FieldElement; - -#[inline(never)] -#[export_name = "util::rand_field_elements"] -pub fn rand_field_elements(num: usize) -> Vec<(F, F)> { - let mut result = Vec::with_capacity(num); - for _ in 0..result.capacity() { - let rand_a = UnsignedInteger { limbs: random() }; - let rand_b = UnsignedInteger { limbs: random() }; - result.push((F::new(rand_a), F::new(rand_b))); - } - result -} - -pub fn starkfield_ops_benchmarks(c: &mut Criterion) { - let input: Vec> = [1, 10, 100, 1000, 10000, 100000, 1000000] - .into_iter() - .map(rand_field_elements) - .collect::>(); - let mut group = c.benchmark_group("Stark FP operations"); - - for i in input.clone().into_iter() { - group.bench_with_input(format!("add {:?}", &i.len()), &i, |bench, i| { - bench.iter(|| { - for (x, y) in i { - black_box(black_box(x) + black_box(y)); - } - }); - }); - } - - for i in input.clone().into_iter() { - group.bench_with_input(format!("mul {:?}", &i.len()), &i, |bench, i| { - bench.iter(|| { - for (x, y) in i { - black_box(black_box(x) * black_box(y)); - } - }); - }); - } - - for i in input.clone().into_iter() { - group.bench_with_input(format!("pow by 1 {:?}", &i.len()), &i, |bench, i| { - bench.iter(|| { - for (x, _) in i { - black_box(black_box(x).pow(1_u64)); - } - }); - }); - } - - // The non-boxed constants are intentional as they are - // normally computed at compile time. - for i in input.clone().into_iter() { - group.bench_with_input(format!("sos_square {:?}", &i.len()), &i, |bench, i| { - bench.iter(|| { - for (x, _) in i { - MontgomeryAlgorithms::sos_square( - black_box(black_box(x.value())), - &>::MODULUS, - &Stark252PrimeField::MU, - ); - } - }); - }); - } - - for i in input.clone().into_iter() { - group.bench_with_input(format!("square {:?}", &i.len()), &i, |bench, i| { - bench.iter(|| { - for (x, _) in i { - black_box(black_box(x).square()); - } - }); - }); - } - - for i in input.clone().into_iter() { - group.bench_with_input(format!("square with pow {:?}", &i.len()), &i, |bench, i| { - bench.iter(|| { - for (x, _) in i { - black_box(black_box(x).pow(2_u64)); - } - }); - }); - } - - for i in input.clone().into_iter() { - group.bench_with_input(format!("square with mul {:?}", &i.len()), &i, |bench, i| { - bench.iter(|| { - for (x, _) in i { - black_box(black_box(x) * black_box(x)); - } - }); - }); - } - - for i in input.clone().into_iter() { - group.bench_with_input( - format!("pow {:?}", &i.len()), - &(i, 5u64), - |bench, (i, a)| { - bench.iter(|| { - for (x, _) in i { - black_box(black_box(x).pow(*a)); - } - }); - }, - ); - } - - for i in input.clone().into_iter() { - group.bench_with_input(format!("sub {:?}", &i.len()), &i, |bench, i| { - bench.iter(|| { - for (x, y) in i { - black_box(black_box(x) - black_box(y)); - } - }); - }); - } - - for i in input.clone().into_iter() { - group.bench_with_input(format!("inv {:?}", &i.len()), &i, |bench, i| { - bench.iter(|| { - for (x, _) in i { - black_box(black_box(x).inv().unwrap()); - } - }); - }); - } - - for i in input.clone().into_iter() { - group.bench_with_input(format!("div {:?}", &i.len()), &i, |bench, i| { - bench.iter(|| { - for (x, y) in i { - black_box(black_box(x) / black_box(y)).unwrap(); - } - }); - }); - } - - for i in input.clone().into_iter() { - group.bench_with_input(format!("eq {:?}", &i.len()), &i, |bench, i| { - bench.iter(|| { - for (x, y) in i { - black_box(black_box(x) == black_box(y)); - } - }); - }); - } - - for i in input.clone().into_iter() { - group.bench_with_input(format!("sqrt {:?}", &i.len()), &i, |bench, i| { - bench.iter(|| { - for (x, _) in i { - black_box(black_box(x).sqrt()); - } - }); - }); - } - - for i in input.clone().into_iter() { - group.bench_with_input(format!("sqrt squared {:?}", &i.len()), &i, |bench, i| { - let i: Vec = i.iter().map(|(x, _)| x * x).collect(); - bench.iter(|| { - for x in &i { - black_box(black_box(x).sqrt()); - } - }); - }); - } - - for i in input.clone().into_iter() { - group.bench_with_input(format!("bitand {:?}", &i.len()), &i, |bench, i| { - // Note: we should strive to have the number of limbs be generic... ideally this benchmark group itself should have a generic type that we call into from the main runner. - let i: Vec<(UnsignedInteger<4>, UnsignedInteger<4>)> = - i.iter().map(|(x, y)| (*x.value(), *y.value())).collect(); - bench.iter(|| { - for (x, y) in &i { - black_box(black_box(*x) & black_box(*y)); - } - }); - }); - } - - for i in input.clone().into_iter() { - group.bench_with_input(format!("bitor {:?}", &i.len()), &i, |bench, i| { - let i: Vec<(UnsignedInteger<4>, UnsignedInteger<4>)> = - i.iter().map(|(x, y)| (*x.value(), *y.value())).collect(); - bench.iter(|| { - for (x, y) in &i { - black_box(black_box(*x) | black_box(*y)); - } - }); - }); - } - - for i in input.clone().into_iter() { - group.bench_with_input(format!("bitxor {:?}", &i.len()), &i, |bench, i| { - let i: Vec<(UnsignedInteger<4>, UnsignedInteger<4>)> = - i.iter().map(|(x, y)| (*x.value(), *y.value())).collect(); - bench.iter(|| { - for (x, y) in &i { - black_box(black_box(*x) ^ black_box(*y)); - } - }); - }); - } -} diff --git a/crates/math/benches/fields/u64_goldilocks.rs b/crates/math/benches/fields/u64_goldilocks.rs deleted file mode 100644 index 01a74cd91..000000000 --- a/crates/math/benches/fields/u64_goldilocks.rs +++ /dev/null @@ -1,196 +0,0 @@ -use std::hint::black_box; - -use criterion::Criterion; -use lambdaworks_math::field::{ - element::FieldElement, fields::u64_goldilocks_field::Goldilocks64Field, -}; -use rand::Rng; -use rand::SeedableRng; - -pub type F = FieldElement; - -pub fn rand_field_elements(num: usize) -> Vec<(F, F)> { - let mut rng = rand_chacha::ChaCha20Rng::seed_from_u64(9001); - let mut result = Vec::with_capacity(num); - for _ in 0..result.capacity() { - result.push((F::new(rng.gen::()), F::new(rng.gen::()))); - } - result -} - -pub fn u64_goldilocks_ops_benchmarks(c: &mut Criterion) { - let input: Vec> = [1, 10, 100, 1000, 10000, 100000, 1000000] - .into_iter() - .map(rand_field_elements) - .collect::>(); - let mut group = c.benchmark_group("u64 Goldilocks FP operations"); - - for i in input.clone().into_iter() { - group.bench_with_input(format!("add {:?}", &i.len()), &i, |bench, i| { - bench.iter(|| { - for (x, y) in i { - black_box(black_box(x) + black_box(y)); - } - }); - }); - } - - for i in input.clone().into_iter() { - group.bench_with_input(format!("mul {:?}", &i.len()), &i, |bench, i| { - bench.iter(|| { - for (x, y) in i { - black_box(black_box(x) * black_box(y)); - } - }); - }); - } - - for i in input.clone().into_iter() { - group.bench_with_input(format!("pow by 1 {:?}", &i.len()), &i, |bench, i| { - bench.iter(|| { - for (x, _) in i { - black_box(black_box(x).pow(1_u64)); - } - }); - }); - } - - for i in input.clone().into_iter() { - group.bench_with_input(format!("square {:?}", &i.len()), &i, |bench, i| { - bench.iter(|| { - for (x, _) in i { - black_box(black_box(x).square()); - } - }); - }); - } - - for i in input.clone().into_iter() { - group.bench_with_input(format!("square with pow {:?}", &i.len()), &i, |bench, i| { - bench.iter(|| { - for (x, _) in i { - black_box(black_box(x).pow(2_u64)); - } - }); - }); - } - - for i in input.clone().into_iter() { - group.bench_with_input(format!("square with mul {:?}", &i.len()), &i, |bench, i| { - bench.iter(|| { - for (x, _) in i { - black_box(black_box(x) * black_box(x)); - } - }); - }); - } - - for i in input.clone().into_iter() { - group.bench_with_input( - format!("pow {:?}", &i.len()), - &(i, 5u64), - |bench, (i, a)| { - bench.iter(|| { - for (x, _) in i { - black_box(black_box(x).pow(*a)); - } - }); - }, - ); - } - - for i in input.clone().into_iter() { - group.bench_with_input(format!("sub {:?}", &i.len()), &i, |bench, i| { - bench.iter(|| { - for (x, y) in i { - black_box(black_box(x) - black_box(y)); - } - }); - }); - } - - for i in input.clone().into_iter() { - group.bench_with_input(format!("inv {:?}", &i.len()), &i, |bench, i| { - bench.iter(|| { - for (x, _) in i { - black_box(black_box(x).inv().unwrap()); - } - }); - }); - } - - for i in input.clone().into_iter() { - group.bench_with_input(format!("div {:?}", &i.len()), &i, |bench, i| { - bench.iter(|| { - for (x, y) in i { - black_box(black_box(x) / black_box(y)).unwrap(); - } - }); - }); - } - - for i in input.clone().into_iter() { - group.bench_with_input(format!("eq {:?}", &i.len()), &i, |bench, i| { - bench.iter(|| { - for (x, y) in i { - black_box(black_box(x) == black_box(y)); - } - }); - }); - } - - for i in input.clone().into_iter() { - group.bench_with_input(format!("sqrt {:?}", &i.len()), &i, |bench, i| { - bench.iter(|| { - for (x, _) in i { - black_box(black_box(x).sqrt()); - } - }); - }); - } - - for i in input.clone().into_iter() { - group.bench_with_input(format!("sqrt squared {:?}", &i.len()), &i, |bench, i| { - let i: Vec = i.iter().map(|(x, _)| x * x).collect(); - bench.iter(|| { - for x in &i { - black_box(black_box(x).sqrt()); - } - }); - }); - } - - for i in input.clone().into_iter() { - group.bench_with_input(format!("bitand {:?}", &i.len()), &i, |bench, i| { - // Note: we should strive to have the number of limbs be generic... ideally this benchmark group itself should have a generic type that we call into from the main runner. - let i: Vec<(u64, u64)> = i.iter().map(|(x, y)| (*x.value(), *y.value())).collect(); - bench.iter(|| { - for (x, y) in &i { - black_box(black_box(*x) & black_box(*y)); - } - }); - }); - } - - for i in input.clone().into_iter() { - group.bench_with_input(format!("bitor {:?}", &i.len()), &i, |bench, i| { - let i: Vec<(u64, u64)> = i.iter().map(|(x, y)| (*x.value(), *y.value())).collect(); - bench.iter(|| { - for (x, y) in &i { - black_box(black_box(*x) | black_box(*y)); - } - }); - }); - } - - for i in input.clone().into_iter() { - group.bench_with_input(format!("bitxor {:?}", &i.len()), &i, |bench, i| { - let i: Vec<(u64, u64)> = i.iter().map(|(x, y)| (*x.value(), *y.value())).collect(); - bench.iter(|| { - for (x, y) in &i { - black_box(black_box(*x) ^ black_box(*y)); - } - }); - }); - } -} diff --git a/crates/math/benches/fields/u64_goldilocks_montgomery.rs b/crates/math/benches/fields/u64_goldilocks_montgomery.rs deleted file mode 100644 index a22eb0e3b..000000000 --- a/crates/math/benches/fields/u64_goldilocks_montgomery.rs +++ /dev/null @@ -1,233 +0,0 @@ -use std::hint::black_box; - -use criterion::Criterion; -use lambdaworks_math::{ - field::{ - element::FieldElement, - fields::{ - fft_friendly::u64_goldilocks::{ - MontgomeryConfigU64GoldilocksPrimeField, U64GoldilocksPrimeField, - }, - montgomery_backed_prime_fields::IsModulus, - }, - }, - unsigned_integer::{ - element::{UnsignedInteger, U64}, - montgomery::MontgomeryAlgorithms, - }, -}; -use rand::{Rng, SeedableRng}; - -pub type F = FieldElement; -const NUM_LIMBS: usize = 1; - -pub fn rand_field_elements(num: usize) -> Vec<(F, F)> { - let mut rng = rand_chacha::ChaCha20Rng::seed_from_u64(9001); - let mut result = Vec::with_capacity(num); - for _ in 0..result.capacity() { - let rand_a = UnsignedInteger { - limbs: [rng.gen::()], - }; - let rand_b = UnsignedInteger { - limbs: [rng.gen::()], - }; - result.push((F::new(rand_a), F::new(rand_b))); - } - result -} - -pub fn u64_goldilocks_montgomery_ops_benchmarks(c: &mut Criterion) { - let input: Vec> = [1, 10, 100, 1000, 10000, 100000, 1000000] - .into_iter() - .map(rand_field_elements) - .collect::>(); - let mut group = c.benchmark_group("U64 Goldilocks Mont operations"); - - for i in input.clone().into_iter() { - group.bench_with_input(format!("add {:?}", &i.len()), &i, |bench, i| { - bench.iter(|| { - for (x, y) in i { - black_box(black_box(x) + black_box(y)); - } - }); - }); - } - - for i in input.clone().into_iter() { - group.bench_with_input(format!("mul {:?}", &i.len()), &i, |bench, i| { - bench.iter(|| { - for (x, y) in i { - black_box(black_box(x) * black_box(y)); - } - }); - }); - } - - for i in input.clone().into_iter() { - group.bench_with_input(format!("pow by 1 {:?}", &i.len()), &i, |bench, i| { - bench.iter(|| { - for (x, _) in i { - black_box(black_box(x).pow(1_u64)); - } - }); - }); - } - - // The non-boxed constants are intentional as they are - // normally computed at compile time. - for i in input.clone().into_iter() { - group.bench_with_input(format!("sos_square {:?}", &i.len()), &i, |bench, i| { - bench.iter(|| { - for (x, _) in i { - MontgomeryAlgorithms::sos_square( - black_box(black_box(x.value())), - &>::MODULUS, - &U64GoldilocksPrimeField::MU, - ); - } - }); - }); - } - - for i in input.clone().into_iter() { - group.bench_with_input(format!("square {:?}", &i.len()), &i, |bench, i| { - bench.iter(|| { - for (x, _) in i { - black_box(black_box(x).square()); - } - }); - }); - } - - for i in input.clone().into_iter() { - group.bench_with_input(format!("square with pow {:?}", &i.len()), &i, |bench, i| { - bench.iter(|| { - for (x, _) in i { - black_box(black_box(x).pow(2_u64)); - } - }); - }); - } - - for i in input.clone().into_iter() { - group.bench_with_input(format!("square with mul {:?}", &i.len()), &i, |bench, i| { - bench.iter(|| { - for (x, _) in i { - black_box(black_box(x) * black_box(x)); - } - }); - }); - } - - for i in input.clone().into_iter() { - group.bench_with_input( - format!("pow {:?}", &i.len()), - &(i, 5u64), - |bench, (i, a)| { - bench.iter(|| { - for (x, _) in i { - black_box(black_box(x).pow(*a)); - } - }); - }, - ); - } - - for i in input.clone().into_iter() { - group.bench_with_input(format!("sub {:?}", &i.len()), &i, |bench, i| { - bench.iter(|| { - for (x, y) in i { - black_box(black_box(x) - black_box(y)); - } - }); - }); - } - - for i in input.clone().into_iter() { - group.bench_with_input(format!("inv {:?}", &i.len()), &i, |bench, i| { - bench.iter(|| { - for (x, _) in i { - black_box(black_box(x).inv().unwrap()); - } - }); - }); - } - - for i in input.clone().into_iter() { - group.bench_with_input(format!("div {:?}", &i.len()), &i, |bench, i| { - bench.iter(|| { - for (x, y) in i { - black_box(black_box(x) / black_box(y)).unwrap(); - } - }); - }); - } - - for i in input.clone().into_iter() { - group.bench_with_input(format!("eq {:?}", &i.len()), &i, |bench, i| { - bench.iter(|| { - for (x, y) in i { - black_box(black_box(x) == black_box(y)); - } - }); - }); - } - - for i in input.clone().into_iter() { - group.bench_with_input(format!("sqrt {:?}", &i.len()), &i, |bench, i| { - bench.iter(|| { - for (x, _) in i { - black_box(black_box(x).sqrt()); - } - }); - }); - } - - for i in input.clone().into_iter() { - group.bench_with_input(format!("sqrt squared {:?}", &i.len()), &i, |bench, i| { - let i: Vec = i.iter().map(|(x, _)| x * x).collect(); - bench.iter(|| { - for x in &i { - black_box(black_box(x).sqrt()); - } - }); - }); - } - - for i in input.clone().into_iter() { - group.bench_with_input(format!("bitand {:?}", &i.len()), &i, |bench, i| { - // Note: we should strive to have the number of limbs be generic... ideally this benchmark group itself should have a generic type that we call into from the main runner. - let i: Vec<(UnsignedInteger, UnsignedInteger)> = - i.iter().map(|(x, y)| (*x.value(), *y.value())).collect(); - bench.iter(|| { - for (x, y) in &i { - black_box(black_box(*x) & black_box(*y)); - } - }); - }); - } - - for i in input.clone().into_iter() { - group.bench_with_input(format!("bitor {:?}", &i.len()), &i, |bench, i| { - let i: Vec<(UnsignedInteger, UnsignedInteger)> = - i.iter().map(|(x, y)| (*x.value(), *y.value())).collect(); - bench.iter(|| { - for (x, y) in &i { - black_box(black_box(*x) | black_box(*y)); - } - }); - }); - } - - for i in input.clone().into_iter() { - group.bench_with_input(format!("bitxor {:?}", &i.len()), &i, |bench, i| { - let i: Vec<(UnsignedInteger, UnsignedInteger)> = - i.iter().map(|(x, y)| (*x.value(), *y.value())).collect(); - bench.iter(|| { - for (x, y) in &i { - black_box(black_box(*x) ^ black_box(*y)); - } - }); - }); - } -} diff --git a/crates/math/benches/iai_elliptic_curve.rs b/crates/math/benches/iai_elliptic_curve.rs deleted file mode 100644 index 7af98abe6..000000000 --- a/crates/math/benches/iai_elliptic_curve.rs +++ /dev/null @@ -1,23 +0,0 @@ -mod elliptic_curves; - -use elliptic_curves::{iai_bls12_377::*, iai_bls12_381::*}; - -iai_callgrind::main!( - callgrind_args = "toggle-collect=util::*"; - functions = bls12_381_operate_with_g1, - bls12_381_operate_with_g2, - bls12_381_operate_with_self_g1, - bls12_381_operate_with_self_g2, - bls12_381_double_g1, - bls12_381_double_g2, - bls12_381_neg_g1, - bls12_381_neg_g2, - bls12_381_compress_g1, - bls12_381_decompress_g1, - bls12_381_subgroup_check_g1, - bls12_381_ate_pairing, - bls12_377_operate_with_g1, - bls12_377_operate_with_self_g1, - bls12_377_double_g1, - bls12_377_neg_g1, -); diff --git a/crates/math/benches/iai_fft.rs b/crates/math/benches/iai_fft.rs deleted file mode 100644 index 1f58e6ae8..000000000 --- a/crates/math/benches/iai_fft.rs +++ /dev/null @@ -1,95 +0,0 @@ -#![allow(dead_code)] // clippy has false positive in benchmarks -use core::hint::black_box; -use lambdaworks_math::field::traits::RootsConfig; - -mod utils; -use utils::fft_functions; -use utils::stark252_utils::{rand_field_elements, rand_poly, twiddles}; - -const SIZE_ORDER: u64 = 10; - -#[inline(never)] -fn seq_fft_benchmarks_rn() { - let mut input = rand_field_elements(SIZE_ORDER); - let twiddles_nat = twiddles(SIZE_ORDER, RootsConfig::Natural); - - fft_functions::ordered_fft_rn(black_box(&mut input), black_box(&twiddles_nat)); -} - -#[inline(never)] -fn seq_fft_benchmarks_nr() { - let mut input = rand_field_elements(SIZE_ORDER); - let twiddles_bitrev = twiddles(SIZE_ORDER, RootsConfig::BitReverse); - - fft_functions::ordered_fft_nr(black_box(&mut input), black_box(&twiddles_bitrev)); -} - -#[inline(never)] -fn seq_twiddles_generation_natural_benchmarks() { - fft_functions::twiddles_generation(black_box(SIZE_ORDER), black_box(RootsConfig::Natural)); -} - -#[inline(never)] -fn seq_twiddles_generation_natural_inversed_benchmarks() { - fft_functions::twiddles_generation( - black_box(SIZE_ORDER), - black_box(RootsConfig::NaturalInversed), - ); -} - -#[inline(never)] -fn seq_twiddles_generation_bitrev_benchmarks() { - fft_functions::twiddles_generation(black_box(SIZE_ORDER), black_box(RootsConfig::BitReverse)); -} - -#[inline(never)] -fn seq_twiddles_generation_bitrev_inversed_benchmarks() { - fft_functions::twiddles_generation( - black_box(SIZE_ORDER), - black_box(RootsConfig::BitReverseInversed), - ); -} - -#[inline(never)] -fn seq_bitrev_permutation_benchmarks() { - let mut input = rand_field_elements(SIZE_ORDER); - fft_functions::bitrev_permute(black_box(&mut input)); -} - -#[inline(never)] -fn seq_poly_evaluation_benchmarks() { - let poly = rand_poly(SIZE_ORDER); - let _ = black_box(fft_functions::poly_evaluate_fft(black_box(&poly))); -} - -#[inline(never)] -fn seq_poly_interpolation_benchmarks() { - let evals = rand_field_elements(SIZE_ORDER); - fft_functions::poly_interpolate_fft(black_box(&evals)); -} - -#[cfg(not(feature = "cuda"))] -iai_callgrind::main!( - callgrind_args = "toggle-collect=util::*"; - functions = seq_fft_benchmarks_nr, - seq_fft_benchmarks_rn, - seq_twiddles_generation_natural_benchmarks, - seq_twiddles_generation_natural_inversed_benchmarks, - seq_twiddles_generation_bitrev_benchmarks, - seq_twiddles_generation_bitrev_inversed_benchmarks, - seq_bitrev_permutation_benchmarks, - seq_poly_evaluation_benchmarks, - seq_poly_interpolation_benchmarks -); - -#[cfg(feature = "cuda")] -iai_callgrind::main!( - callgrind_args = "toggle-collect=util::*"; - functions = seq_fft_benchmarks_nr, - seq_fft_benchmarks_rn, - seq_twiddles_generation_natural_benchmarks, - seq_twiddles_generation_natural_inversed_benchmarks, - seq_twiddles_generation_bitrev_benchmarks, - seq_twiddles_generation_bitrev_inversed_benchmarks, - seq_bitrev_permutation_benchmarks, -); diff --git a/crates/math/benches/iai_field.rs b/crates/math/benches/iai_field.rs deleted file mode 100644 index a62460b76..000000000 --- a/crates/math/benches/iai_field.rs +++ /dev/null @@ -1,66 +0,0 @@ -use criterion::black_box; -use utils::u64_utils; - -mod utils; - -#[inline(never)] -fn fp_add_benchmarks() { - let (x, y) = u64_utils::get_field_elements(); - let _ = black_box(black_box(x) + black_box(y)); -} - -#[inline(never)] -fn fp_mul_benchmarks() { - let (x, y) = u64_utils::get_field_elements(); - let _ = black_box(black_box(x) * black_box(y)); -} - -#[inline(never)] -fn fp_pow_benchmarks() { - let (x, _) = u64_utils::get_field_elements(); - let y: u64 = 5; - let _ = black_box(black_box(x).pow(black_box(y))); -} - -#[inline(never)] -fn fp_sub_benchmarks() { - let (x, y) = u64_utils::get_field_elements(); - let _ = black_box(black_box(x) - black_box(y)); -} - -#[inline(never)] -fn fp_inv_benchmarks() { - let (x, _) = u64_utils::get_field_elements(); - let _ = black_box(black_box(x).inv()); -} - -#[inline(never)] -fn fp_div_benchmarks() { - let (x, y) = u64_utils::get_field_elements(); - let _ = black_box(black_box(x) / black_box(y)); -} - -#[inline(never)] -fn fp_eq_benchmarks() { - let (x, y) = u64_utils::get_field_elements(); - let _ = black_box(black_box(x) == black_box(y)); -} - -#[inline(never)] -fn fp_sqrt_benchmarks() { - // Make sure it has a square root - let x = u64_utils::get_squared_field_element(); - let _ = black_box(black_box(x).sqrt()); -} - -iai_callgrind::main!( - callgrind_args = "toggle-collect=util::*"; - functions = fp_add_benchmarks, - fp_mul_benchmarks, - fp_pow_benchmarks, - fp_sub_benchmarks, - fp_inv_benchmarks, - fp_div_benchmarks, - fp_eq_benchmarks, - fp_sqrt_benchmarks, -); diff --git a/crates/math/benches/iai_polynomial.rs b/crates/math/benches/iai_polynomial.rs deleted file mode 100644 index 38d8e1f7f..000000000 --- a/crates/math/benches/iai_polynomial.rs +++ /dev/null @@ -1,76 +0,0 @@ -use const_random::const_random; -use iai_callgrind::black_box; -use u64_utils::{rand_field_elements, rand_poly, FE}; - -mod utils; -use utils::u64_utils; - -const ORDER: u64 = const_random!(u64) % 8; - -#[inline(never)] -fn poly_evaluate_benchmarks() { - let poly = rand_poly(ORDER); - let x = FE::new(rand::random::()); - black_box(poly.evaluate(black_box(&x))); -} - -#[inline(never)] -fn poly_evaluate_slice_benchmarks() { - let poly = rand_poly(ORDER); - let inputs = rand_field_elements(ORDER); - black_box(poly.evaluate_slice(black_box(&inputs))); -} - -#[inline(never)] -fn poly_add_benchmarks() { - let x_poly = rand_poly(ORDER); - let y_poly = rand_poly(ORDER); - let _ = black_box(black_box(black_box(&x_poly) + black_box(&y_poly))); -} - -#[inline(never)] -fn poly_neg_benchmarks() { - let x_poly = rand_poly(ORDER); - let _ = black_box(-black_box(x_poly)); -} - -#[inline(never)] -fn poly_sub_benchmarks() { - let x_poly = rand_poly(ORDER); - let y_poly = rand_poly(ORDER); - let _ = black_box(black_box(x_poly) - black_box(y_poly)); -} - -#[inline(never)] -fn poly_mul_benchmarks() { - let x_poly = rand_poly(ORDER); - let y_poly = rand_poly(ORDER); - let _ = black_box(black_box(x_poly) * black_box(y_poly)); -} - -#[inline(never)] -fn poly_div_benchmarks() { - let x_poly = rand_poly(ORDER); - let y_poly = rand_poly(ORDER); - let _ = black_box(black_box(x_poly) / black_box(y_poly)); -} - -#[inline(never)] -fn poly_div_ruffini_benchmarks() { - let mut x_poly = rand_poly(ORDER); - let b = rand_field_elements(1)[0]; - black_box(&mut x_poly).ruffini_division_inplace(black_box(&b)); - let _ = black_box(x_poly); -} - -iai_callgrind::main!( - callgrind_args = "toggle-collect=util::*"; - functions = poly_evaluate_benchmarks, - poly_evaluate_slice_benchmarks, - poly_add_benchmarks, - poly_neg_benchmarks, - poly_sub_benchmarks, - poly_mul_benchmarks, - poly_div_benchmarks, - poly_div_ruffini_benchmarks, -); diff --git a/crates/math/benches/polynomials/dense_multilinear_poly.rs b/crates/math/benches/polynomials/dense_multilinear_poly.rs deleted file mode 100644 index 8e35ac661..000000000 --- a/crates/math/benches/polynomials/dense_multilinear_poly.rs +++ /dev/null @@ -1,43 +0,0 @@ -use super::utils::{rand_dense_multilinear_poly, rand_field_elements, FE}; -use const_random::const_random; -use core::hint::black_box; -use criterion::Criterion; -use lambdaworks_math::polynomial::dense_multilinear_poly::DenseMultilinearPolynomial; - -pub fn dense_multilinear_polynomial_benchmarks(c: &mut Criterion) { - let mut group = c.benchmark_group("Polynomial"); - let order = const_random!(u64) % 8; - - group.bench_function("evaluate", |bench| { - let poly = rand_dense_multilinear_poly(order); - let r = rand_field_elements(order); - bench.iter(|| poly.evaluate(black_box(r.clone()))); - }); - - group.bench_function("evaluate_with", |bench| { - let evals = rand_field_elements(order); - let r = rand_field_elements(order); - - bench.iter(|| DenseMultilinearPolynomial::evaluate_with(black_box(&evals), black_box(&r))); - }); - - group.bench_function("merge", |bench| { - let x_poly = rand_dense_multilinear_poly(order); - let y_poly = rand_dense_multilinear_poly(order); - bench.iter(|| { - DenseMultilinearPolynomial::merge(black_box(&[x_poly.clone(), y_poly.clone()])) - }); - }); - - group.bench_function("add", |bench| { - let x_poly = rand_dense_multilinear_poly(order); - let y_poly = rand_dense_multilinear_poly(order); - bench.iter(|| black_box(black_box(x_poly.clone()) + black_box(y_poly.clone()))); - }); - - group.bench_function("mul", |bench| { - let x_poly = rand_dense_multilinear_poly(order); - let y = FE::new(rand::random::()); - bench.iter(|| black_box(black_box(x_poly.clone()) * black_box(y))); - }); -} diff --git a/crates/math/benches/polynomials/mod.rs b/crates/math/benches/polynomials/mod.rs deleted file mode 100644 index d0a4adbeb..000000000 --- a/crates/math/benches/polynomials/mod.rs +++ /dev/null @@ -1,4 +0,0 @@ -pub mod dense_multilinear_poly; -pub mod polynomial; -pub mod sparse_multilinear_poly; -pub mod utils; diff --git a/crates/math/benches/polynomials/polynomial.rs b/crates/math/benches/polynomials/polynomial.rs deleted file mode 100644 index 67cb12c51..000000000 --- a/crates/math/benches/polynomials/polynomial.rs +++ /dev/null @@ -1,105 +0,0 @@ -use super::utils::{rand_complex_mersenne_poly, rand_field_elements, rand_poly, FE}; -use const_random::const_random; -use core::hint::black_box; -use criterion::Criterion; -use lambdaworks_math::{ - field::fields::mersenne31::extensions::Degree2ExtensionField, polynomial::Polynomial, -}; - -pub fn polynomial_benchmarks(c: &mut Criterion) { - let mut group = c.benchmark_group("Polynomial"); - let order = const_random!(u64) % 8; - - group.bench_function("evaluate", |bench| { - let poly = rand_poly(order); - let x = FE::new(rand::random::()); - bench.iter(|| poly.evaluate(black_box(&x))); - }); - - group.bench_function("evaluate_slice", |bench| { - let poly = rand_poly(order); - let inputs = rand_field_elements(order); - bench.iter(|| poly.evaluate_slice(black_box(&inputs))); - }); - - group.bench_function("add", |bench| { - let x_poly = rand_poly(order); - let y_poly = rand_poly(order); - bench.iter(|| black_box(&x_poly) + black_box(&y_poly)); - }); - - group.bench_function("neg", |bench| { - let x_poly = rand_poly(order); - bench.iter(|| -black_box(&x_poly)); - }); - - group.bench_function("sub", |bench| { - let x_poly = rand_poly(order); - let y_poly = rand_poly(order); - bench.iter(|| black_box(&x_poly) - black_box(&y_poly)); - }); - - group.bench_function("mul", |bench| { - let x_poly = rand_poly(order); - let y_poly = rand_poly(order); - bench.iter(|| black_box(&x_poly) * black_box(&y_poly)); - }); - - let big_order = 9; - let x_poly = rand_complex_mersenne_poly(big_order); - let y_poly = rand_complex_mersenne_poly(big_order); - group.bench_function("fast_mul big poly", |bench| { - bench.iter(|| { - black_box(&x_poly) - .fast_fft_multiplication::(black_box(&y_poly)) - .unwrap() - }); - }); - group.bench_function("slow mul big poly", |bench| { - bench.iter(|| black_box(&x_poly) * black_box(&y_poly)); - }); - - let y_poly = rand_complex_mersenne_poly(big_order - 2); - - group.bench_function("fast div big poly", |bench| { - bench - .iter(|| black_box(&x_poly).fast_division::(black_box(&y_poly))); - }); - - group.bench_function("slow div big poly", |bench| { - bench.iter(|| black_box(x_poly.clone()).long_division_with_remainder(black_box(&y_poly))); - }); - - group.bench_function("div", |bench| { - let x_poly = rand_poly(order); - let y_poly = rand_poly(order); - bench.iter_batched( - || (x_poly.clone(), y_poly.clone()), - |(x_poly, y_poly)| black_box(x_poly) / black_box(y_poly), - criterion::BatchSize::SmallInput, - ); - }); - - group.bench_function("div by 'x - b' with generic div", |bench| { - let poly = rand_poly(order); - let m = Polynomial::new_monomial(FE::one(), 1) - rand_field_elements(1)[0]; - bench.iter_batched( - || (poly.clone(), m.clone()), - |(poly, m)| poly / m, - criterion::BatchSize::SmallInput, - ); - }); - - group.bench_function("div by 'x - b' with Ruffini", |bench| { - let poly = rand_poly(order); - let b = rand_field_elements(1)[0]; - bench.iter_batched( - || (poly.clone(), b), - |(mut poly, b)| { - poly.ruffini_division_inplace(&b); - poly - }, - criterion::BatchSize::SmallInput, - ); - }); -} diff --git a/crates/math/benches/polynomials/sparse_multilinear_poly.rs b/crates/math/benches/polynomials/sparse_multilinear_poly.rs deleted file mode 100644 index 470749b32..000000000 --- a/crates/math/benches/polynomials/sparse_multilinear_poly.rs +++ /dev/null @@ -1,45 +0,0 @@ -use super::utils::{rand_field_elements, rand_sparse_multilinear_poly}; -use const_random::const_random; -use core::hint::black_box; -use criterion::Criterion; -use lambdaworks_math::polynomial::sparse_multilinear_poly::SparseMultilinearPolynomial; -use rand::random; - -pub fn sparse_multilinear_polynomial_benchmarks(c: &mut Criterion) { - let mut group = c.benchmark_group("Polynomial"); - let order = const_random!(u64) % 8; - let num_vars = [3, 4, 5, 6, 7, 8, 9, 10]; - - for num_var in num_vars.iter() { - group.bench_with_input( - format!("evaluate {:?}", &num_var), - num_var, - |bench, num_var| { - let poly = rand_sparse_multilinear_poly(*num_var, order); - let r = rand_field_elements(order); - bench.iter(|| poly.evaluate(black_box(&r))); - }, - ); - } - - for num_var in num_vars.iter() { - group.bench_with_input( - format!("evaluate_with {:?}", &num_var), - num_var, - |bench, num_var| { - let evals = rand_field_elements(order) - .into_iter() - .map(|eval| (random(), eval)) - .collect::>(); - let r = rand_field_elements(order); - bench.iter(|| { - SparseMultilinearPolynomial::evaluate_with( - black_box(*num_var), - black_box(&evals), - black_box(&r), - ) - }); - }, - ); - } -} diff --git a/crates/math/benches/polynomials/utils.rs b/crates/math/benches/polynomials/utils.rs deleted file mode 100644 index f4ae16f9b..000000000 --- a/crates/math/benches/polynomials/utils.rs +++ /dev/null @@ -1,89 +0,0 @@ -use const_random::const_random; -use lambdaworks_math::{ - field::{ - element::FieldElement, - fields::{ - mersenne31::{extensions::Degree2ExtensionField, field::Mersenne31Field}, - u64_prime_field::{U64FieldElement, U64PrimeField}, - }, - }, - polynomial::{ - dense_multilinear_poly::DenseMultilinearPolynomial, - sparse_multilinear_poly::SparseMultilinearPolynomial, Polynomial, - }, -}; -use rand::random; - -// Mersenne prime numbers -// https://www.math.utah.edu/~pa/math/mersenne.html -const PRIMES: [u64; 39] = [ - 13, 17, 19, 31, 61, 89, 107, 127, 521, 607, 1279, 2203, 2281, 3217, 4253, 4423, 9689, 9941, - 11213, 19937, 21701, 23209, 44497, 86243, 110503, 132049, 216091, 756839, 859433, 1257787, - 1398269, 2976221, 3021377, 6972593, 13466917, 20996011, 24036583, 25964951, 30402457, -]; - -const MODULUS: u64 = PRIMES[const_random!(usize) % PRIMES.len()]; -pub type FE = U64FieldElement; - -#[allow(dead_code)] -#[inline(never)] -#[export_name = "u64_utils::rand_field_elements"] -pub fn rand_field_elements(order: u64) -> Vec { - let mut result = Vec::with_capacity(1 << order); - for _ in 0..result.capacity() { - result.push(FE::new(random())); - } - result -} - -#[allow(dead_code)] -#[inline(never)] -#[export_name = "u64_utils::rand_poly"] -pub fn rand_poly(order: u64) -> Polynomial { - Polynomial::new(&rand_field_elements(order)) -} -#[allow(dead_code)] -#[inline(never)] -#[export_name = "u64_utils::rand_complex_mersenne_field_elements"] -pub fn rand_complex_mersenne_field_elements( - order: u32, -) -> Vec> { - let mut result = Vec::with_capacity(1 << order); - for _ in 0..result.capacity() { - result.push(FieldElement::::new([ - FieldElement::::new(random()), - FieldElement::::new(random()), - ])); - } - result -} - -#[allow(dead_code)] -#[inline(never)] -#[export_name = "u64_utils::rand_complex_mersenne_poly"] -pub fn rand_complex_mersenne_poly(order: u32) -> Polynomial> { - Polynomial::new(&rand_complex_mersenne_field_elements(order)) -} - -#[allow(dead_code)] -#[inline(never)] -#[export_name = "u64_utils::rand_dense_multilinear_poly"] -pub fn rand_dense_multilinear_poly( - order: u64, -) -> DenseMultilinearPolynomial> { - DenseMultilinearPolynomial::new(rand_field_elements(order)) -} - -#[allow(dead_code)] -#[inline(never)] -#[export_name = "u64_utils::rand_sparse_multilinear_poly"] -pub fn rand_sparse_multilinear_poly( - num_vars: usize, - order: u64, -) -> SparseMultilinearPolynomial> { - let evals = rand_field_elements(order) - .into_iter() - .map(|eval| (random(), eval)) - .collect::>(); - SparseMultilinearPolynomial::new(num_vars, evals) -} diff --git a/crates/math/benches/utils/fft_functions.rs b/crates/math/benches/utils/fft_functions.rs deleted file mode 100644 index 2bb587599..000000000 --- a/crates/math/benches/utils/fft_functions.rs +++ /dev/null @@ -1,39 +0,0 @@ -#![allow(dead_code)] -// clippy has false positive in benchmarks -use criterion::black_box; -use lambdaworks_math::fft::cpu::{ - bit_reversing::in_place_bit_reverse_permute, - fft::{in_place_nr_2radix_fft, in_place_nr_4radix_fft, in_place_rn_2radix_fft}, - roots_of_unity::get_twiddles, -}; -use lambdaworks_math::{field::traits::RootsConfig, polynomial::Polynomial}; - -use super::stark252_utils::{F, FE}; - -pub fn ordered_fft_nr(input: &mut [FE], twiddles: &[FE]) { - in_place_nr_2radix_fft(input, twiddles); -} - -pub fn ordered_fft_rn(input: &mut [FE], twiddles: &[FE]) { - in_place_rn_2radix_fft(input, twiddles); -} - -pub fn ordered_fft_nr4(input: &mut [FE], twiddles: &[FE]) { - in_place_nr_4radix_fft(input, twiddles); -} - -pub fn twiddles_generation(order: u64, config: RootsConfig) { - get_twiddles::(order, config).unwrap(); -} - -pub fn bitrev_permute(input: &mut [FE]) { - in_place_bit_reverse_permute(input); -} - -pub fn poly_evaluate_fft(poly: &Polynomial) -> Vec { - Polynomial::evaluate_fft::(poly, black_box(1), black_box(None)).unwrap() -} - -pub fn poly_interpolate_fft(evals: &[FE]) { - Polynomial::interpolate_fft::(evals).unwrap(); -} diff --git a/crates/math/benches/utils/mod.rs b/crates/math/benches/utils/mod.rs deleted file mode 100644 index 03c7103aa..000000000 --- a/crates/math/benches/utils/mod.rs +++ /dev/null @@ -1,6 +0,0 @@ -// Some of this modules are specific to a group of benchmarks, and so trigger warnings -#![allow(dead_code)] -pub mod fft_functions; -pub mod stark252_utils; -pub mod u64_goldilocks_utils; -pub mod u64_utils; diff --git a/crates/math/benches/utils/stark252_utils.rs b/crates/math/benches/utils/stark252_utils.rs deleted file mode 100644 index 25cdd3040..000000000 --- a/crates/math/benches/utils/stark252_utils.rs +++ /dev/null @@ -1,43 +0,0 @@ -use lambdaworks_math::{ - fft::cpu::{bit_reversing::in_place_bit_reverse_permute, roots_of_unity::get_twiddles}, - field::{ - element::FieldElement, fields::fft_friendly::stark_252_prime_field::Stark252PrimeField, - traits::RootsConfig, - }, - polynomial::Polynomial, - unsigned_integer::element::UnsignedInteger, -}; -use rand::random; - -pub type F = Stark252PrimeField; -pub type FE = FieldElement; - -// NOTE: intentional duplicate to help IAI skip setup code -#[inline(never)] -#[export_name = "util::bitrev_permute"] -pub fn bitrev_permute(input: &mut [FE]) { - in_place_bit_reverse_permute(input); -} - -#[inline(never)] -#[export_name = "util::rand_field_elements"] -pub fn rand_field_elements(order: u64) -> Vec { - let mut result = Vec::with_capacity(1 << order); - for _ in 0..result.capacity() { - let rand_big = UnsignedInteger { limbs: random() }; - result.push(FE::new(rand_big)); - } - result -} - -#[inline(never)] -#[export_name = "util::rand_poly"] -pub fn rand_poly(order: u64) -> Polynomial { - Polynomial::new(&rand_field_elements(order)) -} - -#[inline(never)] -#[export_name = "util::get_twiddles"] -pub fn twiddles(order: u64, config: RootsConfig) -> Vec { - get_twiddles(order, config).unwrap() -} diff --git a/crates/math/benches/utils/u64_goldilocks_utils.rs b/crates/math/benches/utils/u64_goldilocks_utils.rs deleted file mode 100644 index 45883d70a..000000000 --- a/crates/math/benches/utils/u64_goldilocks_utils.rs +++ /dev/null @@ -1,56 +0,0 @@ -use lambdaworks_math::{ - field::element::FieldElement, - field::fields::fft_friendly::u64_goldilocks::U64GoldilocksPrimeField, polynomial::Polynomial, -}; -use rand::random; - -pub type FE = FieldElement; - -#[inline(never)] -#[export_name = "u64_utils::fp_get_goldilocks_primes"] -pub fn get_field_elements() -> ( - FieldElement, - FieldElement, -) { - let x = FieldElement::::from_hex( - "0x03d937c035c878245caf64531a5756109c53068da139362728feb561405371cb", - ) - .unwrap(); - let y = FieldElement::::from_hex( - "0x0208a0a10250e382e1e4bbe2880906c2791bf6275695e02fbbc6aeff9cd8b31a", - ) - .unwrap(); - (x, y) -} - -#[inline(never)] -#[export_name = "u64_utils::fp_squared_goldilocks_prime"] -pub fn get_squared_field_element() -> FieldElement { - let (x, _) = get_field_elements(); - x * x -} - -#[allow(dead_code)] -#[inline(never)] -#[export_name = "u64_utils::rand_field_goldilocks_elements"] -pub fn rand_field_elements(order: u64) -> Vec { - let mut result = Vec::with_capacity(1 << order); - for _ in 0..result.capacity() { - result.push(FE::from(random::())); - } - result -} - -#[allow(dead_code)] -#[inline(never)] -#[export_name = "u64_utils::rand_goldilocks_field_elements_pair"] -pub fn rand_field_elements_pair() -> (FE, FE) { - (FE::from(random::()), FE::from(random::())) -} - -#[allow(dead_code)] -#[inline(never)] -#[export_name = "u64_utils::rand_goldilocks_poly"] -pub fn rand_poly(order: u64) -> Polynomial { - Polynomial::new(&rand_field_elements(order)) -} diff --git a/crates/math/benches/utils/u64_utils.rs b/crates/math/benches/utils/u64_utils.rs deleted file mode 100644 index 6e9139f4a..000000000 --- a/crates/math/benches/utils/u64_utils.rs +++ /dev/null @@ -1,69 +0,0 @@ -use const_random::const_random; -use lambdaworks_math::{ - field::element::FieldElement, - field::fields::{ - fft_friendly::stark_252_prime_field::Stark252PrimeField, u64_prime_field::U64FieldElement, - }, - polynomial::Polynomial, -}; -use rand::random; - -// Mersenne prime numbers -// https://www.math.utah.edu/~pa/math/mersenne.html -const PRIMES: [u64; 39] = [ - 13, 17, 19, 31, 61, 89, 107, 127, 521, 607, 1279, 2203, 2281, 3217, 4253, 4423, 9689, 9941, - 11213, 19937, 21701, 23209, 44497, 86243, 110503, 132049, 216091, 756839, 859433, 1257787, - 1398269, 2976221, 3021377, 6972593, 13466917, 20996011, 24036583, 25964951, 30402457, -]; - -const MODULUS: u64 = PRIMES[const_random!(usize) % PRIMES.len()]; -pub type FE = U64FieldElement; - -#[inline(never)] -#[export_name = "u64_utils::fp_get_primes"] -pub fn get_field_elements() -> ( - FieldElement, - FieldElement, -) { - let x = FieldElement::::from_hex( - "0x03d937c035c878245caf64531a5756109c53068da139362728feb561405371cb", - ) - .unwrap(); - let y = FieldElement::::from_hex( - "0x0208a0a10250e382e1e4bbe2880906c2791bf6275695e02fbbc6aeff9cd8b31a", - ) - .unwrap(); - (x, y) -} - -#[inline(never)] -#[export_name = "u64_utils::fp_squared_prime"] -pub fn get_squared_field_element() -> FieldElement { - let (x, _) = get_field_elements(); - x * x -} - -#[allow(dead_code)] -#[inline(never)] -#[export_name = "u64_utils::rand_field_elements"] -pub fn rand_field_elements(order: u64) -> Vec { - let mut result = Vec::with_capacity(1 << order); - for _ in 0..result.capacity() { - result.push(FE::new(random())); - } - result -} - -#[allow(dead_code)] -#[inline(never)] -#[export_name = "u64_utils::rand_field_elements_pair"] -pub fn rand_field_elements_pair() -> (FE, FE) { - (FE::new(random()), FE::new(random())) -} - -#[allow(dead_code)] -#[inline(never)] -#[export_name = "u64_utils::rand_poly"] -pub fn rand_poly(order: u64) -> Polynomial { - Polynomial::new(&rand_field_elements(order)) -} diff --git a/crates/math/src/circle/README.md b/crates/math/src/circle/README.md deleted file mode 100644 index 2c2aeb361..000000000 --- a/crates/math/src/circle/README.md +++ /dev/null @@ -1,205 +0,0 @@ -# Circle Fast-Fourier Transform (CircleFFT) - - -This folder contains all the necessary tools to work with the [circle FFT](https://eprint.iacr.org/2024/278), which is a suitable way of performing an analogue of the [radix-2 FFT algorithm](../fft/README.md) over fields which are not smooth. We say a finite field is smooth if the size of the multiplicative group of the field is divisible by a sufficiently high power of 2. In the case of $\mathbb{Z}_p$, the previous sentence indicates that $p - 1 = 2^m c$, where $m$ is sufficiently large (for example, $2^{25}$), ensuring we can use the radix-2 Cooley-Tuckey algorithm for the FFT with vectors of size up to $2^{25}$. - -This mathematical structure provides several advantages for computational operations. It leads to a variant of STARKs called Circle STARKs. -For an introduction to circle STARKs, we recommend [our blog](https://blog.lambdaclass.com/an-introduction-to-circle-starks/) or [Vitalik's explanation](https://vitalik.eth.limo/general/2024/07/23/circlestarks.html) - -## Implementation Content - -This module includes the following components: - -- **CirclePoint**: Represents a point in the Circle group. -- **CFFT (Circle Fast Fourier Transform)**: Algorithm for evaluating and interpolating polynomials in the Circle group. -- **Cosets**: Implementation of cosets of the Circle group. -- **Polynomials**: Evaluation and interpolation of bivariable polynomials on the standard coset. -- **Twiddles**: Factors used in the CFFT algorithm. - - -## What is the Circle Group? - -Given a finite field $\mathbb{F}$, the Circle Group over $\mathbb{F}$ consists of all the points $(x, y)$ such that $x, y \in \mathbb{F}$ and $x^2 + y^2 = 1$, with the following group operation: - -$$(a, b) + (c, d) = (a \cdot c - b \cdot d, a \cdot d + b \cdot c)$$ - -Some properties that are useful to have in mind: -- The neutral element is $(1, 0)$. -- The inverse of $(x, y)$ is its conjugate $(x, -y)$. - -### Circle Group implementation -You can find our implementation of the Circle Group in `point.rs`. - -To implement `CirclePoint` for a field, you have to first implement `HasCircleParams` for that field. For example, in `point.rs` you can find our implementation for Mersenne-31 Prime Field. - -```rust -impl HasCircleParams for Mersenne31Field { - type FE = FieldElement; - - const CIRCLE_GENERATOR_X: Self::FE = Self::FE::const_from_raw(2); - - const CIRCLE_GENERATOR_Y: Self::FE = Self::FE::const_from_raw(1268011823); - - /// ORDER = 2^31 - const ORDER: u128 = 2147483648; -} -``` - -## What is a Coset? - -Given $g_n$ a generator of a subgroup of the circle of size $n = 2^k$ for some $k \in \mathbb{N}$, and given another point of the circle $p$ (usually called *shift*), the Coset $p + \langle g_n \rangle$ is the set that results of taking every point in $\langle g_n \rangle$ (the subgroup generated by $g_n$) and adding $p$ to them. - -For example, if $\langle g_4 \rangle = \{ p_1, p_2, p_3, p_4 \}$, then $p + \langle g_4 \rangle = \{ p + p_1, p + p_2, p + p_3, p+ p_4 \}$ - -### Standard Coset -The CFFT algorithm uses what we called a Standard Coset. A Standard Coset is a Coset of the form $g_{2n} + \langle g_n \rangle$. In other worlds, it is a coset of the subgroup of size $n = 2^k$ with the shift $g_{2n}$ (the generator of the subgroup of size $2n = 2^{k+1}$). - -In `coset.rs` you will find our implementation for the Circle Group over the Mersenne-31 Prime Field. You can use it in the following way: - -```rust -use lambdaworks_math::circle::cosets::Coset; -use lambdaworks_math::circle::point::CirclePoint; -use lambdaworks_math::field::fields::mersenne31::field::Mersenne31Field; - -// Create a standard coset of size 2^k -let log_2_size = 3; // k = 3, for a coset of size 8. -let coset = Coset::new_standard(log_2_size); - -// Get the coset generator -let generator = coset.get_generator(); - -// Get all elements of the coset -let elements = coset.get_coset_points(); -``` - - -## Implementation Details for CFFT - -### Overview -The Circle Fast Fourier Transform (CFFT) is used to evaluate and interpolate polynomials efficiently over points in a standard coset of the Circle group. The implementation uses an in-place FFT algorithm operating on slices whose length is a power of two. - -### In-Place FFT Computation -The `cfft` function an input of length $2^n$ and computes an algorithm of $n$ layers. In each layer $i$: -- The input is divided into chunks of size $2^{i+1}$. -- Butterfly operations are applied to pairs of elements within each chunk using precomputed twiddle factors. - -### Twiddle Factors -- **Purpose:** Twiddle factors are needed in the butterfly operation used in the CFFT. -- **Computation:** They are computed by the `get_twiddles` function that takes as input a coset of the Circle group. -- **Configuration:** The twiddles are different depending on whether we want to use them for a CFFT (needed to evaluate a polynomial) or for a ICFFT (needed to interpolate a polynomial). - -### Bit-Reverse Permutation -Before applying the FFT: -- The input coefficients must be reordered into bit-reversed order. -- This reordering, performed by a helper function ( `in_place_bit_reverse_permute`), is essential for the correctness of the in-place FFT computation. - -### Result Ordering -After the FFT: -- The computed evaluations are not in natural order. -- The helper function `order_cfft_result_naive` rearranges these evaluations into the natural order, aligning them with the original order of the coset points. - -### Inverse FFT (ICFFT) -- **Process:** The inverse FFT (`icfft`) mirrors the forward FFT's layered approach, but with operations that invert the transformation. It is used for polynomial interpolation, instead of evaluation. -- **Scaling:** After processing, the result is scaled by the inverse of the input length to recover the original polynomial coefficients. - -### Design Considerations and Optimizations -- **Modularity:** Separating FFT computation from data reordering enhances the maintainability and clarity of the implementation. -- **Optimization Potential:** Although the current ordering functions use a straightforward (naive) approach, there is room for further optimization (e.g., in-place value swapping). -- **Balance:** The design strikes a balance between code clarity and performance, making it easier to understand and improve in the future. - -## API Usage - -### Working with Circle group points - -```rust -use lambdaworks_math::field::{ - element::FieldElement, - fields::mersenne31::field::Mersenne31Field, -}; -use lambdaworks_math::circle::point::CirclePoint; - -// Create a valid point in the Circle group, i.e a point (x, y) -// that satisfies x^2 + y^2 = 1 -let x = FieldElement::::zero(); -let y = FieldElement::::one(); -let point = CirclePoint::new(x, y).unwrap(); - -// Get the neutral element (identity) of the group. -// That is the point (1, 0). -let zero = CirclePoint::::zero(); - -// Group operations -let point1 = CirclePoint::::GENERATOR; -let point2 = point1.double(); // Double a point -let point3 = point1 + point2; // Add two points -let point4 = point1 * 8; // Scalar multiplication -``` - -### Polynomial evaluation and interpolation using CFFT - -```rust -use lambdaworks_math::field::{ - element::FieldElement, - fields::mersenne31::field::Mersenne31Field, -}; -use lambdaworks_math::circle::polynomial::{evaluate_cfft, interpolate_cfft}; - -// Define polynomial coefficients -let coefficients: Vec> = (0..8) - .map(|i| FieldElement::::from(i as u64)) - .collect(); - -// Evaluate the polynomial at Circle group points -let evaluations = evaluate_cfft(coefficients.clone()); - -// Interpolate to recover the coefficients -let recovered_coefficients = interpolate_cfft(evaluations); -``` - -### Using CFFT directly - -```rust -use lambdaworks_math::field::{ - element::FieldElement, - fields::mersenne31::field::Mersenne31Field, -}; -use lambdaworks_math::circle::{ - cfft::{cfft, icfft}, - cosets::Coset, - twiddles::{get_twiddles, TwiddlesConfig}, -}; -use lambdaworks_math::fft::cpu::bit_reversing::in_place_bit_reverse_permute; - -// Prepare data (must be a power of 2 in length) -let mut data: Vec> = (0..8) - .map(|i| FieldElement::::from(i as u64)) - .collect(); - -// Prepare for CFFT -let domain_log_2_size = data.len().trailing_zeros(); -let coset = Coset::new_standard(domain_log_2_size); -let config = TwiddlesConfig::Evaluation; -let twiddles = get_twiddles(coset.clone(), config); - -// Bit-reverse permutation (required before CFFT) -in_place_bit_reverse_permute(&mut data); - -// Perform CFFT in-place -cfft(&mut data, twiddles); - -// For inverse CFFT -let inverse_config = TwiddlesConfig::Interpolation; -let inverse_twiddles = get_twiddles(coset, inverse_config); - -// Perform inverse CFFT -icfft(&mut data, inverse_twiddles); -``` - -## References - -- [An Introduction to Circle STARKs](https://blog.lambdaclass.com/an-introduction-to-circle-starks/) - LambdaClass Blog -- [Vitalik's Explanation of Circle STARKs](https://vitalik.eth.limo/general/2024/07/23/circlestarks.html) -- [Circle FFT Paper](https://eprint.iacr.org/2024/278) -- [Anatomy of a STARK](https://aszepieniec.github.io/stark-anatomy/) - Detailed explanation of STARKs -- [STARKs, Part I: Proofs with Polynomials](https://vitalik.ca/general/2017/11/09/starks_part_1.html) - Vitalik Buterin's series on STARKs - diff --git a/crates/math/src/circle/cfft.rs b/crates/math/src/circle/cfft.rs deleted file mode 100644 index f060e02d6..000000000 --- a/crates/math/src/circle/cfft.rs +++ /dev/null @@ -1,220 +0,0 @@ -extern crate alloc; -use crate::field::{element::FieldElement, fields::mersenne31::field::Mersenne31Field}; -use alloc::vec::Vec; - -#[cfg(feature = "alloc")] -/// fft in place algorithm used to evaluate a polynomial of degree 2^n - 1 in 2^n points. -/// Input must be of size 2^n for some n. -pub fn cfft( - input: &mut [FieldElement], - twiddles: Vec>>, -) { - // If the input size is 2^n, then log_2_size is n. - let log_2_size = input.len().trailing_zeros(); - - // The cfft has n layers. - (0..log_2_size).for_each(|i| { - // In each layer i we split the current input in chunks of size 2^{i+1}. - let chunk_size = 1 << (i + 1); - let half_chunk_size = 1 << i; - input.chunks_mut(chunk_size).for_each(|chunk| { - // We split each chunk in half, calling the first half hi_part and the second hal low_part. - let (hi_part, low_part) = chunk.split_at_mut(half_chunk_size); - - // We apply the corresponding butterfly for every element j of the high and low part. - hi_part - .iter_mut() - .zip(low_part) - .enumerate() - .for_each(|(j, (hi, low))| { - let temp = *low * twiddles[i as usize][j]; - *low = *hi - temp; - *hi += temp - }); - }); - }); -} - -#[cfg(feature = "alloc")] -/// The inverse fft algorithm used to interpolate 2^n points. -/// Input must be of size 2^n for some n. -pub fn icfft( - input: &mut [FieldElement], - twiddles: Vec>>, -) { - // If the input size is 2^n, then log_2_size is n. - let log_2_size = input.len().trailing_zeros(); - - // The icfft has n layers. - (0..log_2_size).for_each(|i| { - // In each layer i we split the current input in chunks of size 2^{n - i}. - let chunk_size = 1 << (log_2_size - i); - let half_chunk_size = chunk_size >> 1; - input.chunks_mut(chunk_size).for_each(|chunk| { - // We split each chunk in half, calling the first half hi_part and the second hal low_part. - let (hi_part, low_part) = chunk.split_at_mut(half_chunk_size); - - // We apply the corresponding butterfly for every element j of the high and low part. - hi_part - .iter_mut() - .zip(low_part) - .enumerate() - .for_each(|(j, (hi, low))| { - let temp = *hi + *low; - *low = (*hi - *low) * twiddles[i as usize][j]; - *hi = temp; - }); - }); - }); -} - -/// This function permutes a slice of field elements to order the result of the cfft in the natural way. -/// We call the natural order to [P(x0, y0), P(x1, y1), P(x2, y2), ...], -/// where (x0, y0) is the first point of the corresponding coset. -/// The cfft doesn't return the evaluations in the natural order. -/// For example, if we apply the cfft to 8 coefficients of a polynomial of degree 7 we'll get the evaluations in this order: -/// [P(x0, y0), P(x2, y2), P(x4, y4), P(x6, y6), P(x7, y7), P(x5, y5), P(x3, y3), P(x1, y1)], -/// where the even indices are found first in ascending order and then the odd indices in descending order. -/// This function permutes the slice [0, 2, 4, 6, 7, 5, 3, 1] into [0, 1, 2, 3, 4, 5, 6, 7]. -/// TODO: This can be optimized by performing in-place value swapping (WIP). -pub fn order_cfft_result_naive( - input: &[FieldElement], -) -> Vec> { - let mut result = Vec::new(); - let length = input.len(); - for i in 0..length / 2 { - result.push(input[i]); // We push the left index. - result.push(input[length - i - 1]); // We push the right index. - } - result -} - -/// This function permutes a slice of field elements to order the input of the icfft in a specific way. -/// For example, if we want to interpolate 8 points we should input them in the icfft in this order: -/// [(x0, y0), (x2, y2), (x4, y4), (x6, y6), (x7, y7), (x5, y5), (x3, y3), (x1, y1)], -/// where the even indices are found first in ascending order and then the odd indices in descending order. -/// This function permutes the slice [0, 1, 2, 3, 4, 5, 6, 7] into [0, 2, 4, 6, 7, 5, 3, 1]. -/// TODO: This can be optimized by performing in-place value swapping (WIP). -pub fn order_icfft_input_naive( - input: &mut [FieldElement], -) -> Vec> { - let mut result = Vec::new(); - - // We push the even indices. - (0..input.len()).step_by(2).for_each(|i| { - result.push(input[i]); - }); - - // We push the odd indices. - (1..input.len()).step_by(2).rev().for_each(|i| { - result.push(input[i]); - }); - result -} - -#[cfg(test)] -mod tests { - use super::*; - type FE = FieldElement; - - #[test] - fn ordering_cfft_result_works_for_4_points() { - let expected_slice = [FE::from(0), FE::from(1), FE::from(2), FE::from(3)]; - - let slice = [FE::from(0), FE::from(2), FE::from(3), FE::from(1)]; - - let res = order_cfft_result_naive(&slice); - - assert_eq!(res, expected_slice) - } - - #[test] - fn ordering_cfft_result_works_for_16_points() { - let expected_slice = [ - FE::from(0), - FE::from(1), - FE::from(2), - FE::from(3), - FE::from(4), - FE::from(5), - FE::from(6), - FE::from(7), - FE::from(8), - FE::from(9), - FE::from(10), - FE::from(11), - FE::from(12), - FE::from(13), - FE::from(14), - FE::from(15), - ]; - - let slice = [ - FE::from(0), - FE::from(2), - FE::from(4), - FE::from(6), - FE::from(8), - FE::from(10), - FE::from(12), - FE::from(14), - FE::from(15), - FE::from(13), - FE::from(11), - FE::from(9), - FE::from(7), - FE::from(5), - FE::from(3), - FE::from(1), - ]; - - let res = order_cfft_result_naive(&slice); - - assert_eq!(res, expected_slice) - } - - #[test] - fn from_natural_to_icfft_input_order_works() { - let mut slice = [ - FE::from(0), - FE::from(1), - FE::from(2), - FE::from(3), - FE::from(4), - FE::from(5), - FE::from(6), - FE::from(7), - FE::from(8), - FE::from(9), - FE::from(10), - FE::from(11), - FE::from(12), - FE::from(13), - FE::from(14), - FE::from(15), - ]; - - let expected_slice = [ - FE::from(0), - FE::from(2), - FE::from(4), - FE::from(6), - FE::from(8), - FE::from(10), - FE::from(12), - FE::from(14), - FE::from(15), - FE::from(13), - FE::from(11), - FE::from(9), - FE::from(7), - FE::from(5), - FE::from(3), - FE::from(1), - ]; - - let res = order_icfft_input_naive(&mut slice); - - assert_eq!(res, expected_slice) - } -} diff --git a/crates/math/src/circle/cosets.rs b/crates/math/src/circle/cosets.rs deleted file mode 100644 index 5f11a6d3f..000000000 --- a/crates/math/src/circle/cosets.rs +++ /dev/null @@ -1,96 +0,0 @@ -extern crate alloc; -use crate::circle::point::CirclePoint; -use crate::field::fields::mersenne31::field::Mersenne31Field; -#[cfg(feature = "alloc")] -use alloc::vec::Vec; - -/// Given g_n, a generator of the subgroup of size n of the circle, i.e. , -/// and given a shift, that is a another point of the circle, -/// we define the coset shift + which is the set of all the points in -/// plus the shift. -/// For example, if = {p1, p2, p3, p4}, then g_8 + = {g_8 + p1, g_8 + p2, g_8 + p3, g_8 + p4}. - -#[derive(Debug, Clone)] -pub struct Coset { - // Coset: shift + where n = 2^{log_2_size}. - // Example: g_16 + , n = 8, log_2_size = 3, shift = g_16. - pub log_2_size: u32, //TODO: Change log_2_size to u8 because log_2_size < 31. - pub shift: CirclePoint, -} - -impl Coset { - pub fn new(log_2_size: u32, shift: CirclePoint) -> Self { - Coset { log_2_size, shift } - } - - /// Returns the coset g_2n + - pub fn new_standard(log_2_size: u32) -> Self { - // shift is a generator of the subgroup of order 2n = 2^{log_2_size + 1}. - let shift = CirclePoint::get_generator_of_subgroup(log_2_size + 1); - Coset { log_2_size, shift } - } - - /// Returns g_n, the generator of the subgroup of order n = 2^log_2_size. - pub fn get_generator(&self) -> CirclePoint { - CirclePoint::GENERATOR.repeated_double(31 - self.log_2_size) - } - - /// Given a standard coset g_2n + , returns the subcoset with half size g_2n + - pub fn half_coset(coset: Self) -> Self { - Coset { - log_2_size: coset.log_2_size - 1, - shift: coset.shift, - } - } - - /// Given a coset shift + G returns the coset -shift + G. - /// Note that (g_2n + ) U (-g_2n + ) = g_2n + . - pub fn conjugate(coset: Self) -> Self { - Coset { - log_2_size: coset.log_2_size, - shift: coset.shift.conjugate(), - } - } - - /// Returns the vector of shift + g for every g in . - /// where g = i * g_n for i = 0, ..., n-1. - #[cfg(feature = "alloc")] - pub fn get_coset_points(coset: &Self) -> Vec> { - // g_n the generator of the subgroup of order n. - let generator_n = CirclePoint::get_generator_of_subgroup(coset.log_2_size); - let size: usize = 1 << coset.log_2_size; - core::iter::successors(Some(coset.shift.clone()), move |prev| { - Some(prev + &generator_n) - }) - .take(size) - .collect() - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn coset_points_vector_has_right_size() { - let coset = Coset::new_standard(3); - let points = Coset::get_coset_points(&coset); - assert_eq!(1 << coset.log_2_size, points.len()) - } - - #[test] - fn antipode_of_coset_point_is_in_coset() { - let coset = Coset::new_standard(3); - let points = Coset::get_coset_points(&coset); - let point = points[2].clone(); - let anitpode_point = points[6].clone(); - assert_eq!(anitpode_point, point.antipode()) - } - - #[test] - fn coset_generator_has_right_order() { - let coset = Coset::new(2, CirclePoint::GENERATOR * 3); - let generator_n = coset.get_generator(); - assert_eq!(generator_n.repeated_double(2), CirclePoint::zero()); - } -} diff --git a/crates/math/src/circle/errors.rs b/crates/math/src/circle/errors.rs deleted file mode 100644 index 51dcb720b..000000000 --- a/crates/math/src/circle/errors.rs +++ /dev/null @@ -1,4 +0,0 @@ -#[derive(Debug)] -pub enum CircleError { - PointDoesntSatisfyCircleEquation, -} diff --git a/crates/math/src/circle/mod.rs b/crates/math/src/circle/mod.rs deleted file mode 100644 index ac576194f..000000000 --- a/crates/math/src/circle/mod.rs +++ /dev/null @@ -1,6 +0,0 @@ -pub mod cfft; -pub mod cosets; -pub mod errors; -pub mod point; -pub mod polynomial; -pub mod twiddles; diff --git a/crates/math/src/circle/point.rs b/crates/math/src/circle/point.rs deleted file mode 100644 index e0e7aa210..000000000 --- a/crates/math/src/circle/point.rs +++ /dev/null @@ -1,302 +0,0 @@ -use super::errors::CircleError; -use crate::field::traits::IsField; -use crate::field::{ - element::FieldElement, - fields::mersenne31::{extensions::Degree4ExtensionField, field::Mersenne31Field}, -}; -use core::ops::{Add, AddAssign, Mul, MulAssign}; - -/// Given a Field F, we implement here the Group which consists of all the points (x, y) such as -/// x in F, y in F and x^2 + y^2 = 1, i.e. the Circle. The operation of the group will have -/// additive notation and is as follows: -/// (a, b) + (c, d) = (a * c - b * d, a * d + b * c) -#[derive(Debug, Clone)] -pub struct CirclePoint { - pub x: FieldElement, - pub y: FieldElement, -} - -impl> CirclePoint { - pub fn new(x: FieldElement, y: FieldElement) -> Result { - if x.square() + y.square() == FieldElement::one() { - Ok(Self { x, y }) - } else { - Err(CircleError::PointDoesntSatisfyCircleEquation) - } - } - - /// Neutral element of the Circle group (with additive notation). - pub fn zero() -> Self { - Self::new(FieldElement::one(), FieldElement::zero()).unwrap() - } - - /// Computes 2(x, y) = (2x^2 - 1, 2xy). - pub fn double(&self) -> Self { - Self::new( - self.x.square().double() - FieldElement::one(), - self.x.double() * self.y.clone(), - ) - .unwrap() - } - - /// Computes 2^n * (x, y). - pub fn repeated_double(self, n: u32) -> Self { - let mut res = self; - for _ in 0..n { - res = res.double(); - } - res - } - - /// Computes the inverse of the point. - /// We are using -(x, y) = (x, -y), i.e. the inverse of the group opertion is conjugation - /// because the norm of every point in the circle is one. - pub fn conjugate(self) -> Self { - Self { - x: self.x, - y: -self.y, - } - } - - pub fn antipode(self) -> Self { - Self { - x: -self.x, - y: -self.y, - } - } - - pub const GENERATOR: Self = Self { - x: F::CIRCLE_GENERATOR_X, - y: F::CIRCLE_GENERATOR_Y, - }; - - /// Returns the generator of the subgroup of order n = 2^log_2_size. - /// We are using that 2^k * g is a generator of the subgroup of order 2^{31 - k}. - pub fn get_generator_of_subgroup(log_2_size: u32) -> Self { - Self::GENERATOR.repeated_double(31 - log_2_size) - } - - pub const ORDER: u128 = F::ORDER; -} - -/// Parameters of the base field that we'll need to define its Circle. -pub trait HasCircleParams { - type FE; - - /// Coordinate x of the generator of the circle group. - const CIRCLE_GENERATOR_X: FieldElement; - - /// Coordinate y of the generator of the circle group. - const CIRCLE_GENERATOR_Y: FieldElement; - - const ORDER: u128; -} - -impl HasCircleParams for Mersenne31Field { - type FE = FieldElement; - - const CIRCLE_GENERATOR_X: Self::FE = Self::FE::const_from_raw(2); - - const CIRCLE_GENERATOR_Y: Self::FE = Self::FE::const_from_raw(1268011823); - - /// ORDER = 2^31 - const ORDER: u128 = 2147483648; -} - -impl HasCircleParams for Degree4ExtensionField { - type FE = FieldElement; - - // These parameters were taken from stwo's implementation: - // https://github.com/starkware-libs/stwo/blob/9cfd48af4e8ac5dd67643a92927c894066fa989c/crates/prover/src/core/circle.rs - const CIRCLE_GENERATOR_X: Self::FE = - Degree4ExtensionField::const_from_coefficients(1, 0, 478637715, 513582971); - - const CIRCLE_GENERATOR_Y: Self::FE = - Degree4ExtensionField::const_from_coefficients(992285211, 649143431, 740191619, 1186584352); - - /// ORDER = (2^31 - 1)^4 - 1 - const ORDER: u128 = 21267647892944572736998860269687930880; -} - -/// Equality between two cricle points. -impl> PartialEq for CirclePoint { - fn eq(&self, other: &Self) -> bool { - self.x == other.x && self.y == other.y - } -} - -/// Addition (i.e. group operation) between two points: -/// (a, b) + (c, d) = (a * c - b * d, a * d + b * c) -impl> Add for &CirclePoint { - type Output = CirclePoint; - fn add(self, other: Self) -> Self::Output { - let x = &self.x * &other.x - &self.y * &other.y; - let y = &self.x * &other.y + &self.y * &other.x; - CirclePoint { x, y } - } -} -impl> Add for CirclePoint { - type Output = CirclePoint; - fn add(self, rhs: CirclePoint) -> Self::Output { - &self + &rhs - } -} -impl> Add> for &CirclePoint { - type Output = CirclePoint; - fn add(self, rhs: CirclePoint) -> Self::Output { - self + &rhs - } -} -impl> Add<&CirclePoint> for CirclePoint { - type Output = CirclePoint; - fn add(self, rhs: &CirclePoint) -> Self::Output { - &self + rhs - } -} -impl> AddAssign<&CirclePoint> for CirclePoint { - fn add_assign(&mut self, rhs: &CirclePoint) { - *self = &*self + rhs; - } -} -impl> AddAssign> for CirclePoint { - fn add_assign(&mut self, rhs: CirclePoint) { - *self += &rhs; - } -} -/// Multiplication between a point and a scalar (i.e. group operation repeatedly): -/// (x, y) * n = (x ,y) + ... + (x, y) n-times. -impl> Mul for &CirclePoint { - type Output = CirclePoint; - fn mul(self, scalar: u128) -> Self::Output { - let mut scalar = scalar; - let mut res = CirclePoint::::zero(); - let mut cur = self.clone(); - loop { - if scalar == 0 { - return res; - } - if scalar & 1 == 1 { - res += &cur; - } - cur = cur.double(); - scalar >>= 1; - } - } -} -impl> Mul for CirclePoint { - type Output = CirclePoint; - fn mul(self, scalar: u128) -> Self::Output { - &self * scalar - } -} -impl> MulAssign for CirclePoint { - fn mul_assign(&mut self, scalar: u128) { - let mut scalar = scalar; - let mut res = CirclePoint::::zero(); - loop { - if scalar == 0 { - *self = res.clone(); - } - if scalar & 1 == 1 { - res += &*self; - } - *self = self.double(); - scalar >>= 1; - } - } -} - -#[cfg(test)] -mod tests { - use super::*; - type F = Mersenne31Field; - type FE = FieldElement; - type G = CirclePoint; - - type Fp4 = Degree4ExtensionField; - type Fp4E = FieldElement; - type G4 = CirclePoint; - - #[test] - fn create_new_valid_g_point() { - let valid_point = G::new(FE::one(), FE::zero()).unwrap(); - let expected = G { - x: FE::one(), - y: FE::zero(), - }; - assert_eq!(valid_point, expected) - } - - #[test] - fn create_new_valid_g4_point() { - let valid_point = G4::new(Fp4E::one(), Fp4E::zero()).unwrap(); - let expected = G4 { - x: Fp4E::one(), - y: Fp4E::zero(), - }; - assert_eq!(valid_point, expected) - } - - #[test] - fn create_new_invalid_circle_point() { - let invalid_point = G::new(FE::one(), FE::one()); - assert!(invalid_point.is_err()) - } - - #[test] - fn create_new_invalid_g4_circle_point() { - let invalid_point = G4::new(Fp4E::one(), Fp4E::one()); - assert!(invalid_point.is_err()) - } - - #[test] - fn zero_plus_zero_is_zero() { - let a = G::zero(); - let b = G::zero(); - assert_eq!(&a + &b, G::zero()) - } - - #[test] - fn generator_plus_zero_is_generator() { - let g = G::GENERATOR; - let zero = G::zero(); - assert_eq!(&g + &zero, g) - } - - #[test] - fn double_equals_mul_two() { - let g = G::GENERATOR; - assert_eq!(g.clone().double(), g * 2) - } - - #[test] - fn mul_eight_equals_double_three_times() { - let g = G::GENERATOR; - assert_eq!(g.clone().repeated_double(3), g * 8) - } - - #[test] - fn generator_g1_has_order_two_pow_31() { - let g = G::GENERATOR; - let n = 31; - assert_eq!(g.repeated_double(n), G::zero()) - } - - #[test] - fn generator_g4_has_the_order_of_the_group() { - let g = G4::GENERATOR; - assert_eq!(g * G4::ORDER, G4::zero()) - } - - #[test] - fn conjugation_is_inverse_operation() { - let g = G::GENERATOR; - assert_eq!(&g.clone() + &g.conjugate(), G::zero()) - } - - #[test] - fn subgroup_generator_has_correct_order() { - let generator_n = G::get_generator_of_subgroup(7); - assert_eq!(generator_n.repeated_double(7), G::zero()); - } -} diff --git a/crates/math/src/circle/polynomial.rs b/crates/math/src/circle/polynomial.rs deleted file mode 100644 index 109ed4e04..000000000 --- a/crates/math/src/circle/polynomial.rs +++ /dev/null @@ -1,310 +0,0 @@ -extern crate alloc; -#[cfg(feature = "alloc")] -use super::{ - cfft::{cfft, icfft, order_cfft_result_naive, order_icfft_input_naive}, - cosets::Coset, - twiddles::{get_twiddles, TwiddlesConfig}, -}; -use crate::{ - fft::cpu::bit_reversing::in_place_bit_reverse_permute, - field::{element::FieldElement, fields::mersenne31::field::Mersenne31Field}, -}; -#[cfg(feature = "alloc")] -use alloc::vec::Vec; - -/// Given the 2^n coefficients of a two-variables polynomial of degree 2^n - 1 in the basis {1, y, x, xy, 2xˆ2 -1, 2xˆ2y-y, 2xˆ3-x, 2xˆ3y-xy,...} -/// returns the evaluation of the polynomial on the points of the standard coset of size 2^n. -/// Note that coeff has to be a vector with length a power of two 2^n. -#[cfg(feature = "alloc")] -pub fn evaluate_cfft( - coeff: Vec>, -) -> Vec> { - let mut coeff = coeff; - - // We get the twiddles for the Evaluation. - let domain_log_2_size: u32 = coeff.len().trailing_zeros(); - let coset = Coset::new_standard(domain_log_2_size); - let config = TwiddlesConfig::Evaluation; - let twiddles = get_twiddles(coset, config); - - // For our algorithm to work, we must give as input the coefficients in bit reverse order. - in_place_bit_reverse_permute::>(&mut coeff); - cfft(&mut coeff, twiddles); - - // The cfft returns the evaluations in a certain order, so we permute them to get the natural order. - order_cfft_result_naive(&coeff) -} - -/// Interpolates the 2^n evaluations of a two-variables polynomial of degree 2^n - 1 on the points of the standard coset of size 2^n. -/// As a result we obtain the coefficients of the polynomial in the basis: {1, y, x, xy, 2xˆ2 -1, 2xˆ2y-y, 2xˆ3-x, 2xˆ3y-xy,...} -/// Note that eval has to be a vector of length a power of two 2^n. -/// If the vector of evaluations is empty, it returns an empty vector. -#[cfg(feature = "alloc")] -pub fn interpolate_cfft( - eval: Vec>, -) -> Vec> { - let mut eval = eval; - - if eval.is_empty() { - let poly: Vec> = Vec::new(); - return poly; - } - - // We get the twiddles for the interpolation. - let domain_log_2_size: u32 = eval.len().trailing_zeros(); - let coset = Coset::new_standard(domain_log_2_size); - let config = TwiddlesConfig::Interpolation; - let twiddles = get_twiddles(coset, config); - - // For our algorithm to work, we must give as input the evaluations ordered in a certain way. - let mut eval_ordered = order_icfft_input_naive(&mut eval); - icfft(&mut eval_ordered, twiddles); - - // The icfft returns the polynomial coefficients in bit reverse order. So we premute it to get the natural order. - in_place_bit_reverse_permute::>(&mut eval_ordered); - - // The icfft returns all the coefficients multiplied by 2^n, the length of the evaluations. - // So we multiply every element that outputs the icfft by the inverse of 2^n to get the actual coefficients. - // Note that this `unwrap` will never panic because eval.len() != 0. - let factor = (FieldElement::::from(eval.len() as u64)) - .inv() - .unwrap(); - eval_ordered.iter().map(|coef| coef * factor).collect() -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::circle::cosets::Coset; - type FE = FieldElement; - use alloc::vec; - - /// Naive evaluation of a polynomial of degree 3. - fn evaluate_poly_4(coef: &[FE; 4], x: FE, y: FE) -> FE { - coef[0] + coef[1] * y + coef[2] * x + coef[3] * x * y - } - - /// Naive evaluation of a polynomial of degree 7. - fn evaluate_poly_8(coef: &[FE; 8], x: FE, y: FE) -> FE { - coef[0] - + coef[1] * y - + coef[2] * x - + coef[3] * x * y - + coef[4] * (x.square().double() - FE::one()) - + coef[5] * (x.square().double() - FE::one()) * y - + coef[6] * ((x.square() * x).double() - x) - + coef[7] * ((x.square() * x).double() - x) * y - } - - /// Naive evaluation of a polynomial of degree 15. - fn evaluate_poly_16(coef: &[FE; 16], x: FE, y: FE) -> FE { - let mut a = x; - let mut v = Vec::new(); - v.push(FE::one()); - v.push(x); - for _ in 2..4 { - a = a.square().double() - FE::one(); - v.push(a); - } - - coef[0] * v[0] - + coef[1] * y * v[0] - + coef[2] * v[1] - + coef[3] * y * v[1] - + coef[4] * v[2] - + coef[5] * y * v[2] - + coef[6] * v[1] * v[2] - + coef[7] * y * v[1] * v[2] - + coef[8] * v[3] - + coef[9] * y * v[3] - + coef[10] * v[1] * v[3] - + coef[11] * y * v[1] * v[3] - + coef[12] * v[2] * v[3] - + coef[13] * y * v[2] * v[3] - + coef[14] * v[1] * v[2] * v[3] - + coef[15] * y * v[1] * v[2] * v[3] - } - - #[test] - /// cfft evaluation equals naive evaluation. - fn cfft_evaluation_4_points() { - // We define the coefficients of a polynomial of degree 3. - let input = [FE::from(1), FE::from(2), FE::from(3), FE::from(4)]; - - // We create the coset points and evaluate the polynomial with the naive function. - let coset = Coset::new_standard(2); - let points = Coset::get_coset_points(&coset); - let mut expected_result: Vec = Vec::new(); - for point in points { - let point_eval = evaluate_poly_4(&input, point.x, point.y); - expected_result.push(point_eval); - } - - let input_vec = input.to_vec(); - // We evaluate the polynomial using now the cfft. - let result = evaluate_cfft(input_vec); - let slice_result: &[FE] = &result; - - assert_eq!(slice_result, expected_result); - } - - #[test] - /// cfft evaluation equals naive evaluation. - fn cfft_evaluation_8_points() { - // We define the coefficients of a polynomial of degree 7. - let input = [ - FE::from(1), - FE::from(2), - FE::from(3), - FE::from(4), - FE::from(5), - FE::from(6), - FE::from(7), - FE::from(8), - ]; - - // We create the coset points and evaluate them without the fft. - let coset = Coset::new_standard(3); - let points = Coset::get_coset_points(&coset); - let mut expected_result: Vec = Vec::new(); - for point in points { - let point_eval = evaluate_poly_8(&input, point.x, point.y); - expected_result.push(point_eval); - } - - // We evaluate the polynomial using now the cfft. - let result = evaluate_cfft(input.to_vec()); - let slice_result: &[FE] = &result; - - assert_eq!(slice_result, expected_result); - } - - #[test] - /// cfft evaluation equals naive evaluation. - fn cfft_evaluation_16_points() { - // We define the coefficients of a polynomial of degree 15. - let input = [ - FE::from(1), - FE::from(2), - FE::from(3), - FE::from(4), - FE::from(5), - FE::from(6), - FE::from(7), - FE::from(8), - FE::from(9), - FE::from(10), - FE::from(11), - FE::from(12), - FE::from(13), - FE::from(14), - FE::from(15), - FE::from(16), - ]; - - // We create the coset points and evaluate them without the fft. - let coset = Coset::new_standard(4); - let points = Coset::get_coset_points(&coset); - let mut expected_result: Vec = Vec::new(); - for point in points { - let point_eval = evaluate_poly_16(&input, point.x, point.y); - expected_result.push(point_eval); - } - - // We evaluate the polynomial using now the cfft. - let result = evaluate_cfft(input.to_vec()); - let slice_result: &[FE] = &result; - - assert_eq!(slice_result, expected_result); - } - - #[test] - fn evaluate_and_interpolate_8_points_is_identity() { - // We define the 8 coefficients of a polynomial of degree 7. - let coeff = vec![ - FE::from(1), - FE::from(2), - FE::from(3), - FE::from(4), - FE::from(5), - FE::from(6), - FE::from(7), - FE::from(8), - ]; - let evals = evaluate_cfft(coeff.clone()); - let new_coeff = interpolate_cfft(evals); - - assert_eq!(coeff, new_coeff); - } - - #[test] - fn evaluate_and_interpolate_8_other_points() { - let coeff = vec![ - FE::from(2147483650), - FE::from(147483647), - FE::from(2147483700), - FE::from(2147483647), - FE::from(3147483647), - FE::from(4147483647), - FE::from(2147483640), - FE::from(5147483647), - ]; - let evals = evaluate_cfft(coeff.clone()); - let new_coeff = interpolate_cfft(evals); - - assert_eq!(coeff, new_coeff); - } - - #[test] - fn evaluate_and_interpolate_32_points() { - // We define 32 coefficients of a polynomial of degree 31. - let coeff = vec![ - FE::from(1), - FE::from(2), - FE::from(3), - FE::from(4), - FE::from(5), - FE::from(6), - FE::from(7), - FE::from(8), - FE::from(9), - FE::from(10), - FE::from(11), - FE::from(12), - FE::from(13), - FE::from(14), - FE::from(15), - FE::from(16), - FE::from(17), - FE::from(18), - FE::from(19), - FE::from(20), - FE::from(21), - FE::from(22), - FE::from(23), - FE::from(24), - FE::from(25), - FE::from(26), - FE::from(27), - FE::from(28), - FE::from(29), - FE::from(30), - FE::from(31), - FE::from(32), - ]; - let evals = evaluate_cfft(coeff.clone()); - let new_coeff = interpolate_cfft(evals); - - assert_eq!(coeff, new_coeff); - } - - #[test] - fn evaluate_and_interpolate_2_pow_20_other_points() { - let coeff: Vec> = - (0..2_u32.pow(20)).map(|i| FE::from(&i)).collect(); - let evals = evaluate_cfft(coeff.clone()); - let new_coeff = interpolate_cfft(evals); - - assert_eq!(coeff, new_coeff); - } -} diff --git a/crates/math/src/circle/twiddles.rs b/crates/math/src/circle/twiddles.rs deleted file mode 100644 index ef63c66f6..000000000 --- a/crates/math/src/circle/twiddles.rs +++ /dev/null @@ -1,83 +0,0 @@ -extern crate alloc; -use crate::{ - circle::cosets::Coset, - field::{element::FieldElement, fields::mersenne31::field::Mersenne31Field}, -}; -use alloc::vec::Vec; - -#[derive(PartialEq)] -pub enum TwiddlesConfig { - Evaluation, - Interpolation, -} -#[cfg(feature = "alloc")] -pub fn get_twiddles( - domain: Coset, - config: TwiddlesConfig, -) -> Vec>> { - // We first take the half coset. - let half_domain_points = Coset::get_coset_points(&Coset::half_coset(domain.clone())); - - // The first set of twiddles are all the y coordinates of the half coset. - let mut twiddles: Vec>> = Vec::new(); - twiddles.push(half_domain_points.iter().map(|p| p.y).collect()); - - if domain.log_2_size >= 2 { - // The second set of twiddles are the x coordinates of the first half of the half coset. - twiddles.push( - half_domain_points - .iter() - .take(half_domain_points.len() / 2) - .map(|p| p.x) - .collect(), - ); - for _ in 0..(domain.log_2_size - 2) { - // The rest of the sets of twiddles are the "square" of the x coordinates of the first half of the previous set. - let prev = twiddles.last().unwrap(); - let cur = prev - .iter() - .take(prev.len() / 2) - .map(|x| x.square().double() - FieldElement::::one()) - .collect(); - twiddles.push(cur); - } - } - - if config == TwiddlesConfig::Interpolation { - // For the interpolation, we need to take the inverse element of each twiddle in the default order. - // We can take inverse being sure that the `unwrap` won't panic because the twiddles are coordinates - // of elements of the coset (or their squares) so they can't be zero. - twiddles.iter_mut().for_each(|x| { - FieldElement::::inplace_batch_inverse(x).unwrap(); - }); - } else { - // For the evaluation, we need reverse the order of the vector of twiddles. - twiddles.reverse(); - } - twiddles -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn evaluation_twiddles_vectors_length_is_correct() { - let domain = Coset::new_standard(20); - let config = TwiddlesConfig::Evaluation; - let twiddles = get_twiddles(domain, config); - for i in 0..twiddles.len() - 1 { - assert_eq!(2 * twiddles[i].len(), twiddles[i + 1].len()) - } - } - - #[test] - fn interpolation_twiddles_vectors_length_is_correct() { - let domain = Coset::new_standard(20); - let config = TwiddlesConfig::Interpolation; - let twiddles = get_twiddles(domain, config); - for i in 0..twiddles.len() - 1 { - assert_eq!(twiddles[i].len(), 2 * twiddles[i + 1].len()) - } - } -} diff --git a/crates/math/src/cyclic_group.rs b/crates/math/src/cyclic_group.rs deleted file mode 100644 index 8a2d66ffa..000000000 --- a/crates/math/src/cyclic_group.rs +++ /dev/null @@ -1,40 +0,0 @@ -use crate::unsigned_integer::traits::IsUnsignedInteger; - -pub trait IsGroup: Clone + PartialEq + Eq { - /// Returns the neutral element of the group. The equality - /// `neutral_element().operate_with(g) == g` must hold - /// for every group element `g`. - fn neutral_element() -> Self; - - /// Check if an element the neutral element. - fn is_neutral_element(&self) -> bool { - self == &Self::neutral_element() - } - - /// Applies the group operation `times` times with itself - /// The operation can be addition or multiplication depending on - /// the notation of the particular group. - fn operate_with_self(&self, mut exponent: T) -> Self { - let mut result = Self::neutral_element(); - let mut base = self.clone(); - - while exponent != T::from(0) { - if exponent & T::from(1) == T::from(1) { - result = Self::operate_with(&result, &base); - } - exponent >>= 1; - base = Self::operate_with(&base, &base); - } - result - } - - /// Applies the group operation between `self` and `other`. - /// The operation can be addition or multiplication depending on - /// the notation of the particular group. - fn operate_with(&self, other: &Self) -> Self; - - /// Provides the inverse of the group element. - /// This is the unique y such that for any x - /// x.operate_with(y) returns the neutral element - fn neg(&self) -> Self; -} diff --git a/crates/math/src/elliptic_curve/README.md b/crates/math/src/elliptic_curve/README.md deleted file mode 100644 index 77957bdf0..000000000 --- a/crates/math/src/elliptic_curve/README.md +++ /dev/null @@ -1,179 +0,0 @@ -# Elliptic curves - -This folder contains the different elliptic curve models currently supported by lambdaworks. For an overview of the curve models, their addition formulas and coordinate systems, see [Hyperelliptic](https://hyperelliptic.org/EFD/g1p/index.html). The models currently supported are: -- [Short Weierstrass](./short_weierstrass/) -- [Twisted Edwards](./edwards/) -- [Montgomery](./montgomery/) - -Each of the curve models can have one or more coordinate systems, such as homogeneous projective, Jacobian, XZ coordinates, etc. These are used for reasons of performance. It is possible to define an operation, $\oplus$, taking two points over an elliptic curve, $E$ and obtain a third one, such that $(E, \oplus)$ is a group. - -This part makes use of lambdaworks finite fields. If you are unfamiliar with it or underlying concepts, refer to the [section on finite fields](../field/README.md). - -## Short Weierstrass - -The following curves are currently supported: -- [BLS12-377](./short_weierstrass/curves/bls12_377/), a pairing-friendly elliptic curve (pairing implementation pending). -- [BLS12-381](./short_weierstrass/curves/bls12_381/), a pairing-friendly elliptic curve. -- [BN-254](./short_weierstrass/curves/bn_254/), a pairing-friendly elliptic curve. Used on Ethereum. -- [Grumpkin](./short_weierstrass/curves/grumpkin/), an elliptic curve that forms a two-cycle with BN-254. This means that the base field for Grumpkin (where the coordinates $x,y$ live) is the scalar field of BN-254 (the field with order equal to the order of the group of the elliptic curve), and the scalar field for Grumpkin is the base field of BN-254. -- [Pallas](./short_weierstrass/curves/pallas/), useful for recursive SNARKs when used with Vesta. -- [Vesta](./short_weierstrass/curves/vesta/), useful for recursive SNARKs when used with Pallas. -- [Starknet's curve](./short_weierstrass/curves/stark_curve.rs) -- [secp256k1](./short_weierstrass/curves/secp256k1/curve.rs): Bitcoin's curve. The implementation is not constant time, so it cannot be used to sign messages! -- [secq256k1](./short_weierstrass/curves/secq256k1/curve.rs): It has the same curve equation as secp256k1, a different generator and their order r and the modulus p are swapped. It uses ```secp256k1_scalarfield``` as a base field, which has modulus r. -- [secp256r1](./short_weierstrass/curves/secp256r1/curve.rs): Used for digital signatures, also known as: P-256 and prime256v1. - -## Twisted Edwards - -The following curves are currently supported: -- [Ed448Goldilocks](./edwards/curves/ed448_goldilocks.rs) -- [Bandersnatch](./edwards/curves/bandersnatch/) -- [TinyJubJub](./edwards/curves/tiny_jub_jub.rs), only for learning purposes. - -## Montgomery - -The following curves are currently supported: -- [TinyJubJub](./montgomery/curves/tiny_jub_jub.rs), only for learning purposes. - -## Implementing Elliptic Curves in lambdaworks - -In order to define your elliptic curve in lambdaworks, you need to implement some traits: -- `IsEllipticCurve` -- `IsShortWeierstrass` -- `IsEdwards` -- `IsMontgomery` - -To create an elliptic curve in Short Weiestrass form, we have to implement the traits `IsEllipticCurve` and `IsShortWeierstrass` (If you want a twisted Edwards, use `IsEdwards`. For Montgomery form, use `IsMontgomery`). Below we show how the Pallas curve is defined: -```rust -#[derive(Clone, Debug)] -pub struct PallasCurve; - -impl IsEllipticCurve for PallasCurve { - type BaseField = Pallas255PrimeField; - type PointRepresentation = ShortWeierstrassProjectivePoint; - - fn generator() -> Self::PointRepresentation { - Self::PointRepresentation::new([ - -FieldElement::::one(), - FieldElement::::from(2), - FieldElement::one(), - ]) - } -} - -impl IsShortWeierstrass for PallasCurve { - fn a() -> FieldElement { - FieldElement::from(0) - } - - fn b() -> FieldElement { - FieldElement::from(5) - } -} -``` - -Here, $a$ and $b$ are the parameters for the Elliptic Curve in Weiestrass form. All curve models have their `defining_equation` method, which allows us to check whether a given $(x,y)$ belongs to the elliptic curve. The `BaseField` is where the coordinates $x,y$ of the curve live. `generator()` gives a point $P$, such that, by doing $P, 2P, 3P, ... , nP$ ($2 P = P \oplus P$) we span all the elements that belong to the Elliptic Curve. - -To implement the `IsShortWeierstrass`, you need to first implement `IsEllipticCurve`. It has 3 methods, two of which need to be implemented for each curve `fn a()` and `fn b()` (the curve parameters) and `fn defining_equation()`, which computes $y^2 - x^3 - a x - b$. If this result is equal to the base field element 0, then the point satisfies the curve equation and is valid (note, however, that the point may not be in the appropriate subgroup of the curve!) - -## Defining points and operating with the curves - -All curves implement the trait `FromAffine`, which lets us define points by providing the pair of values $(x,y)$, where $x$ and $y$ should be in the `BaseField` of the curve. For example -```rust -let x = FE::from_hex_unchecked( - "bd1e740e6b1615ae4c508148ca0c53dbd43f7b2e206195ab638d7f45d51d6b5", - ); -let y = FE::from_hex_unchecked( - "13aacd107ca10b7f8aab570da1183b91d7d86dd723eaa2306b0ef9c5355b91d8", - ); -PallasCurve::create_point_from_affine(x, y).unwrap() -``` -The function has to check whether the point is valid, and, if not, returns an error. - -Each form and coordinate model has to implement the `IsGroup` trait, which will give us all the necessary operations for the group. We need to provide expressions for: -- `fn neutral_element()`, the neutral element for the group operation. In the case of elliptic curves, this is the point at infinity. -- `fn operate_with`, which defines the group operation; it takes two elements in the group and outputs a third one. -- `fn neg`, which gives the inverse of the element. -It also provides the method `fn operate_with_self`, which is used to indicate that repeteadly add one element against itself $n$ times. Here, $n$ should implement the `IsUnsignedInteger` trait. In the case of elliptic curves, this provides the scalar multiplication, $n P$, based on the double and add algorithm (square and multiply). - -Operating is done in the following way: -```rust -// We get a point -let g = PallasCurve::generator(); -let g2 = g.operate_with_self(2_u16); -let g3 = g.operate_with_other(&g2); -``` -`operate_with_self` takes as argument anything that implements the `IsUnsignedInteger` trait. `operate_with_other` takes as argument another point in the elliptic curve. When we operate this way, the $z$ coordinate in the result may be different from $1$. We can transform it back to affine form by using `to_affine`. For example, -```rust -let g = BLS12381Curve::generator(); -let g2 = g.operate_with_self(2_u64); - -// get x and y from affine coordinates -let g2_affine = g2.to_affine(); -let x = g2_affine.x(); -let y = g2_affine.y(); -``` - -## Multiscalar multiplication - -One common operation for different proof systems is the Mutiscalar Multiplication (MSM), which is given by a set of points $P_0 , P_1 , P_2 , ... , P_n$ and scalars $a_0 , a_1 , a_2 ... n_n$ (the scalars belong to the scalar field of the elliptic curve, which is the field whose size matches the size of the elliptic curve's group): -$$R = \sum_k a_k P_k$$ -This operation could be implemented by using `operate_with_self` with each point and scalar and then add the results using `operate_with`, but this is not efficient. lambdaworks provides an optimized [MSM using Pippenger's algorithm](../msm/pippenger.rs). A naïve version is given [here](../msm/naive.rs). Below we show how to use MSM in the context of a polynomial commitment scheme: the scalars are the coefficients of the polynomials and the points are provided by an SRS. -```rust -fn commit(&self, p: &Polynomial>) -> Self::Commitment { - let coefficients: Vec<_> = p - .coefficients - .iter() - .map(|coefficient| coefficient.representative()) - .collect(); - msm( - &coefficients, - &self.srs.powers_main_group[..coefficients.len()], - ) - .expect("`points` is sliced by `cs`'s length") - } -``` - -## Pairing-friendly elliptic curves - -Pairings are an important calculation for BLS signatures and the KZG polynomial commitment scheme. These are functions mapping elements from groups of order $r$ belonging to an elliptic curve to the set of $r$-th roots of unity, $e: G_1 \times G_2 \rightarrow G_t$. They satisfy two properties: -1. Bilinearity -2. Non-degeneracy -Not all elliptic curves have efficiently computable pairings. If the curve is pairing-friendly, we can implement the trait `IsPairing`. Examples of pairing-friendly curves are BLS12-381, BLS12-377, BN254. Curves such as Pallas, Vesta, secp256k1 are not pairing-friendly. For an explanation of pairings, see our [blogpost](https://blog.lambdaclass.com/how-we-implemented-the-bn254-ate-pairing-in-lambdaworks/). - -The pairing function takes pairs of points $(a , b)$, where $a \in G_1$ (formed by coordinates $x,y$ taking values on the base field ${F_p}$) and $b \in G_2$ (formed by coordinates $x,y$ taking values in $F_{ p^2 }$, a quadratic extension of the base field) and outputs an element in the $r$-th roots of unity of $F_{ p^k }$. To use the pairing, provide a slice of pairs and call the function `compute_batch`. For example, -```rust -let p = BN254Curve::generator(); -let q = BN254TwistCurve::generator(); -let pairing_result = BN254AtePairing::compute_batch(&[(&p, &q)]).unwrap(); -``` - -## Elliptic curve point compression and decompression - -Elliptic curve points have to satisfy their defining equation; a pair $(x , y)$ is on the curve if and only if $y^2 = x^3 + ax + b$ for curves defined using the Short Weierstrass form. However, given $x$, we can obtain $y$ up to sign: - -$$y = \pm \sqrt{x^3 + ax + b}$$ - -This allows us to reduce the amount of information we send to define a unique point on the curve. It suffices to specify $x$ and which of the two values (either $-$ or $+$) we should take. This comes pretty handy when we want to send and store signatures or store the structured reference string (SRS) for a proof system, which in general is a large collection of points. We say that points are given in compressed form if we provide the $x$ coordinate and an additional bit to decide which of the two roots we should choose (in real numbers, it would be easy to know which one is the positive and the negative. In finite fields, we select the lexicographically largest or smallest. This means that if we are working modulo $17$ and we want to compute $\sqrt{4}$, we have $2$ and $15$ and $15$ is the lexicographically greatest). - -In many curves, the base field contains some spare bits (as is the case of BLS12-381 or BN254, but not secp256k1), which allows us to codify the extra bit into the free bits of the element. Depending on the number of spare bits, we could compress points in different ways. - -Pairings and elliptic curve point compression and decompression are needed for [BLS signatures](https://www.ietf.org/archive/id/draft-irtf-cfrg-bls-signature-05.html) and in [EIP-4844](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-4844.md). - -## References - -- [HyperElliptic - formulae for EC addition and doubling](https://hyperelliptic.org/EFD/g1p/index.html) -- [An introduction to mathematical cryptography](https://books.google.com.ar/books/about/An_Introduction_to_Mathematical_Cryptogr.html?id=XLY9AnfDhsYC&source=kp_book_description&redir_esc=y) -- [Constantine](https://github.com/mratsim/constantine/tree/master/constantine/math) -- [BN-254 for the rest of us](https://hackmd.io/@jpw/bn254) -- [BLS12-381 for the rest of us](https://hackmd.io/@benjaminion/bls12-381) -- [High-speed implementation of the Optimal Ate Pairing over Barreto-Naehrig curves](https://eprint.iacr.org/2010/354.pdf) -- [Computing the optimal pairing over the BN254 curve](https://hackmd.io/@Wimet/ry7z1Xj-2#Computing-the-Optimal-Ate-Pairing-over-the-BN254-Curve) -- [Pairings for beginners](https://static1.squarespace.com/static/5fdbb09f31d71c1227082339/t/5ff394720493bd28278889c6/1609798774687/PairingsForBeginners.pdf) -- [Pairing-friendly elliptic curves of prime order](https://www.cryptojedi.org/papers/pfcpo.pdf) -- [Need for speed: elliptic curves chapter](https://blog.lambdaclass.com/need-for-speed-elliptic-curves-chapter/) -- [What every developer needs to know about elliptic curves](https://blog.lambdaclass.com/what-every-developer-needs-to-know-about-elliptic-curves/) -- [How we implemented the BN254 Ate pairing in lambdaworks](https://blog.lambdaclass.com/how-we-implemented-the-bn254-ate-pairing-in-lambdaworks/) -- [Exploring elliptic curve pairings by Vitalik](https://medium.com/@VitalikButerin/exploring-elliptic-curve-pairings-c73c1864e627) -- [A survey of elliptic curves for proof systems](https://eprint.iacr.org/2022/586.pdf) -- [Taxonomy of pairing-friendly elliptic curves](https://eprint.iacr.org/2006/372.pdf) diff --git a/crates/math/src/elliptic_curve/edwards/curves/bandersnatch/curve.rs b/crates/math/src/elliptic_curve/edwards/curves/bandersnatch/curve.rs deleted file mode 100644 index a8c413399..000000000 --- a/crates/math/src/elliptic_curve/edwards/curves/bandersnatch/curve.rs +++ /dev/null @@ -1,146 +0,0 @@ -pub use super::field::FqField; -use crate::elliptic_curve::edwards::point::EdwardsProjectivePoint; -use crate::elliptic_curve::traits::IsEllipticCurve; -use crate::{elliptic_curve::edwards::traits::IsEdwards, field::element::FieldElement}; - -pub type BaseBandersnatchFieldElement = FqField; - -#[derive(Clone, Debug)] -pub struct BandersnatchCurve; - -impl IsEllipticCurve for BandersnatchCurve { - type BaseField = BaseBandersnatchFieldElement; - type PointRepresentation = EdwardsProjectivePoint; - - /// Returns the generator point of the Bandersnatch curve. - /// - /// The generator point is defined with coordinates `(x, y, 1)`, where `x` and `y` - /// are precomputed constants that belong to the curve. - /// - /// # Safety - /// - /// - The generator values are taken from the [Arkworks implementation](https://github.com/arkworks-rs/curves/blob/5a41d7f27a703a7ea9c48512a4148443ec6c747e/ed_on_bls12_381_bandersnatch/src/curves/mod.rs#L120) - /// and have been converted to hexadecimal. - /// - `unwrap()` does not panic because: - /// - The generator point is **known to be valid** on the curve. - /// - The function only uses **hardcoded** and **verified** constants. - /// - This function should **never** be modified unless the new generator is fully verified. - fn generator() -> Self::PointRepresentation { - // SAFETY: - // - The generator point coordinates (x, y) are taken from a well-tested, - // verified implementation. - // - The constructor will only fail if the values are invalid, which is - // impossible given that they are constants taken from a trusted source. - let point = Self::PointRepresentation::new([ - FieldElement::::new_base( - "29C132CC2C0B34C5743711777BBE42F32B79C022AD998465E1E71866A252AE18", - ), - FieldElement::::new_base( - "2A6C669EDA123E0F157D8B50BADCD586358CAD81EEE464605E3167B6CC974166", - ), - FieldElement::one(), - ]); - point.unwrap() - } -} - -impl IsEdwards for BandersnatchCurve { - fn a() -> FieldElement { - FieldElement::::new_base( - "73EDA753299D7D483339D80809A1D80553BDA402FFFE5BFEFFFFFFFEFFFFFFFC", - ) - } - - fn d() -> FieldElement { - FieldElement::::new_base( - "6389C12633C267CBC66E3BF86BE3B6D8CB66677177E54F92B369F2F5188D58E7", - ) - } -} - -#[cfg(test)] -mod tests { - - use super::*; - use crate::{ - cyclic_group::IsGroup, elliptic_curve::traits::EllipticCurveError, - field::element::FieldElement, unsigned_integer::element::U256, - }; - - #[allow(clippy::upper_case_acronyms)] - type FEE = FieldElement; - - fn point_1() -> EdwardsProjectivePoint { - let x = FEE::new_base("29C132CC2C0B34C5743711777BBE42F32B79C022AD998465E1E71866A252AE18"); - let y = FEE::new_base("2A6C669EDA123E0F157D8B50BADCD586358CAD81EEE464605E3167B6CC974166"); - - BandersnatchCurve::create_point_from_affine(x, y).unwrap() - } - - #[test] - fn test_scalar_mul() { - let g = BandersnatchCurve::generator(); - let result1 = g.operate_with_self(5u16); - - assert_eq!( - result1.x().clone(), - FEE::new_base("68CBECE0B8FB55450410CBC058928A567EED293D168FAEF44BFDE25F943AABE0") - ); - - let scalar = - U256::from_hex("1CFB69D4CA675F520CCE760202687600FF8F87007419047174FD06B52876E7E6") - .unwrap(); - let result2 = g.operate_with_self(scalar); - - assert_eq!( - result2.x().clone(), - FEE::new_base("68CBECE0B8FB55450410CBC058928A567EED293D168FAEF44BFDE25F943AABE0") - ); - } - - #[test] - fn test_create_valid_point_works() { - let p = BandersnatchCurve::generator(); - - assert_eq!(p, p.clone()); - } - - #[test] - fn create_valid_point_works() { - let p = point_1(); - assert_eq!( - *p.x(), - FEE::new_base("29C132CC2C0B34C5743711777BBE42F32B79C022AD998465E1E71866A252AE18") - ); - assert_eq!( - *p.y(), - FEE::new_base("2A6C669EDA123E0F157D8B50BADCD586358CAD81EEE464605E3167B6CC974166") - ); - assert_eq!(*p.z(), FEE::new_base("1")); - } - - #[test] - fn create_invalid_points_panics() { - assert_eq!( - BandersnatchCurve::create_point_from_affine(FEE::from(1), FEE::from(1)).unwrap_err(), - EllipticCurveError::InvalidPoint - ) - } - - #[test] - fn equality_works() { - let g = BandersnatchCurve::generator(); - let g2 = g.operate_with(&g); - assert_ne!(&g2, &g); - assert_eq!(&g, &g); - } - - #[test] - fn operate_with_self_works_1() { - let g = BandersnatchCurve::generator(); - assert_eq!( - g.operate_with(&g).operate_with(&g), - g.operate_with_self(3_u16) - ); - } -} diff --git a/crates/math/src/elliptic_curve/edwards/curves/bandersnatch/field.rs b/crates/math/src/elliptic_curve/edwards/curves/bandersnatch/field.rs deleted file mode 100644 index 1634df248..000000000 --- a/crates/math/src/elliptic_curve/edwards/curves/bandersnatch/field.rs +++ /dev/null @@ -1,28 +0,0 @@ -//! Base field of bandersantch -- which is also the scalar field of BLS12-381 curve. - -use crate::{ - field::{ - element::FieldElement, - fields::montgomery_backed_prime_fields::{IsModulus, MontgomeryBackendPrimeField}, - }, - unsigned_integer::element::U256, -}; - -pub const BANDERSNATCH_PRIME_FIELD_ORDER: U256 = - U256::from_hex_unchecked("73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001"); - -#[derive(Clone, Debug)] -pub struct FqConfig; - -impl IsModulus for FqConfig { - const MODULUS: U256 = BANDERSNATCH_PRIME_FIELD_ORDER; -} - -pub type FqField = MontgomeryBackendPrimeField; - -impl FieldElement { - pub fn new_base(a_hex: &str) -> Self { - Self::new(U256::from(a_hex)) - } -} -pub type FqElement = FieldElement; diff --git a/crates/math/src/elliptic_curve/edwards/curves/bandersnatch/mod.rs b/crates/math/src/elliptic_curve/edwards/curves/bandersnatch/mod.rs deleted file mode 100644 index f773c8d78..000000000 --- a/crates/math/src/elliptic_curve/edwards/curves/bandersnatch/mod.rs +++ /dev/null @@ -1,2 +0,0 @@ -pub mod curve; -pub mod field; diff --git a/crates/math/src/elliptic_curve/edwards/curves/ed448_goldilocks.rs b/crates/math/src/elliptic_curve/edwards/curves/ed448_goldilocks.rs deleted file mode 100644 index 0b6bd046c..000000000 --- a/crates/math/src/elliptic_curve/edwards/curves/ed448_goldilocks.rs +++ /dev/null @@ -1,113 +0,0 @@ -use crate::{ - elliptic_curve::{ - edwards::{point::EdwardsProjectivePoint, traits::IsEdwards}, - traits::IsEllipticCurve, - }, - field::{element::FieldElement, fields::p448_goldilocks_prime_field::P448GoldilocksPrimeField}, -}; - -#[derive(Debug, Clone)] -pub struct Ed448Goldilocks; - -impl IsEllipticCurve for Ed448Goldilocks { - type BaseField = P448GoldilocksPrimeField; - type PointRepresentation = EdwardsProjectivePoint; - - /// Returns the generator point of the Ed448-Goldilocks curve. - /// - /// This generator is taken from [RFC 7748](https://www.rfc-editor.org/rfc/rfc7748#page-6). - /// - /// # Safety - /// - /// - The generator coordinates `(x, y, 1)` are well-known, predefined constants. - /// - `unwrap()` is used because the values are **known to be valid** points - /// on the Ed448-Goldilocks curve. - /// - This function must **not** be modified unless new constants are mathematically verified. - fn generator() -> Self::PointRepresentation { - // SAFETY: - // - These values are taken from RFC 7748 and are known to be valid. - // - `unwrap()` is safe because `new()` will only fail if the point is - // invalid, which is **not possible** with hardcoded, verified values. - let point= Self::PointRepresentation::new([ - FieldElement::::from_hex("4f1970c66bed0ded221d15a622bf36da9e146570470f1767ea6de324a3d3a46412ae1af72ab66511433b80e18b00938e2626a82bc70cc05e").unwrap(), - FieldElement::::from_hex("693f46716eb6bc248876203756c9c7624bea73736ca3984087789c1e05a0c2d73ad3ff1ce67c39c4fdbd132c4ed7c8ad9808795bf230fa14").unwrap(), - FieldElement::one(), - ]); - point.unwrap() - } -} - -impl IsEdwards for Ed448Goldilocks { - fn a() -> FieldElement { - FieldElement::one() - } - - fn d() -> FieldElement { - -FieldElement::from(39081) - } -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::{ - cyclic_group::IsGroup, elliptic_curve::traits::EllipticCurveError, - field::element::FieldElement, - }; - - #[allow(clippy::upper_case_acronyms)] - type FE = FieldElement; - - fn generator_times_5() -> EdwardsProjectivePoint { - let x = FE::from_hex("7a9f9335a48dcb0e2ba7601eedb50def80cbcf728562ada756d761e8958812808bc0d57a920c3c96f07b2d8cefc6f950d0a99d1092030034").unwrap(); - let y = FE::from_hex("adfd751a2517edd3b9109ce4fd580ade260ca1823ab18fced86551f7b698017127d7a4ee59d2b33c58405512881f225443b4731472f435eb").unwrap(); - Ed448Goldilocks::create_point_from_affine(x, y).unwrap() - } - - fn point_1() -> EdwardsProjectivePoint { - let x = FE::from_hex("c591e0987244569fbb80a8edda5f9c5b30d9e7862acbb19ac0f24b766fe29c5e5782efc0e7a169f0c55c5524f8a9f9333ca985ec56404926").unwrap(); - let y = FE::from_hex("f969573bf05f19ac5824718d7483d3b83a3ece5847e25487ae2a176290ad2b1cb9c7f4dd55f6ca6c50209556d7fc16e0683c3177356ac9bc").unwrap(); - Ed448Goldilocks::create_point_from_affine(x, y).unwrap() - } - - fn point_1_times_7() -> EdwardsProjectivePoint { - let x = FE::from_hex("63c9cd1d79f027458015c2013fc819dd0f46f71c21a11fee0c32998acd17bac5b06d0f2f1e1539cfc33223a6e989b2b119dae9bbb16c3743").unwrap(); - let y = FE::from_hex("654de66ab8be9fbeec6e72798a0ba2bb39c1888b99104de6cb0acf4516ea5e018bd292a1855f0fea673a5d8e8724d1b19ca52817db624f06").unwrap(); - Ed448Goldilocks::create_point_from_affine(x, y).unwrap() - } - - #[test] - fn generator_satisfies_defining_equation() { - let g = Ed448Goldilocks::generator().to_affine(); - assert_eq!(Ed448Goldilocks::defining_equation(g.x(), g.y()), FE::zero()); - } - - #[test] - fn adding_generator_five_times_works() { - let g_times_5 = Ed448Goldilocks::generator().operate_with_self(5_u16); - assert_eq!(g_times_5, generator_times_5()); - } - - #[test] - fn adding_point_1_seven_times_works() { - let point_1 = point_1(); - let point_1_times_7 = point_1_times_7(); - assert_eq!(point_1.operate_with_self(7_u16), point_1_times_7); - } - - #[test] - fn create_valid_point_works() { - let p = point_1(); - assert_eq!(*p.x(), FE::from_hex("c591e0987244569fbb80a8edda5f9c5b30d9e7862acbb19ac0f24b766fe29c5e5782efc0e7a169f0c55c5524f8a9f9333ca985ec56404926").unwrap()); - assert_eq!(*p.y(), FE::from_hex("f969573bf05f19ac5824718d7483d3b83a3ece5847e25487ae2a176290ad2b1cb9c7f4dd55f6ca6c50209556d7fc16e0683c3177356ac9bc").unwrap()); - assert_eq!(*p.z(), FE::one()); - } - - #[test] - fn create_invalid_points_panics() { - assert_eq!( - Ed448Goldilocks::create_point_from_affine(FE::from(1), FE::from(1)).unwrap_err(), - EllipticCurveError::InvalidPoint - ) - } -} diff --git a/crates/math/src/elliptic_curve/edwards/curves/mod.rs b/crates/math/src/elliptic_curve/edwards/curves/mod.rs deleted file mode 100644 index f0fc03b7a..000000000 --- a/crates/math/src/elliptic_curve/edwards/curves/mod.rs +++ /dev/null @@ -1,3 +0,0 @@ -pub mod bandersnatch; -pub mod ed448_goldilocks; -pub mod tiny_jub_jub; diff --git a/crates/math/src/elliptic_curve/edwards/curves/tiny_jub_jub.rs b/crates/math/src/elliptic_curve/edwards/curves/tiny_jub_jub.rs deleted file mode 100644 index df4106051..000000000 --- a/crates/math/src/elliptic_curve/edwards/curves/tiny_jub_jub.rs +++ /dev/null @@ -1,48 +0,0 @@ -use crate::{ - elliptic_curve::{ - edwards::{point::EdwardsProjectivePoint, traits::IsEdwards}, - traits::IsEllipticCurve, - }, - field::{element::FieldElement, fields::u64_prime_field::U64PrimeField}, -}; - -/// Taken from moonmath manual page 97 -#[derive(Debug, Clone)] -pub struct TinyJubJubEdwards; - -impl IsEllipticCurve for TinyJubJubEdwards { - type BaseField = U64PrimeField<13>; - type PointRepresentation = EdwardsProjectivePoint; - - /// Returns the generator point of the TinyJubJub Edwards curve. - /// - /// This generator is taken from **Moonmath Manual (page 97)**. - /// - /// # Safety - /// - /// - The generator coordinates `(8, 5, 1)` are **predefined** and belong to the TinyJubJub curve. - /// - `unwrap()` is used because the generator is a **verified valid point**, - /// meaning there is **no risk** of runtime failure. - /// - This function must **not** be modified unless the new generator is mathematically verified. - fn generator() -> Self::PointRepresentation { - // SAFETY: - // - The generator point `(8, 5, 1)` is **mathematically valid** on the curve. - // - `unwrap()` is safe because we **know** the point satisfies the curve equation. - let point = Self::PointRepresentation::new([ - FieldElement::from(8), - FieldElement::from(5), - FieldElement::one(), - ]); - point.unwrap() - } -} - -impl IsEdwards for TinyJubJubEdwards { - fn a() -> FieldElement { - FieldElement::from(3) - } - - fn d() -> FieldElement { - FieldElement::from(8) - } -} diff --git a/crates/math/src/elliptic_curve/edwards/mod.rs b/crates/math/src/elliptic_curve/edwards/mod.rs deleted file mode 100644 index aa669af45..000000000 --- a/crates/math/src/elliptic_curve/edwards/mod.rs +++ /dev/null @@ -1,3 +0,0 @@ -pub mod curves; -pub mod point; -pub mod traits; diff --git a/crates/math/src/elliptic_curve/edwards/point.rs b/crates/math/src/elliptic_curve/edwards/point.rs deleted file mode 100644 index 5cae60909..000000000 --- a/crates/math/src/elliptic_curve/edwards/point.rs +++ /dev/null @@ -1,256 +0,0 @@ -use crate::{ - cyclic_group::IsGroup, - elliptic_curve::{ - point::ProjectivePoint, - traits::{EllipticCurveError, FromAffine, IsEllipticCurve}, - }, - field::element::FieldElement, -}; - -use super::traits::IsEdwards; - -#[derive(Clone, Debug)] -pub struct EdwardsProjectivePoint(ProjectivePoint); - -impl EdwardsProjectivePoint { - /// Creates an elliptic curve point giving the projective [x: y: z] coordinates. - pub fn new(value: [FieldElement; 3]) -> Result { - let (x, y, z) = (&value[0], &value[1], &value[2]); - - // The point at infinity is (0, 1, 1). - // We convert every (0, y, y) into the infinity. - if x == &FieldElement::::zero() && z == y { - return Ok(Self(ProjectivePoint::new([ - FieldElement::::zero(), - FieldElement::::one(), - FieldElement::::one(), - ]))); - } - if z != &FieldElement::::zero() - && E::defining_equation_projective(x, y, z) == FieldElement::::zero() - { - Ok(Self(ProjectivePoint::new(value))) - } else { - Err(EllipticCurveError::InvalidPoint) - } - } - - /// Returns the `x` coordinate of the point. - pub fn x(&self) -> &FieldElement { - self.0.x() - } - - /// Returns the `y` coordinate of the point. - pub fn y(&self) -> &FieldElement { - self.0.y() - } - - /// Returns the `z` coordinate of the point. - pub fn z(&self) -> &FieldElement { - self.0.z() - } - - /// Returns a tuple [x, y, z] with the coordinates of the point. - pub fn coordinates(&self) -> &[FieldElement; 3] { - self.0.coordinates() - } - - /// Creates the same point in affine coordinates. That is, - /// returns [x / z: y / z: 1] where `self` is [x: y: z]. - /// Panics if `self` is the point at infinity. - pub fn to_affine(&self) -> Self { - Self(self.0.to_affine()) - } -} - -impl PartialEq for EdwardsProjectivePoint { - fn eq(&self, other: &Self) -> bool { - self.0 == other.0 - } -} - -impl FromAffine for EdwardsProjectivePoint { - fn from_affine( - x: FieldElement, - y: FieldElement, - ) -> Result { - let coordinates = [x, y, FieldElement::one()]; - EdwardsProjectivePoint::new(coordinates) - } -} - -impl Eq for EdwardsProjectivePoint {} - -impl IsGroup for EdwardsProjectivePoint { - /// Returns the point at infinity (neutral element) in projective coordinates. - /// - /// # Safety - /// - /// - The values `[0, 1, 1]` are the **canonical representation** of the neutral element - /// in the Edwards curve, meaning they are guaranteed to be a valid point. - /// - `unwrap()` is used because this point is **known** to be valid, so - /// there is no need for additional runtime checks. - fn neutral_element() -> Self { - // SAFETY: - // - `[0, 1, 1]` is a mathematically verified neutral element in Edwards curves. - // - `unwrap()` is safe because this point is **always valid**. - let point = Self::new([ - FieldElement::zero(), - FieldElement::one(), - FieldElement::one(), - ]); - point.unwrap() - } - - fn is_neutral_element(&self) -> bool { - let [px, py, pz] = self.coordinates(); - px == &FieldElement::zero() && py == pz - } - - /// Computes the addition of `self` and `other` using the Edwards curve addition formula. - /// - /// This implementation follows Equation (5.38) from "Moonmath" (page 97). - /// - /// # Safety - /// - /// - The function assumes both `self` and `other` are valid points on the curve. - /// - The resulting coordinates are computed using a well-defined formula that - /// maintains the elliptic curve invariants. - /// - `unwrap()` is safe because the formula guarantees the result is valid. - fn operate_with(&self, other: &Self) -> Self { - // This avoids dropping, which in turn saves us from having to clone the coordinates. - let (s_affine, o_affine) = (self.to_affine(), other.to_affine()); - - let [x1, y1, _] = s_affine.coordinates(); - let [x2, y2, _] = o_affine.coordinates(); - - let one = FieldElement::one(); - let (x1y2, y1x2) = (x1 * y2, y1 * x2); - let (x1x2, y1y2) = (x1 * x2, y1 * y2); - let dx1x2y1y2 = E::d() * &x1x2 * &y1y2; - - let num_s1 = &x1y2 + &y1x2; - let den_s1 = &one + &dx1x2y1y2; - - let num_s2 = &y1y2 - E::a() * &x1x2; - let den_s2 = &one - &dx1x2y1y2; - // SAFETY: The creation of the result point is safe because the inputs are always points that belong to the curve. - // We are using that den_s1 and den_s2 aren't zero. - // See Theorem 3.3 from https://eprint.iacr.org/2007/286.pdf. - let x_coord = (&num_s1 / &den_s1).unwrap(); - let y_coord = (&num_s2 / &den_s2).unwrap(); - let point = Self::new([x_coord, y_coord, one]); - point.unwrap() - } - - /// Returns the additive inverse of the projective point `p` - /// - /// # Safety - /// - /// - Negating the x-coordinate of a valid Edwards point results in another valid point. - /// - `unwrap()` is safe because negation does not break the curve equation. - fn neg(&self) -> Self { - let [px, py, pz] = self.coordinates(); - // SAFETY: - // - The negation formula for Edwards curves is well-defined. - // - The result remains a valid curve point. - let point = Self::new([-px, py.clone(), pz.clone()]); - point.unwrap() - } -} - -#[cfg(test)] -mod tests { - use crate::{ - cyclic_group::IsGroup, - elliptic_curve::{ - edwards::{curves::tiny_jub_jub::TinyJubJubEdwards, point::EdwardsProjectivePoint}, - traits::{EllipticCurveError, IsEllipticCurve}, - }, - field::element::FieldElement, - }; - - fn create_point(x: u64, y: u64) -> EdwardsProjectivePoint { - TinyJubJubEdwards::create_point_from_affine(FieldElement::from(x), FieldElement::from(y)) - .unwrap() - } - - #[test] - fn create_valid_point_works() { - let p = TinyJubJubEdwards::create_point_from_affine( - FieldElement::from(5), - FieldElement::from(5), - ) - .unwrap(); - assert_eq!(p.x(), &FieldElement::from(5)); - assert_eq!(p.y(), &FieldElement::from(5)); - assert_eq!(p.z(), &FieldElement::from(1)); - } - - #[test] - fn create_invalid_point_returns_invalid_point_error() { - let result = TinyJubJubEdwards::create_point_from_affine( - FieldElement::from(5), - FieldElement::from(4), - ); - assert_eq!(result.unwrap_err(), EllipticCurveError::InvalidPoint); - } - - #[test] - fn operate_with_works_for_points_in_tiny_jub_jub() { - let p = EdwardsProjectivePoint::::new([ - FieldElement::from(5), - FieldElement::from(5), - FieldElement::from(1), - ]) - .unwrap(); - let q = EdwardsProjectivePoint::::new([ - FieldElement::from(8), - FieldElement::from(5), - FieldElement::from(1), - ]) - .unwrap(); - let expected = EdwardsProjectivePoint::::new([ - FieldElement::from(0), - FieldElement::from(1), - FieldElement::from(1), - ]) - .unwrap(); - assert_eq!(p.operate_with(&q), expected); - } - - #[test] - fn test_negation_in_edwards() { - let a = create_point(5, 5); - let b = create_point(13 - 5, 5); - - assert_eq!(a.neg(), b); - assert!(a.operate_with(&b).is_neutral_element()); - } - - #[test] - fn operate_with_works_and_cycles_in_tiny_jub_jub() { - let g = create_point(12, 11); - assert_eq!(g.operate_with_self(0_u16), create_point(0, 1)); - assert_eq!(g.operate_with_self(1_u16), create_point(12, 11)); - assert_eq!(g.operate_with_self(2_u16), create_point(8, 5)); - assert_eq!(g.operate_with_self(3_u16), create_point(11, 6)); - assert_eq!(g.operate_with_self(4_u16), create_point(6, 9)); - assert_eq!(g.operate_with_self(5_u16), create_point(10, 0)); - assert_eq!(g.operate_with_self(6_u16), create_point(6, 4)); - assert_eq!(g.operate_with_self(7_u16), create_point(11, 7)); - assert_eq!(g.operate_with_self(8_u16), create_point(8, 8)); - assert_eq!(g.operate_with_self(9_u16), create_point(12, 2)); - assert_eq!(g.operate_with_self(10_u16), create_point(0, 12)); - assert_eq!(g.operate_with_self(11_u16), create_point(1, 2)); - assert_eq!(g.operate_with_self(12_u16), create_point(5, 8)); - assert_eq!(g.operate_with_self(13_u16), create_point(2, 7)); - assert_eq!(g.operate_with_self(14_u16), create_point(7, 4)); - assert_eq!(g.operate_with_self(15_u16), create_point(3, 0)); - assert_eq!(g.operate_with_self(16_u16), create_point(7, 9)); - assert_eq!(g.operate_with_self(17_u16), create_point(2, 6)); - assert_eq!(g.operate_with_self(18_u16), create_point(5, 5)); - assert_eq!(g.operate_with_self(19_u16), create_point(1, 11)); - assert_eq!(g.operate_with_self(20_u16), create_point(0, 1)); - } -} diff --git a/crates/math/src/elliptic_curve/edwards/traits.rs b/crates/math/src/elliptic_curve/edwards/traits.rs deleted file mode 100644 index 6c7152152..000000000 --- a/crates/math/src/elliptic_curve/edwards/traits.rs +++ /dev/null @@ -1,32 +0,0 @@ -use crate::elliptic_curve::traits::IsEllipticCurve; -use crate::field::element::FieldElement; -use core::fmt::Debug; -/// Trait to add elliptic curves behaviour to a struct. -pub trait IsEdwards: IsEllipticCurve + Clone + Debug { - fn a() -> FieldElement; - - fn d() -> FieldElement; - - // Edwards equation in affine coordinates: - // ax^2 + y^2 - 1 = d * x^2 * y^2 - fn defining_equation( - x: &FieldElement, - y: &FieldElement, - ) -> FieldElement { - (Self::a() * x.pow(2_u16) + y.pow(2_u16)) - - FieldElement::::one() - - Self::d() * x.pow(2_u16) * y.pow(2_u16) - } - - // Edwards equation in projective coordinates. - // a * x^2 * z^2 + y^2 * z^2 - z^4 = d * x^2 * y^2 - fn defining_equation_projective( - x: &FieldElement, - y: &FieldElement, - z: &FieldElement, - ) -> FieldElement { - Self::a() * x.square() * z.square() + y.square() * z.square() - - z.square().square() - - Self::d() * x.square() * y.square() - } -} diff --git a/crates/math/src/elliptic_curve/mod.rs b/crates/math/src/elliptic_curve/mod.rs deleted file mode 100644 index c34d69e98..000000000 --- a/crates/math/src/elliptic_curve/mod.rs +++ /dev/null @@ -1,6 +0,0 @@ -pub mod edwards; -pub mod montgomery; -/// Implementation of ProjectivePoint, a generic projective point in a curve. -pub mod point; -pub mod short_weierstrass; -pub mod traits; diff --git a/crates/math/src/elliptic_curve/montgomery/curves/mod.rs b/crates/math/src/elliptic_curve/montgomery/curves/mod.rs deleted file mode 100644 index 55679082c..000000000 --- a/crates/math/src/elliptic_curve/montgomery/curves/mod.rs +++ /dev/null @@ -1 +0,0 @@ -pub mod tiny_jub_jub; diff --git a/crates/math/src/elliptic_curve/montgomery/curves/tiny_jub_jub.rs b/crates/math/src/elliptic_curve/montgomery/curves/tiny_jub_jub.rs deleted file mode 100644 index 9b130e703..000000000 --- a/crates/math/src/elliptic_curve/montgomery/curves/tiny_jub_jub.rs +++ /dev/null @@ -1,49 +0,0 @@ -use crate::{ - elliptic_curve::{ - montgomery::{point::MontgomeryProjectivePoint, traits::IsMontgomery}, - traits::IsEllipticCurve, - }, - field::{element::FieldElement, fields::u64_prime_field::U64PrimeField}, -}; - -/// Taken from moonmath manual page 91 -#[derive(Debug, Clone)] -pub struct TinyJubJubMontgomery; - -impl IsEllipticCurve for TinyJubJubMontgomery { - type BaseField = U64PrimeField<13>; - type PointRepresentation = MontgomeryProjectivePoint; - - /// Returns the generator point of the TinyJubJub Montgomery curve. - /// - /// This generator is taken from **Moonmath Manual (page 91)**. - /// - /// # Safety - /// - /// - The generator coordinates `(3, 5, 1)` are **predefined** and are **valid** points - /// on the TinyJubJub Montgomery curve. - /// - `unwrap()` is used because the generator is **guaranteed** to satisfy - /// the Montgomery curve equation. - /// - This function must **not** be modified unless the new generator is mathematically verified. - fn generator() -> Self::PointRepresentation { - // SAFETY: - // - The generator point `(3, 5, 1)` is **mathematically verified**. - // - `unwrap()` is safe because the input values **guarantee** validity. - let point = Self::PointRepresentation::new([ - FieldElement::from(3), - FieldElement::from(5), - FieldElement::one(), - ]); - point.unwrap() - } -} - -impl IsMontgomery for TinyJubJubMontgomery { - fn a() -> FieldElement { - FieldElement::from(6) - } - - fn b() -> FieldElement { - FieldElement::from(7) - } -} diff --git a/crates/math/src/elliptic_curve/montgomery/mod.rs b/crates/math/src/elliptic_curve/montgomery/mod.rs deleted file mode 100644 index aa669af45..000000000 --- a/crates/math/src/elliptic_curve/montgomery/mod.rs +++ /dev/null @@ -1,3 +0,0 @@ -pub mod curves; -pub mod point; -pub mod traits; diff --git a/crates/math/src/elliptic_curve/montgomery/point.rs b/crates/math/src/elliptic_curve/montgomery/point.rs deleted file mode 100644 index 5bcce1cdf..000000000 --- a/crates/math/src/elliptic_curve/montgomery/point.rs +++ /dev/null @@ -1,297 +0,0 @@ -use crate::{ - cyclic_group::IsGroup, - elliptic_curve::{ - point::ProjectivePoint, - traits::{EllipticCurveError, FromAffine, IsEllipticCurve}, - }, - field::element::FieldElement, -}; - -use super::traits::IsMontgomery; - -#[derive(Clone, Debug)] -pub struct MontgomeryProjectivePoint(ProjectivePoint); - -impl MontgomeryProjectivePoint { - /// Creates an elliptic curve point giving the projective [x: y: z] coordinates. - pub fn new(value: [FieldElement; 3]) -> Result { - let (x, y, z) = (&value[0], &value[1], &value[2]); - - if z != &FieldElement::::zero() - && E::defining_equation_projective(x, y, z) == FieldElement::::zero() - { - Ok(Self(ProjectivePoint::new(value))) - // The point at infinity is (0, 1, 0) - // We convert every (0, _, 0) into the infinity. - } else if x == &FieldElement::::zero() - && z == &FieldElement::::zero() - { - Ok(Self(ProjectivePoint::new([ - FieldElement::::zero(), - FieldElement::::one(), - FieldElement::::zero(), - ]))) - } else { - Err(EllipticCurveError::InvalidPoint) - } - } - - /// Returns the `x` coordinate of the point. - pub fn x(&self) -> &FieldElement { - self.0.x() - } - - /// Returns the `y` coordinate of the point. - pub fn y(&self) -> &FieldElement { - self.0.y() - } - - /// Returns the `z` coordinate of the point. - pub fn z(&self) -> &FieldElement { - self.0.z() - } - - /// Returns a tuple [x, y, z] with the coordinates of the point. - pub fn coordinates(&self) -> &[FieldElement; 3] { - self.0.coordinates() - } - - /// Creates the same point in affine coordinates. That is, - /// returns [x / z: y / z: 1] where `self` is [x: y: z]. - /// Panics if `self` is the point at infinity. - pub fn to_affine(&self) -> Self { - Self(self.0.to_affine()) - } -} - -impl PartialEq for MontgomeryProjectivePoint { - fn eq(&self, other: &Self) -> bool { - self.0 == other.0 - } -} - -impl FromAffine for MontgomeryProjectivePoint { - fn from_affine( - x: FieldElement, - y: FieldElement, - ) -> Result { - let coordinates = [x, y, FieldElement::one()]; - MontgomeryProjectivePoint::new(coordinates) - } -} - -impl Eq for MontgomeryProjectivePoint {} - -impl IsGroup for MontgomeryProjectivePoint { - /// The point at infinity. - /// - /// # Safety - /// - /// - The point `(0, 1, 0)` is a well-defined **neutral element** for Montgomery curves. - /// - `unwrap_unchecked()` is used because this point is **always valid**. - fn neutral_element() -> Self { - // SAFETY: - // - `(0, 1, 0)` is **mathematically valid** as the neutral element. - // - `unwrap_unchecked()` is safe because this is **a known valid point**. - let point = Self::new([ - FieldElement::zero(), - FieldElement::one(), - FieldElement::zero(), - ]); - debug_assert!(point.is_ok()); - point.unwrap() - } - - fn is_neutral_element(&self) -> bool { - let pz = self.z(); - pz == &FieldElement::zero() - } - - /// Computes the addition of `self` and `other`. - /// - /// This implementation follows the addition law for Montgomery curves as described in: - /// **Moonmath Manual, Definition 5.2.2.1, Page 94**. - /// - /// # Safety - /// - /// - This function assumes that both `self` and `other` are **valid** points on the curve. - /// - The resulting point is **guaranteed** to be valid due to the **Montgomery curve addition formula**. - /// - `unwrap()` is used because the formula ensures the result remains a valid curve point. - fn operate_with(&self, other: &Self) -> Self { - // One of them is the neutral element. - if self.is_neutral_element() { - other.clone() - } else if other.is_neutral_element() { - self.clone() - } else { - let [x1, y1, _] = self.to_affine().coordinates().clone(); - let [x2, y2, _] = other.to_affine().coordinates().clone(); - // In this case P == -Q - if x2 == x1 && &y2 + &y1 == FieldElement::zero() { - Self::neutral_element() - // The points are the same P == Q - } else if self == other { - // P = Q = (x, y) - // y cant be zero here because if y = 0 then - // P = Q = (x, 0) and P = -Q, which is the - // previous case. - let one = FieldElement::from(1); - let (a, b) = (E::a(), E::b()); - - let x1a = &a * &x1; - let x1_square = &x1 * &x1; - let num = &x1_square + &x1_square + x1_square + &x1a + x1a + &one; - let den = (&b + &b) * &y1; - - // We are using that den != 0 because b and y1 aren't zero. - // b != 0 because the cofficient b of a montgomery elliptic curve has to be different from zero. - // y1 != 0 because if not, it woould be the case from above: x2 = x1 and y2 + y1 = 0. - let div = unsafe { (num / den).unwrap_unchecked() }; - - let new_x = &div * &div * &b - (&x1 + x2) - a; - let new_y = div * (x1 - &new_x) - y1; - - // SAFETY: - // - The Montgomery addition formula guarantees a **valid** curve point. - // - `unwrap()` is safe because the input points are **valid**. - let point = Self::new([new_x, new_y, one]); - point.unwrap() - // In the rest of the cases we have x1 != x2 - } else { - let num = &y2 - &y1; - let den = &x2 - &x1; - - let div = unsafe { (num / den).unwrap_unchecked() }; - - let new_x = &div * &div * E::b() - (&x1 + &x2) - E::a(); - let new_y = div * (x1 - &new_x) - y1; - - // SAFETY: - // - The result of the Montgomery addition formula is **guaranteed** to be a valid point. - // - `unwrap()` is safe because we **control** the inputs. - let point = Self::new([new_x, new_y, FieldElement::one()]); - point.unwrap() - } - } - } - - /// Returns the additive inverse of the projective point `p` - /// - /// # Safety - /// - /// - The negation formula preserves the curve equation. - /// - `unwrap()` is safe because negation **does not** create invalid points. - fn neg(&self) -> Self { - let [px, py, pz] = self.coordinates(); - // SAFETY: - // - Negating `y` maintains the curve structure. - // - `unwrap()` is safe because negation **is always valid**. - let point = Self::new([px.clone(), -py, pz.clone()]); - debug_assert!(point.is_ok()); - point.unwrap() - } -} - -#[cfg(test)] -mod tests { - use crate::{ - cyclic_group::IsGroup, - elliptic_curve::{ - montgomery::{ - curves::tiny_jub_jub::TinyJubJubMontgomery, point::MontgomeryProjectivePoint, - }, - traits::{EllipticCurveError, IsEllipticCurve}, - }, - field::element::FieldElement, - }; - - fn create_point(x: u64, y: u64) -> MontgomeryProjectivePoint { - TinyJubJubMontgomery::create_point_from_affine(FieldElement::from(x), FieldElement::from(y)) - .unwrap() - } - - #[test] - fn create_valid_point_works() { - let p = TinyJubJubMontgomery::create_point_from_affine( - FieldElement::from(9), - FieldElement::from(2), - ) - .unwrap(); - assert_eq!(p.x(), &FieldElement::from(9)); - assert_eq!(p.y(), &FieldElement::from(2)); - assert_eq!(p.z(), &FieldElement::from(1)); - } - - #[test] - fn create_invalid_point_returns_invalid_point_error() { - let result = TinyJubJubMontgomery::create_point_from_affine( - FieldElement::from(5), - FieldElement::from(4), - ); - assert_eq!(result.unwrap_err(), EllipticCurveError::InvalidPoint); - } - - #[test] - fn operate_with_works_for_points_in_tiny_jub_jub() { - let p = MontgomeryProjectivePoint::::new([ - FieldElement::from(9), - FieldElement::from(2), - FieldElement::from(1), - ]) - .unwrap(); - let q = MontgomeryProjectivePoint::::new([ - FieldElement::from(7), - FieldElement::from(12), - FieldElement::from(1), - ]) - .unwrap(); - let expected = MontgomeryProjectivePoint::::new([ - FieldElement::from(10), - FieldElement::from(3), - FieldElement::from(1), - ]) - .unwrap(); - assert_eq!(p.operate_with(&q), expected); - } - - #[test] - fn test_negation_in_montgomery() { - let a = create_point(9, 2); - let b = create_point(9, 13 - 2); - - assert_eq!(a.neg(), b); - assert!(a.operate_with(&b).is_neutral_element()); - } - - #[test] - fn operate_with_works_and_cycles_in_tiny_jub_jub() { - let g = create_point(9, 2); - assert_eq!( - g.operate_with_self(0_u16), - MontgomeryProjectivePoint::neutral_element() - ); - assert_eq!(g.operate_with_self(1_u16), create_point(9, 2)); - assert_eq!(g.operate_with_self(2_u16), create_point(7, 12)); - assert_eq!(g.operate_with_self(3_u16), create_point(10, 3)); - assert_eq!(g.operate_with_self(4_u16), create_point(8, 12)); - assert_eq!(g.operate_with_self(5_u16), create_point(1, 9)); - assert_eq!(g.operate_with_self(6_u16), create_point(5, 1)); - assert_eq!(g.operate_with_self(7_u16), create_point(4, 9)); - assert_eq!(g.operate_with_self(8_u16), create_point(2, 9)); - assert_eq!(g.operate_with_self(9_u16), create_point(3, 5)); - assert_eq!(g.operate_with_self(10_u16), create_point(0, 0)); - assert_eq!(g.operate_with_self(11_u16), create_point(3, 8)); - assert_eq!(g.operate_with_self(12_u16), create_point(2, 4)); - assert_eq!(g.operate_with_self(13_u16), create_point(4, 4)); - assert_eq!(g.operate_with_self(14_u16), create_point(5, 12)); - assert_eq!(g.operate_with_self(15_u16), create_point(1, 4)); - assert_eq!(g.operate_with_self(16_u16), create_point(8, 1)); - assert_eq!(g.operate_with_self(17_u16), create_point(10, 10)); - assert_eq!(g.operate_with_self(18_u16), create_point(7, 1)); - assert_eq!(g.operate_with_self(19_u16), create_point(9, 11)); - assert_eq!( - g.operate_with_self(20_u16), - MontgomeryProjectivePoint::neutral_element() - ); - } -} diff --git a/crates/math/src/elliptic_curve/montgomery/traits.rs b/crates/math/src/elliptic_curve/montgomery/traits.rs deleted file mode 100644 index 9f8181e87..000000000 --- a/crates/math/src/elliptic_curve/montgomery/traits.rs +++ /dev/null @@ -1,30 +0,0 @@ -use crate::elliptic_curve::traits::IsEllipticCurve; -use crate::field::element::FieldElement; -use core::fmt::Debug; - -/// Trait to add elliptic curves behaviour to a struct. -pub trait IsMontgomery: IsEllipticCurve + Clone + Debug { - fn a() -> FieldElement; - - fn b() -> FieldElement; - - /// Evaluates the equation at (x, y). - /// Used for checking if the point belongs to the elliptic curve. - /// Equation: by^2 = x^3 + ax^2 + x. - fn defining_equation( - x: &FieldElement, - y: &FieldElement, - ) -> FieldElement { - (Self::b() * y.square()) - (x.pow(3_u16) + Self::a() * x.square() + x) - } - - /// Evaluates the equation at the projective point (x, y, z). - /// Projective equation: zby^2 = x^3 + zax^2 + z^2x - fn defining_equation_projective( - x: &FieldElement, - y: &FieldElement, - z: &FieldElement, - ) -> FieldElement { - z * Self::b() * y.square() - x.pow(3_u16) - z * Self::a() * x.square() - z.square() * x - } -} diff --git a/crates/math/src/elliptic_curve/point.rs b/crates/math/src/elliptic_curve/point.rs deleted file mode 100644 index 7652981c5..000000000 --- a/crates/math/src/elliptic_curve/point.rs +++ /dev/null @@ -1,246 +0,0 @@ -use crate::elliptic_curve::traits::IsEllipticCurve; -use crate::field::element::FieldElement; -use core::fmt::Debug; -/// Represents an elliptic curve point using the projective short Weierstrass form: -/// y^2 * z = x^3 + a * x * z^2 + b * z^3, -/// where `x`, `y` and `z` variables are field elements. -#[derive(Debug, Clone)] -pub struct ProjectivePoint { - pub value: [FieldElement; 3], -} - -impl ProjectivePoint { - /// Creates an elliptic curve point giving the projective [x: y: z] coordinates. - pub const fn new(value: [FieldElement; 3]) -> Self { - Self { value } - } - - /// Returns the `x` coordinate of the point. - pub fn x(&self) -> &FieldElement { - &self.value[0] - } - - /// Returns the `y` coordinate of the point. - pub fn y(&self) -> &FieldElement { - &self.value[1] - } - - /// Returns the `z` coordinate of the point. - pub fn z(&self) -> &FieldElement { - &self.value[2] - } - - /// Returns a tuple [x, y, z] with the coordinates of the point. - pub fn coordinates(&self) -> &[FieldElement; 3] { - &self.value - } - - /// Creates the same point in affine coordinates. That is, - /// returns [x / z: y / z: 1] where `self` is [x: y: z]. - /// Panics if `self` is the point at infinity. - pub fn to_affine(&self) -> Self { - let [x, y, z] = self.coordinates(); - // If it's the point at infinite - if z == &FieldElement::zero() { - // We make sure all the points at infinite have the same values - return Self::new([ - FieldElement::zero(), - FieldElement::one(), - FieldElement::zero(), - ]); - }; - let inv_z = z.inv().unwrap(); - ProjectivePoint::new([x * &inv_z, y * inv_z, FieldElement::one()]) - } -} - -impl PartialEq for ProjectivePoint { - fn eq(&self, other: &Self) -> bool { - let [px, py, pz] = self.coordinates(); - let [qx, qy, qz] = other.coordinates(); - (px * qz == pz * qx) && (py * qz == qy * pz) - } -} - -impl Eq for ProjectivePoint {} -#[derive(Debug, Clone)] - -pub struct JacobianPoint { - pub value: [FieldElement; 3], -} - -impl JacobianPoint { - /// Creates an elliptic curve point giving the Jacobian [x: y: z] coordinates. - pub const fn new(value: [FieldElement; 3]) -> Self { - Self { value } - } - - /// Returns the `x` coordinate of the point. - pub fn x(&self) -> &FieldElement { - &self.value[0] - } - - /// Returns the `y` coordinate of the point. - pub fn y(&self) -> &FieldElement { - &self.value[1] - } - - /// Returns the `z` coordinate of the point. - pub fn z(&self) -> &FieldElement { - &self.value[2] - } - - /// Returns a tuple [x, y, z] with the coordinates of the point. - pub fn coordinates(&self) -> &[FieldElement; 3] { - &self.value - } - - pub fn to_affine(&self) -> Self { - let [x, y, z] = self.coordinates(); - // If it's the point at infinite - if z == &FieldElement::zero() { - // We make sure all the points at infinite have the same values - return Self::new([ - FieldElement::one(), - FieldElement::one(), - FieldElement::zero(), - ]); - }; - let inv_z = z.inv().unwrap(); - let inv_z_square = inv_z.square(); - let inv_z_cube = &inv_z_square * &inv_z; - JacobianPoint::new([x * inv_z_square, y * inv_z_cube, FieldElement::one()]) - } -} - -impl PartialEq for JacobianPoint { - fn eq(&self, other: &Self) -> bool { - // In Jacobian coordinates, the equality of two points is defined as: - // X1 * Z2^2 == X2 * Z1^2 y Y1 * Z2^3 == Y2 * Z1^3 - - let [px, py, pz] = self.coordinates(); - let [qx, qy, qz] = other.coordinates(); - - let zp_sq = pz.square(); - let zq_sq = qz.square(); - - let zp_cu = &zp_sq * pz; - let zq_cu = &zq_sq * qz; - - let xp_zq_sq = px * zq_sq; - let xq_zp_sq = qx * zp_sq; - - let yp_zq_cu = py * zq_cu; - let yq_zp_cu = qy * zp_cu; - - (xp_zq_sq == xq_zp_sq) && (yp_zq_cu == yq_zp_cu) - } -} -impl Eq for JacobianPoint {} -#[cfg(test)] -mod tests { - use crate::cyclic_group::IsGroup; - use crate::elliptic_curve::short_weierstrass::curves::test_curve_1::{ - TestCurve1, TestCurvePrimeField, TestCurveQuadraticNonResidue, - TEST_CURVE_1_MAIN_SUBGROUP_ORDER, - }; - use crate::elliptic_curve::short_weierstrass::curves::test_curve_2::TestCurve2; - use crate::field::element::FieldElement; - use crate::unsigned_integer::element::U384; - //use crate::elliptic_curve::curves::test_curve_2::TestCurve2; - use crate::elliptic_curve::traits::{EllipticCurveError, IsEllipticCurve}; - use crate::field::extensions::quadratic::QuadraticExtensionFieldElement; - - #[allow(clippy::upper_case_acronyms)] - type FEE = QuadraticExtensionFieldElement; - - // This tests only apply for the specific curve found in the configuration file. - #[test] - fn create_valid_point_works() { - let point = TestCurve1::create_point_from_affine(FEE::from(35), FEE::from(31)).unwrap(); - assert_eq!(*point.x(), FEE::from(35)); - assert_eq!(*point.y(), FEE::from(31)); - assert_eq!(*point.z(), FEE::from(1)); - } - - #[test] - fn create_invalid_points_panics() { - let a = TestCurve1::create_point_from_affine(FEE::from(0), FEE::from(1)); - assert_eq!(EllipticCurveError::InvalidPoint, a.unwrap_err()); - } - - #[test] - fn equality_works() { - let g = TestCurve1::generator(); - let g2 = g.operate_with(&g); - assert_ne!(&g2, &g); - assert_eq!(&g, &g); - } - - #[test] - fn operate_with_self_works_1() { - let g = TestCurve1::generator(); - assert_eq!( - g.operate_with(&g).operate_with(&g), - g.operate_with_self(3_u16) - ); - } - - #[test] - fn operate_with_self_works_2() { - let mut point_1 = TestCurve1::generator(); - point_1 = point_1.operate_with_self(TEST_CURVE_1_MAIN_SUBGROUP_ORDER as u128); - assert!(point_1.is_neutral_element()); - } - - #[test] - fn doubling_a_point_works() { - let point = TestCurve1::create_point_from_affine(FEE::from(35), FEE::from(31)).unwrap(); - let expected_result = - TestCurve1::create_point_from_affine(FEE::from(25), FEE::from(29)).unwrap(); - assert_eq!(point.operate_with_self(2_u16).to_affine(), expected_result); - } - - #[test] - fn operate_with_self_works_with_test_curve_2() { - let mut point_1 = TestCurve2::generator(); - point_1 = point_1.operate_with_self(15_u16); - - let expected_result = TestCurve2::create_point_from_affine( - FieldElement::new([ - FieldElement::new(U384::from_hex_unchecked( - "7b8ee59e422e702458174c18eb3302e17", - )), - FieldElement::new(U384::from_hex_unchecked( - "1395065adef5a6a5457f1ea600b5a3e4fb", - )), - ]), - FieldElement::new([ - FieldElement::new(U384::from_hex_unchecked( - "e29d5b15c42124cd8f05d3c8500451c33", - )), - FieldElement::new(U384::from_hex_unchecked( - "e836ef62db0a47a63304b67c0de69b140", - )), - ]), - ) - .unwrap(); - - assert_eq!(point_1, expected_result); - } - - #[test] - fn coordinate_getters_work() { - let x = FEE::from(35); - let y = FEE::from(31); - let z = FEE::from(1); - let point = TestCurve1::create_point_from_affine(x.clone(), y.clone()).unwrap(); - let coordinates = point.coordinates(); - assert_eq!(&x, point.x()); - assert_eq!(&y, point.y()); - assert_eq!(&z, point.z()); - assert_eq!(x, coordinates[0]); - assert_eq!(y, coordinates[1]); - assert_eq!(z, coordinates[2]); - } -} diff --git a/crates/math/src/elliptic_curve/short_weierstrass/curves/bls12_377/curve.rs b/crates/math/src/elliptic_curve/short_weierstrass/curves/bls12_377/curve.rs deleted file mode 100644 index 3ab78916f..000000000 --- a/crates/math/src/elliptic_curve/short_weierstrass/curves/bls12_377/curve.rs +++ /dev/null @@ -1,287 +0,0 @@ -use super::{ - field_extension::{BLS12377PrimeField, Degree2ExtensionField}, - pairing::{GAMMA_12, GAMMA_13}, - twist::BLS12377TwistCurve, -}; -use crate::cyclic_group::IsGroup; -use crate::elliptic_curve::short_weierstrass::point::ShortWeierstrassProjectivePoint; -use crate::elliptic_curve::traits::IsEllipticCurve; -use crate::unsigned_integer::element::U256; - -use crate::{ - elliptic_curve::short_weierstrass::traits::IsShortWeierstrass, field::element::FieldElement, -}; - -pub const SUBGROUP_ORDER: U256 = - U256::from_hex_unchecked("0x12ab655e9a2ca55660b44d1e5c37b00159aa76fed00000010a11800000000001"); - -pub const CURVE_COFACTOR: U256 = U256::from_hex_unchecked("0x170b5d44300000000000000000000000"); - -pub type BLS12377FieldElement = FieldElement; -pub type BLS12377TwistCurveFieldElement = FieldElement; - -/// The description of the curve. -#[derive(Clone, Debug)] -pub struct BLS12377Curve; - -impl IsEllipticCurve for BLS12377Curve { - type BaseField = BLS12377PrimeField; - type PointRepresentation = ShortWeierstrassProjectivePoint; - - /// Returns the generator point of the BLS12-377 curve. - /// - /// Generator values are taken from [Neuromancer's BLS12-377 page](https://neuromancer.sk/std/bls/BLS12-377). - /// - /// # Safety - /// - /// - The generator point `(x, y, 1)` is predefined and is **known to be a valid point** on the curve. - /// - `unwrap` is used because this point is **mathematically verified**. - /// - Do **not** modify this function unless a new generator has been **mathematically verified**. - fn generator() -> Self::PointRepresentation { - // SAFETY: - // - These values are mathematically verified and known to be valid points on BLS12-377. - // - `unwrap()` is safe because we **ensure** the input values satisfy the curve equation. - let point= Self::PointRepresentation::new([ - FieldElement::::new_base("8848defe740a67c8fc6225bf87ff5485951e2caa9d41bb188282c8bd37cb5cd5481512ffcd394eeab9b16eb21be9ef"), - FieldElement::::new_base("1914a69c5102eff1f674f5d30afeec4bd7fb348ca3e52d96d182ad44fb82305c2fe3d3634a9591afd82de55559c8ea6"), - FieldElement::one() - ]); - point.unwrap() - } -} - -impl IsShortWeierstrass for BLS12377Curve { - fn a() -> FieldElement { - FieldElement::from(0) - } - - fn b() -> FieldElement { - FieldElement::from(1) - } -} - -/// This is equal to the frobenius trace of the BLS12 377 curve minus one or seed value z. -pub const MILLER_LOOP_CONSTANT: u64 = 0x8508c00000000001; - -/// 𝛽 : primitive cube root of unity of 𝐹ₚ that §satisfies the minimal equation -/// 𝛽² + 𝛽 + 1 = 0 mod 𝑝 -pub const CUBE_ROOT_OF_UNITY_G1: BLS12377FieldElement = FieldElement::from_hex_unchecked( - "0x1ae3a4617c510eabc8756ba8f8c524eb8882a75cc9bc8e359064ee822fb5bffd1e945779fffffffffffffffffffffff", -); - -/// x-coordinate of 𝜁 ∘ 𝜋_q ∘ 𝜁⁻¹, where 𝜁 is the isomorphism u:E'(𝔽ₚ₆) −> E(𝔽ₚ₁₂) from the twist to E -pub const ENDO_U: BLS12377TwistCurveFieldElement = - BLS12377TwistCurveFieldElement::const_from_raw([ - FieldElement::from_hex_unchecked( - "9B3AF05DD14F6EC619AAF7D34594AABC5ED1347970DEC00452217CC900000008508C00000000002", - ), - FieldElement::from_hex_unchecked("0"), - ]); - -/// y-coordinate of 𝜁 ∘ 𝜋_q ∘ 𝜁⁻¹, where 𝜁 is the isomorphism u:E'(𝔽ₚ₆) −> E(𝔽ₚ₁₂) from the twist to E -pub const ENDO_V: BLS12377TwistCurveFieldElement = - BLS12377TwistCurveFieldElement::const_from_raw([ - FieldElement::from_hex_unchecked("1680A40796537CAC0C534DB1A79BEB1400398F50AD1DEC1BCE649CF436B0F6299588459BFF27D8E6E76D5ECF1391C63"), - FieldElement::from_hex_unchecked("0"), - ]); - -impl ShortWeierstrassProjectivePoint { - /// Returns 𝜙(P) = (𝑥, 𝑦) ⇒ (𝛽𝑥, 𝑦), where 𝛽 is the Cube Root of Unity in the base prime field - /// https://eprint.iacr.org/2022/352.pdf 2 Preliminaries - fn phi(&self) -> Self { - let [x, y, z] = self.coordinates(); - let new_x = x * CUBE_ROOT_OF_UNITY_G1; - // SAFETY: The value `x` is computed correctly, so the point is in the curve. - Self::new_unchecked([new_x, y.clone(), z.clone()]) - } - - /// 𝜙(P) = −𝑢²P - /// https://eprint.iacr.org/2022/352.pdf 4.3 Prop. 4 - pub fn is_in_subgroup(&self) -> bool { - self.operate_with_self(MILLER_LOOP_CONSTANT) - .operate_with_self(MILLER_LOOP_CONSTANT) - .neg() - == self.phi() - } -} - -impl ShortWeierstrassProjectivePoint { - /// Computes 𝜓(P) = 𝜁 ∘ 𝜋ₚ ∘ 𝜁⁻¹, where 𝜁 is the isomorphism u:E'(𝔽ₚ₆) −> E(𝔽ₚ₁₂) from the twist to E,, 𝜋ₚ is the p-power frobenius endomorphism - /// and 𝜓 satisifies minmal equation 𝑋² + 𝑡𝑋 + 𝑞 = 𝑂 - /// https://eprint.iacr.org/2022/352.pdf 4.2 (7) - /// ψ(P) = (ψ_x * conjugate(x), ψ_y * conjugate(y), conjugate(z)) - /// - /// # Safety - /// - /// - This function assumes `self` is a valid point on the BLS12-377 **twist** curve. - /// - The conjugation operation preserves validity. - /// - `unwrap()` is used because `psi()` is defined to **always return a valid point**. - fn psi(&self) -> Self { - let [x, y, z] = self.coordinates(); - // SAFETY: - // - `conjugate()` preserves the validity of the field element. - // - `ENDO_U` and `ENDO_V` are precomputed constants that ensure the - // resulting point satisfies the curve equation. - // - `unwrap()` is safe because the transformation follows - // **a known valid isomorphism** between the twist and E. - let point = Self::new([ - x.conjugate() * GAMMA_12, - y.conjugate() * GAMMA_13, - z.conjugate(), - ]); - point.unwrap() - } - - /// 𝜓(P) = 𝑢P, where 𝑢 = SEED of the curve - /// https://eprint.iacr.org/2022/352.pdf 4.2 - pub fn is_in_subgroup(&self) -> bool { - self.psi() == self.operate_with_self(MILLER_LOOP_CONSTANT) - } -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::{ - cyclic_group::IsGroup, elliptic_curve::traits::EllipticCurveError, - field::element::FieldElement, - }; - - use super::BLS12377Curve; - - #[allow(clippy::upper_case_acronyms)] - type FpE = FieldElement; - type Fp2 = FieldElement; - - fn point_1() -> ShortWeierstrassProjectivePoint { - let x = FpE::new_base("134e4cc122cb62a06767fb98e86f2d5f77e2a12fefe23bb0c4c31d1bd5348b88d6f5e5dee2b54db4a2146cc9f249eea"); - let y = FpE::new_base("17949c29effee7a9f13f69b1c28eccd78c1ed12b47068836473481ff818856594fd9c1935e3d9e621901a2d500257a2"); - BLS12377Curve::create_point_from_affine(x, y).unwrap() - } - - fn point_1_times_5() -> ShortWeierstrassProjectivePoint { - let x = FpE::new_base("3c852d5aab73fbb51e57fbf5a0a8b5d6513ec922b2611b7547bfed74cba0dcdfc3ad2eac2733a4f55d198ec82b9964"); - let y = FpE::new_base("a71425e68e55299c64d7eada9ae9c3fb87a9626b941d17128b64685fc07d0e635f3c3a512903b4e0a43e464045967b"); - BLS12377Curve::create_point_from_affine(x, y).unwrap() - } - - #[test] - fn adding_five_times_point_1_works() { - let point_1 = point_1(); - let point_1_times_5 = point_1_times_5(); - assert_eq!(point_1.operate_with_self(5_u16), point_1_times_5); - } - - #[test] - fn add_point1_2point1_with_both_algorithms_matches() { - let point_1 = point_1(); - let point_2 = &point_1.operate_with(&point_1).to_affine(); - - let first_algorithm_result = point_1.operate_with(point_2).to_affine(); - let second_algorithm_result = point_1.operate_with_affine(point_2).to_affine(); - - assert_eq!(first_algorithm_result, second_algorithm_result); - } - - #[test] - fn add_point1_and_42424242point1_with_both_algorithms_matches() { - let point_1 = point_1(); - let point_2 = &point_1.operate_with_self(42424242u128).to_affine(); - - let first_algorithm_result = point_1.operate_with(point_2).to_affine(); - let second_algorithm_result = point_1.operate_with_affine(point_2).to_affine(); - - assert_eq!(first_algorithm_result, second_algorithm_result); - } - - #[test] - fn add_point1_with_point1_both_algorithms_matches() { - let point_1 = point_1().to_affine(); - - let first_algorithm_result = point_1.operate_with(&point_1).to_affine(); - let second_algorithm_result = point_1.operate_with_affine(&point_1).to_affine(); - - assert_eq!(first_algorithm_result, second_algorithm_result); - } - - #[test] - fn add_point2_with_point1_both_algorithms_matches() { - let point_1 = point_1().to_affine(); - - // Create point 2 - let x = FpE::new_base("134e4cc122cb62a06767fb98e86f2d5f77e2a12fefe23bb0c4c31d1bd5348b88d6f5e5dee2b54db4a2146cc9f249eea") * FpE::from(2); - let y = FpE::new_base("17949c29effee7a9f13f69b1c28eccd78c1ed12b47068836473481ff818856594fd9c1935e3d9e621901a2d500257a2") * FpE::from(2); - let z = FpE::from(2); - let point_2 = ShortWeierstrassProjectivePoint::::new([x, y, z]).unwrap(); - - let first_algorithm_result = point_2.operate_with(&point_1).to_affine(); - let second_algorithm_result = point_2.operate_with_affine(&point_1).to_affine(); - - assert_eq!(first_algorithm_result, second_algorithm_result); - } - - #[test] - fn create_valid_point_works() { - let p = point_1(); - assert_eq!(*p.x(), FpE::new_base("134e4cc122cb62a06767fb98e86f2d5f77e2a12fefe23bb0c4c31d1bd5348b88d6f5e5dee2b54db4a2146cc9f249eea")); - assert_eq!(*p.y(), FpE::new_base("17949c29effee7a9f13f69b1c28eccd78c1ed12b47068836473481ff818856594fd9c1935e3d9e621901a2d500257a2")); - assert_eq!(*p.z(), FpE::new_base("1")); - } - - #[test] - fn create_invalid_points_panics() { - assert_eq!( - BLS12377Curve::create_point_from_affine(FpE::from(1), FpE::from(1)).unwrap_err(), - EllipticCurveError::InvalidPoint - ) - } - - #[test] - fn equality_works() { - let g = BLS12377Curve::generator(); - let g2 = g.operate_with(&g); - assert_ne!(&g2, &g); - assert_eq!(&g, &g); - } - - #[test] - fn operate_with_self_works_1() { - let g = BLS12377Curve::generator(); - assert_eq!( - g.operate_with(&g).operate_with(&g), - g.operate_with_self(3_u16) - ); - } - - #[test] - fn generator_g1_is_in_subgroup() { - let g = BLS12377Curve::generator(); - assert!(g.is_in_subgroup()) - } - - #[test] - fn point1_is_in_subgroup() { - let p = point_1(); - assert!(p.is_in_subgroup()) - } - - #[test] - fn arbitrary_g1_point_is_in_subgroup() { - let g = BLS12377Curve::generator().operate_with_self(32u64); - assert!(g.is_in_subgroup()) - } - #[test] - fn generator_g2_is_in_subgroup() { - let g = BLS12377TwistCurve::generator(); - assert!(g.is_in_subgroup()) - } - - #[test] - fn g2_conjugate_works() { - let a = Fp2::zero(); - let mut expected = a.conjugate(); - expected = expected.conjugate(); - - assert_eq!(a, expected); - } -} diff --git a/crates/math/src/elliptic_curve/short_weierstrass/curves/bls12_377/field_extension.rs b/crates/math/src/elliptic_curve/short_weierstrass/curves/bls12_377/field_extension.rs deleted file mode 100644 index d806d7318..000000000 --- a/crates/math/src/elliptic_curve/short_weierstrass/curves/bls12_377/field_extension.rs +++ /dev/null @@ -1,424 +0,0 @@ -use crate::field::{ - element::FieldElement, - errors::FieldError, - extensions::{ - cubic::{CubicExtensionField, HasCubicNonResidue}, - quadratic::{HasQuadraticNonResidue, QuadraticExtensionField}, - }, - fields::montgomery_backed_prime_fields::{IsModulus, MontgomeryBackendPrimeField}, - traits::{IsField, IsSubFieldOf}, -}; -use crate::traits::ByteConversion; -use crate::unsigned_integer::element::U384; - -pub const BLS12377_PRIME_FIELD_ORDER: U384 = U384::from_hex_unchecked("1ae3a4617c510eac63b05c06ca1493b1a22d9f300f5138f1ef3622fba094800170b5d44300000008508c00000000001"); -pub const FP2_RESIDUE: FieldElement =FieldElement::from_hex_unchecked("0x1ae3a4617c510eac63b05c06ca1493b1a22d9f300f5138f1ef3622fba094800170b5d44300000008508bffffffffffc"); - -// FPBLS12377 -#[derive(Clone, Debug)] -pub struct BLS12377FieldModulus; -impl IsModulus for BLS12377FieldModulus { - const MODULUS: U384 = BLS12377_PRIME_FIELD_ORDER; -} - -pub type BLS12377PrimeField = MontgomeryBackendPrimeField; -type Fp2E = FieldElement; - -#[derive(Clone, Debug)] -pub struct Degree2ExtensionField; - -impl IsField for Degree2ExtensionField { - type BaseType = [FieldElement; 2]; - /// Returns the component wise addition of `a` and `b` - fn add(a: &Self::BaseType, b: &Self::BaseType) -> Self::BaseType { - [&a[0] + &b[0], &a[1] + &b[1]] - } - - /// Returns the multiplication of `a` and `b` using the following - /// equation: - /// (a0 + a1 * t) * (b0 + b1 * t) = a0 * b0 + a1 * b1 * Self::residue() + (a0 * b1 + a1 * b0) * t - /// where `t.pow(2)` equals `Q::residue()`. - fn mul(a: &Self::BaseType, b: &Self::BaseType) -> Self::BaseType { - let a0b0 = &a[0] * &b[0]; - let a1b1 = &a[1] * &b[1]; - [&a0b0 + &a1b1 * &FP2_RESIDUE, &a[0] * &b[1] + &a[1] * &b[0]] - } - - fn square(a: &Self::BaseType) -> Self::BaseType { - let [a0, a1] = a; - let v0 = a0 * a1; - let c0 = (a0 + a1) * (a0 + &FP2_RESIDUE * a1) - &v0 - &FP2_RESIDUE * &v0; - let c1 = &v0 + &v0; - [c0, c1] - } - /// Returns the component wise subtraction of `a` and `b` - fn sub(a: &Self::BaseType, b: &Self::BaseType) -> Self::BaseType { - [&a[0] - &b[0], &a[1] - &b[1]] - } - - /// Returns the component wise negation of `a` - fn neg(a: &Self::BaseType) -> Self::BaseType { - [-&a[0], -&a[1]] - } - - /// Returns the multiplicative inverse of `a` - /// This uses the equality `(a0 + a1 * t) * (a0 - a1 * t) = a0.pow(2) - a1.pow(2) * Q::residue()` - fn inv(a: &Self::BaseType) -> Result { - let inv_norm = (a[0].square() - FP2_RESIDUE * a[1].square()).inv()?; - Ok([&a[0] * &inv_norm, -&a[1] * inv_norm]) - } - - /// Returns the division of `a` and `b` - fn div(a: &Self::BaseType, b: &Self::BaseType) -> Result { - let b_inv = &Self::inv(b).map_err(|_| FieldError::DivisionByZero)?; - Ok(::mul(a, b_inv)) - } - - /// Returns a boolean indicating whether `a` and `b` are equal component wise. - fn eq(a: &Self::BaseType, b: &Self::BaseType) -> bool { - a[0] == b[0] && a[1] == b[1] - } - - /// Returns the additive neutral element of the field extension. - fn zero() -> Self::BaseType { - [FieldElement::zero(), FieldElement::zero()] - } - - /// Returns the multiplicative neutral element of the field extension. - fn one() -> Self::BaseType { - [FieldElement::one(), FieldElement::zero()] - } - - /// Returns the element `x * 1` where 1 is the multiplicative neutral element. - fn from_u64(x: u64) -> Self::BaseType { - [FieldElement::from(x), FieldElement::zero()] - } - - /// Takes as input an element of BaseType and returns the internal representation - /// of that element in the field. - /// Note: for this case this is simply the identity, because the components - /// already have correct representations. - fn from_base_type(x: Self::BaseType) -> Self::BaseType { - x - } -} - -impl IsSubFieldOf for BLS12377PrimeField { - fn mul( - a: &Self::BaseType, - b: &::BaseType, - ) -> ::BaseType { - let c0 = FieldElement::from_raw(::mul(a, b[0].value())); - let c1 = FieldElement::from_raw(::mul(a, b[1].value())); - [c0, c1] - } - - fn add( - a: &Self::BaseType, - b: &::BaseType, - ) -> ::BaseType { - let c0 = FieldElement::from_raw(::add(a, b[0].value())); - let c1 = FieldElement::from_raw(*b[1].value()); - [c0, c1] - } - - fn div( - a: &Self::BaseType, - b: &::BaseType, - ) -> Result<::BaseType, FieldError> { - let b_inv = Degree2ExtensionField::inv(b)?; - Ok(>::mul( - a, &b_inv, - )) - } - - fn sub( - a: &Self::BaseType, - b: &::BaseType, - ) -> ::BaseType { - let c0 = FieldElement::from_raw(::sub(a, b[0].value())); - let c1 = FieldElement::from_raw(::neg(b[1].value())); - [c0, c1] - } - - fn embed(a: Self::BaseType) -> ::BaseType { - [FieldElement::from_raw(a), FieldElement::zero()] - } - - #[cfg(feature = "alloc")] - fn to_subfield_vec( - b: ::BaseType, - ) -> alloc::vec::Vec { - b.into_iter().map(|x| x.to_raw()).collect() - } -} - -impl ByteConversion for FieldElement { - #[cfg(feature = "alloc")] - fn to_bytes_be(&self) -> alloc::vec::Vec { - let mut byte_slice = ByteConversion::to_bytes_be(&self.value()[0]); - byte_slice.extend(ByteConversion::to_bytes_be(&self.value()[1])); - byte_slice - } - - #[cfg(feature = "alloc")] - fn to_bytes_le(&self) -> alloc::vec::Vec { - let mut byte_slice = ByteConversion::to_bytes_le(&self.value()[0]); - byte_slice.extend(ByteConversion::to_bytes_le(&self.value()[1])); - byte_slice - } - - fn from_bytes_be(bytes: &[u8]) -> Result - where - Self: core::marker::Sized, - { - const BYTES_PER_FIELD: usize = 48; - let x0 = FieldElement::from_bytes_be(&bytes[0..BYTES_PER_FIELD])?; - let x1 = FieldElement::from_bytes_be(&bytes[BYTES_PER_FIELD..BYTES_PER_FIELD * 2])?; - Ok(Self::new([x0, x1])) - } - - fn from_bytes_le(bytes: &[u8]) -> Result - where - Self: core::marker::Sized, - { - const BYTES_PER_FIELD: usize = 48; - let x0 = FieldElement::from_bytes_le(&bytes[0..BYTES_PER_FIELD])?; - let x1 = FieldElement::from_bytes_le(&bytes[BYTES_PER_FIELD..BYTES_PER_FIELD * 2])?; - Ok(Self::new([x0, x1])) - } -} - -impl FieldElement { - pub fn new_base(a_hex: &str) -> Self { - Self::new([FieldElement::new(U384::from(a_hex)), FieldElement::zero()]) - } - - pub fn conjugate(&self) -> Self { - let [a, b] = self.value(); - Self::new([a.clone(), -b]) - } -} - -#[derive(Debug, Clone)] -pub struct BLS12377Residue; -impl HasQuadraticNonResidue for BLS12377Residue { - fn residue() -> FieldElement { - FP2_RESIDUE - } -} - -#[derive(Debug, Clone)] -pub struct LevelTwoResidue; - -impl HasCubicNonResidue for LevelTwoResidue { - fn residue() -> FieldElement { - FieldElement::new([ - FieldElement::new(U384::from("0")), - FieldElement::new(U384::from("1")), - ]) - } -} -pub type Degree6ExtensionField = CubicExtensionField; -type Fp6E = FieldElement; - -#[derive(Debug, Clone)] -pub struct LevelThreeResidue; -impl HasQuadraticNonResidue for LevelThreeResidue { - fn residue() -> FieldElement { - FieldElement::new([ - FieldElement::zero(), - FieldElement::one(), - FieldElement::zero(), - ]) - } -} - -/// We define Fp12 = Fp6 [w] / (w^2 - v) -pub type Degree12ExtensionField = QuadraticExtensionField; -pub type Fp12E = FieldElement; - -impl FieldElement { - pub fn new_base(a_hex: &str) -> Self { - Self::new([ - FieldElement::new([FieldElement::new(U384::from(a_hex)), FieldElement::zero()]), - FieldElement::zero(), - FieldElement::zero(), - ]) - } -} - -impl FieldElement { - pub fn new_base(a_hex: &str) -> Self { - Self::new(U384::from(a_hex)) - } -} - -impl FieldElement { - pub fn new_base(a_hex: &str) -> Self { - Self::new([Fp2E::new_base(a_hex), Fp2E::zero()]) - } -} - -impl FieldElement { - pub fn new_base(a_hex: &str) -> Self { - Self::new([ - FieldElement::::new_base(a_hex), - FieldElement::zero(), - ]) - } - - pub fn from_coefficients(coefficients: &[&str; 12]) -> Self { - FieldElement::::new([ - FieldElement::new([ - FieldElement::new([ - FieldElement::new(U384::from(coefficients[0])), - FieldElement::new(U384::from(coefficients[1])), - ]), - FieldElement::new([ - FieldElement::new(U384::from(coefficients[2])), - FieldElement::new(U384::from(coefficients[3])), - ]), - FieldElement::new([ - FieldElement::new(U384::from(coefficients[4])), - FieldElement::new(U384::from(coefficients[5])), - ]), - ]), - FieldElement::new([ - FieldElement::new([ - FieldElement::new(U384::from(coefficients[6])), - FieldElement::new(U384::from(coefficients[7])), - ]), - FieldElement::new([ - FieldElement::new(U384::from(coefficients[8])), - FieldElement::new(U384::from(coefficients[9])), - ]), - FieldElement::new([ - FieldElement::new(U384::from(coefficients[10])), - FieldElement::new(U384::from(coefficients[11])), - ]), - ]), - ]) - } -} -impl HasQuadraticNonResidue for LevelTwoResidue { - fn residue() -> FieldElement { - FieldElement::new([ - FieldElement::new(U384::from("0")), - FieldElement::new(U384::from("1")), - ]) - } -} -pub type Degree4ExtensionField = QuadraticExtensionField; - -/// Computes the multiplication of an element of fp2 by the level two non-residue (0+u). -pub fn mul_fp2_by_nonresidue(a: &Fp2E) -> Fp2E { - let c0 = FP2_RESIDUE * &a.value()[1]; // c0 = -5 * a1 - let c1 = a.value()[0].clone(); // c1 = a0 - Fp2E::new([c0, c1]) -} -/// Computes the multiplication of an element of fp6 by the level three non-residue v. -pub fn mul_fp6_by_nonresidue(a: &Fp6E) -> Fp6E { - let [a0, a1, a2] = a.value(); - - let c0 = mul_fp2_by_nonresidue(a2); - let c1 = a0.clone(); - let c2 = a1.clone(); - - Fp6E::new([c0, c1, c2]) -} - -///Multiplication between a = a0 + a1 * w and b = b0 + b1 * w with -/// b1 = b10 + b11 * v + 0 * v^2 which is the case of the line used -/// in the miller loop. -pub fn sparse_fp12_mul(a: &Fp12E, b: &Fp12E) -> Fp12E { - let [a0, a1] = a.value(); - let [b0, b1] = b.value(); - let b00 = &b0.value()[0]; - let [b10, b11, _] = b1.value(); - - let t0 = a0 * b0; - let t1 = a1 * b1; - let c0 = &t0 + mul_fp6_by_nonresidue(&t1); - let t2 = Fp6E::new([b10 + b00, b11.clone(), Fp2E::zero()]); - let mut c1 = (a0 + a1) * t2; - c1 = c1 - t0 - t1; - - Fp12E::new([c0, c1]) -} - -#[cfg(test)] -mod tests { - - use super::*; - type FpE = FieldElement; - type Fp2E = FieldElement; - type Fp6E = FieldElement; - type Fp12E = FieldElement; - - #[test] - fn embed_base_field_with_degree_2_extension() { - let a = FpE::from(3); - let a_extension = Fp2E::from(3); - assert_eq!(a.to_extension::(), a_extension); - } - #[test] - fn add_base_field_with_degree_2_extension() { - let a = FpE::from(3); - let a_extension = Fp2E::from(3); - let b = Fp2E::from(2); - assert_eq!(a + &b, a_extension + b); - } - #[test] - fn mul_degree_2_with_degree_6_extension() { - let a = Fp2E::new([FpE::from(3), FpE::from(4)]); - let a_extension = a.clone().to_extension::(); - let b = Fp6E::from(2); - assert_eq!(a * &b, a_extension * b); - } - #[test] - fn div_degree_6_degree_12_extension() { - let a = Fp6E::from(3); - let a_extension = Fp12E::from(3); - let b = Fp12E::from(2); - assert_eq!((a / &b).unwrap(), (a_extension / b).unwrap()); - } - - #[test] - fn double_equals_sum_two_times() { - let a = FpE::from(3); - assert_eq!(a.double(), a.clone() + a); - } - #[test] - fn base_field_sum_is_asociative() { - let a = FpE::from(3); - let b = FpE::from(2); - let c = &a + &b; - assert_eq!(a.double() + b, a + c); - } - #[test] - fn degree_2_extension_mul_is_conmutative() { - let a = Fp2E::from(3); - let b = Fp2E::new([FpE::from(2), FpE::from(4)]); - assert_eq!(&a * &b, b * a); - } - - #[test] - fn base_field_pow_p_is_identity() { - let a = FpE::from(3); - assert_eq!(a.pow(BLS12377_PRIME_FIELD_ORDER), a); - } - #[test] - fn square_equals_mul_two_times() { - let a = FpE::from(3); - assert_eq!(a.square(), a.clone() * a); - } - #[test] - fn test_mul_fp2_by_nonresidue() { - // Element in Fp2: a = 3 + 5u - let a = Fp2E::new([FpE::from(3), FpE::from(5)]); - // Expected result: (-25) + 3u - let expected = Fp2E::new([-FpE::from(25), FpE::from(3)]); - assert_eq!(mul_fp2_by_nonresidue(&a), expected); - } -} diff --git a/crates/math/src/elliptic_curve/short_weierstrass/curves/bls12_377/mod.rs b/crates/math/src/elliptic_curve/short_weierstrass/curves/bls12_377/mod.rs deleted file mode 100644 index 80faf780a..000000000 --- a/crates/math/src/elliptic_curve/short_weierstrass/curves/bls12_377/mod.rs +++ /dev/null @@ -1,4 +0,0 @@ -pub mod curve; -pub mod field_extension; -pub mod pairing; -pub mod twist; diff --git a/crates/math/src/elliptic_curve/short_weierstrass/curves/bls12_377/pairing.rs b/crates/math/src/elliptic_curve/short_weierstrass/curves/bls12_377/pairing.rs deleted file mode 100644 index c17d654c5..000000000 --- a/crates/math/src/elliptic_curve/short_weierstrass/curves/bls12_377/pairing.rs +++ /dev/null @@ -1,476 +0,0 @@ -use super::{ - curve::BLS12377Curve, - field_extension::{ - mul_fp2_by_nonresidue, sparse_fp12_mul, BLS12377PrimeField, Degree12ExtensionField, - Degree2ExtensionField, Degree4ExtensionField, - }, - twist::BLS12377TwistCurve, -}; -use crate::{cyclic_group::IsGroup, elliptic_curve::traits::IsPairing, errors::PairingError}; - -use crate::{ - elliptic_curve::short_weierstrass::{ - curves::bls12_377::field_extension::Degree6ExtensionField, - point::ShortWeierstrassProjectivePoint, traits::IsShortWeierstrass, - }, - field::element::FieldElement, - unsigned_integer::element::U256, -}; - -type FpE = FieldElement; -type Fp2E = FieldElement; -type Fp4E = FieldElement; -type Fp6E = FieldElement; -type Fp12E = FieldElement; -type G1Point = ShortWeierstrassProjectivePoint; -type G2Point = ShortWeierstrassProjectivePoint; -pub const X: u64 = 0x8508c00000000001; - -// X in binary = 1000010100001000110000000000000000000000000000000000000000000001s -pub const X_BINARY: &[bool] = &[ - true, false, false, false, false, false, false, false, false, false, false, false, false, - false, false, false, false, false, false, false, false, false, false, false, false, false, - false, false, false, false, false, false, false, false, false, false, false, false, false, - false, false, false, false, false, false, false, true, true, false, false, false, true, false, - false, false, false, true, false, true, false, false, false, false, true, -]; - -pub const SUBGROUP_ORDER: U256 = - U256::from_hex_unchecked("12ab655e9a2ca55660b44d1e5c37b00159aa76fed00000010a11800000000001"); - -// GAMMA constants used to compute the Frobenius morphisms -pub const GAMMA_11: Fp2E = Fp2E::const_from_raw([ - FpE::from_hex_unchecked( - "0x9a9975399c019633c1e30682567f915c8a45e0f94ebc8ec681bf34a3aa559db57668e558eb0188e938a9d1104f2031", - ), - FpE::from_hex_unchecked("0"), -]); - -pub const GAMMA_12: Fp2E = Fp2E::const_from_raw([ - FpE::from_hex_unchecked( - "0x9b3af05dd14f6ec619aaf7d34594aabc5ed1347970dec00452217cc900000008508c00000000002", - ), - FpE::from_hex_unchecked("0"), -]); - -pub const GAMMA_13: Fp2E = Fp2E::const_from_raw([ - FpE::from_hex_unchecked( - "0x1680a40796537cac0c534db1a79beb1400398f50ad1dec1bce649cf436b0f6299588459bff27d8e6e76d5ecf1391c63", - ), - FpE::from_hex_unchecked("0"), -]); - -pub const GAMMA_14: Fp2E = Fp2E::const_from_raw([ - FpE::from_hex_unchecked( - "0x9b3af05dd14f6ec619aaf7d34594aabc5ed1347970dec00452217cc900000008508c00000000001", - ), - FpE::from_hex_unchecked("0"), -]); - -pub const GAMMA_15: Fp2E = Fp2E::const_from_raw([ - FpE::from_hex_unchecked( - "0xcd70cb3fc936348d0351d498233f1fe379531411832232f6648a9a9fc0b9c4e3e21b7467077c05853e2c1be0e9fc32", - ), - FpE::from_hex_unchecked("0"), -]); - -/// GAMMA_2i = GAMMA_1i * GAMMA_1i.conjugate() -pub const GAMMA_21: FpE = FpE::from_hex_unchecked( - "0x9b3af05dd14f6ec619aaf7d34594aabc5ed1347970dec00452217cc900000008508c00000000002", -); - -pub const GAMMA_22: FpE = FpE::from_hex_unchecked( - "0x9b3af05dd14f6ec619aaf7d34594aabc5ed1347970dec00452217cc900000008508c00000000001", -); - -pub const GAMMA_23: FpE = - FpE::from_hex_unchecked("0x1ae3a4617c510eac63b05c06ca1493b1a22d9f300f5138f1ef3622fba094800170b5d44300000008508c00000000000"); - -pub const GAMMA_24: FpE = - FpE::from_hex_unchecked("0x1ae3a4617c510eabc8756ba8f8c524eb8882a75cc9bc8e359064ee822fb5bffd1e945779fffffffffffffffffffffff"); - -pub const GAMMA_25: FpE = - FpE::from_hex_unchecked("0x1ae3a4617c510eabc8756ba8f8c524eb8882a75cc9bc8e359064ee822fb5bffd1e94577a00000000000000000000000"); - -/// The inverse of two in Fp as a constant. -pub const TWO_INV: FpE = - FpE::from_hex_unchecked("D71D230BE28875631D82E03650A49D8D116CF9807A89C78F79B117DD04A4000B85AEA2180000004284600000000001"); - -#[derive(Clone)] -pub struct BLS12377AtePairing; - -impl IsPairing for BLS12377AtePairing { - type G1Point = ShortWeierstrassProjectivePoint; - type G2Point = ShortWeierstrassProjectivePoint; - type OutputField = Degree12ExtensionField; - - /// Compute the product of the ate pairings for a list of point pairs. - fn compute_batch( - pairs: &[(&Self::G1Point, &Self::G2Point)], - ) -> Result, PairingError> { - let mut result = FieldElement::one(); - for (p, q) in pairs { - if !p.is_in_subgroup() || !q.is_in_subgroup() { - return Err(PairingError::PointNotInSubgroup); - } - if !p.is_neutral_element() && !q.is_neutral_element() { - let p = p.to_affine(); - let q = q.to_affine(); - result *= miller(&p, &q); - } - } - final_exponentiation(&result) - } -} - -pub fn miller(p: &G1Point, q: &G2Point) -> Fp12E { - let mut t = q.clone(); - let mut f = Fp12E::one(); - - X_BINARY.iter().rev().skip(1).for_each(|&bit| { - let (r, l) = line(p, &t, &t); - f = sparse_fp12_mul(&f.square(), &l); - t = r; - - if bit { - let (r, l) = line(p, &t, q); - f = sparse_fp12_mul(&f, &l); - t = r; - } - }); - f -} -#[allow(clippy::ptr_eq)] -fn line(p: &G1Point, t: &G2Point, q: &G2Point) -> (G2Point, Fp12E) { - let [x_p, y_p, _] = p.coordinates(); - - if t as *const G2Point == q as *const G2Point || t == q { - let a = TWO_INV * t.x() * t.y(); - let b = t.y().square(); - let c = t.z().square(); - let e = BLS12377TwistCurve::b() * (c.double() + &c); - let f = e.double() + &e; - let g = TWO_INV * (&b + &f); - let h = (t.y() + t.z()).square() - (&b + &c); - let i = &e - &b; - let j = t.x().square(); - let e_square = e.square(); - - let x_r = a * (&b - f); - let y_r = g.square() - (e_square.double() + e_square); - let z_r = b * &h; - - debug_assert_eq!( - BLS12377TwistCurve::defining_equation_projective(&x_r, &y_r, &z_r), - Fp2E::zero() - ); - // SAFETY: `unwrap()` is used here because we ensure that `x_r, y_r, z_r` - // satisfy the curve equation. The previous assertion checks that this is indeed the case. - let r = G2Point::new([x_r, y_r, z_r]); - - let l = Fp12E::new([ - Fp6E::new([y_p * (-h), Fp2E::zero(), Fp2E::zero()]), - Fp6E::new([x_p * (j.double() + &j), i, Fp2E::zero()]), - ]); - (r.unwrap(), l) - } else { - let [x_q, y_q, _] = q.coordinates(); - let [x_t, y_t, z_t] = t.coordinates(); - - let a = y_q * z_t; - let b = x_q * z_t; - let theta = t.y() - a; - let lambda = t.x() - b; - let c = theta.square(); - let d = lambda.square(); - let e = &lambda * &d; - let f = z_t * c; - let g = x_t * d; - let h = &e + f - g.double(); - let i = y_t * &e; - let j = &theta * x_q - (&lambda * y_q); - - let x_r = &lambda * &h; - let y_r = &theta * (g - h) - i; - let z_r = z_t * e; - - debug_assert_eq!( - BLS12377TwistCurve::defining_equation_projective(&x_r, &y_r, &z_r), - Fp2E::zero() - ); - // SAFETY: The values `x_r, y_r, z_r` are computed correctly to be on the curve. - // The assertion above verifies that the resulting point is valid. - let r = G2Point::new([x_r, y_r, z_r]); - - let l = Fp12E::new([ - Fp6E::new([y_p * lambda, Fp2E::zero(), Fp2E::zero()]), - Fp6E::new([x_p * (-theta), j, Fp2E::zero()]), - ]); - (r.unwrap(), l) - } -} - -pub fn frobenius(f: &Fp12E) -> Fp12E { - let [a, b] = f.value(); // f = a + bw, where a and b in Fp6. - let [a0, a1, a2] = a.value(); // a = a0 + a1 * v + a2 * v^2, where a0, a1 and a2 in Fp2. - let [b0, b1, b2] = b.value(); // b = b0 + b1 * v + b2 * v^2, where b0, b1 and b2 in Fp2. - - // c1 = a0.conjugate() + a1.conjugate() * GAMMA_12 * v + a2.conjugate() * GAMMA_14 * v^2 - let c1 = Fp6E::new([ - a0.conjugate(), - a1.conjugate() * GAMMA_12, - a2.conjugate() * GAMMA_14, - ]); - - let c2 = Fp6E::new([ - b0.conjugate() * GAMMA_11, - b1.conjugate() * GAMMA_13, - b2.conjugate() * GAMMA_15, - ]); - - Fp12E::new([c1, c2]) //c1 + c2 * w -} -fn frobenius_square( - f: &FieldElement, -) -> FieldElement { - let [a, b] = f.value(); - let [a0, a1, a2] = a.value(); - let [b0, b1, b2] = b.value(); - - let c1 = Fp6E::new([a0.clone(), GAMMA_22 * a1, GAMMA_24 * a2]); - let c2 = Fp6E::new([GAMMA_21 * b0, GAMMA_23 * b1, GAMMA_25 * b2]); - - Fp12E::new([c1, c2]) -} - -////////////////// CYCLOTOMIC SUBGROUP OPERATIONS ////////////////// -// Since the result of the Easy Part of the Final Exponentiation belongs to the cyclotomic -// subgroup of Fp12, we can optimize the square and pow operations used in the Hard Part. -/// Computes the square of an element of a cyclotomic subgroup of Fp12. -/// Algorithm from Constantine's cyclotomic_square_quad_over_cube -/// https://github.com/mratsim/constantine/blob/master/constantine/math/pairings/cyclotomic_subgroups.nim#L354 -pub fn cyclotomic_square(a: &Fp12E) -> Fp12E { - // a = g + h * w - let [g, h] = a.value(); - let [b0, b1, b2] = g.value(); - let [b3, b4, b5] = h.value(); - - let v0 = Fp4E::new([b0.clone(), b4.clone()]).square(); - let v1 = Fp4E::new([b3.clone(), b2.clone()]).square(); - let v2 = Fp4E::new([b1.clone(), b5.clone()]).square(); - - // r = r0 + r1 * w - // r0 = r00 + r01 * v + r02 * v^2 - // r1 = r10 + r11 * v + r12 * v^2 - - // r00 = 3v00 - 2b0 - let mut r00 = &v0.value()[0] - b0; - r00 = r00.double(); - r00 = &v0.value()[0] + r00; - - // r01 = 3v10 -2b1 - let mut r01 = &v1.value()[0] - b1; - r01 = r01.double(); - r01 = &v1.value()[0] + r01; - - // r11 = 3v01 - 2b4 - let mut r11 = &v0.value()[1] + b4; - r11 = r11.double(); - r11 = &v0.value()[1] + r11; - // r12 = 3v11 - 2b5 - let mut r12 = &v1.value()[1] + b5; - r12 = r12.double(); - r12 = &v1.value()[1] + r12; - // r12 = 3v11 - 2b5 - - let v21 = mul_fp2_by_nonresidue(&v2.value()[1]); - let mut r10 = &v21 + b3; - r10 = r10.double(); - r10 += v21; - - // 3 * ( u) * v20 - 2b3 - let mut r02 = &v2.value()[0] - b2; - r02 = r02.double(); - r02 = &v2.value()[0] + r02; - - Fp12E::new([Fp6E::new([r00, r01, r02]), Fp6E::new([r10, r11, r12])]) -} - -// To understand more about how to reduce the final exponentiation -// read "Efficient Final Exponentiation via Cyclotomic Structure for -// Pairings over Families of Elliptic Curves" (https://eprint.iacr.org/2020/875.pdf) - -pub fn final_exponentiation(f: &Fp12E) -> Result { - let f_easy_aux = f.conjugate() * f.inv().map_err(|_| PairingError::DivisionByZero)?; - let mut f_easy = frobenius_square(&f_easy_aux) * &f_easy_aux; - - let mut v2 = cyclotomic_square(&f_easy); // v2 = f² - let mut v0 = cyclotomic_pow_x(&f_easy); // v0 = f^x - let mut v1 = f_easy.conjugate(); // v1 = f^-1 - - // (x−1)² - v0 *= v1; // v0 = f^(x-1) - v1 = cyclotomic_pow_x(&v0); // v1 = (f^(x-1))^(x) - - v0 = v0.conjugate(); // v0 = (f^(x-1))^(-1) - v0 *= &v1; // v0 = (f^(x-1))^(-1) * (f^(x-1))^x = (f^(x-1))^(x-1) = f^((x-1)²) - - // (x+p) - v1 = cyclotomic_pow_x(&v0); // v1 = f^((x-1)².x) - v0 = frobenius(&v0); // f^((x-1)².p) - v0 *= &v1; // f^((x-1)².p + (x-1)².x) = f^((x-1)².(x+p)) - - // + 3 - f_easy *= v2; // f^3 - - // (x²+p²−1) - v2 = cyclotomic_pow_x(&v0); - v1 = cyclotomic_pow_x(&v2); // v1 = f^((x-1)².(x+p).x²) - v2 = frobenius_square(&v0); // v2 = f^((x-1)².(x+p).p²) - v0 = v0.conjugate(); // v0 = f^((x-1)².(x+p).-1) - v0 *= &v1; // v0 = f^((x-1)².(x+p).(x²-1)) - v0 *= &v2; // v0 = f^((x-1)².(x+p).(x²+p²-1)) - - f_easy *= &v0; - Ok(f_easy) -} - -pub fn cyclotomic_pow_x(f: &Fp12E) -> Fp12E { - let mut result = Fp12E::one(); - X_BINARY.iter().rev().for_each(|&bit| { - result = cyclotomic_square(&result); - if bit { - result = &result * f; - } - }); - result -} - -#[cfg(test)] -mod tests { - use crate::{ - cyclic_group::IsGroup, elliptic_curve::traits::IsEllipticCurve, - unsigned_integer::element::U384, - }; - - use super::*; - #[test] - fn test_line_optimized_doubles_point_correctly() { - let g1 = BLS12377Curve::generator().to_affine(); - let g2 = BLS12377TwistCurve::generator().to_affine(); - - let (r, _line) = line(&g1, &g2, &g2); - - let expected = g2.operate_with(&g2); - assert_eq!(r, expected); - } - - #[test] - fn test_line_optimized_adds_points_correctly() { - let g1 = BLS12377Curve::generator().to_affine(); - let g = BLS12377TwistCurve::generator(); - - let a: u64 = 12; - let b: u64 = 23; - - let g2 = g.operate_with_self(a).to_affine(); - let g3 = g.operate_with_self(b).to_affine(); - - let expected = g.operate_with_self(a + b); - let (r, _line) = line(&g1, &g2, &g3); - assert_eq!(r, expected); - } - - #[test] - fn batch_ate_pairing_bilinearity() { - let p = BLS12377Curve::generator(); - let q = BLS12377TwistCurve::generator(); - let a = U384::from_u64(11); - let b = U384::from_u64(93); - - let result = BLS12377AtePairing::compute_batch(&[ - ( - &p.operate_with_self(a).to_affine(), - &q.operate_with_self(b).to_affine(), - ), - ( - &p.operate_with_self(a * b).to_affine(), - &q.neg().to_affine(), - ), - ]) - .unwrap(); - assert_eq!(result, FieldElement::one()); - } - - #[test] - fn ate_pairing_returns_one_when_one_element_is_the_neutral_element() { - let p = BLS12377Curve::generator().to_affine(); - let q = ShortWeierstrassProjectivePoint::neutral_element(); - let result = BLS12377AtePairing::compute_batch(&[(&p.to_affine(), &q)]).unwrap(); - assert_eq!(result, FieldElement::one()); - - let p = ShortWeierstrassProjectivePoint::neutral_element(); - let q = BLS12377TwistCurve::generator(); - let result = BLS12377AtePairing::compute_batch(&[(&p, &q.to_affine())]).unwrap(); - assert_eq!(result, FieldElement::one()); - } - - #[test] - fn ate_pairing_errors_when_one_element_is_not_in_subgroup() { - // p = (0, 1, 1) is in the curve but not in the subgroup. - // Recall that the BLS 12-377 curve equation is y^2 = x^3 + 1. - let p = ShortWeierstrassProjectivePoint::new([ - FieldElement::zero(), - FieldElement::one(), - FieldElement::one(), - ]) - .unwrap(); - let q = ShortWeierstrassProjectivePoint::neutral_element(); - let result = BLS12377AtePairing::compute_batch(&[(&p.to_affine(), &q)]); - assert!(result.is_err()) - } - - #[test] - fn apply_12_times_frobenius_is_identity() { - let f = Fp12E::from_coefficients(&[ - "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", - ]); - let mut result = frobenius(&f); - for _ in 1..12 { - result = frobenius(&result); - } - assert_eq!(f, result) - } - - #[test] - fn apply_6_times_frobenius_square_is_identity() { - let f = Fp12E::from_coefficients(&[ - "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", - ]); - let mut result = frobenius_square(&f); - for _ in 1..6 { - result = frobenius_square(&result); - } - assert_eq!(f, result) - } - - #[test] - fn cyclotomic_square_equals_square() { - let p = BLS12377Curve::generator(); - let q = BLS12377TwistCurve::generator(); - let f = miller(&p, &q); - let f_easy_aux = f.conjugate() * f.inv().unwrap(); // f ^ (p^6 - 1) because f^(p^6) = f.conjugate(). - let f_easy = &frobenius_square(&f_easy_aux) * f_easy_aux; // (f^{p^6 - 1})^(p^2) * (f^{p^6 - 1}). - assert_eq!(cyclotomic_square(&f_easy), f_easy.square()); - } - - #[test] - fn cyclotomic_pow_x_equals_pow() { - let p = BLS12377Curve::generator(); - let q = BLS12377TwistCurve::generator(); - let f = miller(&p, &q); - let f_easy_aux = f.conjugate() * f.inv().unwrap(); // f ^ (p^6 - 1) - let f_easy = frobenius_square(&f_easy_aux) * &f_easy_aux; // f^{(p^2)(p^6 - 1)} - assert_eq!(cyclotomic_pow_x(&f_easy), f_easy.pow(X)); - } -} diff --git a/crates/math/src/elliptic_curve/short_weierstrass/curves/bls12_377/twist.rs b/crates/math/src/elliptic_curve/short_weierstrass/curves/bls12_377/twist.rs deleted file mode 100644 index ff660ec14..000000000 --- a/crates/math/src/elliptic_curve/short_weierstrass/curves/bls12_377/twist.rs +++ /dev/null @@ -1,176 +0,0 @@ -use super::field_extension::Degree2ExtensionField; -use crate::elliptic_curve::short_weierstrass::point::ShortWeierstrassProjectivePoint; -use crate::elliptic_curve::traits::IsEllipticCurve; -use crate::unsigned_integer::element::U384; -use crate::{ - elliptic_curve::short_weierstrass::traits::IsShortWeierstrass, field::element::FieldElement, -}; - -const GENERATOR_X_0: U384 = U384::from_hex_unchecked("0x018480be71c785fec89630a2a3841d01c565f071203e50317ea501f557db6b9b71889f52bb53540274e3e48f7c005196"); -const GENERATOR_X_1: U384 = U384::from_hex_unchecked("0x00ea6040e700403170dc5a51b1b140d5532777ee6651cecbe7223ece0799c9de5cf89984bff76fe6b26bfefa6ea16afe"); -const GENERATOR_Y_0: U384 = U384::from_hex_unchecked("0x00690d665d446f7bd960736bcbb2efb4de03ed7274b49a58e458c282f832d204f2cf88886d8c7c2ef094094409fd4ddf"); -const GENERATOR_Y_1: U384 = U384::from_hex_unchecked("0x00f8169fd28355189e549da3151a70aa61ef11ac3d591bf12463b01acee304c24279b83f5e52270bd9a1cdd185eb8f93"); - -/// The description of the curve. -#[derive(Clone, Debug)] -pub struct BLS12377TwistCurve; - -impl IsEllipticCurve for BLS12377TwistCurve { - type BaseField = Degree2ExtensionField; - type PointRepresentation = ShortWeierstrassProjectivePoint; - - fn generator() -> Self::PointRepresentation { - // SAFETY: - // - The generator point is mathematically verified to be a valid point on the curve. - // - `unwrap()` is safe because the provided coordinates satisfy the curve equation.s - let point = Self::PointRepresentation::new([ - FieldElement::new([ - FieldElement::new(GENERATOR_X_0), - FieldElement::new(GENERATOR_X_1), - ]), - FieldElement::new([ - FieldElement::new(GENERATOR_Y_0), - FieldElement::new(GENERATOR_Y_1), - ]), - FieldElement::one(), - ]); - point.unwrap() - } -} - -impl IsShortWeierstrass for BLS12377TwistCurve { - fn a() -> FieldElement { - FieldElement::zero() - } - - fn b() -> FieldElement { - FieldElement::new([FieldElement::zero(), - FieldElement::from_hex_unchecked("0x10222f6db0fd6f343bd03737460c589dc7b4f91cd5fd889129207b63c6bf8000dd39e5c1ccccccd1c9ed9999999999a")]) - } -} - -#[cfg(test)] -mod tests { - use crate::{ - cyclic_group::IsGroup, - elliptic_curve::{ - short_weierstrass::{ - curves::bls12_377::field_extension::{BLS12377PrimeField, Degree2ExtensionField}, - traits::IsShortWeierstrass, - }, - traits::IsEllipticCurve, - }, - field::element::FieldElement, - unsigned_integer::element::U384, - }; - - use super::BLS12377TwistCurve; - type Level0FE = FieldElement; - type Level1FE = FieldElement; - - #[test] - fn create_generator() { - let g = BLS12377TwistCurve::generator(); - let [x, y, _] = g.coordinates(); - assert_eq!( - BLS12377TwistCurve::defining_equation(x, y), - Level1FE::zero() - ); - } - #[test] - // Values checked in sage - fn add_point_three_times_equals_operate_with_self_3() { - let px = Level1FE::new([ - Level0FE::new(U384::from_hex_unchecked("0x11a87eda97b96e733c2eb833ae35531b87878b416d57b370c7cd13b5f3c413387633b0ca6dfead19305318501376087")), - Level0FE::new(U384::from_hex_unchecked("0xa4a6d842722f2636937acf0e889ab343e121a599b8a3a9bd3be766da7d84f8e060be00f06bb2d29df963bf2d847598")) - ]); - let py = Level1FE::new([ - Level0FE::new(U384::from_hex_unchecked("0x75589c0925d6cf45e715460ea7cb3388d4e21d9b79aa8411567d8de85ba5561bcab80a5c0b363e31817d458e5b2a2a")), - Level0FE::new(U384::from_hex_unchecked("0xcb4e1e4b160cc6c92e7b3dd0b2f4053bc7178d201e7788796a6035c59ccd586635796e97003b1f76eca273576f01ac")) - ]); - - let expectedx = Level1FE::new([ - Level0FE::new(U384::from_hex_unchecked("0x1aba32e70b88834d0cd5fb3a27eda8211eb94b6a191cd287f798145c09dc64dabda377836c31d31cc728635425ccc61")), - Level0FE::new(U384::from_hex_unchecked("0x146b578a9c3d92f64baafa082d27c3446fb197659bbd4ac45e290f5f49ae308599448dc288140a4049bac3c6e3e1aac")) - ]); - let expectedy = Level1FE::new([ - Level0FE::new(U384::from_hex_unchecked("0x1417486a114a5a9074151a3a2c710dc105cce81de69a91067758355cda7049a2ad8077a2343679bba473484ad0cddd6")), - Level0FE::new(U384::from_hex_unchecked("0x195c939995782a07b26e3b44b49d58eb0951d452d0e8928f218a8e63f74f74860cb56265e437da80df67b6254b27cd")) - ]); - let p = BLS12377TwistCurve::create_point_from_affine(px, py).unwrap(); - let expected = BLS12377TwistCurve::create_point_from_affine(expectedx, expectedy).unwrap(); - assert_eq!(p.operate_with_self(3_u16), expected); - } - - #[test] - // Values checked in sage - fn operate_with_self_test() { - let px = Level1FE::new([ - Level0FE::new(U384::from_hex_unchecked("0x2233db786c8cb6a3b6846aebad4ce3f5346961c8bade4c129d920170d1ceeb02d84fd4e12b592f0cba64e083d75167")), - Level0FE::new(U384::from_hex_unchecked("0x12d1c7ac03cb1991993cdb9d38c50588b67c18ed9b9db5f84ac5b59c201f6493a42f690169b96b7a9f12fae4718b6cb")) - ]); - - let py = Level1FE::new([ - Level0FE::new(U384::from_hex_unchecked("0x160cc59bb3929b1073996aa370c880e002b81f3e4f7275636caf55754bbfcfe1d43c5d91ee7f3cb49254be2366d5d0")), - Level0FE::new(U384::from_hex_unchecked("0xe66460970bf2fc79831983744d7c83fad910266fd56f08b4a8f737e7609d88930503091e06228a184627b57c02352")) - ]); - - let qx = Level1FE::new([ - Level0FE::new(U384::from_hex_unchecked("0x1124128cf7166bb67207327079f8a3e75d876e3b6dd54cc05c766951c5d832aa202c57ed2308d78283e4c859be8fee3")), - Level0FE::new(U384::from_hex_unchecked("0x1889e19d4f67d120d367c15f7bc23529fe4e335627e0eb16ec2bafe6199f0e7d393c5413fc7157ec03d5d56e1eb333")) - ]); - - let qy = Level1FE::new([ - Level0FE::new(U384::from_hex_unchecked("0x16db05c371bf6f28e92faa77d3abf5c66c4feea84d334807f46ee69227a2144a827b00f8bcf4179b97922a85ebd5776")), - Level0FE::new(U384::from_hex_unchecked("0x105def68752ac5cb73f3b692b3c877f2febe6cd8a679584f4b1c64f9886f12b15dd7909251bc1e90d559fadd6b1f8f5")) - ]); - - let scalar = U384::from_hex_unchecked( - "0x3061aa3679b1865fa09dac7339a87511147f015aa8997fae59b475d751bc16f", - ); - - let p = BLS12377TwistCurve::create_point_from_affine(px, py).unwrap(); - let q = BLS12377TwistCurve::create_point_from_affine(qx, qy).unwrap(); - - assert_eq!(p.operate_with_self(scalar), q); - } - - #[test] - // Values checked in sage - fn add_two_points() { - let px = Level1FE::new([ - Level0FE::new(U384::from_hex_unchecked("0x11a87eda97b96e733c2eb833ae35531b87878b416d57b370c7cd13b5f3c413387633b0ca6dfead19305318501376087")), - Level0FE::new(U384::from_hex_unchecked("0xa4a6d842722f2636937acf0e889ab343e121a599b8a3a9bd3be766da7d84f8e060be00f06bb2d29df963bf2d847598")) - ]); - - let py = Level1FE::new([ - Level0FE::new(U384::from_hex_unchecked("0x75589c0925d6cf45e715460ea7cb3388d4e21d9b79aa8411567d8de85ba5561bcab80a5c0b363e31817d458e5b2a2a")), - Level0FE::new(U384::from_hex_unchecked("0xcb4e1e4b160cc6c92e7b3dd0b2f4053bc7178d201e7788796a6035c59ccd586635796e97003b1f76eca273576f01ac")) - ]); - - let qx = Level1FE::new([ - Level0FE::new(U384::from_hex_unchecked("0xa27279ea48d8e812223956c84ae89c5de09e67c7052c056d936da4578dcf0b30799f2212af0017fe50e146c5c2ce05")), - Level0FE::new(U384::from_hex_unchecked("0xaed1579f3de386644e0ffac471e98f8f1d97a6be4d88a0c516bc9fd4d7780649424c3d51bb85d074d66560e2c2bb07")) - ]); - - let qy = Level1FE::new([ - Level0FE::new(U384::from_hex_unchecked("0x6e7e249e0d7c81d0fec62a3f96d6fefd7d6c87019559b03664a015a2ed536d98e6432b93ec219426f9729f119c7982")), - Level0FE::new(U384::from_hex_unchecked("0x104c4e4adb48273511a0473d742724f48042b967591ab9b86731bb902debfd44d89571af704340614f6618863fc5841")) - ]); - - let expectedx = Level1FE::new([ - Level0FE::new(U384::from_hex_unchecked("0x19d74f8769a27aae05c5b50b2d051849f6feab554d0fd4f51c7fe43d918fac1d966c97dd6e61b2f7fc13694495dc259")), - Level0FE::new(U384::from_hex_unchecked("0x11f4194886ad75e3baff9853734e9ef4fe5f20d333951ba126bca7dd54ebc4998b17d4d315d29147dc22124c5464a64")) - ]); - let expectedy = Level1FE::new([ - Level0FE::new(U384::from_hex_unchecked("0x14faa941c0cdf5567c294bc9e73e83c4602f331a7400222c8475fba4c6a688b12ce8f7cc20e54eaac1af6046aec58bd")), - Level0FE::new(U384::from_hex_unchecked("0x7cee0c4760e2bf76047cda31141e6242c5893ba5c2fba5f23c0048545912e309dc647f4cfe2db17b23aa2f57c3d8c3")) - ]); - - let p = BLS12377TwistCurve::create_point_from_affine(px, py).unwrap(); - let q = BLS12377TwistCurve::create_point_from_affine(qx, qy).unwrap(); - let expected = BLS12377TwistCurve::create_point_from_affine(expectedx, expectedy).unwrap(); - - assert_eq!(p.operate_with(&q), expected); - } -} diff --git a/crates/math/src/elliptic_curve/short_weierstrass/curves/bls12_381/compression.rs b/crates/math/src/elliptic_curve/short_weierstrass/curves/bls12_381/compression.rs deleted file mode 100644 index 548f2e986..000000000 --- a/crates/math/src/elliptic_curve/short_weierstrass/curves/bls12_381/compression.rs +++ /dev/null @@ -1,381 +0,0 @@ -use super::{field_extension::BLS12381PrimeField, twist::BLS12381TwistCurve}; -use crate::{ - elliptic_curve::short_weierstrass::{ - curves::bls12_381::{curve::BLS12381Curve, field_extension::Degree2ExtensionField, sqrt}, - point::ShortWeierstrassProjectivePoint, - traits::Compress, - }, - field::element::FieldElement, -}; -use core::cmp::Ordering; - -use crate::{ - cyclic_group::IsGroup, elliptic_curve::traits::FromAffine, errors::ByteConversionError, - traits::ByteConversion, -}; - -type G1Point = ShortWeierstrassProjectivePoint; -type G2Point = ShortWeierstrassProjectivePoint; -type BLS12381FieldElement = FieldElement; - -/// This functionality includes the compression and decompression for points belonging to the BLS12-381, following the ideas in -/// Zcash curve compression, check https://hackmd.io/@benjaminion/bls12-381#Point-compression and https://github.com/zcash/librustzcash/blob/6e0364cd42a2b3d2b958a54771ef51a8db79dd29/pairing/src/bls12_381/README.md#serialization -/// The way we encode points differs from the one used ordinarily for serialization in lambdaworks. -/// G1 points are represented by their x coordinate in big-endian form (48 bytes), with the three most significant bits used to give information on the compressed format, whether the point is the point at infinity and which of the two roots to take -/// G2 points are represented by their x coordinate in big-endian form (96 bytes), following the order a * i + b. The three most significant bits contain the same type of information as in G1. -impl Compress for BLS12381Curve { - type G1Point = G1Point; - - type G2Point = G2Point; - - type G1Compressed = [u8; 48]; - - type G2Compressed = [u8; 96]; - - type Error = ByteConversionError; - - #[cfg(feature = "alloc")] - fn compress_g1_point(point: &Self::G1Point) -> Self::G1Compressed { - if *point == G1Point::neutral_element() { - // point is at infinity - let mut x_bytes = [0_u8; 48]; - x_bytes[0] |= 1 << 7; - x_bytes[0] |= 1 << 6; - x_bytes - } else { - // point is not at infinity - let point_affine = point.to_affine(); - let x = point_affine.x(); - let y = point_affine.y(); - let mut x_bytes = [0u8; 48]; - let bytes = x.to_bytes_be(); - x_bytes.copy_from_slice(&bytes); - - // Set first bit to to 1 indicate this is compressed element. - x_bytes[0] |= 1 << 7; - - let y_neg = core::ops::Neg::neg(y); - if y_neg.representative() < y.representative() { - x_bytes[0] |= 1 << 5; - } - x_bytes - } - } - - fn decompress_g1_point(input_bytes: &mut [u8]) -> Result { - if !input_bytes.len() == 48 { - return Err(ByteConversionError::InvalidValue); - } - let first_byte = input_bytes.first().unwrap(); - // We get the 3 most significant bits - let prefix_bits = first_byte >> 5; - let first_bit = (prefix_bits & 4_u8) >> 2; - // If first bit is not 1, then the value is not compressed. - if first_bit != 1 { - return Err(ByteConversionError::ValueNotCompressed); - } - let second_bit = (prefix_bits & 2_u8) >> 1; - // If the second bit is 1, then the compressed point is the - // point at infinity and we return it directly. - if second_bit == 1 { - return Ok(G1Point::neutral_element()); - } - // We obtain the third bit - let third_bit = prefix_bits & 1_u8; - - let first_byte_without_control_bits = (first_byte << 3) >> 3; - input_bytes[0] = first_byte_without_control_bits; - - let x = BLS12381FieldElement::from_bytes_be(input_bytes)?; - - // We apply the elliptic curve formula to know the y^2 value. - let y_squared = x.pow(3_u16) + BLS12381FieldElement::from(4); - - let (y_sqrt_1, y_sqrt_2) = &y_squared.sqrt().ok_or(ByteConversionError::InvalidValue)?; - - // we call "negative" to the greate root, - // if the third bit is 1, we take this grater value. - // Otherwise, we take the second one. - let y = match ( - y_sqrt_1.representative().cmp(&y_sqrt_2.representative()), - third_bit, - ) { - (Ordering::Greater, 0) => y_sqrt_2, - (Ordering::Greater, _) => y_sqrt_1, - (Ordering::Less, 0) => y_sqrt_1, - (Ordering::Less, _) => y_sqrt_2, - (Ordering::Equal, _) => y_sqrt_1, - }; - - let point = - G1Point::from_affine(x, y.clone()).map_err(|_| ByteConversionError::InvalidValue)?; - - point - .is_in_subgroup() - .then_some(point) - .ok_or(ByteConversionError::PointNotInSubgroup) - } - - #[cfg(feature = "alloc")] - fn compress_g2_point(point: &Self::G2Point) -> Self::G2Compressed { - if *point == G2Point::neutral_element() { - // point is at infinity - let mut x_bytes = [0_u8; 96]; - x_bytes[0] |= 1 << 7; - x_bytes[0] |= 1 << 6; - x_bytes - } else { - // point is not at infinity - let point_affine = point.to_affine(); - let x = point_affine.x(); - let y = point_affine.y(); - - let x_rev: FieldElement = - FieldElement::new([x.value()[1].clone(), x.value()[0].clone()]); - let mut x_bytes = [0u8; 96]; - let bytes = x_rev.to_bytes_be(); - x_bytes.copy_from_slice(&bytes); - - // Set first bit to to 1 indicate this is compressed element. - x_bytes[0] |= 1 << 7; - - // Set the 3rd bit based on y value. - let y_neg = -y; - - match ( - y.value()[0] - .representative() - .cmp(&y_neg.value()[0].representative()), - y.value()[1] - .representative() - .cmp(&y_neg.value()[1].representative()), - ) { - (Ordering::Greater, _) | (Ordering::Equal, Ordering::Greater) => { - x_bytes[0] |= 1 << 5; - } - (_, _) => (), - } - x_bytes - } - } - - fn decompress_g2_point(input_bytes: &mut [u8]) -> Result { - if !input_bytes.len() == 96 { - return Err(ByteConversionError::InvalidValue); - } - - let first_byte = input_bytes.first().unwrap(); - - // We get the first 3 bits - let prefix_bits = first_byte >> 5; - let first_bit = (prefix_bits & 4_u8) >> 2; - // If first bit is not 1, then the value is not compressed. - if first_bit != 1 { - return Err(ByteConversionError::InvalidValue); - } - let second_bit = (prefix_bits & 2_u8) >> 1; - // If the second bit is 1, then the compressed point is the - // point at infinity and we return it directly. - if second_bit == 1 { - return Ok(Self::G2Point::neutral_element()); - } - - let third_bit = prefix_bits & 1_u8; - - let first_byte_without_control_bits = (first_byte << 3) >> 3; - input_bytes[0] = first_byte_without_control_bits; - - let input0 = &input_bytes[48..]; - let input1 = &input_bytes[0..48]; - let x0 = BLS12381FieldElement::from_bytes_be(input0).unwrap(); - let x1 = BLS12381FieldElement::from_bytes_be(input1).unwrap(); - let x: FieldElement = FieldElement::new([x0, x1]); - - const VALUE: BLS12381FieldElement = BLS12381FieldElement::from_hex_unchecked("4"); - let b_param_qfe = FieldElement::::new([VALUE, VALUE]); - - let y = sqrt::sqrt_qfe(&(x.pow(3_u64) + b_param_qfe), third_bit) - .ok_or(ByteConversionError::InvalidValue)?; - - Self::G2Point::from_affine(x, y).map_err(|_| ByteConversionError::InvalidValue) - } -} - -#[cfg(test)] -mod tests { - use super::{BLS12381FieldElement, G1Point}; - use crate::elliptic_curve::short_weierstrass::curves::bls12_381::curve::BLS12381Curve; - use crate::elliptic_curve::short_weierstrass::traits::Compress; - use crate::elliptic_curve::traits::{FromAffine, IsEllipticCurve}; - - #[cfg(feature = "alloc")] - use crate::{ - cyclic_group::IsGroup, traits::ByteConversion, unsigned_integer::element::UnsignedInteger, - }; - - #[test] - fn test_zero_point() { - let g1 = BLS12381Curve::generator(); - - assert!(g1.is_in_subgroup()); - let new_x = BLS12381FieldElement::zero(); - let new_y = BLS12381FieldElement::one() + BLS12381FieldElement::one(); - - let false_point2 = G1Point::from_affine(new_x, new_y).unwrap(); - - assert!(!false_point2.is_in_subgroup()); - } - - #[cfg(feature = "alloc")] - #[test] - fn test_g1_compress_generator() { - use crate::elliptic_curve::short_weierstrass::traits::Compress; - - let g = BLS12381Curve::generator(); - let mut compressed_g = BLS12381Curve::compress_g1_point(&g); - let first_byte = compressed_g.first().unwrap(); - - let first_byte_without_control_bits = (first_byte << 3) >> 3; - compressed_g[0] = first_byte_without_control_bits; - - let compressed_g_x = BLS12381FieldElement::from_bytes_be(&compressed_g).unwrap(); - let g_x = g.x(); - - assert_eq!(*g_x, compressed_g_x); - } - - #[cfg(feature = "alloc")] - #[test] - fn test_g1_compress_point_at_inf() { - use crate::elliptic_curve::short_weierstrass::traits::Compress; - - let inf = G1Point::neutral_element(); - let compressed_inf = BLS12381Curve::compress_g1_point(&inf); - let first_byte = compressed_inf.first().unwrap(); - - assert_eq!(*first_byte >> 6, 3_u8); - } - - #[cfg(feature = "alloc")] - #[test] - fn test_compress_decompress_generator() { - use crate::elliptic_curve::short_weierstrass::traits::Compress; - - let g = BLS12381Curve::generator(); - let mut compressed_g_slice = BLS12381Curve::compress_g1_point(&g); - - let decompressed_g = BLS12381Curve::decompress_g1_point(&mut compressed_g_slice).unwrap(); - - assert_eq!(g, decompressed_g); - } - - #[cfg(feature = "alloc")] - #[test] - fn test_compress_decompress_2g() { - let g = BLS12381Curve::generator(); - // calculate g point operate with itself - let g_2 = g.operate_with_self(UnsignedInteger::<4>::from("2")); - - let mut compressed_g2_slice: [u8; 48] = BLS12381Curve::compress_g1_point(&g_2); - - let decompressed_g2 = BLS12381Curve::decompress_g1_point(&mut compressed_g2_slice).unwrap(); - - assert_eq!(g_2, decompressed_g2); - } - - #[cfg(feature = "alloc")] - #[test] - fn test_compress_decompress_generator_g2() { - use crate::elliptic_curve::short_weierstrass::{ - curves::bls12_381::twist::BLS12381TwistCurve, traits::Compress, - }; - - let g = BLS12381TwistCurve::generator(); - let mut compressed_g_slice = BLS12381Curve::compress_g2_point(&g); - - let decompressed_g = BLS12381Curve::decompress_g2_point(&mut compressed_g_slice).unwrap(); - - assert_eq!(g, decompressed_g); - } - - #[cfg(feature = "alloc")] - #[test] - fn test_compress_decompress_generator_g2_neg() { - use crate::elliptic_curve::short_weierstrass::{ - curves::bls12_381::twist::BLS12381TwistCurve, traits::Compress, - }; - - let g = BLS12381TwistCurve::generator(); - let g_neg = g.neg(); - - let mut compressed_g_neg_slice = BLS12381Curve::compress_g2_point(&g_neg); - - let decompressed_g_neg = - BLS12381Curve::decompress_g2_point(&mut compressed_g_neg_slice).unwrap(); - - assert_eq!(g_neg, decompressed_g_neg); - } - - #[cfg(feature = "alloc")] - #[test] - fn test_decompress_g2() { - use crate::{ - elliptic_curve::short_weierstrass::curves::bls12_381::{ - field_extension::Degree2ExtensionField, twist::BLS12381TwistCurve, - }, - field::element::FieldElement, - }; - - let mut compressed_point = [0_u8; 96]; - compressed_point[0] |= 1 << 7; - compressed_point[95] |= 1 << 1; - - // Valig G2 point coordinates: - let x_0 = BLS12381FieldElement::from_hex_unchecked("02"); - let x_1 = BLS12381FieldElement::from_hex_unchecked("0"); - let y_0 = BLS12381FieldElement::from_hex_unchecked("013a59858b6809fca4d9a3b6539246a70051a3c88899964a42bc9a69cf9acdd9dd387cfa9086b894185b9a46a402be73"); - let y_1 = BLS12381FieldElement::from_hex_unchecked("02d27e0ec3356299a346a09ad7dc4ef68a483c3aed53f9139d2f929a3eecebf72082e5e58c6da24ee32e03040c406d4f"); - - let x: FieldElement = FieldElement::new([x_0, x_1]); - let y: FieldElement = FieldElement::new([y_0, y_1]); - - let valid_g2_point = BLS12381TwistCurve::create_point_from_affine(x, y).unwrap(); - - let decompressed_point = BLS12381Curve::decompress_g2_point(&mut compressed_point).unwrap(); - - assert_eq!(valid_g2_point, decompressed_point); - } - - #[cfg(feature = "alloc")] - #[test] - fn test_compress_g2() { - use crate::{ - elliptic_curve::short_weierstrass::{ - curves::bls12_381::{ - field_extension::Degree2ExtensionField, twist::BLS12381TwistCurve, - }, - traits::Compress, - }, - field::element::FieldElement, - }; - - // Valig G2 point coordinates: - let x_0 = BLS12381FieldElement::from_hex_unchecked("02"); - let x_1 = BLS12381FieldElement::from_hex_unchecked("0"); - let y_0 = BLS12381FieldElement::from_hex_unchecked("013a59858b6809fca4d9a3b6539246a70051a3c88899964a42bc9a69cf9acdd9dd387cfa9086b894185b9a46a402be73"); - let y_1 = BLS12381FieldElement::from_hex_unchecked("02d27e0ec3356299a346a09ad7dc4ef68a483c3aed53f9139d2f929a3eecebf72082e5e58c6da24ee32e03040c406d4f"); - - let x: FieldElement = FieldElement::new([x_0, x_1]); - let y: FieldElement = FieldElement::new([y_0, y_1]); - - let point = BLS12381TwistCurve::create_point_from_affine(x, y).unwrap(); - - let compress_point = BLS12381Curve::compress_g2_point(&point); - - let mut valid_compressed_point = [0_u8; 96]; - valid_compressed_point[0] |= 1 << 7; - valid_compressed_point[95] |= 1 << 1; - - assert_eq!(compress_point, valid_compressed_point); - } -} diff --git a/crates/math/src/elliptic_curve/short_weierstrass/curves/bls12_381/curve.rs b/crates/math/src/elliptic_curve/short_weierstrass/curves/bls12_381/curve.rs deleted file mode 100644 index a5fad11bb..000000000 --- a/crates/math/src/elliptic_curve/short_weierstrass/curves/bls12_381/curve.rs +++ /dev/null @@ -1,326 +0,0 @@ -use super::{ - field_extension::{BLS12381PrimeField, Degree2ExtensionField}, - twist::BLS12381TwistCurve, -}; -use crate::cyclic_group::IsGroup; -use crate::elliptic_curve::short_weierstrass::point::ShortWeierstrassProjectivePoint; -use crate::elliptic_curve::traits::IsEllipticCurve; -use crate::unsigned_integer::element::U256; -use crate::{ - elliptic_curve::short_weierstrass::traits::IsShortWeierstrass, field::element::FieldElement, -}; - -pub const SUBGROUP_ORDER: U256 = - U256::from_hex_unchecked("73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001"); - -pub const CURVE_COFACTOR: U256 = U256::from_hex_unchecked("0x396c8c005555e1568c00aaab0000aaab"); - -pub type BLS12381FieldElement = FieldElement; -pub type BLS12381TwistCurveFieldElement = FieldElement; - -/// The description of the curve. -#[derive(Clone, Debug)] -pub struct BLS12381Curve; - -impl IsEllipticCurve for BLS12381Curve { - type BaseField = BLS12381PrimeField; - type PointRepresentation = ShortWeierstrassProjectivePoint; - - /// Returns the generator point of the BLS12-381 curve. - /// - /// # Safety - /// - /// - The generator point is mathematically verified to be a valid point on the curve. - /// - `unwrap()` is safe because the provided coordinates satisfy the curve equation. - fn generator() -> Self::PointRepresentation { - // SAFETY: - // - These values are mathematically verified and known to be valid points on BLS12-381. - // - `unwrap()` is safe because we **ensure** the input values satisfy the curve equation. - let point= Self::PointRepresentation::new([ - FieldElement::::new_base("17f1d3a73197d7942695638c4fa9ac0fc3688c4f9774b905a14e3a3f171bac586c55e83ff97a1aeffb3af00adb22c6bb"), - FieldElement::::new_base("8b3f481e3aaa0f1a09e30ed741d8ae4fcf5e095d5d00af600db18cb2c04b3edd03cc744a2888ae40caa232946c5e7e1"), - FieldElement::one() - ]); - point.unwrap() - } -} - -impl IsShortWeierstrass for BLS12381Curve { - fn a() -> FieldElement { - FieldElement::from(0) - } - - fn b() -> FieldElement { - FieldElement::from(4) - } -} - -/// This is equal to the frobenius trace of the BLS12 381 curve minus one or seed value z. -pub const MILLER_LOOP_CONSTANT: u64 = 0xd201000000010000; - -/// 𝛽 : primitive cube root of unity of 𝐹ₚ that §satisfies the minimal equation -/// 𝛽² + 𝛽 + 1 = 0 mod 𝑝 -pub const CUBE_ROOT_OF_UNITY_G1: BLS12381FieldElement = FieldElement::from_hex_unchecked( - "5f19672fdf76ce51ba69c6076a0f77eaddb3a93be6f89688de17d813620a00022e01fffffffefffe", -); - -/// x-coordinate of 𝜁 ∘ 𝜋_q ∘ 𝜁⁻¹, where 𝜁 is the isomorphism u:E'(𝔽ₚ₆) −> E(𝔽ₚ₁₂) from the twist to E -pub const ENDO_U: BLS12381TwistCurveFieldElement = -BLS12381TwistCurveFieldElement::const_from_raw([ - FieldElement::from_hex_unchecked("0"), - FieldElement::from_hex_unchecked("1a0111ea397fe699ec02408663d4de85aa0d857d89759ad4897d29650fb85f9b409427eb4f49fffd8bfd00000000aaad") -]); - -/// y-coordinate of 𝜁 ∘ 𝜋_q ∘ 𝜁⁻¹, where 𝜁 is the isomorphism u:E'(𝔽ₚ₆) −> E(𝔽ₚ₁₂) from the twist to E -pub const ENDO_V: BLS12381TwistCurveFieldElement = -BLS12381TwistCurveFieldElement::const_from_raw([ - FieldElement::from_hex_unchecked("135203e60180a68ee2e9c448d77a2cd91c3dedd930b1cf60ef396489f61eb45e304466cf3e67fa0af1ee7b04121bdea2"), - FieldElement::from_hex_unchecked("6af0e0437ff400b6831e36d6bd17ffe48395dabc2d3435e77f76e17009241c5ee67992f72ec05f4c81084fbede3cc09") -]); - -impl ShortWeierstrassProjectivePoint { - /// Returns 𝜙(P) = (𝑥, 𝑦) ⇒ (𝛽𝑥, 𝑦), where 𝛽 is the Cube Root of Unity in the base prime field - /// https://eprint.iacr.org/2022/352.pdf 2 Preliminaries - fn phi(&self) -> Self { - let [x, y, z] = self.coordinates(); - let new_x = x * CUBE_ROOT_OF_UNITY_G1; - // SAFETY: The value `x` is computed correctly, so the point is in the curve. - Self::new_unchecked([new_x, y.clone(), z.clone()]) - } - - /// 𝜙(P) = −𝑢²P - /// https://eprint.iacr.org/2022/352.pdf 4.3 Prop. 4 - pub fn is_in_subgroup(&self) -> bool { - self.operate_with_self(MILLER_LOOP_CONSTANT) - .operate_with_self(MILLER_LOOP_CONSTANT) - .neg() - == self.phi() - } -} - -impl ShortWeierstrassProjectivePoint { - /// Computes 𝜓(P) 𝜓(P) = 𝜁 ∘ 𝜋ₚ ∘ 𝜁⁻¹, where 𝜁 is the isomorphism u:E'(𝔽ₚ₆) −> E(𝔽ₚ₁₂) from the twist to E,, 𝜋ₚ is the p-power frobenius endomorphism - /// and 𝜓 satisifies minmal equation 𝑋² + 𝑡𝑋 + 𝑞 = 𝑂 - /// https://eprint.iacr.org/2022/352.pdf 4.2 (7) - /// - /// # Safety - /// - /// - This function assumes `self` is a valid point on the BLS12-381 **twist** curve. - /// - The conjugation operation preserves validity. - /// - `unwrap()` is used because `psi()` is defined to **always return a valid point**. - fn psi(&self) -> Self { - let [x, y, z] = self.coordinates(); - // SAFETY: - // - `conjugate()` preserves the validity of the field element. - // - `ENDO_U` and `ENDO_V` are precomputed constants that ensure the - // resulting point satisfies the curve equation. - // - `unwrap()` is safe because the transformation follows - // **a known valid isomorphism** between the twist and E. - let point = Self::new([ - x.conjugate() * ENDO_U, - y.conjugate() * ENDO_V, - z.conjugate(), - ]); - point.unwrap() - } - - /// 𝜓(P) = 𝑢P, where 𝑢 = SEED of the curve - /// https://eprint.iacr.org/2022/352.pdf 4.2 - pub fn is_in_subgroup(&self) -> bool { - self.psi() == self.operate_with_self(MILLER_LOOP_CONSTANT).neg() - } -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::{ - cyclic_group::IsGroup, - elliptic_curve::{ - short_weierstrass::curves::bls12_381::field_extension::BLS12381_PRIME_FIELD_ORDER, - traits::EllipticCurveError, - }, - field::element::FieldElement, - unsigned_integer::element::U384, - }; - - // -15132376222941642751 = MILLER_LOOP_CONSTANT + 1 = -d20100000000ffff - // we want the positive of this coordinate based on x^2 - tx + q - pub const TRACE_OF_FROBENIUS: U256 = U256::from_u64(15132376222941642751); - - const ENDO_U_2: BLS12381TwistCurveFieldElement = - BLS12381TwistCurveFieldElement::const_from_raw([ - FieldElement::from_hex_unchecked("1a0111ea397fe699ec02408663d4de85aa0d857d89759ad4897d29650fb85f9b409427eb4f49fffd8bfd00000000aaac"), - FieldElement::from_hex_unchecked("0") - ]); - - const ENDO_V_2: BLS12381TwistCurveFieldElement = - BLS12381TwistCurveFieldElement::const_from_raw([ - FieldElement::from_hex_unchecked("1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaaa"), - FieldElement::from_hex_unchecked("0") - ]); - - /// Computes the psi^2() 'Untwist Frobenius Endomorphism' - /// - /// # Safety - /// - /// - This function assumes `p` is a valid point on the BLS12-381 twist curve. - /// - The transformation involves multiplying the x and y coordinates by known constants. - /// - `unwrap()` is used because the resulting point remains valid under the curve equations. - fn psi_square( - p: &ShortWeierstrassProjectivePoint, - ) -> ShortWeierstrassProjectivePoint { - let [x, y, z] = p.coordinates(); - // Since power of frobenius map is 2 we apply once as applying twice is inverse - - // SAFETY: - // - `ENDO_U_2` and `ENDO_V_2` are known valid constants. - // - `unwrap()` is safe because the transformation preserves curve validity. - ShortWeierstrassProjectivePoint::new([x * ENDO_U_2, y * ENDO_V_2, z.clone()]).unwrap() - } - - #[allow(clippy::upper_case_acronyms)] - type FEE = FieldElement; - #[allow(clippy::upper_case_acronyms)] - type FTE = FieldElement; - - fn point_1() -> ShortWeierstrassProjectivePoint { - let x = FEE::new_base("36bb494facde72d0da5c770c4b16d9b2d45cfdc27604a25a1a80b020798e5b0dbd4c6d939a8f8820f042a29ce552ee5"); - let y = FEE::new_base("7acf6e49cc000ff53b06ee1d27056734019c0a1edfa16684da41ebb0c56750f73bc1b0eae4c6c241808a5e485af0ba0"); - BLS12381Curve::create_point_from_affine(x, y).unwrap() - } - - fn point_1_times_5() -> ShortWeierstrassProjectivePoint { - let x = FEE::new_base("32bcce7e71eb50384918e0c9809f73bde357027c6bf15092dd849aa0eac274d43af4c68a65fb2cda381734af5eecd5c"); - let y = FEE::new_base("11e48467b19458aabe7c8a42dc4b67d7390fdf1e150534caadddc7e6f729d8890b68a5ea6885a21b555186452b954d88"); - BLS12381Curve::create_point_from_affine(x, y).unwrap() - } - - #[test] - fn adding_five_times_point_1_works() { - let point_1 = point_1(); - let point_1_times_5 = point_1_times_5(); - assert_eq!(point_1.operate_with_self(5_u16), point_1_times_5); - } - - #[test] - fn create_valid_point_works() { - let p = point_1(); - assert_eq!(*p.x(), FEE::new_base("36bb494facde72d0da5c770c4b16d9b2d45cfdc27604a25a1a80b020798e5b0dbd4c6d939a8f8820f042a29ce552ee5")); - assert_eq!(*p.y(), FEE::new_base("7acf6e49cc000ff53b06ee1d27056734019c0a1edfa16684da41ebb0c56750f73bc1b0eae4c6c241808a5e485af0ba0")); - assert_eq!(*p.z(), FEE::new_base("1")); - } - - #[test] - fn create_invalid_points_returns_an_error() { - assert_eq!( - BLS12381Curve::create_point_from_affine(FEE::from(0), FEE::from(1)), - Err(EllipticCurveError::InvalidPoint) - ); - } - - #[test] - fn equality_works() { - let g = BLS12381Curve::generator(); - let g2 = g.operate_with(&g); - assert_ne!(&g2, &g); - assert_eq!(&g, &g); - } - - #[test] - fn g_operated_with_g_satifies_ec_equation() { - let g = BLS12381Curve::generator(); - let g2 = g.operate_with_self(2_u64); - - // get x and y from affine coordinates - let g2_affine = g2.to_affine(); - let x = g2_affine.x(); - let y = g2_affine.y(); - - // calculate both sides of BLS12-381 equation - let four = FieldElement::from(4); - let y_sq_0 = x.pow(3_u16) + four; - let y_sq_1 = y.pow(2_u16); - - assert_eq!(y_sq_0, y_sq_1); - } - - #[test] - fn operate_with_self_works_1() { - let g = BLS12381Curve::generator(); - assert_eq!( - g.operate_with(&g).operate_with(&g), - g.operate_with_self(3_u16) - ); - } - - #[test] - fn generator_g1_is_in_subgroup() { - let g = BLS12381Curve::generator(); - assert!(g.is_in_subgroup()) - } - - #[test] - fn arbitrary_g1_point_is_in_subgroup() { - let g = BLS12381Curve::generator().operate_with_self(32u64); - assert!(g.is_in_subgroup()) - } - - //TODO - #[test] - fn arbitrary_g1_point_not_in_subgroup() { - let x = FEE::new_base("178212cbe4a3026c051d4f867364b3ea84af623f93233b347ffcd3d6b16f16e0a7aedbe1c78d33c6beca76b2b75c8486"); - let y = FEE::new_base("13a8b1347e5b43bc4051754b2a29928b5df78cf03ca3b1f73d0424b09fccdef116c9f0ecbec7420a99b2dd785209e9d"); - let p = BLS12381Curve::create_point_from_affine(x, y).unwrap(); - assert!(!p.is_in_subgroup()) - } - - #[test] - fn generator_g2_is_in_subgroup() { - let g = BLS12381TwistCurve::generator(); - assert!(g.is_in_subgroup()) - } - - #[test] - fn arbitrary_g2_point_is_in_subgroup() { - let g = BLS12381TwistCurve::generator().operate_with_self(32u64); - assert!(g.is_in_subgroup()) - } - - //`TODO` - #[test] - fn arbitrary_g2_point_not_in_subgroup() { - let x = FTE::new([ - FEE::new(U384::from_hex_unchecked("97798b4a61ac301bbee71e36b5174e2f4adfe3e1729bdae1fcc9965ae84181be373aa80414823eed694f1270014012d")), - FEE::new(U384::from_hex_unchecked("c9852cc6e61868966249aec153b50b29b3c22409f4c7880fd13121981c103c8ef84d9ea29b552431360e82cf69219fa")) - ]); - let y = FTE::new([ - FEE::new(U384::from_hex_unchecked("16cb3a60f3fa52c8273aceeb94c4c7303e8074aa9eedec7355bbb1e8cceedd4ec1497f573f62822140377b8e339619ed")), - FEE::new(U384::from_hex_unchecked("1cd919b08afe06bebe9adf6223a55868a6fd8b77efc5c67b60fff39be36e9b44b7f10db16827c83b43ad2dad1947778")) - ]); - - let p = BLS12381TwistCurve::create_point_from_affine(x, y).unwrap(); - assert!(!p.is_in_subgroup()) - } - - #[test] - fn g2_conjugate_works() { - let a = FTE::zero(); - let mut expected = a.conjugate(); - expected = expected.conjugate(); - - assert_eq!(a, expected); - } - - #[test] - fn untwist_morphism_has_minimal_poly() { - // generator - let p = BLS12381TwistCurve::generator(); - let psi_square = psi_square(&p); - let tx = p.psi().operate_with_self(TRACE_OF_FROBENIUS).neg(); - let q = p.operate_with_self(BLS12381_PRIME_FIELD_ORDER); - // Minimal Polynomial of Untwist Frobenius Endomorphism: X^2 + tX + q, where X = psh(P) -> psi(p)^2 - t * psi(p) + q * p = 0 - let min_poly = psi_square.operate_with(&tx.neg()).operate_with(&q); - assert!(min_poly.is_neutral_element()) - } -} diff --git a/crates/math/src/elliptic_curve/short_weierstrass/curves/bls12_381/default_types.rs b/crates/math/src/elliptic_curve/short_weierstrass/curves/bls12_381/default_types.rs deleted file mode 100644 index dc450a8d1..000000000 --- a/crates/math/src/elliptic_curve/short_weierstrass/curves/bls12_381/default_types.rs +++ /dev/null @@ -1,30 +0,0 @@ -use crate::{ - field::{ - element::FieldElement, - fields::montgomery_backed_prime_fields::{IsModulus, MontgomeryBackendPrimeField}, - traits::IsFFTField, - }, - unsigned_integer::element::{UnsignedInteger, U256}, -}; - -#[derive(Clone, Debug)] -pub struct FrConfig; - -/// Modulus of bls 12 381 subgroup -impl IsModulus for FrConfig { - const MODULUS: U256 = U256::from_hex_unchecked( - "73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001", - ); -} - -/// FrField using MontgomeryBackend for bls 12 381 -pub type FrField = MontgomeryBackendPrimeField; -/// FrElement using MontgomeryBackend for bls 12 381 -pub type FrElement = FieldElement; - -impl IsFFTField for FrField { - const TWO_ADICITY: u64 = 32; - const TWO_ADIC_PRIMITVE_ROOT_OF_UNITY: Self::BaseType = UnsignedInteger::from_hex_unchecked( - "2ab00961a08a499d84dd396c349d9b3cc5e433d6fa78eb2b54cc39d9bb30bbb7", - ); -} diff --git a/crates/math/src/elliptic_curve/short_weierstrass/curves/bls12_381/field_extension.rs b/crates/math/src/elliptic_curve/short_weierstrass/curves/bls12_381/field_extension.rs deleted file mode 100644 index 394282597..000000000 --- a/crates/math/src/elliptic_curve/short_weierstrass/curves/bls12_381/field_extension.rs +++ /dev/null @@ -1,444 +0,0 @@ -use crate::field::{ - element::FieldElement, - errors::FieldError, - extensions::{ - cubic::{CubicExtensionField, HasCubicNonResidue}, - quadratic::{HasQuadraticNonResidue, QuadraticExtensionField}, - }, - fields::montgomery_backed_prime_fields::{IsModulus, MontgomeryBackendPrimeField}, - traits::{IsField, IsSubFieldOf}, -}; -use crate::traits::ByteConversion; -use crate::unsigned_integer::element::U384; - -pub const BLS12381_PRIME_FIELD_ORDER: U384 = U384::from_hex_unchecked("1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaab"); - -// FPBLS12381 -#[derive(Clone, Debug)] -pub struct BLS12381FieldModulus; -impl IsModulus for BLS12381FieldModulus { - const MODULUS: U384 = BLS12381_PRIME_FIELD_ORDER; -} - -pub type BLS12381PrimeField = MontgomeryBackendPrimeField; -type Fp2E = FieldElement; - -////////////////// -#[derive(Clone, Debug)] -pub struct Degree2ExtensionField; - -impl IsField for Degree2ExtensionField { - type BaseType = [FieldElement; 2]; - - /// Returns the component wise addition of `a` and `b` - fn add(a: &Self::BaseType, b: &Self::BaseType) -> Self::BaseType { - [&a[0] + &b[0], &a[1] + &b[1]] - } - - /// Returns the multiplication of `a` and `b` using the following - /// equation: - /// (a0 + a1 * t) * (b0 + b1 * t) = a0 * b0 + a1 * b1 * Self::residue() + (a0 * b1 + a1 * b0) * t - /// where `t.pow(2)` equals `Q::residue()`. - fn mul(a: &Self::BaseType, b: &Self::BaseType) -> Self::BaseType { - let a0b0 = &a[0] * &b[0]; - let a1b1 = &a[1] * &b[1]; - let z = (&a[0] + &a[1]) * (&b[0] + &b[1]); - [&a0b0 - &a1b1, z - a0b0 - a1b1] - } - - fn square(a: &Self::BaseType) -> Self::BaseType { - let [a0, a1] = a; - let v0 = a0 * a1; - let c0 = (a0 + a1) * (a0 - a1); - let c1 = &v0 + &v0; - [c0, c1] - } - /// Returns the component wise subtraction of `a` and `b` - fn sub(a: &Self::BaseType, b: &Self::BaseType) -> Self::BaseType { - [&a[0] - &b[0], &a[1] - &b[1]] - } - - /// Returns the component wise negation of `a` - fn neg(a: &Self::BaseType) -> Self::BaseType { - [-&a[0], -&a[1]] - } - - /// Returns the multiplicative inverse of `a` - /// This uses the equality `(a0 + a1 * t) * (a0 - a1 * t) = a0.pow(2) - a1.pow(2) * Q::residue()` - fn inv(a: &Self::BaseType) -> Result { - let inv_norm = (a[0].pow(2_u64) + a[1].pow(2_u64)).inv()?; - Ok([&a[0] * &inv_norm, -&a[1] * inv_norm]) - } - - /// Returns the division of `a` and `b` - fn div(a: &Self::BaseType, b: &Self::BaseType) -> Result { - let b_inv = &Self::inv(b).map_err(|_| FieldError::DivisionByZero)?; - Ok(::mul(a, b_inv)) - } - - /// Returns a boolean indicating whether `a` and `b` are equal component wise. - fn eq(a: &Self::BaseType, b: &Self::BaseType) -> bool { - a[0] == b[0] && a[1] == b[1] - } - - /// Returns the additive neutral element of the field extension. - fn zero() -> Self::BaseType { - [FieldElement::zero(), FieldElement::zero()] - } - - /// Returns the multiplicative neutral element of the field extension. - fn one() -> Self::BaseType { - [FieldElement::one(), FieldElement::zero()] - } - - /// Returns the element `x * 1` where 1 is the multiplicative neutral element. - fn from_u64(x: u64) -> Self::BaseType { - [FieldElement::from(x), FieldElement::zero()] - } - - /// Takes as input an element of BaseType and returns the internal representation - /// of that element in the field. - /// Note: for this case this is simply the identity, because the components - /// already have correct representations. - fn from_base_type(x: Self::BaseType) -> Self::BaseType { - x - } -} - -impl IsSubFieldOf for BLS12381PrimeField { - fn mul( - a: &Self::BaseType, - b: &::BaseType, - ) -> ::BaseType { - let c0 = FieldElement::from_raw(::mul(a, b[0].value())); - let c1 = FieldElement::from_raw(::mul(a, b[1].value())); - [c0, c1] - } - - fn add( - a: &Self::BaseType, - b: &::BaseType, - ) -> ::BaseType { - let c0 = FieldElement::from_raw(::add(a, b[0].value())); - let c1 = FieldElement::from_raw(*b[1].value()); - [c0, c1] - } - - fn div( - a: &Self::BaseType, - b: &::BaseType, - ) -> Result<::BaseType, FieldError> { - let b_inv = Degree2ExtensionField::inv(b)?; - Ok(>::mul( - a, &b_inv, - )) - } - - fn sub( - a: &Self::BaseType, - b: &::BaseType, - ) -> ::BaseType { - let c0 = FieldElement::from_raw(::sub(a, b[0].value())); - let c1 = FieldElement::from_raw(::neg(b[1].value())); - [c0, c1] - } - - fn embed(a: Self::BaseType) -> ::BaseType { - [FieldElement::from_raw(a), FieldElement::zero()] - } - - #[cfg(feature = "alloc")] - fn to_subfield_vec( - b: ::BaseType, - ) -> alloc::vec::Vec { - b.into_iter().map(|x| x.to_raw()).collect() - } -} - -impl ByteConversion for FieldElement { - #[cfg(feature = "alloc")] - fn to_bytes_be(&self) -> alloc::vec::Vec { - let mut byte_slice = ByteConversion::to_bytes_be(&self.value()[0]); - byte_slice.extend(ByteConversion::to_bytes_be(&self.value()[1])); - byte_slice - } - - #[cfg(feature = "alloc")] - fn to_bytes_le(&self) -> alloc::vec::Vec { - let mut byte_slice = ByteConversion::to_bytes_le(&self.value()[0]); - byte_slice.extend(ByteConversion::to_bytes_le(&self.value()[1])); - byte_slice - } - - fn from_bytes_be(bytes: &[u8]) -> Result - where - Self: core::marker::Sized, - { - const BYTES_PER_FIELD: usize = 48; - let x0 = FieldElement::from_bytes_be(&bytes[0..BYTES_PER_FIELD])?; - let x1 = FieldElement::from_bytes_be(&bytes[BYTES_PER_FIELD..BYTES_PER_FIELD * 2])?; - Ok(Self::new([x0, x1])) - } - - fn from_bytes_le(bytes: &[u8]) -> Result - where - Self: core::marker::Sized, - { - const BYTES_PER_FIELD: usize = 48; - let x0 = FieldElement::from_bytes_le(&bytes[0..BYTES_PER_FIELD])?; - let x1 = FieldElement::from_bytes_le(&bytes[BYTES_PER_FIELD..BYTES_PER_FIELD * 2])?; - Ok(Self::new([x0, x1])) - } -} - -/////////////// -#[derive(Debug, Clone)] -pub struct LevelTwoResidue; -impl HasCubicNonResidue for LevelTwoResidue { - fn residue() -> FieldElement { - FieldElement::new([ - FieldElement::new(U384::from("1")), - FieldElement::new(U384::from("1")), - ]) - } -} - -impl HasQuadraticNonResidue for LevelTwoResidue { - fn residue() -> FieldElement { - FieldElement::new([ - FieldElement::new(U384::from("1")), - FieldElement::new(U384::from("1")), - ]) - } -} -pub type Degree4ExtensionField = QuadraticExtensionField; - -pub type Degree6ExtensionField = CubicExtensionField; - -#[derive(Debug, Clone)] -pub struct LevelThreeResidue; -impl HasQuadraticNonResidue for LevelThreeResidue { - fn residue() -> FieldElement { - FieldElement::new([ - FieldElement::zero(), - FieldElement::one(), - FieldElement::zero(), - ]) - } -} - -pub type Degree12ExtensionField = QuadraticExtensionField; - -impl FieldElement { - pub fn new_base(a_hex: &str) -> Self { - Self::new(U384::from(a_hex)) - } -} - -impl FieldElement { - pub fn new_base(a_hex: &str) -> Self { - Self::new([FieldElement::new(U384::from(a_hex)), FieldElement::zero()]) - } - - pub fn conjugate(&self) -> Self { - let [a, b] = self.value(); - Self::new([a.clone(), -b]) - } -} - -impl FieldElement { - pub fn new_base(a_hex: &str) -> Self { - Self::new([ - FieldElement::new([FieldElement::new(U384::from(a_hex)), FieldElement::zero()]), - FieldElement::zero(), - FieldElement::zero(), - ]) - } -} - -impl FieldElement { - pub fn new_base(a_hex: &str) -> Self { - Self::new([ - FieldElement::::new_base(a_hex), - FieldElement::zero(), - ]) - } - - pub fn from_coefficients(coefficients: &[&str; 12]) -> Self { - FieldElement::::new([ - FieldElement::new([ - FieldElement::new([ - FieldElement::new(U384::from(coefficients[0])), - FieldElement::new(U384::from(coefficients[1])), - ]), - FieldElement::new([ - FieldElement::new(U384::from(coefficients[2])), - FieldElement::new(U384::from(coefficients[3])), - ]), - FieldElement::new([ - FieldElement::new(U384::from(coefficients[4])), - FieldElement::new(U384::from(coefficients[5])), - ]), - ]), - FieldElement::new([ - FieldElement::new([ - FieldElement::new(U384::from(coefficients[6])), - FieldElement::new(U384::from(coefficients[7])), - ]), - FieldElement::new([ - FieldElement::new(U384::from(coefficients[8])), - FieldElement::new(U384::from(coefficients[9])), - ]), - FieldElement::new([ - FieldElement::new(U384::from(coefficients[10])), - FieldElement::new(U384::from(coefficients[11])), - ]), - ]), - ]) - } -} - -/// Computes the multiplication of an element of fp2 by the level two non-residue 9+u. -pub fn mul_fp2_by_nonresidue(a: &Fp2E) -> Fp2E { - // (c0 + c1 * u) * (1 + u) = (c0 - c1) + (c1 + c0) * u - let c0 = &a.value()[0] - &a.value()[1]; // c0 - c1 - let c1 = &a.value()[0] + &a.value()[1]; // c1 + c0 - - Fp2E::new([c0, c1]) -} -#[cfg(test)] -mod tests { - use crate::elliptic_curve::{ - short_weierstrass::curves::bls12_381::twist::BLS12381TwistCurve, traits::IsEllipticCurve, - }; - - use super::*; - type Fp12E = FieldElement; - - #[test] - fn element_squared_1() { - // base = 1 + u + (1 + u)v + (1 + u)v^2 + ((1+u) + (1 + u)v + (1+ u)v^2)w - let element_ones = - Fp12E::from_coefficients(&["1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1"]); - let element_ones_squared = - Fp12E::from_coefficients(&["1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaa1", - "c", - "1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaa5", - "c", - "1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaa9", - "c", - "1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaa3", - "c", - "1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaa7", - "c", - "0", - "c"]); - assert_eq!(element_ones.pow(2_u16), element_ones_squared); - assert_eq!(element_ones.square(), element_ones_squared); - } - - #[test] - fn element_squared_2() { - let element_sequence = - Fp12E::from_coefficients(&["1", "2", "5", "6", "9", "a", "3", "4", "7", "8", "b", "c"]); - - let element_sequence_squared = Fp12E::from_coefficients(&["1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffa87d", - "199", - "1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffa851", - "20b", - "1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffa955", - "1cd", - "1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffa845", - "1e8", - "1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffa8a9", - "202", - "1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaa5d", - "16c"]); - - assert_eq!(element_sequence.pow(2_u16), element_sequence_squared); - assert_eq!(element_sequence.square(), element_sequence_squared); - } - - #[test] - fn to_fp12_unnormalized_computes_correctly() { - let g = BLS12381TwistCurve::generator(); - let expectedx = Fp12E::from_coefficients(&[ - "0", - "0", - "24aa2b2f08f0a91260805272dc51051c6e47ad4fa403b02b4510b647ae3d1770bac0326a805bbefd48056c8c121bdb8", - "13e02b6052719f607dacd3a088274f65596bd0d09920b61ab5da61bbdc7f5049334cf11213945d57e5ac7d055d042b7e", - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0" - ]); - let expectedy = Fp12E::from_coefficients(&[ - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "ce5d527727d6e118cc9cdc6da2e351aadfd9baa8cbdd3a76d429a695160d12c923ac9cc3baca289e193548608b82801", - "606c4a02ea734cc32acd2b02bc28b99cb3e287e85a763af267492ab572e99ab3f370d275cec1da1aaa9075ff05f79be", - "0", - "0" - ]); - let [g_to_fp12_x, g_to_fp12_y] = g.to_fp12_unnormalized(); - assert_eq!(g_to_fp12_x, expectedx); - assert_eq!(g_to_fp12_y, expectedy); - } - - #[test] - fn add_base_field_with_degree_2_extension() { - let a = FieldElement::::from(3); - let a_extension = FieldElement::::from(3); - let b = FieldElement::::from(2); - assert_eq!(a + &b, a_extension + b); - } - - #[test] - fn double_base_field_with_degree_2_extension() { - let a = FieldElement::::from(3); - let b = FieldElement::::from(2); - assert_eq!(a.double(), a.clone() + a); - assert_eq!(b.double(), b.clone() + b); - } - - #[test] - fn mul_base_field_with_degree_2_extension() { - let a = FieldElement::::from(3); - let a_extension = FieldElement::::from(3); - let b = FieldElement::::from(2); - assert_eq!(a * &b, a_extension * b); - } - - #[test] - fn sub_base_field_with_degree_2_extension() { - let a = FieldElement::::from(3); - let a_extension = FieldElement::::from(3); - let b = FieldElement::::from(2); - assert_eq!(a - &b, a_extension - b); - } - - #[test] - fn div_base_field_with_degree_2_extension() { - let a = FieldElement::::from(3); - let a_extension = FieldElement::::from(3); - let b = FieldElement::::from(2); - assert_eq!((a / &b).unwrap(), (a_extension / b).unwrap()); - } - - #[test] - fn embed_base_field_with_degree_2_extension() { - let a = FieldElement::::from(3); - let a_extension = FieldElement::::from(3); - assert_eq!(a.to_extension::(), a_extension); - } -} diff --git a/crates/math/src/elliptic_curve/short_weierstrass/curves/bls12_381/mod.rs b/crates/math/src/elliptic_curve/short_weierstrass/curves/bls12_381/mod.rs deleted file mode 100644 index 0ce0e6803..000000000 --- a/crates/math/src/elliptic_curve/short_weierstrass/curves/bls12_381/mod.rs +++ /dev/null @@ -1,9 +0,0 @@ -pub mod compression; -pub mod curve; -pub mod default_types; -pub mod field_extension; -pub mod sqrt; -pub mod twist; - -#[cfg(feature = "alloc")] -pub mod pairing; diff --git a/crates/math/src/elliptic_curve/short_weierstrass/curves/bls12_381/pairing.rs b/crates/math/src/elliptic_curve/short_weierstrass/curves/bls12_381/pairing.rs deleted file mode 100644 index c1d0fffe1..000000000 --- a/crates/math/src/elliptic_curve/short_weierstrass/curves/bls12_381/pairing.rs +++ /dev/null @@ -1,521 +0,0 @@ -use super::{ - curve::BLS12381Curve, - field_extension::{ - mul_fp2_by_nonresidue, BLS12381PrimeField, Degree12ExtensionField, Degree2ExtensionField, - Degree4ExtensionField, - }, - twist::BLS12381TwistCurve, -}; -use crate::{cyclic_group::IsGroup, elliptic_curve::traits::IsPairing, errors::PairingError}; -use crate::{ - elliptic_curve::short_weierstrass::{ - curves::bls12_381::field_extension::{Degree6ExtensionField, LevelTwoResidue}, - point::ShortWeierstrassProjectivePoint, - traits::IsShortWeierstrass, - }, - field::{element::FieldElement, extensions::cubic::HasCubicNonResidue}, - unsigned_integer::element::U256, -}; - -type FpE = FieldElement; -type Fp2E = FieldElement; -type Fp4E = FieldElement; -type Fp6E = FieldElement; -type Fp12E = FieldElement; - -pub const SUBGROUP_ORDER: U256 = - U256::from_hex_unchecked("73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001"); -// value of x in binary - -// We use |x|, then if needed we apply the minus sign in the final exponentiation by applying the inverse (in this case the conjugate because the element is in the cyclotomic subgroup) -pub const X: u64 = 0xd201000000010000; - -// X = 1101001000000001000000000000000000000000000000010000000000000000 -pub const X_BINARY: &[bool] = &[ - true, true, false, true, false, false, true, false, false, false, false, false, false, false, - false, true, false, false, false, false, false, false, false, false, false, false, false, - false, false, false, false, false, false, false, false, false, false, false, false, false, - false, false, false, false, false, false, false, true, false, false, false, false, false, - false, false, false, false, false, false, false, false, false, false, false, -]; - -// GAMMA constants used to compute the Frobenius morphisms -// We took these constants from https://github.com/hecmas/zkNotebook/blob/main/src/BLS12381/constants.ts -pub const GAMMA_11: Fp2E = Fp2E::const_from_raw([ - FpE::from_hex_unchecked("1904D3BF02BB0667C231BEB4202C0D1F0FD603FD3CBD5F4F7B2443D784BAB9C4F67EA53D63E7813D8D0775ED92235FB8"), - FpE::from_hex_unchecked("FC3E2B36C4E03288E9E902231F9FB854A14787B6C7B36FEC0C8EC971F63C5F282D5AC14D6C7EC22CF78A126DDC4AF3"), -]); - -pub const GAMMA_12: Fp2E = Fp2E::const_from_raw([ - FpE::from_hex_unchecked("0"), - FpE::from_hex_unchecked("1A0111EA397FE699EC02408663D4DE85AA0D857D89759AD4897D29650FB85F9B409427EB4F49FFFD8BFD00000000AAAC"), -]); - -pub const GAMMA_13: Fp2E = Fp2E::const_from_raw([ - FpE::from_hex_unchecked("6AF0E0437FF400B6831E36D6BD17FFE48395DABC2D3435E77F76E17009241C5EE67992F72EC05F4C81084FBEDE3CC09"), - FpE::from_hex_unchecked("6AF0E0437FF400B6831E36D6BD17FFE48395DABC2D3435E77F76E17009241C5EE67992F72EC05F4C81084FBEDE3CC09"), -]); - -pub const GAMMA_14: Fp2E = Fp2E::const_from_raw([ - FpE::from_hex_unchecked("1A0111EA397FE699EC02408663D4DE85AA0D857D89759AD4897D29650FB85F9B409427EB4F49FFFD8BFD00000000AAAD"), - FpE::from_hex_unchecked("0"), -]); - -pub const GAMMA_15: Fp2E = Fp2E::const_from_raw([ - FpE::from_hex_unchecked("5B2CFD9013A5FD8DF47FA6B48B1E045F39816240C0B8FEE8BEADF4D8E9C0566C63A3E6E257F87329B18FAE980078116"), - FpE::from_hex_unchecked("144E4211384586C16BD3AD4AFA99CC9170DF3560E77982D0DB45F3536814F0BD5871C1908BD478CD1EE605167FF82995"), -]); - -/// GAMMA_2i = GAMMA_1i * GAMMA_1i.conjugate() -pub const GAMMA_21: FpE = FpE::from_hex_unchecked( - "5F19672FDF76CE51BA69C6076A0F77EADDB3A93BE6F89688DE17D813620A00022E01FFFFFFFEFFFF", -); - -pub const GAMMA_22: FpE = FpE::from_hex_unchecked( - "5F19672FDF76CE51BA69C6076A0F77EADDB3A93BE6F89688DE17D813620A00022E01FFFFFFFEFFFE", -); - -pub const GAMMA_23: FpE = - FpE::from_hex_unchecked("1A0111EA397FE69A4B1BA7B6434BACD764774B84F38512BF6730D2A0F6B0F6241EABFFFEB153FFFFB9FEFFFFFFFFAAAA"); - -pub const GAMMA_24: FpE = - FpE::from_hex_unchecked("1A0111EA397FE699EC02408663D4DE85AA0D857D89759AD4897D29650FB85F9B409427EB4F49FFFD8BFD00000000AAAC"); - -pub const GAMMA_25: FpE = - FpE::from_hex_unchecked("1A0111EA397FE699EC02408663D4DE85AA0D857D89759AD4897D29650FB85F9B409427EB4F49FFFD8BFD00000000AAAD"); - -#[derive(Clone)] -pub struct BLS12381AtePairing; - -impl IsPairing for BLS12381AtePairing { - type G1Point = ShortWeierstrassProjectivePoint; - type G2Point = ShortWeierstrassProjectivePoint; - type OutputField = Degree12ExtensionField; - - /// Compute the product of the ate pairings for a list of point pairs. - fn compute_batch( - pairs: &[(&Self::G1Point, &Self::G2Point)], - ) -> Result, PairingError> { - let mut result = FieldElement::one(); - for (p, q) in pairs { - if !p.is_in_subgroup() || !q.is_in_subgroup() { - return Err(PairingError::PointNotInSubgroup); - } - if !p.is_neutral_element() && !q.is_neutral_element() { - let p = p.to_affine(); - let q = q.to_affine(); - result *= miller(&q, &p); - } - } - Ok(final_exponentiation(&result)) - } -} - -/// Implements the miller loop for the ate pairing of the BLS12 381 curve. -/// Based on algorithm 9.2, page 212 of the book -/// "Topics in computational number theory" by W. Bons and K. Lenstra -#[allow(unused)] -pub fn miller( - q: &ShortWeierstrassProjectivePoint, - p: &ShortWeierstrassProjectivePoint, -) -> FieldElement { - let mut r = q.clone(); - let mut f = FieldElement::::one(); - X_BINARY.iter().skip(1).for_each(|bit| { - double_accumulate_line(&mut r, p, &mut f); - if *bit { - add_accumulate_line(&mut r, q, p, &mut f); - } - }); - - f.conjugate() -} - -fn double_accumulate_line( - t: &mut ShortWeierstrassProjectivePoint, - p: &ShortWeierstrassProjectivePoint, - accumulator: &mut FieldElement, -) { - let [x1, y1, z1] = t.coordinates(); - let [px, py, _] = p.coordinates(); - let residue = LevelTwoResidue::residue(); - let two_inv = FieldElement::::new_base("d0088f51cbff34d258dd3db21a5d66bb23ba5c279c2895fb39869507b587b120f55ffff58a9ffffdcff7fffffffd556"); - let three = FieldElement::::from(3); - - let a = &two_inv * x1 * y1; - let b = y1.square(); - let c = z1.square(); - let d = &three * &c; - let e = BLS12381TwistCurve::b() * d; - let f = &three * &e; - let g = two_inv * (&b + &f); - let h = (y1 + z1).square() - (&b + &c); - - let x3 = &a * (&b - &f); - let y3 = g.square() - (&three * e.square()); - let z3 = &b * &h; - - let [h0, h1] = h.value(); - let x1_sq_3 = three * x1.square(); - let [x1_sq_30, x1_sq_31] = x1_sq_3.value(); - - // SAFETY: The values `x_3, y_3, z_3` are computed correctly to be on the curve. - t.set_unchecked([x3, y3, z3]); - - // (a0 + a2w2 + a4w4 + a1w + a3w3 + a5w5) * (b0 + b2 w2 + b3 w3) = - // (a0b0 + r (a3b3 + a4b2)) w0 + (a1b0 + r (a4b3 + a5b2)) w - // (a2b0 + r a5b3 + a0b2 ) w2 + (a3b0 + a0b3 + a1b2 ) w3 - // (a4b0 + a1b3 + a2b2 ) w4 + (a5b0 + a2b3 + a3b2 ) w5 - let accumulator_sq = accumulator.square(); - let [x, y] = accumulator_sq.value(); - let [a0, a2, a4] = x.value(); - let [a1, a3, a5] = y.value(); - let b0 = e - b; - let b2 = FieldElement::new([x1_sq_30 * px, x1_sq_31 * px]); - let b3 = FieldElement::::new([-h0 * py, -h1 * py]); - *accumulator = FieldElement::new([ - FieldElement::new([ - a0 * &b0 + &residue * (a3 * &b3 + a4 * &b2), // w0 - a2 * &b0 + &residue * a5 * &b3 + a0 * &b2, // w2 - a4 * &b0 + a1 * &b3 + a2 * &b2, // w4 - ]), - FieldElement::new([ - a1 * &b0 + &residue * (a4 * &b3 + a5 * &b2), // w1 - a3 * &b0 + a0 * &b3 + a1 * &b2, // w3 - a5 * &b0 + a2 * &b3 + a3 * &b2, // w5 - ]), - ]); -} - -fn add_accumulate_line( - t: &mut ShortWeierstrassProjectivePoint, - q: &ShortWeierstrassProjectivePoint, - p: &ShortWeierstrassProjectivePoint, - accumulator: &mut FieldElement, -) { - let [x1, y1, z1] = t.coordinates(); - let [x2, y2, _] = q.coordinates(); - let [px, py, _] = p.coordinates(); - let residue = LevelTwoResidue::residue(); - - let a = y2 * z1; - let b = x2 * z1; - let theta = y1 - a; - let lambda = x1 - b; - let c = theta.square(); - let d = lambda.square(); - let e = &lambda * &d; - let f = z1 * c; - let g = x1 * d; - let h = &e + f - FieldElement::::from(2) * &g; - let i = y1 * &e; - - let x3 = &lambda * &h; - let y3 = &theta * (g - h) - i; - let z3 = z1 * e; - - // SAFETY: The values `x_3, y_3, z_3` are computed correctly to be on the curve. - t.set_unchecked([x3, y3, z3]); - - let [lambda0, lambda1] = lambda.value(); - let [theta0, theta1] = theta.value(); - - let [x, y] = accumulator.value(); - let [a0, a2, a4] = x.value(); - let [a1, a3, a5] = y.value(); - let b0 = -lambda.clone() * y2 + theta.clone() * x2; - let b2 = FieldElement::new([-theta0 * px, -theta1 * px]); - let b3 = FieldElement::::new([lambda0 * py, lambda1 * py]); - *accumulator = FieldElement::new([ - FieldElement::new([ - a0 * &b0 + &residue * (a3 * &b3 + a4 * &b2), // w0 - a2 * &b0 + &residue * a5 * &b3 + a0 * &b2, // w2 - a4 * &b0 + a1 * &b3 + a2 * &b2, // w4 - ]), - FieldElement::new([ - a1 * &b0 + &residue * (a4 * &b3 + a5 * &b2), // w1 - a3 * &b0 + a0 * &b3 + a1 * &b2, // w3 - a5 * &b0 + a2 * &b3 + a3 * &b2, // w5 - ]), - ]); -} - -// To understand more about how to reduce the final exponentiation -// read "Efficient Final Exponentiation via Cyclotomic Structure for -// Pairings over Families of Elliptic Curves" (https://eprint.iacr.org/2020/875.pdf) -// Hard part from https://eprint.iacr.org/2020/875.pdf p14 -// Same implementation as in Constantine https://github.com/mratsim/constantine/blob/master/constantine/math/pairings/pairings_bls12.nim -pub fn final_exponentiation(f: &Fp12E) -> Fp12E { - let f_easy_aux = f.conjugate() * f.inv().unwrap(); - let mut f_easy = frobenius_square(&f_easy_aux) * &f_easy_aux; - - let mut v2 = cyclotomic_square(&f_easy); // v2 = f² - let mut v0 = cyclotomic_pow_x(&f_easy).conjugate(); // v0 = f^x - let mut v1 = f_easy.conjugate(); // v1 = f^-1 - - // (x−1)² - v0 *= v1; // v0 = f^(x-1) - v1 = cyclotomic_pow_x(&v0).conjugate(); // v1 = (f^(x-1))^(x) - - v0 = v0.conjugate(); // v0 = (f^(x-1))^(-1) - v0 *= &v1; // v0 = (f^(x-1))^(-1) * (f^(x-1))^x = (f^(x-1))^(x-1) = f^((x-1)²) - - // (x+p) - v1 = cyclotomic_pow_x(&v0).conjugate(); // v1 = f^((x-1)².x) - v0 = frobenius(&v0); // f^((x-1)².p) - v0 *= &v1; // f^((x-1)².p + (x-1)².x) = f^((x-1)².(x+p)) - - // + 3 - f_easy *= v2; // f^3 - - // (x²+p²−1) - v2 = cyclotomic_pow_x(&v0).conjugate(); - v1 = cyclotomic_pow_x(&v2).conjugate(); // v1 = f^((x-1)².(x+p).x²) - v2 = frobenius_square(&v0); // v2 = f^((x-1)².(x+p).p²) - v0 = v0.conjugate(); // v0 = f^((x-1)².(x+p).-1) - v0 *= &v1; // v0 = f^((x-1)².(x+p).(x²-1)) - v0 *= &v2; // v0 = f^((x-1)².(x+p).(x²+p²-1)) - - f_easy *= &v0; - f_easy -} - -////////////////// FROBENIUS MORPHISIMS ////////////////// -pub fn frobenius(f: &Fp12E) -> Fp12E { - let [a, b] = f.value(); // f = a + bw, where a and b in Fp6. - let [a0, a1, a2] = a.value(); // a = a0 + a1 * v + a2 * v^2, where a0, a1 and a2 in Fp2. - let [b0, b1, b2] = b.value(); // b = b0 + b1 * v + b2 * v^2, where b0, b1 and b2 in Fp2. - - // c1 = a0.conjugate() + a1.conjugate() * GAMMA_12 * v + a2.conjugate() * GAMMA_14 * v^2 - let c1 = Fp6E::new([ - a0.conjugate(), - a1.conjugate() * GAMMA_12, - a2.conjugate() * GAMMA_14, - ]); - - let c2 = Fp6E::new([ - b0.conjugate() * GAMMA_11, - b1.conjugate() * GAMMA_13, - b2.conjugate() * GAMMA_15, - ]); - - Fp12E::new([c1, c2]) //c1 + c2 * w -} - -fn frobenius_square( - f: &FieldElement, -) -> FieldElement { - let [a, b] = f.value(); - let [a0, a1, a2] = a.value(); - let [b0, b1, b2] = b.value(); - - let c1 = Fp6E::new([a0.clone(), GAMMA_22 * a1, GAMMA_24 * a2]); - let c2 = Fp6E::new([GAMMA_21 * b0, GAMMA_23 * b1, GAMMA_25 * b2]); - - Fp12E::new([c1, c2]) -} - -////////////////// CYCLOTOMIC SUBGROUP OPERATIONS ////////////////// -// Since the result of the Easy Part of the Final Exponentiation belongs to the cyclotomic -// subgroup of Fp12, we can optimize the square and pow operations used in the Hard Part. - -/// Computes the square of an element of a cyclotomic subgroup of Fp12. -/// Algorithm from Constantine's cyclotomic_square_quad_over_cube -/// https://github.com/mratsim/constantine/blob/master/constantine/math/pairings/cyclotomic_subgroups.nim#L354 -pub fn cyclotomic_square(a: &Fp12E) -> Fp12E { - // a = g + h * w - let [g, h] = a.value(); - let [b0, b1, b2] = g.value(); - let [b3, b4, b5] = h.value(); - - let v0 = Fp4E::new([b0.clone(), b4.clone()]).square(); - let v1 = Fp4E::new([b3.clone(), b2.clone()]).square(); - let v2 = Fp4E::new([b1.clone(), b5.clone()]).square(); - - // r = r0 + r1 * w - // r0 = r00 + r01 * v + r02 * v^2 - // r1 = r10 + r11 * v + r12 * v^2 - - // r00 = 3v00 - 2b0 - let mut r00 = &v0.value()[0] - b0; - r00 = r00.double(); - r00 += v0.value()[0].clone(); - - // r01 = 3v10 -2b1 - let mut r01 = &v1.value()[0] - b1; - r01 = r01.double(); - r01 += v1.value()[0].clone(); - - // r11 = 3v01 - 2b4 - let mut r11 = &v0.value()[1] + b4; - r11 = r11.double(); - r11 += v0.value()[1].clone(); - - // r12 = 3v11 - 2b5 - let mut r12 = &v1.value()[1] + b5; - r12 = r12.double(); - r12 += v1.value()[1].clone(); - - // 3 * (9 + u) * v21 + 2b3 - let v21 = mul_fp2_by_nonresidue(&v2.value()[1]); - let mut r10 = &v21 + b3; - r10 = r10.double(); - r10 += v21; - - // 3 * (9 + u) * v20 - 2b3 - let mut r02 = &v2.value()[0] - b2; - r02 = r02.double(); - r02 += v2.value()[0].clone(); - - Fp12E::new([Fp6E::new([r00, r01, r02]), Fp6E::new([r10, r11, r12])]) -} - -#[allow(clippy::needless_range_loop)] -pub fn cyclotomic_pow_x(f: &Fp12E) -> Fp12E { - let mut result = Fp12E::one(); - X_BINARY.iter().for_each(|&bit| { - result = cyclotomic_square(&result); - if bit { - result = &result * f; - } - }); - result -} -#[cfg(test)] -mod tests { - use crate::{ - cyclic_group::IsGroup, elliptic_curve::traits::IsEllipticCurve, - unsigned_integer::element::U384, - }; - - use super::*; - - #[test] - fn test_double_accumulate_line_doubles_point_correctly() { - let g1 = BLS12381Curve::generator(); - let g2 = BLS12381TwistCurve::generator(); - let mut r = g2.clone(); - let mut f = FieldElement::one(); - double_accumulate_line(&mut r, &g1, &mut f); - assert_eq!(r, g2.operate_with(&g2)); - } - - #[test] - fn test_add_accumulate_line_adds_points_correctly() { - let g1 = BLS12381Curve::generator(); - let g = BLS12381TwistCurve::generator(); - let a: u64 = 12; - let b: u64 = 23; - let g2 = g.operate_with_self(a).to_affine(); - let g3 = g.operate_with_self(b).to_affine(); - let expected = g.operate_with_self(a + b); - let mut r = g2; - let mut f = FieldElement::one(); - add_accumulate_line(&mut r, &g3, &g1, &mut f); - assert_eq!(r, expected); - } - - #[test] - fn batch_ate_pairing_bilinearity() { - let p = BLS12381Curve::generator(); - let q = BLS12381TwistCurve::generator(); - let a = U384::from_u64(11); - let b = U384::from_u64(93); - - let result = BLS12381AtePairing::compute_batch(&[ - ( - &p.operate_with_self(a).to_affine(), - &q.operate_with_self(b).to_affine(), - ), - ( - &p.operate_with_self(a * b).to_affine(), - &q.neg().to_affine(), - ), - ]) - .unwrap(); - assert_eq!(result, FieldElement::one()); - } - - #[test] - fn ate_pairing_returns_one_when_one_element_is_the_neutral_element() { - let p = BLS12381Curve::generator().to_affine(); - let q = ShortWeierstrassProjectivePoint::neutral_element(); - let result = BLS12381AtePairing::compute_batch(&[(&p.to_affine(), &q)]).unwrap(); - assert_eq!(result, FieldElement::one()); - - let p = ShortWeierstrassProjectivePoint::neutral_element(); - let q = BLS12381TwistCurve::generator(); - let result = BLS12381AtePairing::compute_batch(&[(&p, &q.to_affine())]).unwrap(); - assert_eq!(result, FieldElement::one()); - } - - #[test] - fn ate_pairing_errors_when_one_element_is_not_in_subgroup() { - // p = (0, 2, 1) is in the curve but not in the subgroup. - // Recall that the BLS 12-381 curve equation is y^2 = x^3 + 4. - let p = ShortWeierstrassProjectivePoint::new([ - FieldElement::zero(), - FieldElement::from(2), - FieldElement::one(), - ]) - .unwrap(); - let q = ShortWeierstrassProjectivePoint::neutral_element(); - let result = BLS12381AtePairing::compute_batch(&[(&p.to_affine(), &q)]); - assert!(result.is_err()) - } - - #[test] - fn apply_12_times_frobenius_is_identity() { - let f = Fp12E::from_coefficients(&[ - "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", - ]); - let mut result = frobenius(&f); - for _ in 1..12 { - result = frobenius(&result); - } - assert_eq!(f, result) - } - - #[test] - fn apply_6_times_frobenius_square_is_identity() { - let f = Fp12E::from_coefficients(&[ - "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", - ]); - let mut result = frobenius_square(&f); - for _ in 1..6 { - result = frobenius_square(&result); - } - assert_eq!(f, result) - } - - #[test] - fn cyclotomic_square_equals_square() { - let p = BLS12381Curve::generator(); - let q = BLS12381TwistCurve::generator(); - let f = miller(&q, &p); - let f_easy_aux = f.conjugate() * f.inv().unwrap(); // f ^ (p^6 - 1) because f^(p^6) = f.conjugate(). - let f_easy = &frobenius_square(&f_easy_aux) * f_easy_aux; // (f^{p^6 - 1})^(p^2) * (f^{p^6 - 1}). - assert_eq!(cyclotomic_square(&f_easy), f_easy.square()); - } - - #[test] - fn test_double_accumulate_line_doubles_point_correctl_2() { - let g1 = BLS12381Curve::generator(); - let g2 = BLS12381TwistCurve::generator(); - let mut r = g2.clone(); - let mut f = FieldElement::one(); - double_accumulate_line(&mut r, &g1, &mut f); - let expected_r = g2.operate_with(&g2); - assert_eq!(r.to_affine(), expected_r.to_affine()); - } - - #[test] - fn cyclotomic_pow_x_equals_pow() { - let p = BLS12381Curve::generator(); - let q = BLS12381TwistCurve::generator(); - let f = miller(&q, &p); - let f_easy_aux = f.conjugate() * f.inv().unwrap(); // f ^ (p^6 - 1) because f^(p^6) = f.conjugate(). - let f_easy = &frobenius_square(&f_easy_aux) * f_easy_aux; // (f^{p^6 - 1})^(p^2) * (f^{p^6 - 1}). - assert_eq!(cyclotomic_pow_x(&f_easy), f_easy.pow(X)); - } -} diff --git a/crates/math/src/elliptic_curve/short_weierstrass/curves/bls12_381/sqrt.rs b/crates/math/src/elliptic_curve/short_weierstrass/curves/bls12_381/sqrt.rs deleted file mode 100644 index 1b8081f7c..000000000 --- a/crates/math/src/elliptic_curve/short_weierstrass/curves/bls12_381/sqrt.rs +++ /dev/null @@ -1,128 +0,0 @@ -use crate::field::traits::LegendreSymbol; - -use super::{curve::BLS12381FieldElement, curve::BLS12381TwistCurveFieldElement}; -use core::cmp::Ordering; - -#[must_use] -pub fn select_sqrt_value_from_third_bit( - sqrt_1: BLS12381FieldElement, - sqrt_2: BLS12381FieldElement, - third_bit: u8, -) -> BLS12381FieldElement { - match ( - sqrt_1.representative().cmp(&sqrt_2.representative()), - third_bit, - ) { - (Ordering::Greater, 0) => sqrt_2, - (Ordering::Greater, _) | (Ordering::Less, 0) | (Ordering::Equal, _) => sqrt_1, - (Ordering::Less, _) => sqrt_2, - } -} - -/// * `third_bit` - if 1, then the square root is the greater one, otherwise it is the smaller one. -#[must_use] -pub fn sqrt_qfe( - input: &BLS12381TwistCurveFieldElement, - third_bit: u8, -) -> Option { - // Algorithm 8, https://eprint.iacr.org/2012/685.pdf - if *input == BLS12381TwistCurveFieldElement::zero() { - Some(BLS12381TwistCurveFieldElement::zero()) - } else { - let a = input.value()[0].clone(); - let b = input.value()[1].clone(); - if b == BLS12381FieldElement::zero() { - // second part is zero - let (y_sqrt_1, y_sqrt_2) = a.sqrt()?; - let y_aux = select_sqrt_value_from_third_bit(y_sqrt_1, y_sqrt_2, third_bit); - - Some(BLS12381TwistCurveFieldElement::new([ - y_aux, - BLS12381FieldElement::zero(), - ])) - } else { - // second part of the input field number is non-zero - // instead of "sum" is: -beta - let alpha = a.pow(2u64) + b.pow(2u64); - let gamma = alpha.legendre_symbol(); - match gamma { - LegendreSymbol::One => { - let two = BLS12381FieldElement::from(2u64); - let two_inv = two.inv().unwrap(); - // calculate the square root of alpha - let (y_sqrt1, y_sqrt2) = alpha.sqrt()?; - let mut delta = (a.clone() + y_sqrt1) * two_inv.clone(); - - let legendre_delta = delta.legendre_symbol(); - if legendre_delta == LegendreSymbol::MinusOne { - delta = (a + y_sqrt2) * two_inv; - }; - let (x_sqrt_1, x_sqrt_2) = delta.sqrt()?; - let x_0 = select_sqrt_value_from_third_bit(x_sqrt_1, x_sqrt_2, third_bit); - let x_1 = b * (two * x_0.clone()).inv().unwrap(); - Some(BLS12381TwistCurveFieldElement::new([x_0, x_1])) - } - LegendreSymbol::MinusOne => None, - LegendreSymbol::Zero => { - unreachable!("The input is zero, but we already handled this case.") - } - } - } - } -} - -#[cfg(test)] -mod tests { - use super::super::curve::BLS12381FieldElement; - - #[test] - fn test_sqrt_qfe() { - let c1 = BLS12381FieldElement::from_hex( - "0x13e02b6052719f607dacd3a088274f65596bd0d09920b61ab5da61bbdc7f5049334cf11213945d57e5ac7d055d042b7e", - ).unwrap(); - let c0 = BLS12381FieldElement::from_hex( - "0x024aa2b2f08f0a91260805272dc51051c6e47ad4fa403b02b4510b647ae3d1770bac0326a805bbefd48056c8c121bdb8" - ).unwrap(); - let qfe = super::BLS12381TwistCurveFieldElement::new([c0, c1]); - - let b1 = BLS12381FieldElement::from_hex("0x4").unwrap(); - let b0 = BLS12381FieldElement::from_hex("0x4").unwrap(); - let qfe_b = super::BLS12381TwistCurveFieldElement::new([b0, b1]); - - let cubic_value = qfe.pow(3_u64) + qfe_b; - let root = super::sqrt_qfe(&cubic_value, 0).unwrap(); - - let c0_expected = BLS12381FieldElement::from_hex("0x0ce5d527727d6e118cc9cdc6da2e351aadfd9baa8cbdd3a76d429a695160d12c923ac9cc3baca289e193548608b82801").unwrap(); - let c1_expected = BLS12381FieldElement::from_hex("0x0606c4a02ea734cc32acd2b02bc28b99cb3e287e85a763af267492ab572e99ab3f370d275cec1da1aaa9075ff05f79be").unwrap(); - let qfe_expected = super::BLS12381TwistCurveFieldElement::new([c0_expected, c1_expected]); - - let value_root = root.value(); - let value_qfe_expected = qfe_expected.value(); - - assert_eq!(value_root[0].clone(), value_qfe_expected[0].clone()); - assert_eq!(value_root[1].clone(), value_qfe_expected[1].clone()); - } - - #[test] - fn test_sqrt_qfe_2() { - let c0 = BLS12381FieldElement::from_hex("0x02").unwrap(); - let c1 = BLS12381FieldElement::from_hex("0x00").unwrap(); - let qfe = super::BLS12381TwistCurveFieldElement::new([c0, c1]); - - let c0_expected = BLS12381FieldElement::from_hex("0x013a59858b6809fca4d9a3b6539246a70051a3c88899964a42bc9a69cf9acdd9dd387cfa9086b894185b9a46a402be73").unwrap(); - let c1_expected = BLS12381FieldElement::from_hex("0x02d27e0ec3356299a346a09ad7dc4ef68a483c3aed53f9139d2f929a3eecebf72082e5e58c6da24ee32e03040c406d4f").unwrap(); - let qfe_expected = super::BLS12381TwistCurveFieldElement::new([c0_expected, c1_expected]); - - let b1 = BLS12381FieldElement::from_hex("0x4").unwrap(); - let b0 = BLS12381FieldElement::from_hex("0x4").unwrap(); - let qfe_b = super::BLS12381TwistCurveFieldElement::new([b0, b1]); - - let root = super::sqrt_qfe(&(qfe.pow(3_u64) + qfe_b), 0).unwrap(); - - let value_root = root.value(); - let value_qfe_expected = qfe_expected.value(); - - assert_eq!(value_root[0].clone(), value_qfe_expected[0].clone()); - assert_eq!(value_root[1].clone(), value_qfe_expected[1].clone()); - } -} diff --git a/crates/math/src/elliptic_curve/short_weierstrass/curves/bls12_381/twist.rs b/crates/math/src/elliptic_curve/short_weierstrass/curves/bls12_381/twist.rs deleted file mode 100644 index ccce55866..000000000 --- a/crates/math/src/elliptic_curve/short_weierstrass/curves/bls12_381/twist.rs +++ /dev/null @@ -1,235 +0,0 @@ -use crate::cyclic_group::IsGroup; -use crate::elliptic_curve::short_weierstrass::point::ShortWeierstrassProjectivePoint; -use crate::elliptic_curve::traits::IsEllipticCurve; -use crate::unsigned_integer::element::U384; -use crate::{ - elliptic_curve::short_weierstrass::traits::IsShortWeierstrass, field::element::FieldElement, -}; - -use super::field_extension::{Degree12ExtensionField, Degree2ExtensionField}; - -const GENERATOR_X_0: U384 = U384::from_hex_unchecked("024aa2b2f08f0a91260805272dc51051c6e47ad4fa403b02b4510b647ae3d1770bac0326a805bbefd48056c8c121bdb8"); -const GENERATOR_X_1: U384 = U384::from_hex_unchecked("13e02b6052719f607dacd3a088274f65596bd0d09920b61ab5da61bbdc7f5049334cf11213945d57e5ac7d055d042b7e"); -const GENERATOR_Y_0: U384 = U384::from_hex_unchecked("0ce5d527727d6e118cc9cdc6da2e351aadfd9baa8cbdd3a76d429a695160d12c923ac9cc3baca289e193548608b82801"); -const GENERATOR_Y_1: U384 = U384::from_hex_unchecked("0606c4a02ea734cc32acd2b02bc28b99cb3e287e85a763af267492ab572e99ab3f370d275cec1da1aaa9075ff05f79be"); - -/// The description of the curve. -#[derive(Clone, Debug)] -pub struct BLS12381TwistCurve; - -impl IsEllipticCurve for BLS12381TwistCurve { - type BaseField = Degree2ExtensionField; - type PointRepresentation = ShortWeierstrassProjectivePoint; - - fn generator() -> Self::PointRepresentation { - // SAFETY: - // - The generator point is mathematically verified to be a valid point on the curve. - // - `unwrap()` is safe because the provided coordinates satisfy the curve equation. - let point = Self::PointRepresentation::new([ - FieldElement::new([ - FieldElement::new(GENERATOR_X_0), - FieldElement::new(GENERATOR_X_1), - ]), - FieldElement::new([ - FieldElement::new(GENERATOR_Y_0), - FieldElement::new(GENERATOR_Y_1), - ]), - FieldElement::one(), - ]); - point.unwrap() - } -} - -impl IsShortWeierstrass for BLS12381TwistCurve { - fn a() -> FieldElement { - FieldElement::zero() - } - - fn b() -> FieldElement { - FieldElement::new([FieldElement::from(4), FieldElement::from(4)]) - } -} - -impl ShortWeierstrassProjectivePoint { - /// This function is related to the map ψ: E_twist(𝔽p²) -> E(𝔽p¹²). - /// Given an affine point G in E_twist(𝔽p²) returns x, y such that - /// ψ(G) = (x', y', 1) with x' = x * x'' and y' = y * y'' - /// for some x'', y'' in 𝔽p². - /// This is meant only to be used in the miller loop of the - /// ate pairing before the final exponentiation. - /// This is because elements in 𝔽p² raised to that - /// power are 1 and so the final result of the ate pairing - /// doens't depend on having this function output the exact - /// values of x' and y'. And it is enough to work with x and y. - pub fn to_fp12_unnormalized(&self) -> [FieldElement; 2] { - if self.is_neutral_element() { - [FieldElement::zero(), FieldElement::one()] - } else { - let [qx, qy, _] = self.coordinates(); - - let result_x = FieldElement::new([ - FieldElement::new([FieldElement::zero(), qx.clone(), FieldElement::zero()]), - FieldElement::zero(), - ]); - - let result_y = FieldElement::new([ - FieldElement::zero(), - FieldElement::new([FieldElement::zero(), qy.clone(), FieldElement::zero()]), - ]); - - [result_x, result_y] - } - } -} - -#[cfg(test)] -mod tests { - use crate::{ - cyclic_group::IsGroup, - elliptic_curve::{ - short_weierstrass::{ - curves::bls12_381::field_extension::{BLS12381PrimeField, Degree2ExtensionField}, - traits::IsShortWeierstrass, - }, - traits::IsEllipticCurve, - }, - field::element::FieldElement, - unsigned_integer::element::U384, - }; - - use super::BLS12381TwistCurve; - type Level0FE = FieldElement; - type Level1FE = FieldElement; - - #[cfg(feature = "alloc")] - use crate::elliptic_curve::short_weierstrass::point::{ - Endianness, PointFormat, ShortWeierstrassProjectivePoint, - }; - - #[test] - fn create_generator() { - let g = BLS12381TwistCurve::generator(); - let [x, y, _] = g.coordinates(); - assert_eq!( - BLS12381TwistCurve::defining_equation(x, y), - Level1FE::zero() - ); - } - - #[cfg(feature = "alloc")] - #[test] - fn serialize_deserialize_generator() { - let g = BLS12381TwistCurve::generator(); - let bytes = g.serialize(PointFormat::Projective, Endianness::LittleEndian); - - let deserialized = ShortWeierstrassProjectivePoint::::deserialize( - &bytes, - PointFormat::Projective, - Endianness::LittleEndian, - ) - .unwrap(); - - assert_eq!(deserialized, g); - } - - #[test] - fn add_points() { - let px = Level1FE::new([ - Level0FE::new(U384::from_hex_unchecked("97798b4a61ac301bbee71e36b5174e2f4adfe3e1729bdae1fcc9965ae84181be373aa80414823eed694f1270014012d")), - Level0FE::new(U384::from_hex_unchecked("c9852cc6e61868966249aec153b50b29b3c22409f4c7880fd13121981c103c8ef84d9ea29b552431360e82cf69219fa")) - ]); - let py = Level1FE::new([ - Level0FE::new(U384::from_hex_unchecked("16cb3a60f3fa52c8273aceeb94c4c7303e8074aa9eedec7355bbb1e8cceedd4ec1497f573f62822140377b8e339619ed")), - Level0FE::new(U384::from_hex_unchecked("1cd919b08afe06bebe9adf6223a55868a6fd8b77efc5c67b60fff39be36e9b44b7f10db16827c83b43ad2dad1947778")) - ]); - let qx = Level1FE::new([ - Level0FE::new(U384::from_hex_unchecked("b6bce994c23f6505131a5f6d4ce4ba30f5dab726780bef00517585cab02e17f4d015b26eeaff376dc236af26c0210f1")), - Level0FE::new(U384::from_hex_unchecked("163163e71fdd96a84b6a24d3e7cd9d7c0f06961e6fe8b7ec9b27bef1dbef5cbaf557563f725229fc79814a294c0b8511")) - ]); - let qy = Level1FE::new([ - Level0FE::new(U384::from_hex_unchecked("1c6afffac96cd457b4ac797e5cef6951c83bb328737f57df44ba0cc513d499f736816877a6cf87f1359e79d10151e14")), - Level0FE::new(U384::from_hex_unchecked("79e40e569c20182726c148ca72a6e862d03317a2cf75cd19c2be36e29e03da70acbefbfa7a4c4e1c088bf94ae6ba6ce")) - ]); - let expectedx = Level1FE::new([ - Level0FE::new(U384::from_hex_unchecked("63f209cd306e632cc91089bd6b3bb02a6679fd02931a6e2292976589426dfdff9366829d5f45d982413e8b9514e8965")), - Level0FE::new(U384::from_hex_unchecked("11aae43845fcb3e633217c2851889cddb939a3d2ddf00a64e4e0a723c362dff2caabc640a1095ac5be4075d4f7edf17f")) - ]); - let expectedy = Level1FE::new([ - Level0FE::new(U384::from_hex_unchecked("83e21ca01826bca9221373faf03132b80128c24760c639b44bd7e0b6c11537ef239c01d31a25a58c7f67fb16df0234b")), - Level0FE::new(U384::from_hex_unchecked("f45243fd699bba6c6ca644ad8070f7812e4987fb2c91f64139a293958ed373814ef7317c11c3496cd93b88871f5d2c7")) - ]); - let p = BLS12381TwistCurve::create_point_from_affine(px, py).unwrap(); - let q = BLS12381TwistCurve::create_point_from_affine(qx, qy).unwrap(); - let expected = BLS12381TwistCurve::create_point_from_affine(expectedx, expectedy).unwrap(); - assert_eq!(p.operate_with(&q), expected); - } - - #[test] - // Numbers checked in SAGE - fn add_points2() { - let px = Level1FE::new([ - Level0FE::new(U384::from_hex_unchecked("0x1414a51107b5ca989957046a1126425d371f5124215e294770f67fbf14dd92bbf1c9c2dbf35441769fa88427c17f0bb5")), - Level0FE::new(U384::from_hex_unchecked("0x6224c8c8d6ecb882197551c68a25340be33975948d7da7568f6e00131307dc3688d320ad3c3c7cb95625082a47908f2")) - ]); - let py = Level1FE::new([ - Level0FE::new(U384::from_hex_unchecked("0xa69bb992a48dabcc49ab3fa1508bbc1acae14a9af09db39290b303de518806cec0067486adb6044f936d4bd2e5a151")), - Level0FE::new(U384::from_hex_unchecked("0x98d34282ed5a2e265e455af63c66f7b5dd1557296f775463bcea891a14a801baa172e923055c4bb0fd5343e86294f41")) - ]); - - let qx = Level1FE::new([ - Level0FE::new(U384::from_hex_unchecked("0xae980b8c7483736e1904a1643e7a46f9980e52a5e65f0c5f7d195b30efd7173adea992c49a9073572d64ba67470e406")), - Level0FE::new(U384::from_hex_unchecked("0x57d195c5f11d93558b52a74be27ae07f82f908ce35fabe58ce212c6d0bcef4a9f25e31fe92b2a49ea3fbc5d6c8cde99")) - ]); - let qy = Level1FE::new([ - Level0FE::new(U384::from_hex_unchecked("0x160c8799bfe8b80732e9ccb33ad35d1d23b6d01d8170ad088118a3cfe2a97dba2cf06ac3ce202fe039b105082ea48c22")), - Level0FE::new(U384::from_hex_unchecked("0xc1e4cf5b2ca3deb2ec4f95b0ca0dbe79b0fc119f16f525e1d00054f009bcf2e2bde26f820b163e488bdc248adee1bfe")) - ]); - - let expectedx = Level1FE::new([ - Level0FE::new(U384::from_hex_unchecked("0xc9ef648884ce132f76f51a3fc536cc33e93c3a687c518abe1a0ddd4389df68c28214505a4b4a11a62d5b251badc7f9")), - Level0FE::new(U384::from_hex_unchecked("0x6a43f39c279791e3ae0d36c3c24bee770593b80f44f931d70bbdcda17f9ed53b682dba192aa3c92b18d25e5d49b7d04")) - ]); - let expectedy = Level1FE::new([ - Level0FE::new(U384::from_hex_unchecked("0x128764b35cb5dbfe604968c5e3734ca80e16ee940f966c260096c29dd15aa6c6de3b1fc68a085403b8bdc012bf3b5b30")), - Level0FE::new(U384::from_hex_unchecked("0x13471bd588bda43ce76f52dba32298bb46cfcf97a5f4484486e4394f6e38f7bd807ba62216d57ed8fd9df5f608c55ef1")) - ]); - - let p = BLS12381TwistCurve::create_point_from_affine(px, py).unwrap(); - let q = BLS12381TwistCurve::create_point_from_affine(qx, qy).unwrap(); - let expected = BLS12381TwistCurve::create_point_from_affine(expectedx, expectedy).unwrap(); - assert_eq!(p.operate_with(&q), expected); - } - - #[test] - // Numbers checked in SAGE - fn operate_with_self_test() { - let px = Level1FE::new([ - Level0FE::new(U384::from_hex_unchecked("0x1414a51107b5ca989957046a1126425d371f5124215e294770f67fbf14dd92bbf1c9c2dbf35441769fa88427c17f0bb5")), - Level0FE::new(U384::from_hex_unchecked("0x6224c8c8d6ecb882197551c68a25340be33975948d7da7568f6e00131307dc3688d320ad3c3c7cb95625082a47908f2")) - ]); - - let py = Level1FE::new([ - Level0FE::new(U384::from_hex_unchecked("0xa69bb992a48dabcc49ab3fa1508bbc1acae14a9af09db39290b303de518806cec0067486adb6044f936d4bd2e5a151")), - Level0FE::new(U384::from_hex_unchecked("0x98d34282ed5a2e265e455af63c66f7b5dd1557296f775463bcea891a14a801baa172e923055c4bb0fd5343e86294f41")) - ]); - - let qx = Level1FE::new([ - Level0FE::new(U384::from_hex_unchecked("0x16ba99ac9a28190dd74b8988e7a833f60e398472c363c2254c7db3138aff3a0858fb23e6cd2ca814a021b6b3b983f14a")), - Level0FE::new(U384::from_hex_unchecked("0xe1356660c4a00b7ba5021f81949bd96680df9fa464a70d257c7b1bcae0e28ec15d84ddcef2ca2e4e8531f50177685dd")) - ]); - - let qy = Level1FE::new([ - Level0FE::new(U384::from_hex_unchecked("0x9883c1c7d10c32d584f1cf5f0a7c0742f9b283144290afd6871abcb585e434516cefd2b159d99d75771f5658f0af628")), - Level0FE::new(U384::from_hex_unchecked("0x13e5df65d734c9decf24356dacfcf9c4a317e5d21a7d1ada728f59e46ddfb137214bab47e8629a8016b6e508cafe141a")) - ]); - - let scalar = U384::from_hex_unchecked( - "0x1752428b56412bc55b5c6aca6e1811d1b5d810afd55169d8cffeae326bc8d6ea", - ); - - let p = BLS12381TwistCurve::create_point_from_affine(px, py).unwrap(); - let q = BLS12381TwistCurve::create_point_from_affine(qx, qy).unwrap(); - - assert_eq!(p.operate_with_self(scalar), q); - } -} diff --git a/crates/math/src/elliptic_curve/short_weierstrass/curves/bn_254/compression.rs b/crates/math/src/elliptic_curve/short_weierstrass/curves/bn_254/compression.rs deleted file mode 100644 index 1c9a15c9a..000000000 --- a/crates/math/src/elliptic_curve/short_weierstrass/curves/bn_254/compression.rs +++ /dev/null @@ -1,415 +0,0 @@ -use super::{field_extension::BN254PrimeField, twist::BN254TwistCurve}; -use crate::{ - elliptic_curve::short_weierstrass::{ - curves::bn_254::{curve::BN254Curve, field_extension::Degree2ExtensionField, sqrt}, - point::ShortWeierstrassProjectivePoint, - traits::{Compress, IsShortWeierstrass}, - }, - field::element::FieldElement, -}; -use core::cmp::Ordering; - -use crate::{ - cyclic_group::IsGroup, elliptic_curve::traits::FromAffine, errors::ByteConversionError, - traits::ByteConversion, -}; - -type G1Point = ShortWeierstrassProjectivePoint; -type G2Point = ShortWeierstrassProjectivePoint; -type BN254FieldElement = FieldElement; - -/// As we have less than 3 bits available in our coordinate x, we can't follow BLS12-381 style encoding. -/// We use the 2 most significant bits instead -/// 00: uncompressed -/// 10: compressed and y_neg >= y -/// 11: compressed and y_neg < y -/// 01: compressed infinity point -/// the "uncompressed infinity point" will just have 00 (uncompressed) followed by zeroes (infinity = 0,0 in affine coordinates). -/// adapted from gnark https://github.com/consensys/gnark-crypto/blob/v0.13.0/ecc/bn254/marshal.go -impl Compress for BN254Curve { - type G1Point = G1Point; - - type G2Point = G2Point; - - type G1Compressed = [u8; 32]; - - type G2Compressed = [u8; 64]; - - type Error = ByteConversionError; - - #[cfg(feature = "alloc")] - fn compress_g1_point(point: &Self::G1Point) -> Self::G1Compressed { - if *point == G1Point::neutral_element() { - // Point is at infinity - let mut x_bytes = [0_u8; 32]; - x_bytes[0] |= 1 << 6; // x_bytes = 01000000 - x_bytes - } else { - // Point is not at infinity - let point_affine = point.to_affine(); - let x = point_affine.x(); - let y = point_affine.y(); - - let mut x_bytes = [0u8; 32]; - let bytes = x.to_bytes_be(); - x_bytes.copy_from_slice(&bytes); - // Set first bit to 1 to indicate this is a compressed element. - x_bytes[0] |= 1 << 7; // x_bytes = 10000000 - - let y_neg = core::ops::Neg::neg(y); - if y_neg.representative() < y.representative() { - x_bytes[0] |= 1 << 6; // x_bytes = 11000000 - } - x_bytes - } - } - - fn decompress_g1_point(input_bytes: &mut [u8]) -> Result { - // We check that input_bytes has 32 bytes. - if !input_bytes.len() == 32 { - return Err(ByteConversionError::InvalidValue); - } - - let first_byte = input_bytes.first().unwrap(); - // We get the 2 most significant bits - let prefix_bits = first_byte >> 6; - - // If first two bits are 00, then the value is not compressed. - if prefix_bits == 0_u8 { - return Err(ByteConversionError::ValueNotCompressed); - } - - // If first two bits are 01, then the compressed point is the - // point at infinity and we return it directly. - if prefix_bits == 1_u8 { - return Ok(G1Point::neutral_element()); - } - - let first_byte_without_control_bits = (first_byte << 2) >> 2; - input_bytes[0] = first_byte_without_control_bits; - - let x = BN254FieldElement::from_bytes_be(input_bytes)?; - - // We apply the elliptic curve formula to know the y^2 value. - let y_squared = x.pow(3_u16) + BN254FieldElement::from(3); - - let (y_sqrt_1, y_sqrt_2) = &y_squared.sqrt().ok_or(ByteConversionError::InvalidValue)?; - - // If the frist two bits are 10, we take the smaller root. - // If the first two bits are 11, we take the grater one. - let y = match ( - y_sqrt_1.representative().cmp(&y_sqrt_2.representative()), - prefix_bits, - ) { - (Ordering::Greater, 2_u8) => y_sqrt_2, - (Ordering::Greater, _) => y_sqrt_1, - (Ordering::Less, 2_u8) => y_sqrt_1, - (Ordering::Less, _) => y_sqrt_2, - (Ordering::Equal, _) => y_sqrt_1, - }; - - let point = - G1Point::from_affine(x, y.clone()).map_err(|_| ByteConversionError::InvalidValue)?; - - Ok(point) - } - - #[cfg(feature = "alloc")] - fn compress_g2_point(point: &Self::G2Point) -> Self::G2Compressed { - if *point == G2Point::neutral_element() { - // Point is at infinity - let mut x_bytes = [0_u8; 64]; - x_bytes[0] |= 1 << 6; // x_bytes = 01000000 - x_bytes - } else { - // Point is not at infinity - let point_affine = point.to_affine(); - let x = point_affine.x(); - let y = point_affine.y(); - - let x_rev: FieldElement = - FieldElement::new([x.value()[1].clone(), x.value()[0].clone()]); - let mut x_bytes = [0u8; 64]; - let bytes = x_rev.to_bytes_be(); - x_bytes.copy_from_slice(&bytes); - - // Set first bit to to 1 indicate this is compressed element. - x_bytes[0] |= 1 << 7; - - // We see if y_neg < y lexicographically where the lexicographic order is as follows: - // Let a = a0 + a1 * u and b = b0 + b1 * u in Fp2, then a < b if a0 < b0 or - // a0 = b0 and a1 < b1. - let y_neg = -y; - match ( - y.value()[0] - .representative() - .cmp(&y_neg.value()[0].representative()), - y.value()[1] - .representative() - .cmp(&y_neg.value()[1].representative()), - ) { - (Ordering::Greater, _) | (Ordering::Equal, Ordering::Greater) => { - x_bytes[0] |= 1 << 6; // Prefix: 11 - } - (_, _) => (), - } - x_bytes - } - } - - #[allow(unused)] - fn decompress_g2_point(input_bytes: &mut [u8]) -> Result { - if !input_bytes.len() == 64 { - return Err(ByteConversionError::InvalidValue); - } - - let first_byte = input_bytes.first().unwrap(); - - // We get the first 2 bits. - let prefix_bits = first_byte >> 6; - - // If first two bits are 00, then the value is not compressed. - if prefix_bits == 0_u8 { - return Err(ByteConversionError::InvalidValue); - } - - // If the first two bits are 01, then the compressed point is the - // point at infinity and we return it directly. - if prefix_bits == 1_u8 { - return Ok(Self::G2Point::neutral_element()); - } - - let second_bit = prefix_bits & 1_u8; - let first_byte_without_control_bits = (first_byte << 2) >> 2; - input_bytes[0] = first_byte_without_control_bits; - - let input1 = &input_bytes[0..32]; - let input0 = &input_bytes[32..]; - let x0 = BN254FieldElement::from_bytes_be(input0).unwrap(); - let x1 = BN254FieldElement::from_bytes_be(input1).unwrap(); - let x: FieldElement = FieldElement::new([x0, x1]); - - let b_param_qfe = BN254TwistCurve::b(); - - // If the first two bits are 11, then the square root chosen is the greater one. - // So we should use sqrt_qfe with the input 1. - let y = sqrt::sqrt_qfe(&(x.pow(3_u64) + b_param_qfe), second_bit) - .ok_or(ByteConversionError::InvalidValue)?; - - Self::G2Point::from_affine(x, y).map_err(|_| ByteConversionError::InvalidValue) - } -} - -#[cfg(test)] -mod tests { - use super::*; - use super::{BN254FieldElement, G1Point, G2Point}; - use crate::elliptic_curve::short_weierstrass::curves::bn_254::curve::BN254Curve; - use crate::elliptic_curve::short_weierstrass::curves::bn_254::twist::BN254TwistCurve; - use crate::elliptic_curve::short_weierstrass::traits::Compress; - use crate::elliptic_curve::traits::{FromAffine, IsEllipticCurve}; - - type FpE = BN254FieldElement; - type Fp2E = FieldElement; - - #[cfg(feature = "alloc")] - use crate::{ - cyclic_group::IsGroup, traits::ByteConversion, unsigned_integer::element::UnsignedInteger, - }; - - #[cfg(feature = "alloc")] - #[test] - fn test_g1_compress_generator() { - use crate::elliptic_curve::short_weierstrass::traits::Compress; - - let g = BN254Curve::generator(); - let mut compressed_g = BN254Curve::compress_g1_point(&g); - let first_byte = compressed_g.first().unwrap(); - - let first_byte_without_control_bits = (first_byte << 2) >> 2; - compressed_g[0] = first_byte_without_control_bits; - - let compressed_g_x = BN254FieldElement::from_bytes_be(&compressed_g).unwrap(); - let g_x = g.x(); - - assert_eq!(*g_x, compressed_g_x); - } - - #[cfg(feature = "alloc")] - #[test] - fn test_g1_compress_point_at_inf() { - use crate::elliptic_curve::short_weierstrass::traits::Compress; - - let inf = G1Point::neutral_element(); - let compressed_inf = BN254Curve::compress_g1_point(&inf); - let first_byte = compressed_inf.first().unwrap(); - - assert_eq!(*first_byte >> 6, 1_u8); - } - - #[cfg(feature = "alloc")] - #[test] - fn g1_compress_decompress_is_identity() { - use crate::elliptic_curve::short_weierstrass::traits::Compress; - - let g = BN254Curve::generator(); - - let mut compressed_g_slice: [u8; 32] = BN254Curve::compress_g1_point(&g); - - let decompressed_g = BN254Curve::decompress_g1_point(&mut compressed_g_slice).unwrap(); - - assert_eq!(g, decompressed_g); - } - - #[cfg(feature = "alloc")] - #[test] - fn g1_compress_decompress_is_identity_2() { - let g = BN254Curve::generator().operate_with_self(UnsignedInteger::<4>::from("2")); - - let mut compressed_g_slice: [u8; 32] = BN254Curve::compress_g1_point(&g); - - let decompressed_g = BN254Curve::decompress_g1_point(&mut compressed_g_slice).unwrap(); - - assert_eq!(g, decompressed_g); - } - - #[cfg(feature = "alloc")] - #[test] - fn test_g2_compress_generator() { - use crate::elliptic_curve::short_weierstrass::traits::Compress; - - let g = BN254TwistCurve::generator(); - let mut compressed_g = BN254Curve::compress_g2_point(&g); - let first_byte = compressed_g.first().unwrap(); - - let first_byte_without_control_bits = (first_byte << 2) >> 2; - compressed_g[0] = first_byte_without_control_bits; - - let [x1, x0] = FieldElement::::from_bytes_be(&compressed_g) - .unwrap() - .value() - .clone(); - let compressed_g_x = FieldElement::::new([x0, x1]); - let g_x = g.x(); - - assert_eq!(*g_x, compressed_g_x); - } - - #[cfg(feature = "alloc")] - #[test] - fn test_g2_compress_point_at_inf() { - use crate::elliptic_curve::short_weierstrass::traits::Compress; - - let inf = G2Point::neutral_element(); - let compressed_inf = BN254Curve::compress_g2_point(&inf); - let first_byte = compressed_inf.first().unwrap(); - - assert_eq!(*first_byte >> 6, 1_u8); - } - - #[cfg(feature = "alloc")] - #[test] - fn g2_compress_decompress_is_identity() { - use crate::elliptic_curve::short_weierstrass::traits::Compress; - - let g = BN254TwistCurve::generator(); - - let mut compressed_g_slice: [u8; 64] = BN254Curve::compress_g2_point(&g); - - let decompressed_g = BN254Curve::decompress_g2_point(&mut compressed_g_slice).unwrap(); - - assert_eq!(g, decompressed_g); - } - - #[cfg(feature = "alloc")] - #[test] - fn g2_compress_decompress_is_identity_2() { - let g = BN254TwistCurve::generator().operate_with_self(UnsignedInteger::<4>::from("2")); - - let mut compressed_g_slice: [u8; 64] = BN254Curve::compress_g2_point(&g); - - let decompressed_g = BN254Curve::decompress_g2_point(&mut compressed_g_slice).unwrap(); - - assert_eq!(g, decompressed_g); - } - - #[cfg(feature = "alloc")] - #[test] - fn g2_compress_decompress_is_identity_3() { - use crate::unsigned_integer::element::U256; - - let g = G2Point::from_affine( - Fp2E::new([ - FpE::new(U256::from_hex_unchecked( - "1800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed", - )), - FpE::new(U256::from_hex_unchecked( - "198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c2", - )), - ]), - Fp2E::new([ - FpE::new(U256::from_hex_unchecked( - "12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa", - )), - FpE::new(U256::from_hex_unchecked( - "090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b", - )), - ]), - ) - .unwrap(); - - let mut compressed_g_slice: [u8; 64] = BN254Curve::compress_g2_point(&g); - - let decompressed_g = BN254Curve::decompress_g2_point(&mut compressed_g_slice).unwrap(); - - assert_eq!(g, decompressed_g); - } - - #[cfg(feature = "alloc")] - #[test] - fn g2_compress_decompress_is_identity_4() { - use crate::unsigned_integer::element::U256; - - let g = G2Point::from_affine( - Fp2E::new([ - FpE::new(U256::from_hex_unchecked( - "3010c68cb50161b7d1d96bb71edfec9880171954e56871abf3d93cc94d745fa1", - )), - FpE::new(U256::from_hex_unchecked( - "0476be093a6d2b4bbf907172049874af11e1b6267606e00804d3ff0037ec57fd", - )), - ]), - Fp2E::new([ - FpE::new(U256::from_hex_unchecked( - "01b33461f39d9e887dbb100f170a2345dde3c07e256d1dfa2b657ba5cd030427", - )), - FpE::new(U256::from_hex_unchecked( - "14c059d74e5b6c4ec14ae5864ebe23a71781d86c29fb8fb6cce94f70d3de7a21", - )), - ]), - ) - .unwrap(); - // calculate g point operate with itself - let g_2 = g.operate_with_self(UnsignedInteger::<4>::from("2")); - - let mut compressed_g2_slice: [u8; 64] = BN254Curve::compress_g2_point(&g_2); - - let decompressed_g2 = BN254Curve::decompress_g2_point(&mut compressed_g2_slice).unwrap(); - - assert_eq!(g_2, decompressed_g2); - } - - #[test] - fn g1_decompress_wrong_bytes_length() { - let mut input_bytes: [u8; 31] = [0; 31]; - let result = BN254Curve::decompress_g1_point(&mut input_bytes); - assert!(result.is_err()); - } - - #[test] - fn g2_decompress_wrong_bytes_length() { - let mut input_bytes: [u8; 65] = [0; 65]; - let result = BN254Curve::decompress_g2_point(&mut input_bytes); - assert!(result.is_err()); - } -} diff --git a/crates/math/src/elliptic_curve/short_weierstrass/curves/bn_254/curve.rs b/crates/math/src/elliptic_curve/short_weierstrass/curves/bn_254/curve.rs deleted file mode 100644 index dd6f25652..000000000 --- a/crates/math/src/elliptic_curve/short_weierstrass/curves/bn_254/curve.rs +++ /dev/null @@ -1,325 +0,0 @@ -use super::{ - field_extension::{BN254PrimeField, Degree2ExtensionField}, - pairing::{GAMMA_12, GAMMA_13, X}, - twist::BN254TwistCurve, -}; -use crate::cyclic_group::IsGroup; -use crate::elliptic_curve::short_weierstrass::point::ShortWeierstrassProjectivePoint; -use crate::elliptic_curve::traits::IsEllipticCurve; -use crate::{ - elliptic_curve::short_weierstrass::traits::IsShortWeierstrass, field::element::FieldElement, -}; - -pub type BN254FieldElement = FieldElement; -pub type BN254TwistCurveFieldElement = FieldElement; - -#[derive(Clone, Debug)] -pub struct BN254Curve; - -impl IsEllipticCurve for BN254Curve { - type BaseField = BN254PrimeField; - type PointRepresentation = ShortWeierstrassProjectivePoint; - - /// Returns the generator point of the BN254 curve. - /// - /// # Safety - /// - /// - The generator point is mathematically verified to be a valid point on the curve. - /// - `unwrap()` is safe because the provided coordinates satisfy the curve equation. - fn generator() -> Self::PointRepresentation { - // SAFETY: - // - The generator coordinates `(1, 2, 1)` are **predefined** and belong to the BN254 curve. - // - `unwrap()` is safe because we **ensure** the input values satisfy the curve equation. - let point = Self::PointRepresentation::new([ - FieldElement::::one(), - FieldElement::::from(2), - FieldElement::one(), - ]); - point.unwrap() - } -} - -impl IsShortWeierstrass for BN254Curve { - fn a() -> FieldElement { - FieldElement::from(0) - } - - fn b() -> FieldElement { - FieldElement::from(3) - } -} - -impl ShortWeierstrassProjectivePoint { - pub fn is_in_subgroup(&self) -> bool { - true - } -} - -impl ShortWeierstrassProjectivePoint { - /// phi morphism used to G2 subgroup check for twisted curve. - /// We also use phi at the last lines of the Miller Loop of the pairing. - /// phi(q) = (x^p, y^p, z^p), where (x, y, z) are the projective coordinates of q. - /// See https://hackmd.io/@Wimet/ry7z1Xj-2#Subgroup-Checks. - /// - /// # Safety - /// - /// - The function assumes `self` is a valid point on the BN254 twist curve. - /// - The transformation follows a known isomorphism and preserves validity. - pub fn phi(&self) -> Self { - let [x, y, z] = self.coordinates(); - // SAFETY: - // - `conjugate()` preserves the validity of the field element. - // - `unwrap()` is safe because the transformation follows - // **a known valid isomorphism** between the twist and E. - let point = Self::new([ - x.conjugate() * GAMMA_12, - y.conjugate() * GAMMA_13, - z.conjugate(), - ]); - point.unwrap() - } - - // Checks if a G2 point is in the subgroup of the twisted curve. - pub fn is_in_subgroup(&self) -> bool { - let q_times_x = &self.operate_with_self(X); - let q_times_x_plus_1 = &self.operate_with(q_times_x); - let q_times_2x = q_times_x.double(); - - // (x+1)Q + phi(xQ) + phi(phi(xQ)) == phi(phi(phi(2xQ))) - q_times_x_plus_1.operate_with(&q_times_x.phi().operate_with(&q_times_x.phi().phi())) - == q_times_2x.phi().phi().phi() - } -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::{ - cyclic_group::IsGroup, elliptic_curve::traits::EllipticCurveError, - field::element::FieldElement, unsigned_integer::element::U256, - }; - - use super::BN254Curve; - - #[allow(clippy::upper_case_acronyms)] - type FpE = FieldElement; - type Fp2E = FieldElement; - - /* - Sage script: - - p = 21888242871839275222246405745257275088696311157297823662689037894645226208583 - Fbn128base = GF(p) - bn128 = EllipticCurve(Fbn128base,[0,3]) - bn128.random_point() - (17846236917809265466108795494334003231858579470112820692700477163012827709147 : - 17004516321005754027668809192838483252304167776681765357426682819242643291917 : - 1) - */ - fn point() -> ShortWeierstrassProjectivePoint { - let x = FpE::from_hex_unchecked( - "27749cb56beffb211b6622d7366253aa8208cf0aff7867d7945f53f3997cfedb", - ); - let y = FpE::from_hex_unchecked( - "2598371545fd02273e206c4a3e5e6d062c46baade65567b817c343170a15ff0d", - ); - BN254Curve::create_point_from_affine(x, y).unwrap() - } - - /* - Sage script: - - p = 21888242871839275222246405745257275088696311157297823662689037894645226208583 - a = 0 - b = 3 - Fp = GF(p) - G1 = EllipticCurve(Fp, [a, b]) - - P = G1(17846236917809265466108795494334003231858579470112820692700477163012827709147,17004516321005754027668809192838483252304167776681765357426682819242643291917) - - P * 5 - - (10253039145495711056399135467328321588927131913042076209148619870699206197155 : 16767740621810149881158172518644598727924612864724721353109859494126614321586 : 1) - - hex(10253039145495711056399135467328321588927131913042076209148619870699206197155) - = 0x16ab03b69dfb4f870b0143ebf6a71b7b2e4053ca7a4421d09a913b8b834bbfa3 - - hex(16767740621810149881158172518644598727924612864724721353109859494126614321586) = - 0x2512347279ba1049ef97d4ec348d838f939d2b7623e88f4826643cf3889599b2 - */ - - fn point_times_5() -> ShortWeierstrassProjectivePoint { - let x = FpE::from_hex_unchecked( - "16ab03b69dfb4f870b0143ebf6a71b7b2e4053ca7a4421d09a913b8b834bbfa3", - ); - let y = FpE::from_hex_unchecked( - "2512347279ba1049ef97d4ec348d838f939d2b7623e88f4826643cf3889599b2", - ); - BN254Curve::create_point_from_affine(x, y).unwrap() - } - - #[test] - fn adding_five_times_point_works() { - let point = point(); - let point_times_5 = point_times_5(); - assert_eq!(point.operate_with_self(5_u16), point_times_5); - } - - #[test] - fn create_valid_point_works() { - let p = point(); - assert_eq!( - *p.x(), - FpE::new_base("27749cb56beffb211b6622d7366253aa8208cf0aff7867d7945f53f3997cfedb") - ); - assert_eq!( - *p.y(), - FpE::new_base("2598371545fd02273e206c4a3e5e6d062c46baade65567b817c343170a15ff0d") - ); - assert_eq!(*p.z(), FpE::one()); - } - - #[test] - fn addition_with_neutral_element_returns_same_element() { - let p = point(); - assert_eq!( - *p.x(), - FpE::new_base("27749cb56beffb211b6622d7366253aa8208cf0aff7867d7945f53f3997cfedb") - ); - assert_eq!( - *p.y(), - FpE::new_base("2598371545fd02273e206c4a3e5e6d062c46baade65567b817c343170a15ff0d") - ); - - let neutral_element = ShortWeierstrassProjectivePoint::::neutral_element(); - - assert_eq!(p.operate_with(&neutral_element), p); - } - - #[test] - fn neutral_element_plus_neutral_element_is_neutral_element() { - let neutral_element = ShortWeierstrassProjectivePoint::::neutral_element(); - - assert_eq!( - neutral_element.operate_with(&neutral_element), - neutral_element - ); - } - - #[test] - fn create_invalid_points_returns_an_error() { - assert_eq!( - BN254Curve::create_point_from_affine(FpE::from(0), FpE::from(1)), - Err(EllipticCurveError::InvalidPoint) - ); - } - - #[test] - fn equality_works() { - let g = BN254Curve::generator(); - let g2 = g.operate_with(&g); - assert_ne!(&g2, &g); - assert_eq!(&g, &g); - } - - #[test] - fn g_operated_with_g_satifies_ec_equation() { - let g = BN254Curve::generator(); - let g2 = g.operate_with_self(2_u64); - - // get x and y from affine coordinates - let g2_affine = g2.to_affine(); - let x = g2_affine.x(); - let y = g2_affine.y(); - - // calculate both sides of BN254 equation - let three = FpE::from(3); - let y_sq_0 = x.pow(3_u16) + three; - let y_sq_1 = y.pow(2_u16); - - assert_eq!(y_sq_0, y_sq_1); - } - - #[test] - fn operate_with_self_works_1() { - let g = BN254Curve::generator(); - assert_eq!( - g.operate_with(&g).operate_with(&g), - g.operate_with_self(3_u16) - ); - } - - #[test] - fn operate_with_self_works_2() { - let g = BN254TwistCurve::generator(); - assert_eq!( - (g.operate_with_self(X)).double(), - (g.operate_with_self(2 * X)) - ) - } - - #[test] - fn operate_with_self_works_3() { - let g = BN254TwistCurve::generator(); - assert_eq!( - (g.operate_with_self(X)).operate_with(&g), - (g.operate_with_self(X + 1)) - ) - } - - #[test] - fn generator_g2_is_in_subgroup() { - let g = BN254TwistCurve::generator(); - assert!(g.is_in_subgroup()) - } - - #[test] - fn other_g2_point_is_in_subgroup() { - let g = BN254TwistCurve::generator().operate_with_self(32u64); - assert!(g.is_in_subgroup()) - } - - #[test] - fn invalid_g2_is_not_in_subgroup() { - let q = ShortWeierstrassProjectivePoint::::new([ - Fp2E::new([ - FpE::new(U256::from_hex_unchecked( - "1800deef121f1e76426a00665e5c4479674322d4f75edaddde46bd5cd992f6ed", - )), - FpE::new(U256::from_hex_unchecked( - "198e9393920daef312c20b9f1099ecefa8b45575d349b0a6f04c16d0d58af900", - )), - ]), - Fp2E::new([ - FpE::new(U256::from_hex_unchecked( - "22376289c558493c1d6cc413a5f07dcb54526a964e4e687b65a881aa9752faa2", - )), - FpE::new(U256::from_hex_unchecked( - "05a7a5759338c23ca603c1c4adf979e004c2f3e3c5bad6f07693c59a85d600a9", - )), - ]), - Fp2E::one(), - ]) - .unwrap(); - assert!(!q.is_in_subgroup()) - } - - #[test] - fn g2_conjugate_two_times_is_identity() { - let a = Fp2E::zero(); - let mut expected = a.conjugate(); - expected = expected.conjugate(); - - assert_eq!(a, expected); - } - - #[test] - fn apply_12_times_phi_is_identity() { - let q = BN254TwistCurve::generator(); - let mut result = q.phi(); - for _ in 1..12 { - result = result.phi(); - } - assert_eq!(q, result) - } -} diff --git a/crates/math/src/elliptic_curve/short_weierstrass/curves/bn_254/default_types.rs b/crates/math/src/elliptic_curve/short_weierstrass/curves/bn_254/default_types.rs deleted file mode 100644 index 482e8d8f2..000000000 --- a/crates/math/src/elliptic_curve/short_weierstrass/curves/bn_254/default_types.rs +++ /dev/null @@ -1,22 +0,0 @@ -use crate::{ - field::{ - element::FieldElement, - fields::montgomery_backed_prime_fields::{IsModulus, MontgomeryBackendPrimeField}, - }, - unsigned_integer::element::U256, -}; - -#[derive(Clone, Debug)] -pub struct FrConfig; - -/// Modulus of bn 254 subgroup r = 21888242871839275222246405745257275088548364400416034343698204186575808495617, aka order -impl IsModulus for FrConfig { - const MODULUS: U256 = U256::from_hex_unchecked( - "30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001", - ); -} - -/// FrField using MontgomeryBackend for Bn254 -pub type FrField = MontgomeryBackendPrimeField; -/// FrElement using MontgomeryBackend for Bn254 -pub type FrElement = FieldElement; diff --git a/crates/math/src/elliptic_curve/short_weierstrass/curves/bn_254/field_extension.rs b/crates/math/src/elliptic_curve/short_weierstrass/curves/bn_254/field_extension.rs deleted file mode 100644 index 474666314..000000000 --- a/crates/math/src/elliptic_curve/short_weierstrass/curves/bn_254/field_extension.rs +++ /dev/null @@ -1,457 +0,0 @@ -use crate::field::{ - element::FieldElement, - errors::FieldError, - extensions::{ - cubic::{CubicExtensionField, HasCubicNonResidue}, - quadratic::{HasQuadraticNonResidue, QuadraticExtensionField}, - }, - fields::montgomery_backed_prime_fields::{IsModulus, MontgomeryBackendPrimeField}, - traits::{IsField, IsSubFieldOf}, -}; -#[cfg(feature = "alloc")] -use crate::traits::ByteConversion; -use crate::unsigned_integer::element::U256; - -pub const BN254_PRIME_FIELD_ORDER: U256 = - U256::from_hex_unchecked("30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47"); - -/// We define Fp for BN254 -#[derive(Clone, Debug)] -pub struct BN254FieldModulus; -impl IsModulus for BN254FieldModulus { - const MODULUS: U256 = BN254_PRIME_FIELD_ORDER; -} - -pub type BN254PrimeField = MontgomeryBackendPrimeField; - -/// We define Fp2E = Fp [u] / (u^2 + 1) -/// We could define it using the quadratic extension of lambdaworks, but we can optimize its operations -/// using these algorithms. -#[derive(Clone, Debug)] -pub struct Degree2ExtensionField; - -type Fp2E = FieldElement; - -impl IsField for Degree2ExtensionField { - type BaseType = [FieldElement; 2]; - - /// Returns the component wise addition of `a` and `b` - fn add(a: &Self::BaseType, b: &Self::BaseType) -> Self::BaseType { - [&a[0] + &b[0], &a[1] + &b[1]] - } - - /// Returns the multiplication of `a` and `b` using the following - /// equation: - /// (a0 + a1 * u) * (b0 + b1 * u) = a0 * b0 + a1 * b1 * residue() + (a0 * b1 + a1 * b0) * u - /// where `u.pow(2)` equals `residue()`. - fn mul(a: &Self::BaseType, b: &Self::BaseType) -> Self::BaseType { - [&a[0] * &b[0] - &a[1] * &b[1], &a[0] * &b[1] + &a[1] * &b[0]] - } - - fn square(a: &Self::BaseType) -> Self::BaseType { - let [a0, a1] = a; - let v0 = a0 * a1; - let c0 = (a0 + a1) * (a0 - a1); - let c1 = &v0 + &v0; - [c0, c1] - } - /// Returns the component wise subtraction of `a` and `b` - fn sub(a: &Self::BaseType, b: &Self::BaseType) -> Self::BaseType { - [&a[0] - &b[0], &a[1] - &b[1]] - } - - /// Returns the component wise negation of `a` - fn neg(a: &Self::BaseType) -> Self::BaseType { - [-&a[0], -&a[1]] - } - - /// Returns the multiplicative inverse of `a` - /// This uses the equality `(a0 + a1 * u) * (a0 - a1 * u) = a0.pow(2) - a1.pow(2) * residue()` - fn inv(a: &Self::BaseType) -> Result { - let inv_norm = (a[0].square() + a[1].square()).inv()?; - Ok([&a[0] * &inv_norm, -&a[1] * inv_norm]) - } - - /// Returns the division of `a` and `b` - fn div(a: &Self::BaseType, b: &Self::BaseType) -> Result { - let b_inv = &Self::inv(b).map_err(|_| FieldError::DivisionByZero)?; - Ok(::mul(a, b_inv)) - } - - /// Returns a boolean indicating whether `a` and `b` are equal component wise. - fn eq(a: &Self::BaseType, b: &Self::BaseType) -> bool { - a[0] == b[0] && a[1] == b[1] - } - - /// Returns the additive neutral element of the field extension. - fn zero() -> Self::BaseType { - [FieldElement::zero(), FieldElement::zero()] - } - - /// Returns the multiplicative neutral element of the field extension. - fn one() -> Self::BaseType { - [FieldElement::one(), FieldElement::zero()] - } - - /// Returns the element `x * 1` where 1 is the multiplicative neutral element. - fn from_u64(x: u64) -> Self::BaseType { - [FieldElement::from(x), FieldElement::zero()] - } - - /// Takes as input an element of BaseType and returns the internal representation - /// of that element in the field. - /// Note: for this case this is simply the identity, because the components - /// already have correct representations. - fn from_base_type(x: Self::BaseType) -> Self::BaseType { - x - } -} - -impl IsSubFieldOf for BN254PrimeField { - fn mul( - a: &Self::BaseType, - b: &::BaseType, - ) -> ::BaseType { - let c0 = FieldElement::from_raw(::mul(a, b[0].value())); - let c1 = FieldElement::from_raw(::mul(a, b[1].value())); - [c0, c1] - } - - fn add( - a: &Self::BaseType, - b: &::BaseType, - ) -> ::BaseType { - let c0 = FieldElement::from_raw(::add(a, b[0].value())); - let c1 = FieldElement::from_raw(*b[1].value()); - [c0, c1] - } - - fn div( - a: &Self::BaseType, - b: &::BaseType, - ) -> Result<::BaseType, FieldError> { - let b_inv = Degree2ExtensionField::inv(b)?; - Ok(>::mul( - a, &b_inv, - )) - } - - fn sub( - a: &Self::BaseType, - b: &::BaseType, - ) -> ::BaseType { - let c0 = FieldElement::from_raw(::sub(a, b[0].value())); - let c1 = FieldElement::from_raw(::neg(b[1].value())); - [c0, c1] - } - - fn embed(a: Self::BaseType) -> ::BaseType { - [FieldElement::from_raw(a), FieldElement::zero()] - } - - #[cfg(feature = "alloc")] - fn to_subfield_vec( - b: ::BaseType, - ) -> alloc::vec::Vec { - b.into_iter().map(|x| x.to_raw()).collect() - } -} - -#[derive(Debug, Clone)] -pub struct BN254Residue; -impl HasQuadraticNonResidue for BN254Residue { - fn residue() -> FieldElement { - -FieldElement::one() - } -} - -#[cfg(feature = "alloc")] -impl ByteConversion for FieldElement { - #[cfg(feature = "alloc")] - fn to_bytes_be(&self) -> alloc::vec::Vec { - let mut byte_slice = ByteConversion::to_bytes_be(&self.value()[0]); - byte_slice.extend(ByteConversion::to_bytes_be(&self.value()[1])); - byte_slice - } - - #[cfg(feature = "alloc")] - fn to_bytes_le(&self) -> alloc::vec::Vec { - let mut byte_slice = ByteConversion::to_bytes_le(&self.value()[0]); - byte_slice.extend(ByteConversion::to_bytes_le(&self.value()[1])); - byte_slice - } - - #[cfg(feature = "alloc")] - fn from_bytes_be(bytes: &[u8]) -> Result - where - Self: core::marker::Sized, - { - const BYTES_PER_FIELD: usize = 32; - let x0 = FieldElement::from_bytes_be(&bytes[0..BYTES_PER_FIELD])?; - let x1 = FieldElement::from_bytes_be(&bytes[BYTES_PER_FIELD..BYTES_PER_FIELD * 2])?; - Ok(Self::new([x0, x1])) - } - - #[cfg(feature = "alloc")] - fn from_bytes_le(bytes: &[u8]) -> Result - where - Self: core::marker::Sized, - { - const BYTES_PER_FIELD: usize = 32; - let x0 = FieldElement::from_bytes_le(&bytes[0..BYTES_PER_FIELD])?; - let x1 = FieldElement::from_bytes_le(&bytes[BYTES_PER_FIELD..BYTES_PER_FIELD * 2])?; - Ok(Self::new([x0, x1])) - } -} - -#[derive(Debug, Clone)] -pub struct LevelTwoResidue; - -impl HasQuadraticNonResidue for LevelTwoResidue { - fn residue() -> FieldElement { - FieldElement::new([FieldElement::from(9), FieldElement::one()]) - } -} - -impl HasCubicNonResidue for LevelTwoResidue { - fn residue() -> FieldElement { - FieldElement::new([FieldElement::from(9), FieldElement::one()]) - } -} - -/// We define Fp4 = Fp2 [V] / (V^2 - (9+u)) -pub type Degree4ExtensionField = QuadraticExtensionField; - -/// Computes the multiplication of an element of fp2 by the level two non-residue 9+u. -pub fn mul_fp2_by_nonresidue(a: &Fp2E) -> Fp2E { - // (c0 + c1 * u) * (9 + u) = (9 * c0 - c1) + (9c1 + c0) * u - let f = a.double().double().double(); - let c0 = &f.value()[0] - &a.value()[1] + &a.value()[0]; // 9c0 - c1 - let c1 = &f.value()[1] + &a.value()[1] + &a.value()[0]; // 9c1 + c0 - Fp2E::new([c0, c1]) -} - -/// We define Fp6 = Fp2 [v] / (v^3 - (9+u)) -pub type Degree6ExtensionField = CubicExtensionField; -pub type Fp6E = FieldElement; - -/// Computes the multiplication of an element of fp6 by the level three non-residue v. -/// See Sparse Multiplication A from https://hackmd.io/@Wimet/ry7z1Xj-2#Fp6-Arithmetic. -pub fn mul_fp6_by_nonresidue(a: &Fp6E) -> Fp6E { - Fp6E::new([ - mul_fp2_by_nonresidue(&a.value()[2]), - a.value()[0].clone(), - a.value()[1].clone(), - ]) -} - -#[derive(Debug, Clone)] -pub struct LevelThreeResidue; -impl HasQuadraticNonResidue for LevelThreeResidue { - fn residue() -> FieldElement { - FieldElement::new([ - FieldElement::zero(), - FieldElement::one(), - FieldElement::zero(), - ]) - } -} - -/// We define Fp12 = Fp6 [w] / (w^2 - v) -pub type Degree12ExtensionField = QuadraticExtensionField; -pub type Fp12E = FieldElement; - -///Multiplication between a = a0 + a1 * w and b = b0 + b1 * w with -/// b1 = b10 + b11 * v + 0 * v^2 which is the case of the line used -/// in the miller loop. -pub fn sparse_fp12_mul(a: &Fp12E, b: &Fp12E) -> Fp12E { - let [a0, a1] = a.value(); - let [b0, b1] = b.value(); - let b00 = &b0.value()[0]; - let [b10, b11, _] = b1.value(); - - let t0 = a0 * b0; - let t1 = a1 * b1; - let c0 = &t0 + mul_fp6_by_nonresidue(&t1); - let t2 = Fp6E::new([b10 + b00, b11.clone(), Fp2E::zero()]); - let mut c1 = (a0 + a1) * t2; - c1 = c1 - t0 - t1; - - Fp12E::new([c0, c1]) -} - -impl FieldElement { - pub fn new_base(a_hex: &str) -> Self { - Self::new(U256::from(a_hex)) - } -} - -impl FieldElement { - pub fn new_base(a_hex: &str) -> Self { - Self::new([FieldElement::new(U256::from(a_hex)), FieldElement::zero()]) - } - pub fn conjugate(&self) -> Self { - let [a, b] = self.value(); - Self::new([a.clone(), -b]) - } -} - -impl FieldElement { - pub fn new_base(a_hex: &str) -> Self { - Self::new([Fp2E::new_base(a_hex), Fp2E::zero()]) - } -} - -impl FieldElement { - pub fn new_base(a_hex: &str) -> Self { - Self::new([ - FieldElement::new([FieldElement::new(U256::from(a_hex)), FieldElement::zero()]), - FieldElement::zero(), - FieldElement::zero(), - ]) - } -} - -impl FieldElement { - pub fn new_base(a_hex: &str) -> Self { - Self::new([ - FieldElement::::new_base(a_hex), - FieldElement::zero(), - ]) - } - - pub fn from_coefficients(coefficients: &[&str; 12]) -> Self { - FieldElement::::new([ - FieldElement::new([ - FieldElement::new([ - FieldElement::new(U256::from(coefficients[0])), - FieldElement::new(U256::from(coefficients[1])), - ]), - FieldElement::new([ - FieldElement::new(U256::from(coefficients[2])), - FieldElement::new(U256::from(coefficients[3])), - ]), - FieldElement::new([ - FieldElement::new(U256::from(coefficients[4])), - FieldElement::new(U256::from(coefficients[5])), - ]), - ]), - FieldElement::new([ - FieldElement::new([ - FieldElement::new(U256::from(coefficients[6])), - FieldElement::new(U256::from(coefficients[7])), - ]), - FieldElement::new([ - FieldElement::new(U256::from(coefficients[8])), - FieldElement::new(U256::from(coefficients[9])), - ]), - FieldElement::new([ - FieldElement::new(U256::from(coefficients[10])), - FieldElement::new(U256::from(coefficients[11])), - ]), - ]), - ]) - } -} - -#[cfg(test)] -mod tests { - - use super::*; - type FpE = FieldElement; - type Fp2E = FieldElement; - type Fp4E = FieldElement; - type Fp6E = FieldElement; - type Fp12E = FieldElement; - - #[test] - fn embed_base_field_with_degree_2_extension() { - let a = FpE::from(3); - let a_extension = Fp2E::from(3); - assert_eq!(a.to_extension::(), a_extension); - } - - #[test] - fn add_base_field_with_degree_2_extension() { - let a = FpE::from(3); - let a_extension = Fp2E::from(3); - let b = Fp2E::from(2); - assert_eq!(a + &b, a_extension + b); - } - - #[test] - fn mul_degree_2_with_degree_6_extension() { - let a = Fp2E::new([FpE::from(3), FpE::from(4)]); - let a_extension = a.clone().to_extension::(); - let b = Fp6E::from(2); - assert_eq!(a * &b, a_extension * b); - } - - #[test] - fn mul_degree_2_with_degree_4_extension() { - let a = Fp2E::new([FpE::from(3), FpE::from(4)]); - let a_extension = a.clone().to_extension::(); - let b = Fp4E::from(2); - assert_eq!(a * &b, a_extension * b); - } - - #[test] - fn div_degree_6_degree_12_extension() { - let a = Fp6E::from(3); - let a_extension = Fp12E::from(3); - let b = Fp12E::from(2); - assert_eq!((a / &b).unwrap(), (a_extension / b).unwrap()); - } - - #[test] - fn double_equals_sum_two_times() { - let a = FpE::from(3); - assert_eq!(a.double(), a.clone() + a); - } - - #[test] - fn base_field_sum_is_asociative() { - let a = FpE::from(3); - let b = FpE::from(2); - let c = &a + &b; - assert_eq!(a.double() + b, a + c); - } - - #[test] - fn degree_2_extension_mul_is_conmutative() { - let a = Fp2E::from(3); - let b = Fp2E::new([FpE::from(2), FpE::from(4)]); - assert_eq!(&a * &b, b * a); - } - - #[test] - fn base_field_pow_p_is_identity() { - let a = FpE::from(3); - assert_eq!(a.pow(BN254_PRIME_FIELD_ORDER), a); - } - - #[test] - fn mul_fp2_by_nonresidue2_is_correct() { - let a = Fp2E::new([FpE::from(2), FpE::from(4)]); - assert_eq!( - &a * >::residue(), - mul_fp2_by_nonresidue(&a) - ) - } - - #[test] - fn sparse_fp12_mul_multiplies_correctly() { - let a = Fp12E::new([Fp6E::from(2), Fp6E::from(3)]); - let b = Fp12E::new([ - Fp6E::new([Fp2E::from(4), Fp2E::zero(), Fp2E::zero()]), - Fp6E::new([Fp2E::from(2), Fp2E::from(5), Fp2E::zero()]), - ]); - assert_eq!(sparse_fp12_mul(&a, &b), a * b) - } - - #[test] - fn mul_fp6_by_nonresidue_is_correct() { - let a = Fp6E::from(3); - assert_eq!(mul_fp6_by_nonresidue(&a), a * LevelThreeResidue::residue()) - } -} diff --git a/crates/math/src/elliptic_curve/short_weierstrass/curves/bn_254/mod.rs b/crates/math/src/elliptic_curve/short_weierstrass/curves/bn_254/mod.rs deleted file mode 100644 index dae5eebe5..000000000 --- a/crates/math/src/elliptic_curve/short_weierstrass/curves/bn_254/mod.rs +++ /dev/null @@ -1,7 +0,0 @@ -pub mod compression; -pub mod curve; -pub mod default_types; -pub mod field_extension; -pub mod pairing; -pub mod sqrt; -pub mod twist; diff --git a/crates/math/src/elliptic_curve/short_weierstrass/curves/bn_254/pairing.rs b/crates/math/src/elliptic_curve/short_weierstrass/curves/bn_254/pairing.rs deleted file mode 100644 index afdc37e7d..000000000 --- a/crates/math/src/elliptic_curve/short_weierstrass/curves/bn_254/pairing.rs +++ /dev/null @@ -1,1055 +0,0 @@ -use super::{ - curve::BN254Curve, - field_extension::{ - mul_fp2_by_nonresidue, BN254PrimeField, Degree12ExtensionField, Degree2ExtensionField, - Degree4ExtensionField, - }, - twist::BN254TwistCurve, -}; -use crate::{ - cyclic_group::IsGroup, - elliptic_curve::{ - short_weierstrass::{ - curves::bn_254::field_extension::sparse_fp12_mul, traits::IsShortWeierstrass, - }, - traits::IsPairing, - }, - errors::PairingError, -}; -use crate::{ - elliptic_curve::short_weierstrass::{ - curves::bn_254::field_extension::Degree6ExtensionField, - point::ShortWeierstrassProjectivePoint, - }, - field::element::FieldElement, -}; - -type FpE = FieldElement; -type Fp2E = FieldElement; -type Fp4E = FieldElement; -type Fp6E = FieldElement; -type Fp12E = FieldElement; -type G1Point = ShortWeierstrassProjectivePoint; -type G2Point = ShortWeierstrassProjectivePoint; - -// You can find an explanation of the next implemetation in our post -// https://blog.lambdaclass.com/how-we-implemented-the-bn254-ate-pairing-in-lambdaworks/ -// There you'll come across a path to understand the naive implementation of the pairing -// using the functions miller_naive() and final_exponentiation_naive(). -// We then optimized the pairing using the functions miller_optimized() and final_exponentiation_optimized(). -// You'll find both the naive and optimized versions below. - -////////////////// CONSTANTS ////////////////// - -/// x = 4965661367192848881. -/// A constant of the curve. -/// See https://hackmd.io/@jpw/bn254#Barreto-Naehrig-curves -pub const X: u64 = 0x44e992b44a6909f1; - -/// x = 100010011101001100100101011010001001010011010010000100111110001 -pub const X_BINARY: &[bool] = &[ - true, false, false, false, true, false, false, true, true, true, false, true, false, false, - true, true, false, false, true, false, false, true, false, true, false, true, true, false, - true, false, false, false, true, false, false, true, false, true, false, false, true, true, - false, true, false, false, true, false, false, false, false, true, false, false, true, true, - true, true, true, false, false, false, true, -]; - -/// Constant used in the Miller Loop. -/// MILLER_CONSTANT = 6x + 2 = 29793968203157093288. -/// Note that this is a representation using {1, -1, 0}, but it isn't a NAF representation -/// because it has non-zero values adjacent. -/// See arkworks library https://github.com/arkworks-rs/algebra/blob/master/curves/bn254/src/curves/mod.rs#L21 (constant called ATE_LOOP_COUNT). -/// Notice that MILLER_CONSTANT has been updated to one with hamming weight of 22 instead of 26. -/// To see the old version of the constant check the post https://hackmd.io/@Wimet/ry7z1Xj-2#The-Pairing. -pub const MILLER_CONSTANT: &[i8] = &[ - 0, 0, 0, 1, 0, 1, 0, -1, 0, 0, -1, 0, 0, 0, 1, 0, 0, -1, 0, -1, 0, 0, 0, 1, 0, -1, 0, 0, 0, 0, - -1, 0, 0, 1, 0, -1, 0, 0, 1, 0, 0, 0, 0, 0, -1, 0, 0, -1, 0, 1, 0, -1, 0, 0, 0, -1, 0, -1, 0, - 0, 0, 1, 0, 1, 1, -]; - -/// GAMMA constants used to compute the Frobenius morphisms and G2 subgroup check. -/// We took these constants from https://github.com/hecmas/zkNotebook/blob/main/src/BN254/constants.ts#L48 -/// GAMMA_1i = (9 + u)^{i(p-1) / 6} for all i = 1..5 -pub const GAMMA_11: Fp2E = Fp2E::const_from_raw([ - FpE::from_hex_unchecked("1284B71C2865A7DFE8B99FDD76E68B605C521E08292F2176D60B35DADCC9E470"), - FpE::from_hex_unchecked("246996F3B4FAE7E6A6327CFE12150B8E747992778EEEC7E5CA5CF05F80F362AC"), -]); - -pub const GAMMA_12: Fp2E = Fp2E::const_from_raw([ - FpE::from_hex_unchecked("2FB347984F7911F74C0BEC3CF559B143B78CC310C2C3330C99E39557176F553D"), - FpE::from_hex_unchecked("16C9E55061EBAE204BA4CC8BD75A079432AE2A1D0B7C9DCE1665D51C640FCBA2"), -]); - -pub const GAMMA_13: Fp2E = Fp2E::const_from_raw([ - FpE::from_hex_unchecked("63CF305489AF5DCDC5EC698B6E2F9B9DBAAE0EDA9C95998DC54014671A0135A"), - FpE::from_hex_unchecked("7C03CBCAC41049A0704B5A7EC796F2B21807DC98FA25BD282D37F632623B0E3"), -]); - -pub const GAMMA_14: Fp2E = Fp2E::const_from_raw([ - FpE::from_hex_unchecked("5B54F5E64EEA80180F3C0B75A181E84D33365F7BE94EC72848A1F55921EA762"), - FpE::from_hex_unchecked("2C145EDBE7FD8AEE9F3A80B03B0B1C923685D2EA1BDEC763C13B4711CD2B8126"), -]); - -pub const GAMMA_15: Fp2E = Fp2E::const_from_raw([ - FpE::from_hex_unchecked("183C1E74F798649E93A3661A4353FF4425C459B55AA1BD32EA2C810EAB7692F"), - FpE::from_hex_unchecked("12ACF2CA76FD0675A27FB246C7729F7DB080CB99678E2AC024C6B8EE6E0C2C4B"), -]); - -/// GAMMA_2i = GAMMA_1i * GAMMA_1i.conjugate() -pub const GAMMA_21: FpE = - FpE::from_hex_unchecked("30644E72E131A0295E6DD9E7E0ACCCB0C28F069FBB966E3DE4BD44E5607CFD49"); - -pub const GAMMA_22: FpE = - FpE::from_hex_unchecked("30644E72E131A0295E6DD9E7E0ACCCB0C28F069FBB966E3DE4BD44E5607CFD48"); - -pub const GAMMA_23: FpE = - FpE::from_hex_unchecked("30644E72E131A029B85045B68181585D97816A916871CA8D3C208C16D87CFD46"); - -pub const GAMMA_24: FpE = - FpE::from_hex_unchecked("59E26BCEA0D48BACD4F263F1ACDB5C4F5763473177FFFFFE"); - -pub const GAMMA_25: FpE = - FpE::from_hex_unchecked("59E26BCEA0D48BACD4F263F1ACDB5C4F5763473177FFFFFF"); - -/// GAMMA_3i = GAMMA_1i * GAMMA_2i -pub const GAMMA_31: Fp2E = Fp2E::const_from_raw([ - FpE::from_hex_unchecked("19DC81CFCC82E4BBEFE9608CD0ACAA90894CB38DBE55D24AE86F7D391ED4A67F"), - FpE::from_hex_unchecked("ABF8B60BE77D7306CBEEE33576139D7F03A5E397D439EC7694AA2BF4C0C101"), -]); - -pub const GAMMA_32: Fp2E = Fp2E::const_from_raw([ - FpE::from_hex_unchecked("856E078B755EF0ABAFF1C77959F25AC805FFD3D5D6942D37B746EE87BDCFB6D"), - FpE::from_hex_unchecked("4F1DE41B3D1766FA9F30E6DEC26094F0FDF31BF98FF2631380CAB2BAAA586DE"), -]); - -pub const GAMMA_33: Fp2E = Fp2E::const_from_raw([ - FpE::from_hex_unchecked("2A275B6D9896AA4CDBF17F1DCA9E5EA3BBD689A3BEA870F45FCC8AD066DCE9ED"), - FpE::from_hex_unchecked("28A411B634F09B8FB14B900E9507E9327600ECC7D8CF6EBAB94D0CB3B2594C64"), -]); - -pub const GAMMA_34: Fp2E = Fp2E::const_from_raw([ - FpE::from_hex_unchecked("BC58C6611C08DAB19BEE0F7B5B2444EE633094575B06BCB0E1A92BC3CCBF066"), - FpE::from_hex_unchecked("23D5E999E1910A12FEB0F6EF0CD21D04A44A9E08737F96E55FE3ED9D730C239F"), -]); - -pub const GAMMA_35: Fp2E = Fp2E::const_from_raw([ - FpE::from_hex_unchecked("13C49044952C0905711699FA3B4D3F692ED68098967C84A5EBDE847076261B43"), - FpE::from_hex_unchecked("16DB366A59B1DD0B9FB1B2282A48633D3E2DDAEA200280211F25041384282499"), -]); - -/// The inverse of two in Fp as a constant. -pub const TWO_INV: FpE = - FpE::from_hex_unchecked("183227397098D014DC2822DB40C0AC2ECBC0B548B438E5469E10460B6C3E7EA4"); - -////////////////// PAIRING ////////////////// - -pub struct BN254AtePairing; -impl IsPairing for BN254AtePairing { - type G1Point = ShortWeierstrassProjectivePoint; - type G2Point = ShortWeierstrassProjectivePoint; - type OutputField = Degree12ExtensionField; - - /// Computes the product of the ate pairing for a list of point pairs. - /// To optimize the pairing computation, we compute first all the miller loops - /// and multiply each other (so that we can then do the final exponentiation). - fn compute_batch( - pairs: &[(&Self::G1Point, &Self::G2Point)], - ) -> Result, PairingError> { - let mut result = Fp12E::one(); - for (p, q) in pairs { - // We don't need to check if p is in the subgroup because the subgroup oF G1 is G1. - // See https://hackmd.io/@jpw/bn254#Subgroup-checks. - if !q.is_in_subgroup() { - return Err(PairingError::PointNotInSubgroup); - } - if !p.is_neutral_element() && !q.is_neutral_element() { - let p = p.to_affine(); - let q = q.to_affine(); - result *= miller_optimized(&p, &q); - } - } - Ok(final_exponentiation_optimized(&result)) - } -} - -/// Computes Miller loop using oprate_with(), operate_with_self() and line_naive(). -/// See https://eprint.iacr.org/2010/354.pdf (Page 4, Algorithm 1). -pub fn miller_naive(p: &G1Point, q: &G2Point) -> Fp12E { - let mut t = q.clone(); - let mut f = Fp12E::one(); - let q_neg = &q.neg(); - MILLER_CONSTANT.iter().rev().skip(1).for_each(|m| { - f = f.square() * line_naive(p, &t, &t); - t = t.double(); - - if *m == -1 { - f *= line_naive(p, &t, q_neg); - t = t.operate_with_affine(q_neg); - } else if *m == 1 { - f *= line_naive(p, &t, q); - t = t.operate_with_affine(q); - } - }); - - // q1 = ((x_q)^p, (y_q)^p, (z_q)^p) - // See https://hackmd.io/@Wimet/ry7z1Xj-2#The-Last-two-Lines - let q1 = q.phi(); - f *= line_naive(p, &t, &q1); - t = t.operate_with(&q1); - - // q2 = ((x_q1)^p, (y_q1)^p, (z_q1)^p) - let q2 = q1.phi(); - f *= line_naive(p, &t, &q2.neg()); - - f -} - -/// Computes the same algorithm as miller_naive but optimized using line_optimized() -pub fn miller_optimized(p: &G1Point, q: &G2Point) -> Fp12E { - let mut t = q.clone(); - let mut f = Fp12E::one(); - let q_neg = &q.neg(); - MILLER_CONSTANT.iter().rev().skip(1).for_each(|m| { - let (r, l) = line_optimized(p, &t, &t); - f = sparse_fp12_mul(&f.square(), &l); - t = r; - - if *m == -1 { - let (r, l) = line_optimized(p, &t, q_neg); - f = sparse_fp12_mul(&f, &l); - t = r; - } else if *m == 1 { - let (r, l) = line_optimized(p, &t, q); - f = sparse_fp12_mul(&f, &l); - t = r; - } - }); - - // q1 = ((x_q)^p, (y_q)^p, (z_q)^p) - // See https://hackmd.io/@Wimet/ry7z1Xj-2#The-Last-two-Lines - let q1 = q.phi(); - let (r, l) = line_optimized(p, &t, &q1); - f = sparse_fp12_mul(&f, &l); - t = r; - - // q2 = ((x_q1)^p, (y_q1)^p, (z_q1)^p) - let q2 = q1.phi(); - f = sparse_fp12_mul(&f, &line_optimized(p, &t, &q2.neg()).1); - - f -} - -/// Depending on the case, it computes the tangent line of t or the line -/// between t and q evaluated in p. -/// Algorithm adapted from Arkowork's double_in_place and add_in_place. -/// See https://github.com/arkworks-rs/algebra/blob/master/ec/src/models/bn/g2.rs#L25. -/// See https://eprint.iacr.org/2013/722.pdf (Page 13, Equations 11 and 13). -fn line_naive(p: &G1Point, t: &G2Point, q: &G2Point) -> Fp12E { - let [x_p, y_p, _] = p.coordinates(); - - if t == q { - let b = t.y().square(); - let c = t.z().square(); - let e = BN254TwistCurve::b() * (c.double() + &c); - let h = (t.y() + t.z()).square() - (&b + &c); - let i = &e - &b; - let j = t.x().square(); - - // We are transforming one representation of Fp12 into another: - // If f in Fp12, then f = g + h * w = g0 + h0 * w + g1 * w^2 + h1 * w^3 + g2 * w^4 + h2 * w^5, - // where g = g0 + g1 * v + g2 * v^2, - // and h = h0 + h1 * v + h2 * v^2. - // See https://hackmd.io/@Wimet/ry7z1Xj-2#Tower-of-Extension-Fields. - Fp12E::new([ - Fp6E::new([y_p * (-h), Fp2E::zero(), Fp2E::zero()]), - Fp6E::new([x_p * (j.double() + &j), i, Fp2E::zero()]), - ]) - } else { - let [x_q, y_q, _] = q.coordinates(); - - let theta = t.y() - (y_q * t.z()); - let lambda = t.x() - (x_q * t.z()); - let j = &theta * x_q - (&lambda * y_q); - - Fp12E::new([ - Fp6E::new([y_p * lambda, Fp2E::zero(), Fp2E::zero()]), - Fp6E::new([x_p * (-theta), j, Fp2E::zero()]), - ]) - } -} - -/// Depending on the case, it computes 2t or t + q and the tangent line of t or -/// the line between t and q evaluated in p. -/// Algorithm adapted from Arkowork's double_in_place and add_in_place. -/// See https://github.com/arkworks-rs/algebra/blob/master/ec/src/models/bn/g2.rs#L25. -/// See https://eprint.iacr.org/2013/722.pdf (Page 13, Equations 11 and 13). -#[allow(clippy::ptr_eq)] -fn line_optimized(p: &G1Point, t: &G2Point, q: &G2Point) -> (G2Point, Fp12E) { - let [x_p, y_p, _] = p.coordinates(); - - if t as *const G2Point == q as *const G2Point || t == q { - let a = TWO_INV * t.x() * t.y(); - let b = t.y().square(); - let c = t.z().square(); - let e = BN254TwistCurve::b() * (c.double() + &c); - let f = e.double() + &e; - let g = TWO_INV * (&b + &f); - let h = (t.y() + t.z()).square() - (&b + &c); - let i = &e - &b; - let j = t.x().square(); - let e_square = e.square(); - - let x_r = a * (&b - f); - let y_r = g.square() - (e_square.double() + e_square); - let z_r = b * &h; - - debug_assert_eq!( - BN254TwistCurve::defining_equation_projective(&x_r, &y_r, &z_r), - Fp2E::zero(), - ); - // SAFETY: `unwrap()` is used here because we ensure that `x_r, y_r, z_r` - // satisfy the curve equation. The previous assertion checks that this is indeed the case. - let r = G2Point::new([x_r, y_r, z_r]); - - let l = Fp12E::new([ - Fp6E::new([y_p * (-h), Fp2E::zero(), Fp2E::zero()]), - Fp6E::new([x_p * (j.double() + &j), i, Fp2E::zero()]), - ]); - (r.unwrap(), l) - } else { - let [x_q, y_q, _] = q.coordinates(); - let [x_t, y_t, z_t] = t.coordinates(); - - let a = y_q * z_t; - let b = x_q * z_t; - let theta = t.y() - a; - let lambda = t.x() - b; - let c = theta.square(); - let d = lambda.square(); - let e = &lambda * &d; - let f = z_t * c; - let g = x_t * d; - let h = &e + f - g.double(); - let i = y_t * &e; - let j = &theta * x_q - (&lambda * y_q); - - let x_r = &lambda * &h; - let y_r = &theta * (g - h) - i; - let z_r = z_t * e; - - debug_assert_eq!( - BN254TwistCurve::defining_equation_projective(&x_r, &y_r, &z_r), - Fp2E::zero() - ); - // SAFETY: `unwrap()` is used here because we ensure that `x_r, y_r, z_r` - // satisfy the curve equation. The previous assertion checks that this is indeed the case. - let r = G2Point::new([x_r, y_r, z_r]); - - let l = Fp12E::new([ - Fp6E::new([y_p * lambda, Fp2E::zero(), Fp2E::zero()]), - Fp6E::new([x_p * (-theta), j, Fp2E::zero()]), - ]); - (r.unwrap(), l) - } -} - -/// Computes f ^ {(p^12 - 1) / r} -/// using that (p^12 - 1)/r = (p^6 - 1) * (p^2 + 1) * (p^4 - p^2 + 1)/r. -/// Algorithm taken from https://hackmd.io/@Wimet/ry7z1Xj-2#Final-Exponentiation. -pub fn final_exponentiation_naive( - f: &FieldElement, -) -> FieldElement { - // Easy part: - // Computes f ^ {(p^6 - 1) * (p^2 + 1)} - let f_easy_aux = f.conjugate() * f.inv().unwrap(); // f ^ (p^6 - 1) because f^(p^6) = f.conjugate(). - let f_easy = &frobenius_square(&f_easy_aux) * f_easy_aux; // (f^{p^6 - 1})^(p^2) * (f^{p^6 - 1}). - - // Hard part: - // Computes f_easy ^ ((p^4 - p^2 + 1) / r) - // See https://hackmd.io/@Wimet/ry7z1Xj-2#The-Hard-Part, where f_easy is called m. - // We define different exponentiation of f_easy that we will use later. - let mx = f_easy.pow(X); - let mx2 = mx.pow(X); - let mx3 = mx2.pow(X); - let mp = frobenius(&f_easy); - let mp2 = frobenius_square(&f_easy); - let mp3 = frobenius_cube(&f_easy); - let mxp = frobenius(&mx); // (m^x)^p - let mx2p = frobenius(&mx2); // (m^{x^2})^p - let mx3p = frobenius(&mx3); // (m^{x^3})^p - let mx2p2 = frobenius_square(&mx2); // (m^{x^2})^p^2 - - let y0 = mp * mp2 * mp3; - let y1 = f_easy.conjugate(); - let y2 = mx2p2; - let y3 = mxp.conjugate(); - let y4 = (mx * mx2p).conjugate(); - let y5 = mx2.conjugate(); - let y6 = (mx3 * mx3p).conjugate(); - - y0 * y1.square() - * y2.pow(6usize) - * y3.pow(12usize) - * y4.pow(18usize) - * y5.pow(30usize) - * y6.pow(36usize) -} - -/// Computes the final exponentiation algorithm optimized -/// using cyclotomic_pow_x() and cyclotomic_square(). -/// See https://hackmd.io/@Wimet/ry7z1Xj-2#Final-Exponentiation. -pub fn final_exponentiation_optimized( - f: &FieldElement, -) -> FieldElement { - // Easy part: - // Computes f ^ {(p^6 - 1) * (p^2 + 1)} - let f_easy_aux = f.conjugate() * f.inv().unwrap(); - let f_easy = &frobenius_square(&f_easy_aux) * f_easy_aux; - - // Optimal Hard Part from the post - // https://hackmd.io/@Wimet/ry7z1Xj-2#The-Hard-Part - let mx = cyclotomic_pow_x(&f_easy); - let mx2 = cyclotomic_pow_x(&mx); - let mx3 = cyclotomic_pow_x(&mx2); - let mp = frobenius(&f_easy); - let mp2 = frobenius_square(&f_easy); - let mp3 = frobenius_cube(&f_easy); - let mxp = frobenius(&mx); // (m^x)^p - let mx2p = frobenius(&mx2); // (m^{x^2})^p - let mx3p = frobenius(&mx3); // (m^{x^3})^p - let mx2p2 = frobenius_square(&mx2); // (m^{x^2})^p^2 - - let y0 = &mp * &mp2 * &mp3; - let y1 = f_easy.conjugate(); - let y2 = mx2p2; - let y3 = mxp.conjugate(); - let y4 = (mx * mx2p).conjugate(); - let y5 = mx2.conjugate(); - let y6 = (mx3 * mx3p).conjugate(); - - let t01 = cyclotomic_square(&y6) * y4 * &y5; - let t11 = &t01 * y3 * y5; - let t02 = t01 * y2; - let t12 = cyclotomic_square(&t11) * t02; - let t13 = cyclotomic_square(&t12); - let t14 = &t13 * y0; - let t03 = t13 * y1; - //let t04 = cyclotomic_square(&t03) * t14; - - cyclotomic_square(&t03) * t14 -} - -////////////////// FROBENIUS MORPHISIMS ////////////////// - -/// Computes the Frobenius morphism: f^p. -/// See https://hackmd.io/@Wimet/ry7z1Xj-2#Fp12-Arithmetic (First Frobenius Operator). -pub fn frobenius(f: &Fp12E) -> Fp12E { - let [a, b] = f.value(); // f = a + bw, where a and b in Fp6. - let [a0, a1, a2] = a.value(); // a = a0 + a1 * v + a2 * v^2, where a0, a1 and a2 in Fp2. - let [b0, b1, b2] = b.value(); // b = b0 + b1 * v + b2 * v^2, where b0, b1 and b2 in Fp2. - - // c1 = a0.conjugate() + a1.conjugate() * GAMMA_12 * v + a2.conjugate() * GAMMA_14 * v^2 - let c1 = Fp6E::new([ - a0.conjugate(), - a1.conjugate() * GAMMA_12, - a2.conjugate() * GAMMA_14, - ]); - - let c2 = Fp6E::new([ - b0.conjugate() * GAMMA_11, - b1.conjugate() * GAMMA_13, - b2.conjugate() * GAMMA_15, - ]); - - Fp12E::new([c1, c2]) //c1 + c2 * w -} - -/// Computes f^(p^2) -pub fn frobenius_square( - f: &FieldElement, -) -> FieldElement { - let [a, b] = f.value(); - let [a0, a1, a2] = a.value(); - let [b0, b1, b2] = b.value(); - - let c1 = Fp6E::new([a0.clone(), GAMMA_22 * a1, GAMMA_24 * a2]); - let c2 = Fp6E::new([GAMMA_21 * b0, GAMMA_23 * b1, GAMMA_25 * b2]); - - Fp12E::new([c1, c2]) -} - -/// Computes f^(p^3) -pub fn frobenius_cube( - f: &FieldElement, -) -> FieldElement { - let [a, b] = f.value(); - let [a0, a1, a2] = a.value(); - let [b0, b1, b2] = b.value(); - - let c1 = Fp6E::new([ - a0.conjugate(), - a1.conjugate() * GAMMA_32, - a2.conjugate() * GAMMA_34, - ]); - - let c2 = Fp6E::new([ - b0.conjugate() * GAMMA_31, - b1.conjugate() * GAMMA_33, - b2.conjugate() * GAMMA_35, - ]); - - Fp12E::new([c1, c2]) -} - -////////////////// CYCLOTOMIC SUBGROUP OPERATIONS ////////////////// - -// Since the result of the Easy Part of the Final Exponentiation belongs to the cyclotomic -// subgroup of Fp12, we can optimize the square and pow operations used in the Hard Part. - -/// Compute the square of an element of a cyclotomic subgroup of Fp12. -/// Algorithm from Constantine's cyclotomic_square_quad_over_cube: -/// -pub fn cyclotomic_square(a: &Fp12E) -> Fp12E { - // a = g + h * w - let [g, h] = a.value(); - let [b0, b1, b2] = g.value(); - let [b3, b4, b5] = h.value(); - - let v0 = Fp4E::new([b0.clone(), b4.clone()]).square(); - let v1 = Fp4E::new([b3.clone(), b2.clone()]).square(); - let v2 = Fp4E::new([b1.clone(), b5.clone()]).square(); - - // r = r0 + r1 * w - // r0 = r00 + r01 * v + r02 * v^2 - // r1 = r10 + r11 * v + r12 * v^2 - - // r00 = 3v00 - 2b0 - let mut r00 = &v0.value()[0] - b0; - r00 = r00.double(); - r00 += v0.value()[0].clone(); - - // r01 = 3v10 -2b1 - let mut r01 = &v1.value()[0] - b1; - r01 = r01.double(); - r01 += v1.value()[0].clone(); - - // r11 = 3v01 - 2b4 - let mut r11 = &v0.value()[1] + b4; - r11 = r11.double(); - r11 += v0.value()[1].clone(); - - // r12 = 3v11 - 2b5 - let mut r12 = &v1.value()[1] + b5; - r12 = r12.double(); - r12 += v1.value()[1].clone(); - - // 3 * (9 + u) * v21 + 2b3 - let v21 = mul_fp2_by_nonresidue(&v2.value()[1]); - let mut r10 = &v21 + b3; - r10 = r10.double(); - r10 += v21; - - // 3 * (9 + u) * v20 - 2b3 - let mut r02 = &v2.value()[0] - b2; - r02 = r02.double(); - r02 += v2.value()[0].clone(); - - Fp12E::new([Fp6E::new([r00, r01, r02]), Fp6E::new([r10, r11, r12])]) -} - -/// Computes f^x where f is in the cyclotomic subgroup of Fp12. -/// Algorithm from https://hackmd.io/@Wimet/ry7z1Xj-2#Exponentiation-in-the-Cyclotomic-Subgroup. -#[allow(clippy::needless_range_loop)] -pub fn cyclotomic_pow_x(f: &Fp12E) -> Fp12E { - let mut result = Fp12E::one(); - X_BINARY.iter().for_each(|&bit| { - result = cyclotomic_square(&result); - if bit { - result = &result * f; - } - }); - result -} - -#[cfg(test)] -/// We took the G1 and G2 points from: -/// https://github.com/lambdaclass/zksync_era_precompiles/blob/4bdfebf831e21d58c5ba6945d4524763f1ef64d4/tests/tests/ecpairing_tests.rs -mod tests { - use crate::elliptic_curve::traits::FromAffine; - use crate::unsigned_integer::element::U256; - use crate::{ - cyclic_group::IsGroup, elliptic_curve::traits::IsEllipticCurve, - unsigned_integer::element::U384, - }; - - use super::*; - - #[test] - // e(ap, bq) = e(abp, q) = e(p, abq) = e(bp, aq) = e(ap, q)^b - fn batch_ate_pairing_bilinearity() { - let p = BN254Curve::generator(); - let q = BN254TwistCurve::generator(); - - let a = U384::from_u64(11); - let b = U384::from_u64(93); - - let result_1 = BN254AtePairing::compute_batch(&[ - (&p.operate_with_self(a), &q.operate_with_self(b)), - (&p.operate_with_self(a * b), &q.neg()), - ]) - .unwrap(); - assert_eq!(result_1, Fp12E::one()); - - let result_2 = BN254AtePairing::compute_batch(&[ - (&p.operate_with_self(a), &q.operate_with_self(b)), - (&p.neg(), &q.operate_with_self(a * b)), - ]) - .unwrap(); - assert_eq!(result_2, Fp12E::one()); - - let result_3 = BN254AtePairing::compute_batch(&[ - (&p.operate_with_self(a), &q.operate_with_self(b)), - (&p.operate_with_self(b), &q.operate_with_self(a).neg()), - ]) - .unwrap(); - assert_eq!(result_3, Fp12E::one()); - - let result_4 = BN254AtePairing::compute_batch(&[ - (&p.operate_with_self(a), &q.operate_with_self(b).neg()), - (&p.operate_with_self(b), &q), - (&p.operate_with_self(b), &q), - (&p.operate_with_self(b), &q), - (&p.operate_with_self(b), &q), - (&p.operate_with_self(b), &q), - (&p.operate_with_self(b), &q), - (&p.operate_with_self(b), &q), - (&p.operate_with_self(b), &q), - (&p.operate_with_self(b), &q), - (&p.operate_with_self(b), &q), - (&p.operate_with_self(b), &q), - ]) - .unwrap(); - assert_eq!(result_4, Fp12E::one()); - } - - #[test] - fn ate_pairing_returns_one_when_one_element_is_the_neutral_element() { - let p1 = BN254Curve::generator(); - let q1 = G2Point::neutral_element(); - let result_1 = BN254AtePairing::compute_batch(&[(&p1, &q1)]).unwrap(); - assert_eq!(result_1, Fp12E::one()); - - let p2 = G1Point::neutral_element(); - let q2 = BN254TwistCurve::generator(); - let result_2 = BN254AtePairing::compute_batch(&[(&p2, &q2)]).unwrap(); - assert_eq!(result_2, Fp12E::one()); - - let result_3 = BN254AtePairing::compute_batch(&[(&p2, &q1)]).unwrap(); - assert_eq!(result_3, Fp12E::one()); - } - - #[test] - fn ate_pairing_errors_when_g2_element_is_not_in_subgroup() { - let p = BN254Curve::generator(); - let q = G2Point::new([ - Fp2E::new([ - FpE::new(U256::from_hex_unchecked( - "1800deef121f1e76426a00665e5c4479674322d4f75edaddde46bd5cd992f6ed", - )), - FpE::new(U256::from_hex_unchecked( - "198e9393920daef312c20b9f1099ecefa8b45575d349b0a6f04c16d0d58af900", - )), - ]), - Fp2E::new([ - FpE::new(U256::from_hex_unchecked( - "22376289c558493c1d6cc413a5f07dcb54526a964e4e687b65a881aa9752faa2", - )), - FpE::new(U256::from_hex_unchecked( - "05a7a5759338c23ca603c1c4adf979e004c2f3e3c5bad6f07693c59a85d600a9", - )), - ]), - Fp2E::one(), - ]) - .unwrap(); - let result = BN254AtePairing::compute_batch(&[(&p, &q)]); - assert!(result.is_err()) - } - - #[test] - fn apply_12_times_frobenius_is_identity() { - let f = Fp12E::from_coefficients(&[ - "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", - ]); - let mut result = frobenius(&f); - for _ in 1..12 { - result = frobenius(&result); - } - assert_eq!(f, result) - } - - #[test] - fn apply_6_times_frobenius_square_is_identity() { - let f = Fp12E::from_coefficients(&[ - "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", - ]); - let mut result = frobenius_square(&f); - for _ in 1..6 { - result = frobenius_square(&result); - } - assert_eq!(f, result) - } - - #[test] - fn apply_4_times_frobenius_cube_is_identity() { - let f = Fp12E::from_coefficients(&[ - "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", - ]); - let mut result = frobenius_cube(&f); - for _ in 1..4 { - result = frobenius_cube(&result); - } - assert_eq!(f, result) - } - - #[test] - fn two_pairs_of_points_match_1() { - let p1 = G1Point::from_affine( - FpE::new(U256::from_hex_unchecked( - "0000000000000000000000000000000000000000000000000000000000000001", - )), - FpE::new(U256::from_hex_unchecked( - "0000000000000000000000000000000000000000000000000000000000000002", - )), - ) - .unwrap(); - - let q1 = G2Point::from_affine( - Fp2E::new([ - FpE::new(U256::from_hex_unchecked( - "1800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed", - )), - FpE::new(U256::from_hex_unchecked( - "198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c2", - )), - ]), - Fp2E::new([ - FpE::new(U256::from_hex_unchecked( - "12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa", - )), - FpE::new(U256::from_hex_unchecked( - "090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b", - )), - ]), - ) - .unwrap(); - - let p2 = G1Point::from_affine( - FpE::new(U256::from_hex_unchecked( - "0000000000000000000000000000000000000000000000000000000000000001", - )), - FpE::new(U256::from_hex_unchecked( - "30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd45", - )), - ) - .unwrap(); - - let q2 = G2Point::from_affine( - Fp2E::new([ - FpE::new(U256::from_hex_unchecked( - "1800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed", - )), - FpE::new(U256::from_hex_unchecked( - "198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c2", - )), - ]), - Fp2E::new([ - FpE::new(U256::from_hex_unchecked( - "12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa", - )), - FpE::new(U256::from_hex_unchecked( - "090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b", - )), - ]), - ) - .unwrap(); - - let result = BN254AtePairing::compute_batch(&[(&p1, &q1), (&p2, &q2)]).unwrap(); - - assert_eq!(result, Fp12E::one()); - } - - #[test] - fn two_pairs_of_points_match_2() { - let p1 = G1Point::from_affine( - FpE::new(U256::from_hex_unchecked( - "105456a333e6d636854f987ea7bb713dfd0ae8371a72aea313ae0c32c0bf1016", - )), - FpE::new(U256::from_hex_unchecked( - "0cf031d41b41557f3e7e3ba0c51bebe5da8e6ecd855ec50fc87efcdeac168bcc", - )), - ) - .unwrap(); - - let q1 = G2Point::from_affine( - Fp2E::new([ - FpE::new(U256::from_hex_unchecked( - "3010c68cb50161b7d1d96bb71edfec9880171954e56871abf3d93cc94d745fa1", - )), - FpE::new(U256::from_hex_unchecked( - "0476be093a6d2b4bbf907172049874af11e1b6267606e00804d3ff0037ec57fd", - )), - ]), - Fp2E::new([ - FpE::new(U256::from_hex_unchecked( - "01b33461f39d9e887dbb100f170a2345dde3c07e256d1dfa2b657ba5cd030427", - )), - FpE::new(U256::from_hex_unchecked( - "14c059d74e5b6c4ec14ae5864ebe23a71781d86c29fb8fb6cce94f70d3de7a21", - )), - ]), - ) - .unwrap(); - - let p2 = G1Point::from_affine( - FpE::new(U256::from_hex_unchecked( - "0000000000000000000000000000000000000000000000000000000000000001", - )), - FpE::new(U256::from_hex_unchecked( - "0000000000000000000000000000000000000000000000000000000000000002", - )), - ) - .unwrap(); - - let q2 = G2Point::from_affine( - Fp2E::new([ - FpE::new(U256::from_hex_unchecked( - "290158a80cd3d66530f74dc94c94adb88f5cdb481acca997b6e60071f08a115f", - )), - FpE::new(U256::from_hex_unchecked( - "1a2c3013d2ea92e13c800cde68ef56a294b883f6ac35d25f587c09b1b3c635f7", - )), - ]), - Fp2E::new([ - FpE::new(U256::from_hex_unchecked( - "29d1691530ca701b4a106054688728c9972c8512e9789e9567aae23e302ccd75", - )), - FpE::new(U256::from_hex_unchecked( - "2f997f3dbd66a7afe07fe7862ce239edba9e05c5afff7f8a1259c9733b2dfbb9", - )), - ]), - ) - .unwrap(); - - let result = BN254AtePairing::compute_batch(&[(&p1, &q1), (&p2, &q2)]).unwrap(); - assert_eq!(result, Fp12E::one()); - } - - #[test] - fn two_pairs_of_points_fail() { - let p1 = G1Point::from_affine( - FpE::new(U256::from_hex_unchecked( - "0000000000000000000000000000000000000000000000000000000000000001", - )), - FpE::new(U256::from_hex_unchecked( - "0000000000000000000000000000000000000000000000000000000000000002", - )), - ) - .unwrap(); - - let q1 = G2Point::from_affine( - Fp2E::new([ - FpE::new(U256::from_hex_unchecked( - "1800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed", - )), - FpE::new(U256::from_hex_unchecked( - "198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c2", - )), - ]), - Fp2E::new([ - FpE::new(U256::from_hex_unchecked( - "12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa", - )), - FpE::new(U256::from_hex_unchecked( - "090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b", - )), - ]), - ) - .unwrap(); - - let p2 = G1Point::from_affine( - FpE::new(U256::from_hex_unchecked( - "0000000000000000000000000000000000000000000000000000000000000001", - )), - FpE::new(U256::from_hex_unchecked( - "0000000000000000000000000000000000000000000000000000000000000002", - )), - ) - .unwrap(); - - let q2 = G2Point::from_affine( - Fp2E::new([ - FpE::new(U256::from_hex_unchecked( - "1800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed", - )), - FpE::new(U256::from_hex_unchecked( - "198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c2", - )), - ]), - Fp2E::new([ - FpE::new(U256::from_hex_unchecked( - "12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa", - )), - FpE::new(U256::from_hex_unchecked( - "090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b", - )), - ]), - ) - .unwrap(); - - let result = BN254AtePairing::compute_batch(&[(&p1, &q1), (&p2, &q2)]).unwrap(); - assert!(result != Fp12E::one()); - } - - #[test] - fn three_pairs_of_points_fail() { - let p1 = G1Point::from_affine( - FpE::new(U256::from_hex_unchecked( - "105456a333e6d636854f987ea7bb713dfd0ae8371a72aea313ae0c32c0bf1016", - )), - FpE::new(U256::from_hex_unchecked( - "0cf031d41b41557f3e7e3ba0c51bebe5da8e6ecd855ec50fc87efcdeac168bcc", - )), - ) - .unwrap(); - - let q1 = G2Point::from_affine( - Fp2E::new([ - FpE::new(U256::from_hex_unchecked( - "3010c68cb50161b7d1d96bb71edfec9880171954e56871abf3d93cc94d745fa1", - )), - FpE::new(U256::from_hex_unchecked( - "0476be093a6d2b4bbf907172049874af11e1b6267606e00804d3ff0037ec57fd", - )), - ]), - Fp2E::new([ - FpE::new(U256::from_hex_unchecked( - "01b33461f39d9e887dbb100f170a2345dde3c07e256d1dfa2b657ba5cd030427", - )), - FpE::new(U256::from_hex_unchecked( - "14c059d74e5b6c4ec14ae5864ebe23a71781d86c29fb8fb6cce94f70d3de7a21", - )), - ]), - ) - .unwrap(); - - let p2 = G1Point::from_affine( - FpE::new(U256::from_hex_unchecked( - "0000000000000000000000000000000000000000000000000000000000000001", - )), - FpE::new(U256::from_hex_unchecked( - "0000000000000000000000000000000000000000000000000000000000000002", - )), - ) - .unwrap(); - - let q2 = G2Point::from_affine( - Fp2E::new([ - FpE::new(U256::from_hex_unchecked( - "290158a80cd3d66530f74dc94c94adb88f5cdb481acca997b6e60071f08a115f", - )), - FpE::new(U256::from_hex_unchecked( - "1a2c3013d2ea92e13c800cde68ef56a294b883f6ac35d25f587c09b1b3c635f7", - )), - ]), - Fp2E::new([ - FpE::new(U256::from_hex_unchecked( - "0692e55db067300e6e3fe56218fa2f940054e57e7ef92bf7d475a9d8a8502fd2", - )), - FpE::new(U256::from_hex_unchecked( - "00cacf3523caf879d7d05e30549f1e6fdce364cbb8724b0329c6c2a39d4f018e", - )), - ]), - ) - .unwrap(); - - let p3 = G1Point::from_affine( - FpE::new(U256::from_hex_unchecked( - "0000000000000000000000000000000000000000000000000000000000000001", - )), - FpE::new(U256::from_hex_unchecked( - "0000000000000000000000000000000000000000000000000000000000000002", - )), - ) - .unwrap(); - - let q3 = G2Point::from_affine( - Fp2E::new([ - FpE::new(U256::from_hex_unchecked( - "1800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed", - )), - FpE::new(U256::from_hex_unchecked( - "198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c2", - )), - ]), - Fp2E::new([ - FpE::new(U256::from_hex_unchecked( - "12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa", - )), - FpE::new(U256::from_hex_unchecked( - "090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b", - )), - ]), - ) - .unwrap(); - - let result = BN254AtePairing::compute_batch(&[(&p1, &q1), (&p2, &q2), (&p3, &q3)]).unwrap(); - assert!(result != Fp12E::one()); - } - - const R: U256 = U256::from_hex_unchecked( - "30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001", - ); - - #[test] - fn pairing_result_pow_r_is_one() { - let p = BN254Curve::generator(); - let q = BN254TwistCurve::generator(); - let pairing_result = BN254AtePairing::compute_batch(&[(&p, &q)]).unwrap(); - assert_eq!(pairing_result.pow(R), Fp12E::one()); - } - - #[test] - fn pairing_is_non_degenerate() { - let p = BN254Curve::generator(); - let q = BN254TwistCurve::generator(); - let pairing_result = BN254AtePairing::compute_batch(&[(&p, &q)]).unwrap(); - assert_ne!(pairing_result, Fp12E::one()); - } - - #[test] - fn cyclotomic_square_equals_square() { - let p = BN254Curve::generator(); - let q = BN254TwistCurve::generator(); - let f = miller_optimized(&p, &q); - let f_easy_aux = f.conjugate() * f.inv().unwrap(); // f ^ (p^6 - 1) because f^(p^6) = f.conjugate(). - let f_easy = &frobenius_square(&f_easy_aux) * f_easy_aux; // (f^{p^6 - 1})^(p^2) * (f^{p^6 - 1}). - assert_eq!(cyclotomic_square(&f_easy), f_easy.square()); - } - - #[test] - fn cyclotomic_pow_x_equals_pow() { - let p = BN254Curve::generator(); - let q = BN254TwistCurve::generator(); - let f = miller_optimized(&p, &q); - let f_easy_aux = f.conjugate() * f.inv().unwrap(); // f ^ (p^6 - 1) because f^(p^6) = f.conjugate(). - let f_easy = &frobenius_square(&f_easy_aux) * f_easy_aux; // (f^{p^6 - 1})^(p^2) * (f^{p^6 - 1}). - assert_eq!(cyclotomic_pow_x(&f_easy), f_easy.pow(X)); - } - - #[test] - fn constant_two_inv_is_iwo_inverse() { - assert_eq!(TWO_INV, FpE::from(2).inv().unwrap()); - assert_eq!(TWO_INV * FpE::from(2), FpE::one()); - } -} diff --git a/crates/math/src/elliptic_curve/short_weierstrass/curves/bn_254/sqrt.rs b/crates/math/src/elliptic_curve/short_weierstrass/curves/bn_254/sqrt.rs deleted file mode 100644 index e3b86d4bd..000000000 --- a/crates/math/src/elliptic_curve/short_weierstrass/curves/bn_254/sqrt.rs +++ /dev/null @@ -1,202 +0,0 @@ -use super::curve::{BN254FieldElement, BN254TwistCurveFieldElement}; -use crate::field::traits::LegendreSymbol; -use core::cmp::Ordering; - -pub const TWO_INV: BN254FieldElement = BN254FieldElement::from_hex_unchecked( - "183227397098D014DC2822DB40C0AC2ECBC0B548B438E5469E10460B6C3E7EA4", -); - -#[must_use] -pub fn select_sqrt_value_from_third_bit( - sqrt_1: BN254FieldElement, - sqrt_2: BN254FieldElement, - third_bit: u8, -) -> BN254FieldElement { - match ( - sqrt_1.representative().cmp(&sqrt_2.representative()), - third_bit, - ) { - (Ordering::Greater, 0) => sqrt_2, - (Ordering::Greater, _) | (Ordering::Less, 0) | (Ordering::Equal, _) => sqrt_1, - (Ordering::Less, _) => sqrt_2, - } -} - -/// * `third_bit` - if 1, then the square root is the greater one, otherwise it is the smaller one. -#[must_use] -pub fn sqrt_qfe( - input: &BN254TwistCurveFieldElement, - third_bit: u8, -) -> Option { - // Algorithm 8, https://eprint.iacr.org/2012/685.pdf - if *input == BN254TwistCurveFieldElement::zero() { - Some(BN254TwistCurveFieldElement::zero()) - } else { - let a = input.value()[0].clone(); - let b = input.value()[1].clone(); - if b == BN254FieldElement::zero() { - // second part is zero - let (y_sqrt_1, y_sqrt_2) = a.sqrt()?; - let y_aux = select_sqrt_value_from_third_bit(y_sqrt_1, y_sqrt_2, third_bit); - - Some(BN254TwistCurveFieldElement::new([ - y_aux, - BN254FieldElement::zero(), - ])) - } else { - // second part of the input field number is non-zero - // instead of "sum" is: -beta - let alpha = a.square() + b.square(); - let gamma = alpha.legendre_symbol(); - match gamma { - LegendreSymbol::One => { - let two = BN254FieldElement::from(2u64); - // calculate the square root of alpha - let (y_sqrt1, y_sqrt2) = alpha.sqrt()?; - let mut delta = (&a + y_sqrt1) * TWO_INV; - - let legendre_delta = delta.legendre_symbol(); - if legendre_delta == LegendreSymbol::MinusOne { - delta = (a + y_sqrt2) * TWO_INV; - }; - let (x_sqrt_1, x_sqrt_2) = delta.sqrt()?; - let x_0 = select_sqrt_value_from_third_bit(x_sqrt_1, x_sqrt_2, third_bit); - let x_1 = b * (two * &x_0).inv().unwrap(); - Some(BN254TwistCurveFieldElement::new([x_0, x_1])) - } - LegendreSymbol::MinusOne => None, - LegendreSymbol::Zero => { - unreachable!("The input is zero, but we already handled this case.") - } - } - } - } -} - -#[cfg(test)] -mod tests { - use super::super::curve::{BN254FieldElement, BN254TwistCurveFieldElement}; - use super::super::twist::BN254TwistCurve; - use crate::cyclic_group::IsGroup; - use crate::elliptic_curve::short_weierstrass::traits::IsShortWeierstrass; - use crate::elliptic_curve::traits::IsEllipticCurve; - use rand::{rngs::StdRng, Rng, SeedableRng}; - - #[test] - /// We took the q1 point of the test two_pairs_of_points_match_1 from pairing.rs - /// to get the values of x and y. - fn test_sqrt_qfe() { - // Coordinate x of q. - let x = super::BN254TwistCurveFieldElement::new([ - BN254FieldElement::from_hex_unchecked( - "1800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed", - ), - BN254FieldElement::from_hex_unchecked( - "198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c2", - ), - ]); - - let qfe_b = BN254TwistCurve::b(); - // The equation of the twisted curve is y^2 = x^3 + 3 /(9+u) - let y_square = x.square() * &x + qfe_b; - let y = super::sqrt_qfe(&y_square, 0).unwrap(); - - // Coordinate y of q. - let y_expected = super::BN254TwistCurveFieldElement::new([ - BN254FieldElement::from_hex_unchecked( - "12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa", - ), - BN254FieldElement::from_hex_unchecked( - "090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b", - ), - ]); - - let value_y = y.value(); - let value_y_expected = y_expected.value(); - - assert_eq!(value_y[0].clone(), value_y_expected[0].clone()); - assert_eq!(value_y[1].clone(), value_y_expected[1].clone()); - } - - #[test] - /// We took the q1 point of the test two_pairs_of_points_match_2 from pairing.rs - fn test_sqrt_qfe_2() { - let x = super::BN254TwistCurveFieldElement::new([ - BN254FieldElement::from_hex_unchecked( - "3010c68cb50161b7d1d96bb71edfec9880171954e56871abf3d93cc94d745fa1", - ), - BN254FieldElement::from_hex_unchecked( - "0476be093a6d2b4bbf907172049874af11e1b6267606e00804d3ff0037ec57fd", - ), - ]); - - let qfe_b = BN254TwistCurve::b(); - - let y_square = x.pow(3_u64) + qfe_b; - let y = super::sqrt_qfe(&y_square, 0).unwrap(); - - let y_expected = super::BN254TwistCurveFieldElement::new([ - BN254FieldElement::from_hex_unchecked( - "01b33461f39d9e887dbb100f170a2345dde3c07e256d1dfa2b657ba5cd030427", - ), - BN254FieldElement::from_hex_unchecked( - "14c059d74e5b6c4ec14ae5864ebe23a71781d86c29fb8fb6cce94f70d3de7a21", - ), - ]); - - let value_y = y.value(); - let value_y_expected = y_expected.value(); - - assert_eq!(value_y[0].clone(), value_y_expected[0].clone()); - assert_eq!(value_y[1].clone(), value_y_expected[1].clone()); - } - - #[test] - fn test_sqrt_qfe_3() { - let g = BN254TwistCurve::generator().to_affine(); - let y = &g.coordinates()[1]; - let y_square = &y.square(); - let y_result = super::sqrt_qfe(y_square, 0).unwrap(); - - assert_eq!(y_result, y.clone()); - } - - #[test] - fn test_sqrt_qfe_4() { - let g = BN254TwistCurve::generator() - .operate_with_self(2_u16) - .to_affine(); - let y = &g.coordinates()[1]; - let y_square = &y.square(); - let y_result = super::sqrt_qfe(y_square, 0).unwrap(); - - assert_eq!(y_result, y.clone()); - } - - #[test] - fn test_sqrt_qfe_5() { - let a = BN254TwistCurveFieldElement::new([ - BN254FieldElement::from(3), - BN254FieldElement::from(4), - ]); - let a_square = a.square(); - let a_result = super::sqrt_qfe(&a_square, 0).unwrap(); - - assert_eq!(a_result, a); - } - - #[test] - fn test_sqrt_qfe_random() { - let mut rng = StdRng::seed_from_u64(42); - let a_val: u64 = rng.gen(); - let b_val: u64 = rng.gen(); - let a = BN254TwistCurveFieldElement::new([ - BN254FieldElement::from(a_val), - BN254FieldElement::from(b_val), - ]); - let a_square = a.square(); - let a_result = super::sqrt_qfe(&a_square, 0).unwrap(); - - assert_eq!(a_result, a); - } -} diff --git a/crates/math/src/elliptic_curve/short_weierstrass/curves/bn_254/twist.rs b/crates/math/src/elliptic_curve/short_weierstrass/curves/bn_254/twist.rs deleted file mode 100644 index d9df4dcab..000000000 --- a/crates/math/src/elliptic_curve/short_weierstrass/curves/bn_254/twist.rs +++ /dev/null @@ -1,202 +0,0 @@ -use crate::elliptic_curve::short_weierstrass::point::ShortWeierstrassProjectivePoint; -use crate::elliptic_curve::traits::IsEllipticCurve; -use crate::unsigned_integer::element::U256; -use crate::{ - elliptic_curve::short_weierstrass::traits::IsShortWeierstrass, field::element::FieldElement, -}; - -use super::field_extension::Degree2ExtensionField; - -// X_0 : 10857046999023057135944570762232829481370756359578518086990519993285655852781 -const GENERATOR_X_0: U256 = - U256::from_hex_unchecked("1800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed"); -// X_1 : 11559732032986387107991004021392285783925812861821192530917403151452391805634 -const GENERATOR_X_1: U256 = - U256::from_hex_unchecked("198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c2"); -// Y_0 : 8495653923123431417604973247489272438418190587263600148770280649306958101930 -const GENERATOR_Y_0: U256 = - U256::from_hex_unchecked("12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa"); -// Y_1 : 4082367875863433681332203403145435568316851327593401208105741076214120093531 -const GENERATOR_Y_1: U256 = - U256::from_hex_unchecked("90689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b"); - -/// The description of the curve. -#[derive(Clone, Debug)] -pub struct BN254TwistCurve; - -impl IsEllipticCurve for BN254TwistCurve { - type BaseField = Degree2ExtensionField; - type PointRepresentation = ShortWeierstrassProjectivePoint; - - fn generator() -> Self::PointRepresentation { - // SAFETY: - // - The generator point is mathematically verified to be a valid point on the curve. - // - `unwrap()` is safe because the provided coordinates satisfy the curve equation. - let point = Self::PointRepresentation::new([ - FieldElement::new([ - FieldElement::new(GENERATOR_X_0), - FieldElement::new(GENERATOR_X_1), - ]), - FieldElement::new([ - FieldElement::new(GENERATOR_Y_0), - FieldElement::new(GENERATOR_Y_1), - ]), - FieldElement::one(), - ]); - point.unwrap() - } -} - -impl IsShortWeierstrass for BN254TwistCurve { - fn a() -> FieldElement { - FieldElement::zero() - } - - fn b() -> FieldElement { - FieldElement::new([ - FieldElement::from_hex_unchecked( - "2b149d40ceb8aaae81be18991be06ac3b5b4c5e559dbefa33267e6dc24a138e5", - ), - FieldElement::from_hex_unchecked( - "9713b03af0fed4cd2cafadeed8fdf4a74fa084e52d1852e4a2bd0685c315d2", - ), - ]) - } -} - -#[cfg(test)] -mod tests { - use crate::{ - cyclic_group::IsGroup, - elliptic_curve::{ - short_weierstrass::{ - curves::bn_254::field_extension::{BN254PrimeField, Degree2ExtensionField}, - traits::IsShortWeierstrass, - }, - traits::IsEllipticCurve, - }, - field::element::FieldElement, - unsigned_integer::element::U256, - }; - - use super::BN254TwistCurve; - type Level0FE = FieldElement; - type Level1FE = FieldElement; - - #[cfg(feature = "alloc")] - use crate::elliptic_curve::short_weierstrass::point::{ - Endianness, PointFormat, ShortWeierstrassProjectivePoint, - }; - - #[test] - fn create_generator() { - let g = BN254TwistCurve::generator(); - let [x, y, _] = g.coordinates(); - assert_eq!(BN254TwistCurve::defining_equation(x, y), Level1FE::zero()); - } - - #[cfg(feature = "std")] - #[test] - fn serialize_deserialize_generator() { - let g = BN254TwistCurve::generator(); - let bytes = g.serialize(PointFormat::Projective, Endianness::LittleEndian); - - let deserialized = ShortWeierstrassProjectivePoint::::deserialize( - &bytes, - PointFormat::Projective, - Endianness::LittleEndian, - ) - .unwrap(); - - assert_eq!(deserialized, g); - } - - /* - Sage script: - - a = 0 - b = 3 - p = 21888242871839275222246405745257275088696311157297823662689037894645226208583 - Fp = GF(p) - G1 = EllipticCurve(Fp, [a, b]) - - Fp = GF(p) - K. = PolynomialRing(Fp) - non_residue_fp = -1 - fp2. = Fp.extension(u^2 - non_residue_fp) - non_residue_twist = fp2([9, 1]) - - g1 = EllipticCurve(Fp, [a, b]) - g2 = EllipticCurve(fp2, [0, b/fp2(non_residue_twist)]) - - px = fp2([0x8ae7459fe0d23419ec54b150574b77b1d0aa0785ce98d43365898a1d9168a2a,0x235ec75b0bbcca3f1bab9f3aa4c65d52ccb479cb398b54bd4b0f7e3a24454b44]) - py = fp2([0x214ebea9c718706be05072da305b74c1585f9e75dbf99c7859bf2292e07c1691, 0x2efdafd49b6e2d718b4e3b3d78939a6463f5f84f4343ec6b1161971dd38af12f]) - qx = fp2([0x2b87b85159311f97b20b4c0e27eed978cf94984b06203f853c43192de1579324, 0x27b8bc83db0109e29df764a1379a0b9ba3dab41db33aa9be4aef88d4dd9cb275]) - qy = fp2([0x18e358db9be18771bb6d8ba89a7be0d521782f10af8398e981b1dd252d114bed, 0x8aa3f8a241032d3832b0f52403eb4ea852e23ec4c1e6d39b08de5ac36a2d43b]) - rx = fp2([0x27e1bb6cb3f893ef4af84ff82bd36b0c0832e3c5d4649da024b41bfecdc74233,0xb04a4feada4eba73191184c5f39f98e7319dc888a2b258697511a2035723656]) - ry = fp2([0xa5490e3b00bc8e434f9a1ba734b05c27c525889bf117bb4d293f5aa54b238c5,0x136ce0ba382e5d37c3e05eff8365e0e6857eefa150096af33bdbdf327649c0eb]) - - p = g2(px,py) - q = g2(qx,qy) - r = g2(rx,ry) - - p + q == r - */ - - #[test] - fn add_points() { - let px = Level1FE::new([ - Level0FE::new(U256::from_hex_unchecked( - "8ae7459fe0d23419ec54b150574b77b1d0aa0785ce98d43365898a1d9168a2a", - )), - Level0FE::new(U256::from_hex_unchecked( - "235ec75b0bbcca3f1bab9f3aa4c65d52ccb479cb398b54bd4b0f7e3a24454b44", - )), - ]); - let py = Level1FE::new([ - Level0FE::new(U256::from_hex_unchecked( - "214ebea9c718706be05072da305b74c1585f9e75dbf99c7859bf2292e07c1691", - )), - Level0FE::new(U256::from_hex_unchecked( - "2efdafd49b6e2d718b4e3b3d78939a6463f5f84f4343ec6b1161971dd38af12f", - )), - ]); - // Similarly for point Q - let qx = Level1FE::new([ - Level0FE::new(U256::from_hex_unchecked( - "2b87b85159311f97b20b4c0e27eed978cf94984b06203f853c43192de1579324", - )), - Level0FE::new(U256::from_hex_unchecked( - "27b8bc83db0109e29df764a1379a0b9ba3dab41db33aa9be4aef88d4dd9cb275", - )), - ]); - let qy = Level1FE::new([ - Level0FE::new(U256::from_hex_unchecked( - "18e358db9be18771bb6d8ba89a7be0d521782f10af8398e981b1dd252d114bed", - )), - Level0FE::new(U256::from_hex_unchecked( - "8aa3f8a241032d3832b0f52403eb4ea852e23ec4c1e6d39b08de5ac36a2d43b", - )), - ]); - let expectedx = Level1FE::new([ - Level0FE::new(U256::from_hex_unchecked( - "27e1bb6cb3f893ef4af84ff82bd36b0c0832e3c5d4649da024b41bfecdc74233", - )), - Level0FE::new(U256::from_hex_unchecked( - "b04a4feada4eba73191184c5f39f98e7319dc888a2b258697511a2035723656", - )), - ]); - let expectedy = Level1FE::new([ - Level0FE::new(U256::from_hex_unchecked( - "a5490e3b00bc8e434f9a1ba734b05c27c525889bf117bb4d293f5aa54b238c5", - )), - Level0FE::new(U256::from_hex_unchecked( - "136ce0ba382e5d37c3e05eff8365e0e6857eefa150096af33bdbdf327649c0eb", - )), - ]); - let p = BN254TwistCurve::create_point_from_affine(px, py).unwrap(); - let q = BN254TwistCurve::create_point_from_affine(qx, qy).unwrap(); - let expected = BN254TwistCurve::create_point_from_affine(expectedx, expectedy).unwrap(); - assert_eq!(p.operate_with(&q), expected); - } -} diff --git a/crates/math/src/elliptic_curve/short_weierstrass/curves/grumpkin/curve.rs b/crates/math/src/elliptic_curve/short_weierstrass/curves/grumpkin/curve.rs deleted file mode 100644 index e0d1e5e3d..000000000 --- a/crates/math/src/elliptic_curve/short_weierstrass/curves/grumpkin/curve.rs +++ /dev/null @@ -1,354 +0,0 @@ -use crate::elliptic_curve::short_weierstrass::point::ShortWeierstrassProjectivePoint; -use crate::elliptic_curve::traits::IsEllipticCurve; -use crate::field::fields::montgomery_backed_prime_fields::{ - IsModulus, MontgomeryBackendPrimeField, -}; -use crate::unsigned_integer::element::U256; -use crate::{ - elliptic_curve::short_weierstrass::traits::IsShortWeierstrass, field::element::FieldElement, -}; - -/// Grumpkin an elliptic curve on top of BN254 for SNARK efficient group operations used by the Aztec Protocol. -/// p = 21888242871839275222246405745257275088548364400416034343698204186575808495617 -/// a = 0 -/// b = -17 -/// r = 21888242871839275222246405745257275088696311157297823662689037894645226208583 -/// Grumpkin is a cycle curve together with BN254 meaning the field and group order of Grumpkin are equal to the group and field order of BN254 G1. -#[derive(Clone, Debug)] -pub struct GrumpkinCurve; - -impl IsEllipticCurve for GrumpkinCurve { - type BaseField = GrumpkinPrimeField; - type PointRepresentation = ShortWeierstrassProjectivePoint; - - // G = (1, sprt(-16)) = (1, 17631683881184975370165255887551781615748388533673675138860) = (0x1, 0x2cf135e7506a45d632d270d45f1181294833fc48d823f272c) - fn generator() -> Self::PointRepresentation { - // SAFETY: - // - The generator point is mathematically verified to be a valid point on the curve. - // - `unwrap()` is safe because the provided coordinates satisfy the curve equation. - let point = Self::PointRepresentation::new([ - FieldElement::::one(), - FieldElement::::from_hex_unchecked( - "0x2cf135e7506a45d632d270d45f1181294833fc48d823f272c", - ), - FieldElement::one(), - ]); - point.unwrap() - } -} - -impl IsShortWeierstrass for GrumpkinCurve { - // a = 0 - fn a() -> FieldElement { - FieldElement::from(0) - } - - // b = -17 - fn b() -> FieldElement { - -FieldElement::from(17) - } -} - -// Grumpkin Fp -// p = 21888242871839275222246405745257275088548364400416034343698204186575808495617 -// Equal tp BN254 Fr -pub type GrumpkinFieldElement = FieldElement; - -pub const GRUMPKIN_PRIME_FIELD_ORDER: U256 = - U256::from_hex_unchecked("0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001"); - -#[derive(Clone, Debug)] -pub struct GrumpkinFieldModulus; -impl IsModulus for GrumpkinFieldModulus { - const MODULUS: U256 = GRUMPKIN_PRIME_FIELD_ORDER; -} - -pub type GrumpkinPrimeField = MontgomeryBackendPrimeField; - -impl FieldElement { - pub fn new_base(a_hex: &str) -> Self { - Self::new(U256::from(a_hex)) - } -} - -#[derive(Clone, Debug)] -pub struct FrConfig; - -/// Modulus (Order) of Grumpkin Fr -// r = 21888242871839275222246405745257275088696311157297823662689037894645226208583 -impl IsModulus for FrConfig { - const MODULUS: U256 = U256::from_hex_unchecked( - "0x30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47", - ); -} - -/// Grumpkin Fr -/// Equal to BN254 Fp -pub type FrField = MontgomeryBackendPrimeField; -/// FrElement using MontgomeryBackend for Grumpkin -pub type FrElement = FieldElement; - -#[cfg(test)] -mod tests { - use super::*; - use crate::{ - cyclic_group::IsGroup, elliptic_curve::traits::EllipticCurveError, - field::element::FieldElement, - }; - - use super::GrumpkinCurve; - - #[allow(clippy::upper_case_acronyms)] - type FE = GrumpkinFieldElement; - type G = ShortWeierstrassProjectivePoint; - - /* - Sage script: - p = 21888242871839275222246405745257275088548364400416034343698204186575808495617 - Fp = GF(p) - a = Fp(0) - b = Fp(-17) - Grumpkin = EllipticCurve(Fp, [a, b]) - A = Grumpkin.random_point() - A - (15485117031023686537706709698619053905755909389649581280762364787786480506330 : - 8998283053861550708725041915039948040873858194502192019982314435709819336827 : - 1) - hex(15485117031023686537706709698619053905755909389649581280762364787786480506330) = 0x223c44015b1ab0705802e079ad06dc25f608633c83192ed0720bd396ab3a55da - hex(8998283053861550708725041915039948040873858194502192019982314435709819336827) = 0x13e4d9047d76f812c834a27f2bbaab6ca5fd62ed34ac2e1ff1870ab083f2b87b - */ - fn point_a() -> ShortWeierstrassProjectivePoint { - let x = FE::from_hex_unchecked( - "0x223c44015b1ab0705802e079ad06dc25f608633c83192ed0720bd396ab3a55da", - ); - let y = FE::from_hex_unchecked( - "0x13e4d9047d76f812c834a27f2bbaab6ca5fd62ed34ac2e1ff1870ab083f2b87b", - ); - GrumpkinCurve::create_point_from_affine(x, y).unwrap() - } - - /* - Sage script: - p = 21888242871839275222246405745257275088548364400416034343698204186575808495617 - Fp = GF(p) - a = Fp(0) - b = Fp(-17) - Grumpkin = EllipticCurve(Fp, [a, b]) - B = Grumpkin.random_point() - B - (3268464931655688768836539556233897014960714449731494149362780603936864794149 : - 21602298792738915452436924250014118513160691830629170285452329874861320517987 : - 1) - hex(3268464931655688768836539556233897014960714449731494149362780603936864794149) = 0x739e2b6472d405bbc4263464866a15483858b3083004a4246834e8fbba0f625 - hex(21602298792738915452436924250014118513160691830629170285452329874861320517987) = 0x2fc277c38298f4cca2798dcf7f8cc4e943c3e913fff7f8adca03db5920073963 - */ - fn point_b() -> ShortWeierstrassProjectivePoint { - let x = FE::from_hex_unchecked( - "0x739e2b6472d405bbc4263464866a15483858b3083004a4246834e8fbba0f625", - ); - let y = FE::from_hex_unchecked( - "0x2fc277c38298f4cca2798dcf7f8cc4e943c3e913fff7f8adca03db5920073963", - ); - GrumpkinCurve::create_point_from_affine(x, y).unwrap() - } - - /* - Sage script: - p = 21888242871839275222246405745257275088548364400416034343698204186575808495617 - Fp = GF(p) - a = Fp(0) - b = Fp(-17) - Grumpkin = EllipticCurve(Fp, [a, b]) - A = Grumpkin(15485117031023686537706709698619053905755909389649581280762364787786480506330 : - 8998283053861550708725041915039948040873858194502192019982314435709819336827 : - 1) - B = Grumpkin( - 3268464931655688768836539556233897014960714449731494149362780603936864794149 : - 21602298792738915452436924250014118513160691830629170285452329874861320517987 : - 1) - C = A + B - C - (19589511723685515259809937322960156453150080799519189498903102061830421386151 : - 8780443621842377450927011336607258768303562920725643392690114380788691381924 : - 1) - hex(19589511723685515259809937322960156453150080799519189498903102061830421386151) = 0x2b4f454dbe9c92fa321542229d214338d5459b71e9e2d995318d9358256093a7 - hex(8780443621842377450927011336607258768303562920725643392690114380788691381924) = 0x13698e12e1076132830116e982f293dd07271f34aa03ebfe23eb9301229c2ea4 - */ - fn point_c() -> ShortWeierstrassProjectivePoint { - let x = FE::from_hex_unchecked( - "0x2b4f454dbe9c92fa321542229d214338d5459b71e9e2d995318d9358256093a7", - ); - let y = FE::from_hex_unchecked( - "0x13698e12e1076132830116e982f293dd07271f34aa03ebfe23eb9301229c2ea4", - ); - GrumpkinCurve::create_point_from_affine(x, y).unwrap() - } - - /* - Sage script: - p = 21888242871839275222246405745257275088548364400416034343698204186575808495617 - Fp = GF(p) - a = Fp(0) - b = Fp(-17) - Grumpkin = EllipticCurve(Fp, [a, b]) - P = Grumpkin.random_point() - P * 5 - (15046418650485865292177180299665505401798701105523584252220614421753423008361 : 17852720053004908540584849282553401192842244835354847668310708345588581105130 : 1) - hex(15046418650485865292177180299665505401798701105523584252220614421753423008361) = 0x2143f89e0ac0942ed1a891a83b5e5b3d4ed46722c24f72dfbdd5fedad27d1269 - hex(17852720053004908540584849282553401192842244835354847668310708345588581105130) = 0x2778480e45647fbe25e497e995c2ac24b6e0411fb01c657460412c142d2f7dea - */ - fn point_a_times_5() -> ShortWeierstrassProjectivePoint { - let x = FE::from_hex_unchecked( - "0x2143f89e0ac0942ed1a891a83b5e5b3d4ed46722c24f72dfbdd5fedad27d1269", - ); - let y = FE::from_hex_unchecked( - "0x2778480e45647fbe25e497e995c2ac24b6e0411fb01c657460412c142d2f7dea", - ); - GrumpkinCurve::create_point_from_affine(x, y).unwrap() - } - - #[test] - fn a_operate_with_b_is_c() { - assert_eq!(point_a().operate_with(&point_b()), point_c()) - } - - #[test] - fn adding_five_times_point_works() { - let point = point_a(); - let point_times_5 = point_a_times_5(); - assert_eq!(point.operate_with_self(5_u16), point_times_5); - } - - #[test] - fn create_valid_point_works() { - let p = point_a(); - assert_eq!( - *p.x(), - FE::new_base("0x223c44015b1ab0705802e079ad06dc25f608633c83192ed0720bd396ab3a55da") - ); - assert_eq!( - *p.y(), - FE::new_base("0x13e4d9047d76f812c834a27f2bbaab6ca5fd62ed34ac2e1ff1870ab083f2b87b") - ); - assert_eq!(*p.z(), FE::one()); - } - - #[test] - fn addition_with_neutral_element_returns_same_element() { - let p = point_a(); - assert_eq!( - *p.x(), - FE::new_base("0x223c44015b1ab0705802e079ad06dc25f608633c83192ed0720bd396ab3a55da") - ); - assert_eq!( - *p.y(), - FE::new_base("0x13e4d9047d76f812c834a27f2bbaab6ca5fd62ed34ac2e1ff1870ab083f2b87b") - ); - - let neutral_element = ShortWeierstrassProjectivePoint::::neutral_element(); - - assert_eq!(p.operate_with(&neutral_element), p); - } - - #[test] - fn neutral_element_plus_neutral_element_is_neutral_element() { - let neutral_element = ShortWeierstrassProjectivePoint::::neutral_element(); - - assert_eq!( - neutral_element.operate_with(&neutral_element), - neutral_element - ); - } - - #[test] - fn create_invalid_points_returns_an_error() { - assert_eq!( - GrumpkinCurve::create_point_from_affine(FE::from(0), FE::from(1)), - Err(EllipticCurveError::InvalidPoint) - ); - } - - #[test] - fn equality_works() { - let g = GrumpkinCurve::generator(); - let g2 = g.operate_with(&g); - assert_ne!(&g2, &g); - assert_eq!(&g, &g); - } - - #[test] - fn g_operated_with_g_satifies_ec_equation() { - let g = GrumpkinCurve::generator(); - let g2 = g.operate_with_self(2_u64); - - // get x and y from affine coordinates - let g2_affine = g2.to_affine(); - let x = g2_affine.x(); - let y = g2_affine.y(); - - // calculate both sides y² = 𝑥³ + 𝑎𝑥² + b - // a = 0 - // b = -17 - let b = -FieldElement::from(17); - let y_sq_0 = x.pow(3_u16) + b; - let y_sq_1 = y.pow(2_u16); - - assert_eq!(y_sq_0, y_sq_1); - } - - // checks: P * O = O * P - #[test] - fn add_inf_to_point_should_not_modify_point() { - // Pick an arbitrary point - let point = point_a(); - // P * O - let left = point.operate_with(&G::neutral_element()); - // O * P - let right = G::neutral_element().operate_with(&point); - assert_eq!(left, point); - assert_eq!(right, point); - } - - // P * -P = O - #[test] - fn add_opposite_of_a_point_to_itself_gives_neutral_element() { - // Pick an arbitrary point - let point = point_a(); - // P * O - let neg_point = point.neg(); - let res = point.operate_with(&neg_point); - assert_eq!(res, G::neutral_element()); - } - - //Scalar mul depends only on the scalar mod r - #[test] - fn scalar_mul_depends_on_scalar_mod_r() { - let r = FrConfig::MODULUS; - let gen = GrumpkinCurve::generator(); - let gen_neg = gen.neg(); - let g = gen.operate_with_self(r); - - let r_sub_one = r - U256::from(1u64); - let op3 = gen.operate_with_self(r_sub_one); - - // random scalar value - let s = U256::from(3u64); - let blinded_scalar = (s * r) + s; - let op1 = gen.operate_with_self(s); - let op2 = gen.operate_with_self(blinded_scalar); - - assert_eq!(op1, op2); - assert_eq!(g, G::neutral_element()); - assert_ne!(op1, G::neutral_element()); - assert_eq!(gen_neg, op3); - } - - #[test] - fn operate_with_self_works_1() { - let g = GrumpkinCurve::generator(); - assert_eq!( - g.operate_with(&g).operate_with(&g), - g.operate_with_self(3_u16) - ); - } -} diff --git a/crates/math/src/elliptic_curve/short_weierstrass/curves/grumpkin/mod.rs b/crates/math/src/elliptic_curve/short_weierstrass/curves/grumpkin/mod.rs deleted file mode 100644 index 201a862ce..000000000 --- a/crates/math/src/elliptic_curve/short_weierstrass/curves/grumpkin/mod.rs +++ /dev/null @@ -1 +0,0 @@ -pub mod curve; diff --git a/crates/math/src/elliptic_curve/short_weierstrass/curves/mod.rs b/crates/math/src/elliptic_curve/short_weierstrass/curves/mod.rs deleted file mode 100644 index e3af22688..000000000 --- a/crates/math/src/elliptic_curve/short_weierstrass/curves/mod.rs +++ /dev/null @@ -1,12 +0,0 @@ -pub mod bls12_377; -pub mod bls12_381; -pub mod bn_254; -pub mod grumpkin; -pub mod pallas; -pub mod secp256k1; -pub mod secp256r1; -pub mod secq256k1; -pub mod stark_curve; -pub mod test_curve_1; -pub mod test_curve_2; -pub mod vesta; diff --git a/crates/math/src/elliptic_curve/short_weierstrass/curves/pallas/curve.rs b/crates/math/src/elliptic_curve/short_weierstrass/curves/pallas/curve.rs deleted file mode 100644 index 62d5a5604..000000000 --- a/crates/math/src/elliptic_curve/short_weierstrass/curves/pallas/curve.rs +++ /dev/null @@ -1,140 +0,0 @@ -use crate::elliptic_curve::short_weierstrass::point::ShortWeierstrassProjectivePoint; -use crate::elliptic_curve::traits::IsEllipticCurve; -use crate::field::fields::pallas_field::Pallas255PrimeField; -use crate::{ - elliptic_curve::short_weierstrass::traits::IsShortWeierstrass, field::element::FieldElement, -}; - -#[derive(Clone, Debug)] -pub struct PallasCurve; - -impl IsEllipticCurve for PallasCurve { - type BaseField = Pallas255PrimeField; - type PointRepresentation = ShortWeierstrassProjectivePoint; - - fn generator() -> Self::PointRepresentation { - // SAFETY: - // - The generator point is mathematically verified to be a valid point on the curve. - // - `unwrap()` is safe because the provided coordinates satisfy the curve equation. - let point = Self::PointRepresentation::new([ - -FieldElement::::one(), - FieldElement::::from(2), - FieldElement::one(), - ]); - point.unwrap() - } -} - -impl IsShortWeierstrass for PallasCurve { - fn a() -> FieldElement { - FieldElement::from(0) - } - - fn b() -> FieldElement { - FieldElement::from(5) - } -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::{ - cyclic_group::IsGroup, elliptic_curve::traits::EllipticCurveError, - field::element::FieldElement, - }; - - use super::PallasCurve; - - #[allow(clippy::upper_case_acronyms)] - type FE = FieldElement; - - fn point_1() -> ShortWeierstrassProjectivePoint { - let x = FE::from_hex_unchecked( - "bd1e740e6b1615ae4c508148ca0c53dbd43f7b2e206195ab638d7f45d51d6b5", - ); - let y = FE::from_hex_unchecked( - "13aacd107ca10b7f8aab570da1183b91d7d86dd723eaa2306b0ef9c5355b91d8", - ); - PallasCurve::create_point_from_affine(x, y).unwrap() - } - - fn point_1_times_5() -> ShortWeierstrassProjectivePoint { - let x = FE::from_hex_unchecked( - "17a21304fffd6749d6173d4e0acd9724d98a97453b3491c0e5a53b06cf039b13", - ); - let y = FE::from_hex_unchecked( - "2f9bde429091a1089e52a6cc5dc789e1a58eeded0cf72dccc33b7af685a982d", - ); - PallasCurve::create_point_from_affine(x, y).unwrap() - } - - #[test] - fn adding_five_times_point_1_works() { - let point_1 = point_1(); - let point_1_times_5 = point_1_times_5(); - assert_eq!(point_1.operate_with_self(5_u16), point_1_times_5); - } - - #[test] - fn create_valid_point_works() { - let p = point_1(); - assert_eq!( - *p.x(), - FE::from_hex_unchecked( - "bd1e740e6b1615ae4c508148ca0c53dbd43f7b2e206195ab638d7f45d51d6b5" - ) - ); - assert_eq!( - *p.y(), - FE::from_hex_unchecked( - "13aacd107ca10b7f8aab570da1183b91d7d86dd723eaa2306b0ef9c5355b91d8" - ) - ); - assert_eq!(*p.z(), FE::from_hex_unchecked("1")); - } - - #[test] - fn create_invalid_points_returns_an_error() { - assert_eq!( - PallasCurve::create_point_from_affine(FE::from(0), FE::from(1)), - Err(EllipticCurveError::InvalidPoint) - ); - } - - #[test] - fn equality_works() { - let g = PallasCurve::generator(); - let g2 = g.operate_with_self(2_u16); - let g2_other = g.operate_with(&g); - assert_ne!(&g2, &g); - assert_eq!(&g, &g); - assert_eq!(&g2, &g2_other); - } - - #[test] - fn g_operated_with_g_satifies_ec_equation() { - let g = PallasCurve::generator(); - let g2 = g.operate_with_self(2_u16); - - // get x and y from affine coordinates - let g2_affine = g2.to_affine(); - let x = g2_affine.x(); - let y = g2_affine.y(); - - // calculate both sides of Pallas curve equation - let five = PallasCurve::b(); - let y_sq_0 = x.pow(3_u16) + five; - let y_sq_1 = y.pow(2_u16); - - assert_eq!(y_sq_0, y_sq_1); - } - - #[test] - fn operate_with_self_works_1() { - let g = PallasCurve::generator(); - assert_eq!( - g.operate_with(&g).operate_with(&g), - g.operate_with_self(3_u16) - ); - } -} diff --git a/crates/math/src/elliptic_curve/short_weierstrass/curves/pallas/mod.rs b/crates/math/src/elliptic_curve/short_weierstrass/curves/pallas/mod.rs deleted file mode 100644 index 201a862ce..000000000 --- a/crates/math/src/elliptic_curve/short_weierstrass/curves/pallas/mod.rs +++ /dev/null @@ -1 +0,0 @@ -pub mod curve; diff --git a/crates/math/src/elliptic_curve/short_weierstrass/curves/secp256k1/curve.rs b/crates/math/src/elliptic_curve/short_weierstrass/curves/secp256k1/curve.rs deleted file mode 100644 index b885ecb9f..000000000 --- a/crates/math/src/elliptic_curve/short_weierstrass/curves/secp256k1/curve.rs +++ /dev/null @@ -1,169 +0,0 @@ -use crate::elliptic_curve::short_weierstrass::point::ShortWeierstrassProjectivePoint; -use crate::elliptic_curve::traits::IsEllipticCurve; -use crate::field::fields::secp256k1_field::Secp256k1PrimeField; -use crate::{ - elliptic_curve::short_weierstrass::traits::IsShortWeierstrass, field::element::FieldElement, -}; - -/// This implementation is not constant time and cannot be used to sign messages. You can use it to check signatures -#[derive(Clone, Debug)] -pub struct Secp256k1Curve; - -impl IsEllipticCurve for Secp256k1Curve { - type BaseField = Secp256k1PrimeField; - type PointRepresentation = ShortWeierstrassProjectivePoint; - - fn generator() -> Self::PointRepresentation { - // SAFETY: - // - The generator point is mathematically verified to be a valid point on the curve. - // - `unwrap()` is safe because the provided coordinates satisfy the curve equation. - let point = Self::PointRepresentation::new([ - FieldElement::::from_hex_unchecked( - "79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798", - ), - FieldElement::::from_hex_unchecked( - "483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8", - ), - FieldElement::one(), - ]); - point.unwrap() - } -} - -impl IsShortWeierstrass for Secp256k1Curve { - fn a() -> FieldElement { - FieldElement::from(0) - } - - fn b() -> FieldElement { - FieldElement::from(7) - } -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::{ - cyclic_group::IsGroup, elliptic_curve::traits::EllipticCurveError, - field::element::FieldElement, unsigned_integer::element::U256, - }; - - use super::Secp256k1Curve; - - #[allow(clippy::upper_case_acronyms)] - type FE = FieldElement; - - fn point_1() -> ShortWeierstrassProjectivePoint { - let x = FE::from_hex_unchecked( - "79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798", - ); - let y = FE::from_hex_unchecked( - "483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8", - ); - Secp256k1Curve::create_point_from_affine(x, y).unwrap() - } - - fn point_1_times_5() -> ShortWeierstrassProjectivePoint { - let x = FE::from_hex_unchecked( - "2F8BDE4D1A07209355B4A7250A5C5128E88B84BDDC619AB7CBA8D569B240EFE4", - ); - let y = FE::from_hex_unchecked( - "D8AC222636E5E3D6D4DBA9DDA6C9C426F788271BAB0D6840DCA87D3AA6AC62D6", - ); - Secp256k1Curve::create_point_from_affine(x, y).unwrap() - } - - #[test] - fn adding_five_times_point_1_works() { - let point_1 = point_1(); - let point_1_times_5 = point_1_times_5(); - assert_eq!(point_1.operate_with_self(5_u16), point_1_times_5); - } - - #[test] - fn create_valid_point_works() { - let p = point_1(); - assert_eq!( - *p.x(), - FE::from_hex_unchecked( - "79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798" - ) - ); - assert_eq!( - *p.y(), - FE::from_hex_unchecked( - "483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8" - ) - ); - assert_eq!(*p.z(), FE::from_hex_unchecked("1")); - } - - #[test] - fn create_invalid_points_returns_an_error() { - assert_eq!( - Secp256k1Curve::create_point_from_affine(FE::from(0), FE::from(1)), - Err(EllipticCurveError::InvalidPoint) - ); - } - - #[test] - fn equality_works() { - let g = Secp256k1Curve::generator(); - let g2 = g.operate_with_self(2_u16); - let g2_other = g.operate_with(&g); - assert_ne!(&g2, &g); - assert_eq!(&g, &g); - assert_eq!(&g2, &g2_other); - } - - #[test] - fn g_operated_with_g_satifies_ec_equation() { - let g = Secp256k1Curve::generator(); - let g2 = g.operate_with_self(2_u16); - - // get x and y from affine coordinates - let g2_affine = g2.to_affine(); - let x = g2_affine.x(); - let y = g2_affine.y(); - - // calculate both sides of secp256k1 curve equation - let seven = Secp256k1Curve::b(); - let y_sq_0 = x.pow(3_u16) + seven; - let y_sq_1 = y.pow(2_u16); - - assert_eq!(y_sq_0, y_sq_1); - } - - #[test] - fn operate_with_self_works_1() { - let g = Secp256k1Curve::generator(); - assert_eq!( - g.operate_with(&g).operate_with(&g), - g.operate_with_self(3_u16) - ); - } - - #[test] - fn generator_has_right_order() { - let g = Secp256k1Curve::generator(); - assert_eq!( - g.operate_with_self(U256::from_hex_unchecked( - "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141" - )) - .to_affine(), - ShortWeierstrassProjectivePoint::neutral_element() - ); - } - - #[test] - fn inverse_works() { - let g = Secp256k1Curve::generator(); - assert_eq!( - g.operate_with_self(U256::from_hex_unchecked( - "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd036413C" - )) - .to_affine(), - g.operate_with_self(5u64).neg().to_affine() - ); - } -} diff --git a/crates/math/src/elliptic_curve/short_weierstrass/curves/secp256k1/mod.rs b/crates/math/src/elliptic_curve/short_weierstrass/curves/secp256k1/mod.rs deleted file mode 100644 index 201a862ce..000000000 --- a/crates/math/src/elliptic_curve/short_weierstrass/curves/secp256k1/mod.rs +++ /dev/null @@ -1 +0,0 @@ -pub mod curve; diff --git a/crates/math/src/elliptic_curve/short_weierstrass/curves/secp256r1/curve.rs b/crates/math/src/elliptic_curve/short_weierstrass/curves/secp256r1/curve.rs deleted file mode 100644 index 0d643f706..000000000 --- a/crates/math/src/elliptic_curve/short_weierstrass/curves/secp256r1/curve.rs +++ /dev/null @@ -1,173 +0,0 @@ -use crate::elliptic_curve::short_weierstrass::point::ShortWeierstrassProjectivePoint; -use crate::elliptic_curve::traits::IsEllipticCurve; -use crate::field::fields::secp256r1_field::Secp256r1PrimeField; -use crate::{ - elliptic_curve::short_weierstrass::traits::IsShortWeierstrass, field::element::FieldElement, -}; - -/// This implementation is not constant time and cannot be used to sign messages. You can use it to check signatures -#[derive(Clone, Debug)] -pub struct Secp256r1Curve; - -impl IsEllipticCurve for Secp256r1Curve { - type BaseField = Secp256r1PrimeField; - type PointRepresentation = ShortWeierstrassProjectivePoint; - - fn generator() -> Self::PointRepresentation { - // SAFETY: - // - The generator point is mathematically verified to be a valid point on the curve. - // - `unwrap()` is safe because the provided coordinates satisfy the curve equation. - let point = Self::PointRepresentation::new([ - FieldElement::::from_hex_unchecked( - "6b17d1f2e12c4247f8bce6e563a440f277037d812deb33a0f4a13945d898c296", - ), - FieldElement::::from_hex_unchecked( - "4fe342e2fe1a7f9b8ee7eb4a7c0f9e162bce33576b315ececbb6406837bf51f5", - ), - FieldElement::one(), - ]); - point.unwrap() - } -} - -impl IsShortWeierstrass for Secp256r1Curve { - fn a() -> FieldElement { - FieldElement::::from_hex_unchecked( - "ffffffff00000001000000000000000000000000fffffffffffffffffffffffc", - ) - } - fn b() -> FieldElement { - FieldElement::::from_hex_unchecked( - "5ac635d8aa3a93e7b3ebbd55769886bc651d06b0cc53b0f63bce3c3e27d2604b", - ) - } -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::{ - cyclic_group::IsGroup, elliptic_curve::traits::EllipticCurveError, - field::element::FieldElement, unsigned_integer::element::U256, - }; - - use super::Secp256r1Curve; - - #[allow(clippy::upper_case_acronyms)] - type FE = FieldElement; - - fn point_1() -> ShortWeierstrassProjectivePoint { - let x = FE::from_hex_unchecked( - "6b17d1f2e12c4247f8bce6e563a440f277037d812deb33a0f4a13945d898c296", - ); - let y = FE::from_hex_unchecked( - "4fe342e2fe1a7f9b8ee7eb4a7c0f9e162bce33576b315ececbb6406837bf51f5", - ); - Secp256r1Curve::create_point_from_affine(x, y).unwrap() - } - - fn point_1_times_5() -> ShortWeierstrassProjectivePoint { - let x = FE::from_hex_unchecked( - "51590B7A515140D2D784C85608668FDFEF8C82FD1F5BE52421554A0DC3D033ED", - ); - let y = FE::from_hex_unchecked( - "E0C17DA8904A727D8AE1BF36BF8A79260D012F00D4D80888D1D0BB44FDA16DA4", - ); - Secp256r1Curve::create_point_from_affine(x, y).unwrap() - } - - #[test] - fn adding_five_times_point_1_works() { - let point_1 = point_1(); - let point_1_times_5 = point_1_times_5(); - assert_eq!(point_1.operate_with_self(5_u16), point_1_times_5); - } - - #[test] - fn create_valid_point_works() { - let p = point_1(); - assert_eq!( - *p.x(), - FE::from_hex_unchecked( - "6b17d1f2e12c4247f8bce6e563a440f277037d812deb33a0f4a13945d898c296" - ) - ); - assert_eq!( - *p.y(), - FE::from_hex_unchecked( - "4fe342e2fe1a7f9b8ee7eb4a7c0f9e162bce33576b315ececbb6406837bf51f5" - ) - ); - assert_eq!(*p.z(), FE::from_hex_unchecked("1")); - } - - #[test] - fn create_invalid_points_returns_an_error() { - assert_eq!( - Secp256r1Curve::create_point_from_affine(FE::from(0), FE::from(1)), - Err(EllipticCurveError::InvalidPoint) - ); - } - - #[test] - fn equality_works() { - let g = Secp256r1Curve::generator(); - let g2 = g.operate_with_self(2_u16); - let g2_other = g.operate_with(&g); - assert_ne!(&g2, &g); - assert_eq!(&g, &g); - assert_eq!(&g2, &g2_other); - } - - #[test] - fn g_operated_with_g_satifies_ec_equation() { - let g = Secp256r1Curve::generator(); - let g2 = g.operate_with_self(2_u16); - - // get x and y from affine coordinates - let g2_affine = g2.to_affine(); - let x = g2_affine.x(); - let y = g2_affine.y(); - - // calculate both sides of Secp256r1 curve equation - let a = Secp256r1Curve::a(); - let b = Secp256r1Curve::b(); - let y_sq_0 = x.pow(3_u16) + (a * x) + b; - let y_sq_1 = y.pow(2_u16); - - assert_eq!(y_sq_0, y_sq_1); - } - - #[test] - fn operate_with_self_works_1() { - let g = Secp256r1Curve::generator(); - assert_eq!( - g.operate_with(&g).operate_with(&g), - g.operate_with_self(3_u16) - ); - } - - #[test] - fn generator_has_right_order() { - let g = Secp256r1Curve::generator(); - assert_eq!( - g.operate_with_self(U256::from_hex_unchecked( - "0xffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551" - )) - .to_affine(), - ShortWeierstrassProjectivePoint::neutral_element() - ); - } - - #[test] - fn inverse_works() { - let g = Secp256r1Curve::generator(); - assert_eq!( - g.operate_with_self(U256::from_hex_unchecked( - "FFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC63254C" - )) - .to_affine(), - g.operate_with_self(5u64).neg().to_affine() - ); - } -} diff --git a/crates/math/src/elliptic_curve/short_weierstrass/curves/secp256r1/mod.rs b/crates/math/src/elliptic_curve/short_weierstrass/curves/secp256r1/mod.rs deleted file mode 100644 index 201a862ce..000000000 --- a/crates/math/src/elliptic_curve/short_weierstrass/curves/secp256r1/mod.rs +++ /dev/null @@ -1 +0,0 @@ -pub mod curve; diff --git a/crates/math/src/elliptic_curve/short_weierstrass/curves/secq256k1/curve.rs b/crates/math/src/elliptic_curve/short_weierstrass/curves/secq256k1/curve.rs deleted file mode 100644 index 8101aadc7..000000000 --- a/crates/math/src/elliptic_curve/short_weierstrass/curves/secq256k1/curve.rs +++ /dev/null @@ -1,172 +0,0 @@ -use crate::elliptic_curve::short_weierstrass::point::ShortWeierstrassProjectivePoint; -use crate::elliptic_curve::traits::IsEllipticCurve; -use crate::field::fields::secp256k1_scalarfield::Secp256k1ScalarField; -use crate::{ - elliptic_curve::short_weierstrass::traits::IsShortWeierstrass, field::element::FieldElement, -}; - -#[derive(Clone, Debug)] -pub struct Secq256k1Curve; - -impl IsEllipticCurve for Secq256k1Curve { - type BaseField = Secp256k1ScalarField; - type PointRepresentation = ShortWeierstrassProjectivePoint; - - fn generator() -> Self::PointRepresentation { - // SAFETY: - // - The generator point is mathematically verified to be a valid point on the curve. - // - `unwrap()` is safe because the provided coordinates satisfy the curve equation. - let point = Self::PointRepresentation::new([ - FieldElement::::from_hex_unchecked( - "76C39F5585CB160EB6B06C87A2CE32E23134E45A097781A6A24288E37702EDA6", - ), - FieldElement::::from_hex_unchecked( - "3FFC646C7B2918B5DC2D265A8E82A7F7D18983D26E8DC055A4120DDAD952677F", - ), - FieldElement::one(), - ]); - point.unwrap() - } -} - -impl IsShortWeierstrass for Secq256k1Curve { - fn a() -> FieldElement { - FieldElement::from(0) - } - - fn b() -> FieldElement { - FieldElement::from(7) - } -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::{ - cyclic_group::IsGroup, elliptic_curve::traits::EllipticCurveError, - field::element::FieldElement, unsigned_integer::element::U256, - }; - - use super::Secq256k1Curve; - - #[allow(clippy::upper_case_acronyms)] - type FE = FieldElement; - - fn point_1() -> ShortWeierstrassProjectivePoint { - let x = FE::from_hex_unchecked( - "76C39F5585CB160EB6B06C87A2CE32E23134E45A097781A6A24288E37702EDA6", - ); - let y = FE::from_hex_unchecked( - "3FFC646C7B2918B5DC2D265A8E82A7F7D18983D26E8DC055A4120DDAD952677F", - ); - Secq256k1Curve::create_point_from_affine(x, y).unwrap() - } - - fn point_1_times_5() -> ShortWeierstrassProjectivePoint { - let x = FE::from_hex_unchecked( - "8656a2c13dd0a3bfa362d2ff8c00281341ff3a79cbbe8857f2d20b398041a21a", - ); - let y = FE::from_hex_unchecked( - "468ed8bcfcd4ed2b3bf154414b9e48d8c5ce54f6616846a7cf6a725f70d34a63", - ); - let z = FE::from_hex_unchecked( - "bb26eae3d2b9603d98dff86d87175f442e539c07bbe4ef5712e47c4d72c89734", - ); - ShortWeierstrassProjectivePoint::::new([x, y, z]).unwrap() - } - - #[test] - fn adding_five_times_point_1_works() { - let point_1 = point_1(); - let point_1_times_5 = point_1_times_5(); - assert_eq!(point_1.operate_with_self(5_u16), point_1_times_5); - } - - #[test] - fn create_valid_point_works() { - let p = point_1(); - assert_eq!( - *p.x(), - FE::from_hex_unchecked( - "76C39F5585CB160EB6B06C87A2CE32E23134E45A097781A6A24288E37702EDA6" - ) - ); - assert_eq!( - *p.y(), - FE::from_hex_unchecked( - "3FFC646C7B2918B5DC2D265A8E82A7F7D18983D26E8DC055A4120DDAD952677F" - ) - ); - assert_eq!(*p.z(), FE::from_hex_unchecked("1")); - } - - #[test] - fn create_invalid_points_returns_an_error() { - assert_eq!( - Secq256k1Curve::create_point_from_affine(FE::from(0), FE::from(1)), - Err(EllipticCurveError::InvalidPoint) - ); - } - - #[test] - fn equality_works() { - let g = Secq256k1Curve::generator(); - let g2 = g.operate_with_self(2_u16); - let g2_other = g.operate_with(&g); - assert_ne!(&g2, &g); - assert_eq!(&g, &g); - assert_eq!(&g2, &g2_other); - } - - #[test] - fn g_operated_with_g_satifies_ec_equation() { - let g = Secq256k1Curve::generator(); - let g2 = g.operate_with_self(2_u16); - - // get x and y from affine coordinates - let g2_affine = g2.to_affine(); - let x = g2_affine.x(); - let y = g2_affine.y(); - - // calculate both sides of secq256k1 curve equation - let seven = Secq256k1Curve::b(); - let y_sq_0 = x.pow(3_u16) + seven; - let y_sq_1 = y.pow(2_u16); - - assert_eq!(y_sq_0, y_sq_1); - } - - #[test] - fn operate_with_self_works() { - let g = Secq256k1Curve::generator(); - assert_eq!( - g.operate_with(&g).operate_with(&g), - g.operate_with_self(3_u16) - ); - } - - #[test] - fn generator_has_right_order() { - let g = Secq256k1Curve::generator(); - assert_eq!( - g.operate_with_self(U256::from_hex_unchecked( - "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F" - )) - .to_affine(), - ShortWeierstrassProjectivePoint::neutral_element() - ); - } - - #[test] - /// (r - 5)g = rg - 5g = 0 - 5g = -5g - fn inverse_works() { - let g = Secq256k1Curve::generator(); - assert_eq!( - g.operate_with_self(U256::from_hex_unchecked( - "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2A" - )) - .to_affine(), - g.operate_with_self(5u64).neg().to_affine() - ); - } -} diff --git a/crates/math/src/elliptic_curve/short_weierstrass/curves/secq256k1/mod.rs b/crates/math/src/elliptic_curve/short_weierstrass/curves/secq256k1/mod.rs deleted file mode 100644 index 201a862ce..000000000 --- a/crates/math/src/elliptic_curve/short_weierstrass/curves/secq256k1/mod.rs +++ /dev/null @@ -1 +0,0 @@ -pub mod curve; diff --git a/crates/math/src/elliptic_curve/short_weierstrass/curves/stark_curve.rs b/crates/math/src/elliptic_curve/short_weierstrass/curves/stark_curve.rs deleted file mode 100644 index 7ec8ffd95..000000000 --- a/crates/math/src/elliptic_curve/short_weierstrass/curves/stark_curve.rs +++ /dev/null @@ -1,59 +0,0 @@ -use crate::{ - elliptic_curve::{ - short_weierstrass::{point::ShortWeierstrassProjectivePoint, traits::IsShortWeierstrass}, - traits::IsEllipticCurve, - }, - field::{ - element::FieldElement, fields::fft_friendly::stark_252_prime_field::Stark252PrimeField, - }, -}; - -#[derive(Clone, Debug)] -pub struct StarkCurve; - -impl StarkCurve { - pub const fn from_affine_hex_string_unchecked( - x_hex: &str, - y_hex: &str, - ) -> ShortWeierstrassProjectivePoint { - // SAFETY: The values `x_hex, y_hex` must represent valid coordinates that satisfy - // the curve equation. Invalid coordinates violate the invariant and produce - // silently incorrect results in subsequent operations. - ShortWeierstrassProjectivePoint::new_unchecked([ - FieldElement::::from_hex_unchecked(x_hex), - FieldElement::::from_hex_unchecked(y_hex), - FieldElement::::from_hex_unchecked("1"), - ]) - } -} - -impl IsEllipticCurve for StarkCurve { - type BaseField = Stark252PrimeField; - type PointRepresentation = ShortWeierstrassProjectivePoint; - - fn generator() -> Self::PointRepresentation { - // SAFETY: - // - The generator point is mathematically verified to be a valid point on the curve. - Self::PointRepresentation::new_unchecked([ - FieldElement::::from_hex_unchecked( - "1EF15C18599971B7BECED415A40F0C7DEACFD9B0D1819E03D723D8BC943CFCA", - ), - FieldElement::::from_hex_unchecked( - "5668060AA49730B7BE4801DF46EC62DE53ECD11ABE43A32873000C36E8DC1F", - ), - FieldElement::one(), - ]) - } -} - -impl IsShortWeierstrass for StarkCurve { - fn a() -> FieldElement { - FieldElement::::from_hex_unchecked("1") - } - - fn b() -> FieldElement { - FieldElement::::from_hex_unchecked( - "6F21413EFBE40DE150E596D72F7A8C5609AD26C15C915C1F4CDFCB99CEE9E89", - ) - } -} diff --git a/crates/math/src/elliptic_curve/short_weierstrass/curves/test_curve_1.rs b/crates/math/src/elliptic_curve/short_weierstrass/curves/test_curve_1.rs deleted file mode 100644 index 2612fea28..000000000 --- a/crates/math/src/elliptic_curve/short_weierstrass/curves/test_curve_1.rs +++ /dev/null @@ -1,64 +0,0 @@ -/// Example curve taken from the book "Pairing for beginners", page 57. -/// Defines the basic constants needed to describe a curve in the short Weierstrass form. -/// This small curve has only 5 elements. -use crate::{ - elliptic_curve::{ - short_weierstrass::{point::ShortWeierstrassProjectivePoint, traits::IsShortWeierstrass}, - traits::IsEllipticCurve, - }, - field::{ - element::FieldElement, - extensions::quadratic::{HasQuadraticNonResidue, QuadraticExtensionField}, - fields::u64_prime_field::U64PrimeField, - }, -}; - -/// Order of the base field (e.g.: order of the coordinates) -pub const TEST_CURVE_1_PRIME_FIELD_ORDER: u64 = 59; - -/// Order of the subgroup of the curve. -pub const TEST_CURVE_1_MAIN_SUBGROUP_ORDER: u64 = 5; - -pub type TestCurvePrimeField = U64PrimeField; - -/// In F59 the element -1 is not a square. We use this property -/// to construct a Quadratic Field Extension out of it by adding -/// its square root. -#[derive(Debug, Clone)] -pub struct TestCurveQuadraticNonResidue; -impl HasQuadraticNonResidue for TestCurveQuadraticNonResidue { - fn residue() -> FieldElement { - -FieldElement::one() - } -} - -/// The description of the curve. -#[derive(Clone, Debug)] -pub struct TestCurve1; - -impl IsEllipticCurve for TestCurve1 { - type BaseField = QuadraticExtensionField; - type PointRepresentation = ShortWeierstrassProjectivePoint; - - fn generator() -> Self::PointRepresentation { - // SAFETY: - // - The generator point is mathematically verified to be a valid point on the curve. - // - `unwrap()` is safe because the provided coordinates satisfy the curve equation. - let point = Self::PointRepresentation::new([ - FieldElement::from(35), - FieldElement::from(31), - FieldElement::one(), - ]); - point.unwrap() - } -} - -impl IsShortWeierstrass for TestCurve1 { - fn a() -> FieldElement { - FieldElement::from(1) - } - - fn b() -> FieldElement { - FieldElement::from(0) - } -} diff --git a/crates/math/src/elliptic_curve/short_weierstrass/curves/test_curve_2.rs b/crates/math/src/elliptic_curve/short_weierstrass/curves/test_curve_2.rs deleted file mode 100644 index 0357ab41c..000000000 --- a/crates/math/src/elliptic_curve/short_weierstrass/curves/test_curve_2.rs +++ /dev/null @@ -1,86 +0,0 @@ -use crate::elliptic_curve::short_weierstrass::point::ShortWeierstrassProjectivePoint; -use crate::elliptic_curve::traits::IsEllipticCurve; -use crate::field::fields::montgomery_backed_prime_fields::{ - IsModulus, MontgomeryBackendPrimeField, -}; -use crate::unsigned_integer::element::U384; -use crate::{ - elliptic_curve::short_weierstrass::traits::IsShortWeierstrass, - field::{ - element::FieldElement, - extensions::quadratic::{HasQuadraticNonResidue, QuadraticExtensionField}, - }, -}; - -/// Order of the base field (e.g.: order of the coordinates) -pub const TEST_CURVE_2_PRIME_FIELD_ORDER: U384 = - U384::from_hex_unchecked("150b4c0967215604b841bb57053fcb86cf"); - -/// Order of the subgroup of the curve. -pub const TEST_CURVE_2_MAIN_SUBGROUP_ORDER: U384 = - U384::from_hex_unchecked("40a065fb5a76390de709fb229"); - -// FPBLS12381 -#[derive(Clone, Debug)] -pub struct TestCurve2Modulus; -impl IsModulus for TestCurve2Modulus { - const MODULUS: U384 = TEST_CURVE_2_PRIME_FIELD_ORDER; -} - -type TestCurve2PrimeField = MontgomeryBackendPrimeField; - -/// In F59 the element -1 is not a square. We use this property -/// to construct a Quadratic Field Extension out of it by adding -/// its square root. -#[derive(Debug, Clone)] -pub struct TestCurve2QuadraticNonResidue; -impl HasQuadraticNonResidue for TestCurve2QuadraticNonResidue { - fn residue() -> FieldElement { - -FieldElement::one() - } -} - -/// The description of the curve. -#[derive(Clone, Debug)] -pub struct TestCurve2; - -impl IsEllipticCurve for TestCurve2 { - type BaseField = QuadraticExtensionField; - type PointRepresentation = ShortWeierstrassProjectivePoint; - - fn generator() -> Self::PointRepresentation { - // SAFETY: - // - The generator point is mathematically verified to be a valid point on the curve. - // - `unwrap()` is safe because the provided coordinates satisfy the curve equation. - let point = Self::PointRepresentation::new([ - FieldElement::new([ - FieldElement::new(U384::from_hex_unchecked( - "21acedb641ca6d0f8b60148123a999801", - )), - FieldElement::new(U384::from_hex_unchecked( - "14d34d94f7de312859a8a0d9dbc67159d3", - )), - ]), - FieldElement::new([ - FieldElement::new(U384::from_hex_unchecked( - "2ac53e77afe8d841c8eb660761c4b873a", - )), - FieldElement::new(U384::from_hex_unchecked( - "108a9e1c5514b0921cd5781a7f71130142", - )), - ]), - FieldElement::one(), - ]); - point.unwrap() - } -} - -impl IsShortWeierstrass for TestCurve2 { - fn a() -> FieldElement { - FieldElement::from(0) - } - - fn b() -> FieldElement { - FieldElement::from(1) - } -} diff --git a/crates/math/src/elliptic_curve/short_weierstrass/curves/vesta/curve.rs b/crates/math/src/elliptic_curve/short_weierstrass/curves/vesta/curve.rs deleted file mode 100644 index c76c079e2..000000000 --- a/crates/math/src/elliptic_curve/short_weierstrass/curves/vesta/curve.rs +++ /dev/null @@ -1,140 +0,0 @@ -use crate::elliptic_curve::short_weierstrass::point::ShortWeierstrassProjectivePoint; -use crate::elliptic_curve::traits::IsEllipticCurve; -use crate::field::fields::vesta_field::Vesta255PrimeField; -use crate::{ - elliptic_curve::short_weierstrass::traits::IsShortWeierstrass, field::element::FieldElement, -}; - -#[derive(Clone, Debug)] -pub struct VestaCurve; - -impl IsEllipticCurve for VestaCurve { - type BaseField = Vesta255PrimeField; - type PointRepresentation = ShortWeierstrassProjectivePoint; - - fn generator() -> Self::PointRepresentation { - // SAFETY: - // - The generator point is mathematically verified to be a valid point on the curve. - // - `unwrap()` is safe because the provided coordinates satisfy the curve equation. - let point = Self::PointRepresentation::new([ - -FieldElement::::one(), - FieldElement::::from(2), - FieldElement::one(), - ]); - point.unwrap() - } -} - -impl IsShortWeierstrass for VestaCurve { - fn a() -> FieldElement { - FieldElement::from(0) - } - - fn b() -> FieldElement { - FieldElement::from(5) - } -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::{ - cyclic_group::IsGroup, elliptic_curve::traits::EllipticCurveError, - field::element::FieldElement, - }; - - use super::VestaCurve; - - #[allow(clippy::upper_case_acronyms)] - type FE = FieldElement; - - fn point_1() -> ShortWeierstrassProjectivePoint { - let x = FE::from_hex_unchecked( - "c4e6a8789457a64e1638783181963d4c4399a5a8cdb30af4038664ce431033c", - ); - let y = FE::from_hex_unchecked( - "2d8c9125be9a3ac50371e462f63dfc3fbbf645e9a93d6b7da71c13d3065e3ce5", - ); - VestaCurve::create_point_from_affine(x, y).unwrap() - } - - fn point_1_times_5() -> ShortWeierstrassProjectivePoint { - let x = FE::from_hex_unchecked( - "1266f29f1478410eaa62fb1ab064f7d9259f515600544165972a89c9941c72c3", - ); - let y = FE::from_hex_unchecked( - "3a893b592bd487cd25c5d4237b02987e1b78206e70989f3209e24a40b89499fd", - ); - VestaCurve::create_point_from_affine(x, y).unwrap() - } - - #[test] - fn adding_five_times_point_1_works() { - let point_1 = point_1(); - let point_1_times_5 = point_1_times_5(); - assert_eq!(point_1.operate_with_self(5_u16), point_1_times_5); - } - - #[test] - fn create_valid_point_works() { - let p = point_1(); - assert_eq!( - *p.x(), - FE::from_hex_unchecked( - "c4e6a8789457a64e1638783181963d4c4399a5a8cdb30af4038664ce431033c" - ) - ); - assert_eq!( - *p.y(), - FE::from_hex_unchecked( - "2d8c9125be9a3ac50371e462f63dfc3fbbf645e9a93d6b7da71c13d3065e3ce5" - ) - ); - assert_eq!(*p.z(), FE::from_hex_unchecked("1")); - } - - #[test] - fn create_invalid_points_returns_an_error() { - assert_eq!( - VestaCurve::create_point_from_affine(FE::from(0), FE::from(1)), - Err(EllipticCurveError::InvalidPoint) - ); - } - - #[test] - fn equality_works() { - let g = VestaCurve::generator(); - let g2 = g.operate_with_self(2_u16); - let g2_other = g.operate_with(&g); - assert_ne!(&g2, &g); - assert_eq!(&g, &g); - assert_eq!(&g2, &g2_other); - } - - #[test] - fn g_operated_with_g_satifies_ec_equation() { - let g = VestaCurve::generator(); - let g2 = g.operate_with_self(2_u16); - - // get x and y from affine coordinates - let g2_affine = g2.to_affine(); - let x = g2_affine.x(); - let y = g2_affine.y(); - - // calculate both sides of Pallas curve equation - let five = VestaCurve::b(); - let y_sq_0 = x.pow(3_u16) + five; - let y_sq_1 = y.pow(2_u16); - - assert_eq!(y_sq_0, y_sq_1); - } - - #[test] - fn operate_with_self_works_1() { - let g = VestaCurve::generator(); - assert_eq!( - g.operate_with(&g).operate_with(&g), - g.operate_with_self(3_u16) - ); - } -} diff --git a/crates/math/src/elliptic_curve/short_weierstrass/curves/vesta/mod.rs b/crates/math/src/elliptic_curve/short_weierstrass/curves/vesta/mod.rs deleted file mode 100644 index 201a862ce..000000000 --- a/crates/math/src/elliptic_curve/short_weierstrass/curves/vesta/mod.rs +++ /dev/null @@ -1 +0,0 @@ -pub mod curve; diff --git a/crates/math/src/elliptic_curve/short_weierstrass/errors.rs b/crates/math/src/elliptic_curve/short_weierstrass/errors.rs deleted file mode 100644 index e5fc78b6d..000000000 --- a/crates/math/src/elliptic_curve/short_weierstrass/errors.rs +++ /dev/null @@ -1,10 +0,0 @@ -use crate::errors::{ByteConversionError, DeserializationError}; - -impl From for DeserializationError { - fn from(error: ByteConversionError) -> Self { - match error { - ByteConversionError::FromBEBytesError => DeserializationError::FieldFromBytesError, - ByteConversionError::FromLEBytesError => DeserializationError::FieldFromBytesError, - } - } -} diff --git a/crates/math/src/elliptic_curve/short_weierstrass/mod.rs b/crates/math/src/elliptic_curve/short_weierstrass/mod.rs deleted file mode 100644 index 98b6f53c9..000000000 --- a/crates/math/src/elliptic_curve/short_weierstrass/mod.rs +++ /dev/null @@ -1,6 +0,0 @@ -/// Implementation of particular cases of elliptic curves. -pub mod curves; -/// Structs for points -pub mod point; -/// Common behaviour for Elliptic curves. -pub mod traits; diff --git a/crates/math/src/elliptic_curve/short_weierstrass/point.rs b/crates/math/src/elliptic_curve/short_weierstrass/point.rs deleted file mode 100644 index b384d5291..000000000 --- a/crates/math/src/elliptic_curve/short_weierstrass/point.rs +++ /dev/null @@ -1,1001 +0,0 @@ -use crate::{ - cyclic_group::IsGroup, - elliptic_curve::{ - point::{JacobianPoint, ProjectivePoint}, - traits::{EllipticCurveError, FromAffine, IsEllipticCurve}, - }, - errors::DeserializationError, - field::element::FieldElement, - traits::{ByteConversion, Deserializable}, -}; - -use super::traits::IsShortWeierstrass; - -#[cfg(feature = "alloc")] -use crate::traits::AsBytes; -#[cfg(feature = "alloc")] -use alloc::vec::Vec; - -#[derive(Clone, Debug)] -pub struct ShortWeierstrassProjectivePoint(ProjectivePoint); - -impl ShortWeierstrassProjectivePoint { - /// Creates an elliptic curve point giving the projective [x: y: z] coordinates. - pub fn new(value: [FieldElement; 3]) -> Result { - let (x, y, z) = (&value[0], &value[1], &value[2]); - if z != &FieldElement::::zero() - && E::defining_equation_projective(x, y, z) == FieldElement::::zero() - { - Ok(Self(ProjectivePoint::new(value))) - // The point at infinity is (0, 1, 0) - // We convert every (0, _, 0) into the infinity. - } else if x == &FieldElement::::zero() - && z == &FieldElement::::zero() - { - Ok(Self(ProjectivePoint::new([ - FieldElement::::zero(), - FieldElement::::one(), - FieldElement::::zero(), - ]))) - } else { - Err(EllipticCurveError::InvalidPoint) - } - } - - /// Creates an elliptic curve point giving the projective [x: y: z] coordinates without - /// checking that the point satisfies the curve equation. - pub const fn new_unchecked(value: [FieldElement; 3]) -> Self { - // SAFETY: The caller MUST ensure that [x:y:z] represents valid point on the - // curve. Passing arbitrary coordinates here can violate the invariant - // and produce silently incorrect results in subsequent operations. - Self(ProjectivePoint::new(value)) - } - - /// Changes the point coordinates without checking that it satisfies the curve equation. - pub fn set_unchecked(&mut self, value: [FieldElement; 3]) { - // SAFETY: The caller MUST ensure that the provided coordinates represent a valid curve - // point. Setting invalid coordinates may lead to silently incorrect computations later on. - self.0.value = value - } - - /// Returns the `x` coordinate of the point. - pub fn x(&self) -> &FieldElement { - self.0.x() - } - - /// Returns the `y` coordinate of the point. - pub fn y(&self) -> &FieldElement { - self.0.y() - } - - /// Returns the `z` coordinate of the point. - pub fn z(&self) -> &FieldElement { - self.0.z() - } - - /// Returns a tuple [x, y, z] with the coordinates of the point. - pub fn coordinates(&self) -> &[FieldElement; 3] { - self.0.coordinates() - } - - /// Returns the affine representation of the point [x, y, 1] - pub fn to_affine(&self) -> Self { - Self(self.0.to_affine()) - } - - /// Performs the group operation between a point and itself a + a = 2a in - /// additive notation - pub fn double(&self) -> Self { - if self.is_neutral_element() { - return self.clone(); - } - let [px, py, pz] = self.coordinates(); - - let px_square = px * px; - let three_px_square = &px_square + &px_square + &px_square; - let w = E::a() * pz * pz + three_px_square; - let w_square = &w * &w; - - let s = py * pz; - let s_square = &s * &s; - let s_cube = &s * &s_square; - let two_s_cube = &s_cube + &s_cube; - let four_s_cube = &two_s_cube + &two_s_cube; - let eight_s_cube = &four_s_cube + &four_s_cube; - - let b = px * py * &s; - let two_b = &b + &b; - let four_b = &two_b + &two_b; - let eight_b = &four_b + &four_b; - - let h = &w_square - eight_b; - let hs = &h * &s; - - let pys_square = py * py * s_square; - let two_pys_square = &pys_square + &pys_square; - let four_pys_square = &two_pys_square + &two_pys_square; - let eight_pys_square = &four_pys_square + &four_pys_square; - - let xp = &hs + &hs; - let yp = w * (four_b - &h) - eight_pys_square; - let zp = eight_s_cube; - - debug_assert_eq!( - E::defining_equation_projective(&xp, &yp, &zp), - FieldElement::::zero() - ); - // SAFETY: The values `x_p, y_p, z_p` are computed correctly to be on the curve. - // The assertion above verifies that the resulting point is valid. - Self::new_unchecked([xp, yp, zp]) - } - // https://hyperelliptic.org/EFD/g1p/data/shortw/projective/addition/madd-1998-cmo - /// More efficient than operate_with, but must ensure that other is in affine form - pub fn operate_with_affine(&self, other: &Self) -> Self { - if self.is_neutral_element() { - return other.clone(); - } - if other.is_neutral_element() { - return self.clone(); - } - - let [px, py, pz] = self.coordinates(); - let [qx, qy, _qz] = other.coordinates(); - let u = qy * pz; - let v = qx * pz; - - if u == *py { - if v != *px || *py == FieldElement::zero() { - // SAFETY: The point (0, 1, 0) is defined as the point at infinity. - return Self::new_unchecked([ - FieldElement::zero(), - FieldElement::one(), - FieldElement::zero(), - ]); - } else { - return self.double(); - } - } - - let u = &u - py; - let v = &v - px; - let vv = &v * &v; - let uu = &u * &u; - let vvv = &v * &vv; - let r = &vv * px; - let a = &uu * pz - &vvv - &r - &r; - - let x = &v * &a; - let y = &u * (&r - &a) - &vvv * py; - let z = &vvv * pz; - - debug_assert_eq!( - E::defining_equation_projective(&x, &y, &z), - FieldElement::::zero() - ); - // SAFETY: The values `x, y, z` are computed correctly to be on the curve. - // The assertion above verifies that the resulting point is valid. - Self::new_unchecked([x, y, z]) - } -} - -impl PartialEq for ShortWeierstrassProjectivePoint { - fn eq(&self, other: &Self) -> bool { - self.0 == other.0 - } -} - -impl Eq for ShortWeierstrassProjectivePoint {} - -impl FromAffine for ShortWeierstrassProjectivePoint { - fn from_affine( - x: FieldElement, - y: FieldElement, - ) -> Result { - let coordinates = [x, y, FieldElement::one()]; - ShortWeierstrassProjectivePoint::new(coordinates) - } -} - -impl IsGroup for ShortWeierstrassProjectivePoint { - /// The point at infinity. - fn neutral_element() -> Self { - // SAFETY: - // - `(0, 1, 0)` is **mathematically valid** as the neutral element. - Self::new_unchecked([ - FieldElement::zero(), - FieldElement::one(), - FieldElement::zero(), - ]) - } - - fn is_neutral_element(&self) -> bool { - let pz = self.z(); - pz == &FieldElement::zero() - } - - /// Computes the addition of `self` and `other`. - /// Taken from "Moonmath" (Algorithm 7, page 89) - fn operate_with(&self, other: &Self) -> Self { - if other.is_neutral_element() { - self.clone() - } else if self.is_neutral_element() { - other.clone() - } else { - let [px, py, pz] = self.coordinates(); - let [qx, qy, qz] = other.coordinates(); - let u1 = qy * pz; - let u2 = py * qz; - let v1 = qx * pz; - let v2 = px * qz; - if v1 == v2 { - if u1 != u2 || *py == FieldElement::zero() { - Self::neutral_element() - } else { - self.double() - } - } else { - let u = u1 - &u2; - let v = v1 - &v2; - let w = pz * qz; - - let u_square = &u * &u; - let v_square = &v * &v; - let v_cube = &v * &v_square; - let v_square_v2 = &v_square * &v2; - - let a = &u_square * &w - &v_cube - (&v_square_v2 + &v_square_v2); - - let xp = &v * &a; - let yp = u * (&v_square_v2 - a) - &v_cube * u2; - let zp = &v_cube * w; - - debug_assert_eq!( - E::defining_equation_projective(&xp, &yp, &zp), - FieldElement::::zero() - ); - // SAFETY: The values `x_p, y_p, z_p` are computed correctly to be on the curve. - // The assertion above verifies that the resulting point is valid. - Self::new_unchecked([xp, yp, zp]) - } - } - } - - /// Returns the additive inverse of the projective point `p` - fn neg(&self) -> Self { - let [px, py, pz] = self.coordinates(); - // SAFETY: - // - Negating `y` maintains the curve structure. - Self::new_unchecked([px.clone(), -py, pz.clone()]) - } -} - -#[derive(PartialEq)] -pub enum PointFormat { - Projective, - Uncompressed, - // Compressed, -} - -#[derive(PartialEq)] -/// Describes the endianess of the internal types of the types -/// For example, in a field made with limbs of u64 -/// this is the endianess of those u64 -pub enum Endianness { - BigEndian, - LittleEndian, -} - -impl ShortWeierstrassProjectivePoint -where - E: IsShortWeierstrass, - FieldElement: ByteConversion, -{ - /// Serialize the points in the given format - #[cfg(feature = "alloc")] - pub fn serialize(&self, point_format: PointFormat, endianness: Endianness) -> Vec { - // TODO: Add more compact serialization formats - // Uncompressed affine / Compressed - - let mut bytes: Vec = Vec::new(); - let x_bytes: Vec; - let y_bytes: Vec; - let z_bytes: Vec; - - match point_format { - PointFormat::Projective => { - let [x, y, z] = self.coordinates(); - if endianness == Endianness::BigEndian { - x_bytes = x.to_bytes_be(); - y_bytes = y.to_bytes_be(); - z_bytes = z.to_bytes_be(); - } else { - x_bytes = x.to_bytes_le(); - y_bytes = y.to_bytes_le(); - z_bytes = z.to_bytes_le(); - } - bytes.extend(&x_bytes); - bytes.extend(&y_bytes); - bytes.extend(&z_bytes); - } - PointFormat::Uncompressed => { - let affine_representation = self.to_affine(); - let [x, y, _z] = affine_representation.coordinates(); - if endianness == Endianness::BigEndian { - x_bytes = x.to_bytes_be(); - y_bytes = y.to_bytes_be(); - } else { - x_bytes = x.to_bytes_le(); - y_bytes = y.to_bytes_le(); - } - bytes.extend(&x_bytes); - bytes.extend(&y_bytes); - } - } - bytes - } - - pub fn deserialize( - bytes: &[u8], - point_format: PointFormat, - endianness: Endianness, - ) -> Result { - match point_format { - PointFormat::Projective => { - if !bytes.len().is_multiple_of(3) { - return Err(DeserializationError::InvalidAmountOfBytes); - } - - let len = bytes.len() / 3; - let x: FieldElement; - let y: FieldElement; - let z: FieldElement; - - if endianness == Endianness::BigEndian { - x = ByteConversion::from_bytes_be(&bytes[..len])?; - y = ByteConversion::from_bytes_be(&bytes[len..len * 2])?; - z = ByteConversion::from_bytes_be(&bytes[len * 2..])?; - } else { - x = ByteConversion::from_bytes_le(&bytes[..len])?; - y = ByteConversion::from_bytes_le(&bytes[len..len * 2])?; - z = ByteConversion::from_bytes_le(&bytes[len * 2..])?; - } - - let Ok(z_inv) = z.inv() else { - let point = Self::new([x, y, z]) - .map_err(|_| DeserializationError::FieldFromBytesError)?; - return if point.is_neutral_element() { - Ok(point) - } else { - Err(DeserializationError::FieldFromBytesError) - }; - }; - let x_affine = &x * &z_inv; - let y_affine = &y * &z_inv; - if E::defining_equation(&x_affine, &y_affine) == FieldElement::zero() { - Self::new([x, y, z]).map_err(|_| DeserializationError::FieldFromBytesError) - } else { - Err(DeserializationError::FieldFromBytesError) - } - } - PointFormat::Uncompressed => { - if !bytes.len().is_multiple_of(2) { - return Err(DeserializationError::InvalidAmountOfBytes); - } - - let len = bytes.len() / 2; - let x: FieldElement; - let y: FieldElement; - - if endianness == Endianness::BigEndian { - x = ByteConversion::from_bytes_be(&bytes[..len])?; - y = ByteConversion::from_bytes_be(&bytes[len..])?; - } else { - x = ByteConversion::from_bytes_le(&bytes[..len])?; - y = ByteConversion::from_bytes_le(&bytes[len..])?; - } - - let z = FieldElement::::one(); - let point = - Self::new([x, y, z]).map_err(|_| DeserializationError::FieldFromBytesError)?; - Ok(point) - } - } - } -} - -#[cfg(feature = "alloc")] -impl AsBytes for ShortWeierstrassProjectivePoint -where - E: IsShortWeierstrass, - FieldElement: ByteConversion, -{ - fn as_bytes(&self) -> alloc::vec::Vec { - self.serialize(PointFormat::Projective, Endianness::LittleEndian) - } -} - -#[cfg(feature = "alloc")] -impl From> for alloc::vec::Vec -where - E: IsShortWeierstrass, - FieldElement: ByteConversion, -{ - fn from(value: ShortWeierstrassProjectivePoint) -> Self { - value.as_bytes() - } -} - -impl Deserializable for ShortWeierstrassProjectivePoint -where - E: IsShortWeierstrass, - FieldElement: ByteConversion, -{ - fn deserialize(bytes: &[u8]) -> Result - where - Self: Sized, - { - Self::deserialize(bytes, PointFormat::Projective, Endianness::LittleEndian) - } -} - -#[derive(Clone, Debug)] -pub struct ShortWeierstrassJacobianPoint(pub JacobianPoint); - -impl ShortWeierstrassJacobianPoint { - /// Creates an elliptic curve point giving the jacobian [x: y: z] coordinates. - pub fn new(value: [FieldElement; 3]) -> Result { - let (x, y, z) = (&value[0], &value[1], &value[2]); - - if z != &FieldElement::::zero() - && E::defining_equation_jacobian(x, y, z) == FieldElement::::zero() - { - Ok(Self(JacobianPoint::new(value))) - // The point at infinity is (1, 1, 0) - // We convert every (x, x, 0) into the infinity. - } else if z == &FieldElement::::zero() && x == y { - Ok(Self(JacobianPoint::new([ - FieldElement::::one(), - FieldElement::::one(), - FieldElement::::zero(), - ]))) - } else { - Err(EllipticCurveError::InvalidPoint) - } - } - - /// Creates an elliptic curve point giving the projective [x: y: z] coordinates without - /// checking that the point satisfies the curve equation. - pub const fn new_unchecked(value: [FieldElement; 3]) -> Self { - // SAFETY: The caller MUST ensure that [x:y:z] represents either a valid point on the - // curve. Passing arbitrary coordinates here can violate the invariant - // and produce silently incorrect results in subsequent operations. - Self(JacobianPoint::new(value)) - } - - /// Returns the `x` coordinate of the point. - pub fn x(&self) -> &FieldElement { - self.0.x() - } - - /// Returns the `y` coordinate of the point. - pub fn y(&self) -> &FieldElement { - self.0.y() - } - - /// Returns the `z` coordinate of the point. - pub fn z(&self) -> &FieldElement { - self.0.z() - } - - /// Returns a tuple [x, y, z] with the coordinates of the point. - pub fn coordinates(&self) -> &[FieldElement; 3] { - self.0.coordinates() - } - - /// Creates the same point in affine coordinates. That is, - /// returns [x / z^2: y / z^3: 1] where `self` is [x: y: z]. - /// Panics if `self` is the point at infinity. - pub fn to_affine(&self) -> Self { - Self(self.0.to_affine()) - } - - /// Applies the group operation between a point and itself - pub fn double(&self) -> Self { - if self.is_neutral_element() { - return self.clone(); - } - let [x1, y1, z1] = self.coordinates(); - //http://www.hyperelliptic.org/EFD/g1p/data/shortw/jacobian-0/doubling/dbl-2009-l - - if E::a() == FieldElement::zero() { - let a = x1.square(); // A = x1^2 - let b = y1.square(); // B = y1^2 - let c = b.square(); // C = B^2 - let x1_plus_b = x1 + &b; // (X1 + B) - let x1_plus_b_square = x1_plus_b.square(); // (X1 + B)^2 - let d = (&x1_plus_b_square - &a - &c).double(); // D = 2 * ((X1 + B)^2 - A - C) - let e = &a.double() + &a; // E = 3 * A - let f = e.square(); // F = E^2 - let x3 = &f - &d.double(); // X3 = F - 2 * D - let y3 = &e * (&d - &x3) - &c.double().double().double(); // Y3 = E * (D - X3) - 8 * C - let z3 = (y1 * z1).double(); // Z3 = 2 * Y1 * Z1 - - debug_assert_eq!( - E::defining_equation_jacobian(&x3, &y3, &z3), - FieldElement::::zero() - ); - // SAFETY: The values `x_3, y_3, z_3` are computed correctly to be on the curve. - // The assertion above verifies that the resulting point is valid. - Self::new_unchecked([x3, y3, z3]) - } else { - // http://www.hyperelliptic.org/EFD/g1p/data/shortw/jacobian-0/doubling/dbl-2009-alnr - // http://www.hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-0.html#doubling-dbl-2009-l - let xx = x1.square(); // XX = X1^2 - let yy = y1.square(); // YY = Y1^2 - let yyyy = yy.square(); // YYYY = YY^2 - let zz = z1.square(); // ZZ = Z1^2 - let s = ((x1 + &yy).square() - &xx - &yyyy).double(); // S = 2 * ((X1 + YY)^2 - XX - YYYY) - let m = &xx.double() + &xx + &E::a() * &zz.square(); // M = 3 * XX + a * ZZ^2 - let x3 = m.square() - &s.double(); // X3 = M^2 - 2 * S - let y3 = m * (&s - &x3) - &yyyy.double().double().double(); // Y3 = M * (S - X3) - 8 * YYYY - let z3 = (y1 + z1).square() - &yy - &zz; // Z3 = (Y1 + Z1)^2 - YY - ZZ - - debug_assert_eq!( - E::defining_equation_jacobian(&x3, &y3, &z3), - FieldElement::::zero() - ); - // SAFETY: The values `x_3, y_3, z_3` are computed correctly to be on the curve. - // The assertion above verifies that the resulting point is valid. - Self::new_unchecked([x3, y3, z3]) - } - } - - /// More efficient than operate_with. Other should be in affine form! - pub fn operate_with_affine(&self, other: &Self) -> Self { - let [x1, y1, z1] = self.coordinates(); - let [x2, y2, _z2] = other.coordinates(); - - if self.is_neutral_element() { - return other.clone(); - } - if other.is_neutral_element() { - return self.clone(); - } - - let z1z1 = z1.square(); - let u1 = x1; - let u2 = x2 * &z1z1; - let s1 = y1; - let s2 = y2 * z1 * &z1z1; - - if *u1 == u2 { - if *s1 == s2 { - self.double() // Is the same point - } else { - Self::neutral_element() // P + (-P) = 0 - } - } else { - let h = &u2 - u1; - let hh = h.square(); - let hhh = &h * &hh; - let r = &s2 - s1; - let v = u1 * &hh; - let x3 = r.square() - (&hhh + &v + &v); - let y3 = r * (&v - &x3) - s1 * &hhh; - let z3 = z1 * &h; - - debug_assert_eq!( - E::defining_equation_jacobian(&x3, &y3, &z3), - FieldElement::::zero() - ); - // SAFETY: The values `x_3, y_3, z_3` are computed correctly to be on the curve. - // The assertion above verifies that the resulting point is valid. - Self::new_unchecked([x3, y3, z3]) - } - } -} - -impl PartialEq for ShortWeierstrassJacobianPoint { - fn eq(&self, other: &Self) -> bool { - self.0 == other.0 - } -} - -impl Eq for ShortWeierstrassJacobianPoint {} - -impl FromAffine for ShortWeierstrassJacobianPoint { - fn from_affine( - x: FieldElement, - y: FieldElement, - ) -> Result { - let coordinates = [x, y, FieldElement::one()]; - ShortWeierstrassJacobianPoint::new(coordinates) - } -} - -impl IsGroup for ShortWeierstrassJacobianPoint { - /// The point at infinity. - fn neutral_element() -> Self { - // SAFETY: - // - `(1, 1, 0)` is **mathematically valid** as the neutral element. - Self::new_unchecked([ - FieldElement::one(), - FieldElement::one(), - FieldElement::zero(), - ]) - } - - fn is_neutral_element(&self) -> bool { - let pz = self.z(); - pz == &FieldElement::zero() - } - - /// Computes the addition of `self` and `other`. - /// https://github.com/mratsim/constantine/blob/65147ed815d96fa94a05d307c1d9980877b7d0e8/constantine/math/elliptic/ec_shortweierstrass_jacobian.md - fn operate_with(&self, other: &Self) -> Self { - if self.is_neutral_element() { - return other.clone(); - } - - if other.is_neutral_element() { - return self.clone(); - } - - let [x1, y1, z1] = self.coordinates(); - let [x2, y2, z2] = other.coordinates(); - - let z1_sq = z1.square(); // Z1^2 - let z2_sq = z2.square(); // Z2^2 - - let u1 = x1 * &z2_sq; // U1 = X1 * Z2^2 - let u2 = x2 * &z1_sq; // U2 = X2 * Z1^2 - - let z1_cu = z1 * &z1_sq; // Z1^3 - let z2_cu = z2 * &z2_sq; // Z2^3 - - let s1 = y1 * &z2_cu; // S1 = Y1 * Z2^3 - let s2 = y2 * &z1_cu; // S2 = Y2 * Z1^3 - - if u1 == u2 { - if s1 == s2 { - return self.double(); // P + P = 2P - } else { - return Self::neutral_element(); // P + (-P) = 0 - } - } - // H = U2 - U1 - let h = u2 - &u1; - // I = (2 * H)^2 - let i = h.double().square(); - // J = H * I - let j = -(&h * &i); - - // R = 2 * (S2 - S1) - let r = (s2 - &s1).double(); - - // V = U1 * I - let v = u1 * &i; - - // X3 = R^2 + J - 2 * V - let x3 = r.square() + &j - v.double(); - - // Y3 = R * (V - X3) + 2 * S1 * J - let y3 = r * (v - &x3) + (s1 * &j.double()); - - // Z3 = 2 * Z1 * Z2 * H - let z3 = z1 * z2; - let z3 = z3.double() * h; - - debug_assert_eq!( - E::defining_equation_jacobian(&x3, &y3, &z3), - FieldElement::::zero() - ); - // SAFETY: The values `x_3, y_3, z_3` are computed correctly to be on the curve. - // The assertion above verifies that the resulting point is valid. - Self::new_unchecked([x3, y3, z3]) - } - - /// Returns the additive inverse of the jacobian point `p` - fn neg(&self) -> Self { - let [x, y, z] = self.coordinates(); - // SAFETY: - // - The negation formula for Short Weierstrass curves is well-defined. - // - The result remains a valid curve point. - Self::new_unchecked([x.clone(), -y, z.clone()]) - } -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::elliptic_curve::short_weierstrass::curves::bls12_381::curve::BLS12381Curve; - - use crate::elliptic_curve::short_weierstrass::curves::bls12_381::curve::{ - CURVE_COFACTOR, SUBGROUP_ORDER, - }; - #[cfg(feature = "alloc")] - use crate::{ - elliptic_curve::short_weierstrass::curves::bls12_381::field_extension::BLS12381PrimeField, - field::element::FieldElement, - }; - - #[cfg(feature = "alloc")] - #[allow(clippy::upper_case_acronyms)] - type FEE = FieldElement; - - #[cfg(feature = "alloc")] - fn point() -> ShortWeierstrassProjectivePoint { - let x = FEE::new_base("36bb494facde72d0da5c770c4b16d9b2d45cfdc27604a25a1a80b020798e5b0dbd4c6d939a8f8820f042a29ce552ee5"); - let y = FEE::new_base("7acf6e49cc000ff53b06ee1d27056734019c0a1edfa16684da41ebb0c56750f73bc1b0eae4c6c241808a5e485af0ba0"); - BLS12381Curve::create_point_from_affine(x, y).unwrap() - } - - #[cfg(feature = "alloc")] - #[test] - fn operate_with_works_jacobian() { - let x = FEE::new_base("36bb494facde72d0da5c770c4b16d9b2d45cfdc27604a25a1a80b020798e5b0dbd4c6d939a8f8820f042a29ce552ee5"); - let y = FEE::new_base("7acf6e49cc000ff53b06ee1d27056734019c0a1edfa16684da41ebb0c56750f73bc1b0eae4c6c241808a5e485af0ba0"); - let p = ShortWeierstrassJacobianPoint::::from_affine(x, y).unwrap(); - - assert_eq!(p.operate_with(&p), p.double()); - } - - #[cfg(feature = "alloc")] - #[test] - fn operate_with_self_works_jacobian() { - let x = FEE::new_base("36bb494facde72d0da5c770c4b16d9b2d45cfdc27604a25a1a80b020798e5b0dbd4c6d939a8f8820f042a29ce552ee5"); - let y = FEE::new_base("7acf6e49cc000ff53b06ee1d27056734019c0a1edfa16684da41ebb0c56750f73bc1b0eae4c6c241808a5e485af0ba0"); - let p = ShortWeierstrassJacobianPoint::::from_affine(x, y).unwrap(); - - assert_eq!( - p.operate_with_self(5_u16), - p.double().double().operate_with(&p) - ); - } - #[cfg(feature = "alloc")] - #[test] - fn byte_conversion_from_and_to_be_projective() { - let expected_point = point(); - let bytes_be = expected_point.serialize(PointFormat::Projective, Endianness::BigEndian); - - let result = ShortWeierstrassProjectivePoint::deserialize( - &bytes_be, - PointFormat::Projective, - Endianness::BigEndian, - ); - assert_eq!(expected_point, result.unwrap()); - } - - #[cfg(feature = "alloc")] - #[test] - fn byte_conversion_from_and_to_be_uncompressed() { - let expected_point = point(); - let bytes_be = expected_point.serialize(PointFormat::Uncompressed, Endianness::BigEndian); - let result = ShortWeierstrassProjectivePoint::deserialize( - &bytes_be, - PointFormat::Uncompressed, - Endianness::BigEndian, - ); - assert_eq!(expected_point, result.unwrap()); - } - - #[cfg(feature = "alloc")] - #[test] - fn byte_conversion_from_and_to_le_projective() { - let expected_point = point(); - let bytes_be = expected_point.serialize(PointFormat::Projective, Endianness::LittleEndian); - - let result = ShortWeierstrassProjectivePoint::deserialize( - &bytes_be, - PointFormat::Projective, - Endianness::LittleEndian, - ); - assert_eq!(expected_point, result.unwrap()); - } - - #[cfg(feature = "alloc")] - #[test] - fn byte_conversion_from_and_to_le_uncompressed() { - let expected_point = point(); - let bytes_be = - expected_point.serialize(PointFormat::Uncompressed, Endianness::LittleEndian); - - let result = ShortWeierstrassProjectivePoint::deserialize( - &bytes_be, - PointFormat::Uncompressed, - Endianness::LittleEndian, - ); - assert_eq!(expected_point, result.unwrap()); - } - - #[cfg(feature = "alloc")] - #[test] - fn byte_conversion_from_and_to_with_mixed_le_and_be_does_not_work_projective() { - let bytes = point().serialize(PointFormat::Projective, Endianness::LittleEndian); - - let result = ShortWeierstrassProjectivePoint::::deserialize( - &bytes, - PointFormat::Projective, - Endianness::BigEndian, - ); - - assert_eq!( - result.unwrap_err(), - DeserializationError::FieldFromBytesError - ); - } - - #[cfg(feature = "alloc")] - #[test] - fn byte_conversion_from_and_to_with_mixed_le_and_be_does_not_work_uncompressed() { - let bytes = point().serialize(PointFormat::Uncompressed, Endianness::LittleEndian); - - let result = ShortWeierstrassProjectivePoint::::deserialize( - &bytes, - PointFormat::Uncompressed, - Endianness::BigEndian, - ); - - assert_eq!( - result.unwrap_err(), - DeserializationError::FieldFromBytesError - ); - } - - #[cfg(feature = "alloc")] - #[test] - fn byte_conversion_from_and_to_with_mixed_be_and_le_does_not_work_projective() { - let bytes = point().serialize(PointFormat::Projective, Endianness::BigEndian); - - let result = ShortWeierstrassProjectivePoint::::deserialize( - &bytes, - PointFormat::Projective, - Endianness::LittleEndian, - ); - - assert_eq!( - result.unwrap_err(), - DeserializationError::FieldFromBytesError - ); - } - - #[cfg(feature = "alloc")] - #[test] - fn byte_conversion_from_and_to_with_mixed_be_and_le_does_not_work_uncompressed() { - let bytes = point().serialize(PointFormat::Uncompressed, Endianness::BigEndian); - - let result = ShortWeierstrassProjectivePoint::::deserialize( - &bytes, - PointFormat::Uncompressed, - Endianness::LittleEndian, - ); - - assert_eq!( - result.unwrap_err(), - DeserializationError::FieldFromBytesError - ); - } - - #[test] - fn cannot_create_point_from_wrong_number_of_bytes_le_projective() { - let bytes = &[0_u8; 13]; - - let result = ShortWeierstrassProjectivePoint::::deserialize( - bytes, - PointFormat::Projective, - Endianness::LittleEndian, - ); - - assert_eq!( - result.unwrap_err(), - DeserializationError::InvalidAmountOfBytes - ); - } - - #[test] - fn cannot_create_point_from_wrong_number_of_bytes_le_uncompressed() { - let bytes = &[0_u8; 13]; - - let result = ShortWeierstrassProjectivePoint::::deserialize( - bytes, - PointFormat::Uncompressed, - Endianness::LittleEndian, - ); - - assert_eq!( - result.unwrap_err(), - DeserializationError::InvalidAmountOfBytes - ); - } - - #[test] - fn cannot_create_point_from_wrong_number_of_bytes_be_projective() { - let bytes = &[0_u8; 13]; - - let result = ShortWeierstrassProjectivePoint::::deserialize( - bytes, - PointFormat::Projective, - Endianness::BigEndian, - ); - - assert_eq!( - result.unwrap_err(), - DeserializationError::InvalidAmountOfBytes - ); - } - - #[test] - fn cannot_create_point_from_wrong_number_of_bytes_be_uncompressed() { - let bytes = &[0_u8; 13]; - - let result = ShortWeierstrassProjectivePoint::::deserialize( - bytes, - PointFormat::Uncompressed, - Endianness::BigEndian, - ); - - assert_eq!( - result.unwrap_err(), - DeserializationError::InvalidAmountOfBytes - ); - } - - #[test] - fn test_jacobian_vs_projective_operation() { - let x = FEE::new_base("36bb494facde72d0da5c770c4b16d9b2d45cfdc27604a25a1a80b020798e5b0dbd4c6d939a8f8820f042a29ce552ee5"); - let y = FEE::new_base("7acf6e49cc000ff53b06ee1d27056734019c0a1edfa16684da41ebb0c56750f73bc1b0eae4c6c241808a5e485af0ba0"); - - let p = ShortWeierstrassJacobianPoint::::from_affine(x.clone(), y.clone()) - .unwrap(); - let q = ShortWeierstrassProjectivePoint::::from_affine(x, y).unwrap(); - - let sum_jacobian = p.operate_with_self(7_u16); - let sum_projective = q.operate_with_self(7_u16); - - // Convert the result to affine coordinates - let sum_jacobian_affine = sum_jacobian.to_affine(); - let [x_j, y_j, _] = sum_jacobian_affine.coordinates(); - - // Convert the result to affine coordinates - let binding = sum_projective.to_affine(); - let [x_p, y_p, _] = binding.coordinates(); - - assert_eq!(x_j, x_p, "x coordintates do not match"); - assert_eq!(y_j, y_p, "y coordinates do not match"); - } - - #[test] - fn test_multiplication_by_order_projective() { - let x = FEE::new_base("36bb494facde72d0da5c770c4b16d9b2d45cfdc27604a25a1a80b020798e5b0dbd4c6d939a8f8820f042a29ce552ee5"); - let y = FEE::new_base("7acf6e49cc000ff53b06ee1d27056734019c0a1edfa16684da41ebb0c56750f73bc1b0eae4c6c241808a5e485af0ba0"); - - let p = ShortWeierstrassProjectivePoint::::from_affine(x.clone(), y.clone()) - .unwrap(); - - let g = p - .operate_with_self(SUBGROUP_ORDER) - .operate_with_self(CURVE_COFACTOR); - - assert!( - g.is_neutral_element(), - "Multiplication by order should result in the neutral element" - ); - } - - #[test] - fn test_multiplication_by_order_jacobian() { - let x = FEE::new_base("36bb494facde72d0da5c770c4b16d9b2d45cfdc27604a25a1a80b020798e5b0dbd4c6d939a8f8820f042a29ce552ee5"); - let y = FEE::new_base("7acf6e49cc000ff53b06ee1d27056734019c0a1edfa16684da41ebb0c56750f73bc1b0eae4c6c241808a5e485af0ba0"); - - let p = ShortWeierstrassJacobianPoint::::from_affine(x.clone(), y.clone()) - .unwrap(); - let g = p - .operate_with_self(SUBGROUP_ORDER) - .operate_with_self(CURVE_COFACTOR); - - assert!( - g.is_neutral_element(), - "Multiplication by order should result in the neutral element" - ); - } -} diff --git a/crates/math/src/elliptic_curve/short_weierstrass/traits.rs b/crates/math/src/elliptic_curve/short_weierstrass/traits.rs deleted file mode 100644 index 79c0a48af..000000000 --- a/crates/math/src/elliptic_curve/short_weierstrass/traits.rs +++ /dev/null @@ -1,75 +0,0 @@ -use crate::cyclic_group::IsGroup; -use crate::elliptic_curve::traits::IsEllipticCurve; -use crate::field::element::FieldElement; -use core::fmt::Debug; - -/// Trait to add elliptic curves behaviour to a struct. -/// We use the short Weierstrass form equation: `y^2 = x^3 + a * x + b`. -pub trait IsShortWeierstrass: IsEllipticCurve + Clone + Debug { - /// `a` coefficient for the equation `y^2 = x^3 + a * x + b`. - fn a() -> FieldElement; - - /// `b` coefficient for the equation `y^2 = x^3 + a * x + b`. - fn b() -> FieldElement; - - /// Evaluates the residual of the Short Weierstrass equation - /// R = y^2 - x^3 - ax - b - /// If R == 0, then the point is in the curve - /// Use in affine coordinates - fn defining_equation( - x: &FieldElement, - y: &FieldElement, - ) -> FieldElement { - y.square() - ((x.square() + Self::a()) * x + Self::b()) - } - - // Evaluates the projective equation: - // y^2 * z = x^3 + a * x * z^2 + b * z^3 - fn defining_equation_projective( - x: &FieldElement, - y: &FieldElement, - z: &FieldElement, - ) -> FieldElement { - y.square() * z - ((x.square() + Self::a() * z.square()) * x + Self::b() * z.square() * z) - } - - // Evaluates the jacobian equation: - // y^2 = x^3 + a * x * z^4 + b * z^6 - fn defining_equation_jacobian( - x: &FieldElement, - y: &FieldElement, - z: &FieldElement, - ) -> FieldElement { - y.square() - - ((x.square() + Self::a() * z.square().square()) * x - + Self::b() * z.square().square() * z.square()) - } -} - -pub trait Compress { - type G1Point: IsGroup; - type G2Point: IsGroup; - type G1Compressed; - type G2Compressed; - type Error; - - /// Gives a compressed representation of a G1 point in the elliptic curve - /// Providing the x coordinate and an additional bit provides a way of retrieving the - /// y coordinate by sending less information - #[cfg(feature = "alloc")] - fn compress_g1_point(point: &Self::G1Point) -> Self::G1Compressed; - - /// Gives a compressed representation of a G2 point in the elliptic curve - /// Providing the x coordinate and an additional bit provides a way of retrieving the - /// y coordinate by sending less information - #[cfg(feature = "alloc")] - fn compress_g2_point(point: &Self::G2Point) -> Self::G2Compressed; - - /// Given a slice of bytes, representing the x coordinate plus additional bits, recovers - /// the elliptic curve point in G1 - fn decompress_g1_point(input_bytes: &mut [u8]) -> Result; - - /// Given a slice of bytes, representing the x coordinate plus additional bits, recovers - /// the elliptic curve point in G2 - fn decompress_g2_point(input_bytes: &mut [u8]) -> Result; -} diff --git a/crates/math/src/elliptic_curve/traits.rs b/crates/math/src/elliptic_curve/traits.rs deleted file mode 100644 index 0fbbd60c6..000000000 --- a/crates/math/src/elliptic_curve/traits.rs +++ /dev/null @@ -1,57 +0,0 @@ -use crate::{ - cyclic_group::IsGroup, - errors::PairingError, - field::{element::FieldElement, traits::IsField}, -}; -use core::fmt::Debug; - -#[derive(Debug, PartialEq, Eq)] -pub enum EllipticCurveError { - InvalidPoint, -} - -pub trait IsEllipticCurve { - /// BaseField is the field used for each of the coordinates of a point p - /// belonging to the curve. - type BaseField: IsField + Clone + Debug; - - /// The representation of the point. For example it can be projective - /// coordinates, affine coordinates, XYZZ, depending on the curve and its - /// possible optimizations. - type PointRepresentation: IsGroup + FromAffine; - - /// Returns the generator of the main subgroup. - fn generator() -> Self::PointRepresentation; - - /// Returns an affine point. - fn create_point_from_affine( - x: FieldElement, - y: FieldElement, - ) -> Result { - Self::PointRepresentation::from_affine(x, y) - } -} - -pub trait FromAffine: Sized { - fn from_affine(x: FieldElement, y: FieldElement) -> Result; -} - -pub trait IsPairing { - type G1Point: IsGroup; - type G2Point: IsGroup; - type OutputField: IsField; - - /// Compute the product of the pairings for a list of point pairs. - /// More efficient than just calling the pairing for each pair of points individually - fn compute_batch( - pairs: &[(&Self::G1Point, &Self::G2Point)], - ) -> Result, PairingError>; - - /// Compute the ate pairing between point `p` in G1 and `q` in G2. - fn compute( - p: &Self::G1Point, - q: &Self::G2Point, - ) -> Result, PairingError> { - Self::compute_batch(&[(p, q)]) - } -} diff --git a/crates/math/src/errors.rs b/crates/math/src/errors.rs deleted file mode 100644 index 1c65a2345..000000000 --- a/crates/math/src/errors.rs +++ /dev/null @@ -1,42 +0,0 @@ -#[derive(Debug, PartialEq, Eq)] -pub enum ByteConversionError { - FromBEBytesError, - FromLEBytesError, - InvalidValue, - PointNotInSubgroup, - ValueNotCompressed, - ValueNotReduced, -} - -#[derive(Debug, PartialEq, Eq)] -pub enum CreationError { - InvalidHexString, - InvalidDecString, - HexStringIsTooBig, - RepresentativeOutOfRange, - EmptyString, -} - -#[derive(Debug, PartialEq, Eq)] -pub enum DeserializationError { - InvalidAmountOfBytes, - FieldFromBytesError, - PointerSizeError, - InvalidValue, -} - -#[derive(Debug, PartialEq, Eq)] -pub enum PairingError { - PointNotInSubgroup, - DivisionByZero, -} - -impl From for DeserializationError { - fn from(error: ByteConversionError) -> Self { - match error { - ByteConversionError::FromBEBytesError => DeserializationError::FieldFromBytesError, - ByteConversionError::FromLEBytesError => DeserializationError::FieldFromBytesError, - _ => DeserializationError::InvalidValue, - } - } -} diff --git a/crates/math/src/fft/README.md b/crates/math/src/fft/README.md deleted file mode 100644 index 41edca22b..000000000 --- a/crates/math/src/fft/README.md +++ /dev/null @@ -1,200 +0,0 @@ -# lambdaworks Fast Fourier Transform - -This folder contains the [fast Fourier transform](https://en.wikipedia.org/wiki/Fast_Fourier_transform) (FFT) over finite fields (also known as number theoretic transform, NTT). If you are unfamiliar with how lambdaworks handles fields, see [examples](../../../../examples/README.md). Currently, the following algorithms are supported: -- Cooley-Tukey Radix-2 -- Cooley-Tukey Radix-4 - -We are also planning on adding a mixed radix algorithm. To use the FFT, the length of the vector, $n$, should be a power of $2$ (or $4$), that is, $2^m = n$. The FFT should be used with fields implementing the `IsFFTFriendly` trait. The FFT works by recursively breaking a length $n$ FFT into $2$ $n/2$ FFTs, until we reach a sufficiently small size that can be solved. The core operation of the FFT is the butterfly. To combine the elements, we need to sample the twiddle factors, which we obtain from the roots of unity. The FFT can accept the input in natural order and returns the output in reverse order (nr) or the input is in reverse order and the output is in natural order (rn). - -Since the main applications of the FFT are related to polynomial evaluation and interpolation, we provide functions describing these operations, which call the FFT under the hood: -- `evaluate_fft` -- `evaluate_offset_fft` -- `interpolate_fft` -- `interpolate_offset_fft` - -These functions can be used with [univariate polynomials](./README.md). To use the functions, -```rust -let p_1 = Polynomial::new(&[FE::new(3), FE::new(4), FE::new(5) FE::new(6)]); -let evaluations = Polynomial::evaluate_offset_fft(p_1, 4, 4, FE::new(3))?; -``` -Interpolate takes a vector of length $2^m$, which we take them to be the evaluations of a polynomial $p$ over values of $x$ of the form $\{ offset.g^0, offset.g, offset.g^2 \dots offset.g^{n - 1} \}$, where $g$ is a generator of the $n$-th roots of unity. For example, -```rust -let evaluations = [FE::new(1), FE::new(2), FE::new(3) FE::new(4)]; -let poly = Polynomial::interpolate_fft(&evaluations).unwrap(); -``` - -These building blocks are used, for example, in the computation of the trace polynomials in the STARK protocol. The following function computes the polynomials whose evaluations coincide with the trace columns: -```rust -pub fn compute_trace_polys(&self) -> Vec>> - where - S: IsFFTField + IsSubFieldOf, - FieldElement: Send + Sync, - { - let columns = self.columns(); - #[cfg(feature = "parallel")] - let iter = columns.par_iter(); - #[cfg(not(feature = "parallel"))] - let iter = columns.iter(); - - iter.map(|col| Polynomial::interpolate_fft::(col)) - .collect::>>, FFTError>>() - .unwrap() - } -``` - -## Background on the Fast-Fourier transform for finite fields - -Given a finite field $\mathbb{F}_p$ we say that $\omega$ is an r-th primitive root of unity if $\omega^r \equiv 1 \pmod{p}$ and $\omega^k \not \equiv 1 \pmod{p}$ for $k \not \equiv 0 \pmod{r}$ (this means that $\omega$'s powers are different from 1 for all $k = 1,2,... r - 1$ and $\omega$ spans a group with $r$ elements). If $n$ divides $r$, $\omega^{r/n}$ is an n-th primitive root. - -Throughout, we will work with $2^m$-th roots of unity, which have a nice structure. If $\omega$ is such a root, we see that the set formed by its powers is given by $1, \omega, \omega^2, ... , - 1, - \omega , - \omega^2 , ...$, since $\omega^{2^{m - 1}} \equiv 1 \pmod{p}$. This way, we see that we always have $b$ and $-b$ in the group, which can save a lot of time if we use these points to evaluate a polynomial. - -Take a polynomial with coefficients in $\mathbb{F}_p$, $p(x) = a_0 + a_1 x + a_2 x^2 + ... + a_n x^n$. We can write the polynomial in the following form: - -$$p(x) = (a_0 + a_2 x^2 + a_4 x^4 + ... + a_{n - 1} x^{n - 1}) + x (a_1 + a_3 x^2 + a_5 x^4 + ... + a_{n} x^{n - 1})$$ - -More compactly, - -$$p(x) = p_e (x^2 ) + x p_o (x^2 )$$ - -The original polynomial has $n + 1$ coefficients, while each of the $p_i (x^2 )$ has $(n + 1)/2$ (we will suppose that the polynomial has $n + 1 = 2^m$, if not we can pad the polynomial with zero coefficients in the highest powers). Take $b$ and $-b$: both have the same square, $b^2 = ( - b )^2 = c$, so - -$$p(b) = p_e (c) + b p_o (c)$$ - -$$p(b) = p_e (c) - b p_o (c)$$ - -This means that, for the price of evaluating $p_e (c)$ and $p_o (c)$ (each needing at most $\mathcal{O}((n + 1)/2)$ operations) we get two evaluations of $p(x)$ for the cost of one additional multiplication and addition/subtraction. Because $b$ is a $2^m$-th root of unity, $b^2$ is an $2^{m - 1}$-th root of unity. - -Since $p_e (x^2)$ and $p_o(x^2)$ are also polynomials, we can use the same trick once again. Moreover, the nice group structure we have says we need to only evaluate at half the $2^{m - 2}$ roots of unity. This provides a powerful recursive structure, where we reduce the problem to computing the evaluations of two polynomials over a smaller subset. We can do this until we get to the square roots of unity (1 and -1), where it is easy to compute and then we just recombine all the evaluations! - -We will provide an example for $n = 7$, which will be sufficient to grasp the general idea. We will denote $\omega$ a primitive 8-th root of unity and $i = \omega^2$ a primitive 4-th root of unity. We have the polynomial: - -$$p(x) = a_0 + a_1 x + a_2 x^2 + a_3 x^3 + a_4 x^4 + a_5 x^5 + a^6 x^6 + a_7 x^7$$ - -The 8th-roots of unity are $1, \omega, i, \omega^3, -1, -\omega, -i, -\omega^3$. The evaluations would be: - -$$p(1) = a_0 + a_1 + a_2 + a_3 + a_4 + a_5 + a^6 + a_7$$ - -$$p(\omega) = a_0 + a_1 \omega + a_2 i + a_3 \omega^3 + - a_4 - a_5 \omega - a^6 i - a_7 \omega^3$$ - -$$p(i) = a_0 + a_1 i - a_2 - a_3 i + a_4 + a_5 i - a^6 - a_7 i$$ - -$$p(\omega^3) = a_0 + a_1 \omega^3 - a_2 i + a_3 \omega - a_4 - a_5 \omega^3 + a^6 i - a_7 \omega$$ - -$$p(- 1) = a_0 - a_1 + a_2 - a_3 + a_4 - a_5 + a^6 - a_7$$ - -$$p(- \omega) = a_0 - a_1 \omega + a_2 i - a_3 \omega^3 + - a_4 + a_5 \omega - a^6 i + a_7 \omega^3$$ - -$$p(-i) = a_0 - a_1 i - a_2 + a_3 i + a_4 - a_5 i - a^6 + a_7 i$$ - -$$p(\omega^3) = a_0 - a_1 \omega^3 - a_2 i - a_3 \omega + - a_4 + a_5 \omega^3 + a^6 i + a_7 \omega$$ - -Let's work with the idea behind the FFT and see that we arrive at the same results (when doing the computations, we used $\omega^4 \equiv - 1 \pmod{p}$) and $i^2 \equiv -1 \pmod{p}$. The first step is breaking $p(x)$ into its odd and even parts: - -$$p_e (x^2) = a_0 + a_2 x^2 + a_4 x^4 + a_6 x^6$$ - -$$p_o (x^2) = a_1 + a_3 x^2 + a_5 x^4 + a_7 x^6$$ - -We can break them again, - -$$p_{ee} (x^4) = a_0 + a_4 x^4$$ - -$$p_{eo} (x^4) = a_2 + a_6 x^4$$ - -$$p_{oe} (x^4) = a_1 + a_5 x^4$$ - -$$p_{oo} (x^4) = a_3 + a_7 x^4$$ - -Since ${\omega^r}^4 \equiv {\omega^4}^r \equiv (-1)^r \pmod{p}$, so $x^4$ can only take two values, -1 and 1. Each of the polynomials can be easily evaluated: - -$$p_{ee} (1) = a_0 + a_4$$ - -$$p_{ee} (- 1) = a_0 - a_4$$ - -$$p_{eo} (1) = a_2 + a_6$$ - -$$p_{eo} (- 1) = a_2 - a_6$$ - -$$p_{oe} (1) = a_1 + a_5$$ - -$$p_{oe} (- 1) = a_1 - a_5$$ - -$$p_{oo} (1) = a_3 + a_7$$ - -$$p_{oo} (- 1) = a_3 - a_7$$ - -If you take a look at each pair of evaluations, they have the form: - -$$p(w) = a + w b$$ - -$$p(-w) = a - wb$$ - -These are the butterflies, where $w = 1$ in this case, which is one of the elements in the square roots of unity. We now have to go one level above, using $p_{ee} (1)$ and $p_{eo} (1)$ to obtain the evaluations of $p_e (x)$ at $x = \pm \sqrt{1}$. Remember that in the change of variables $y = x^2$ we sent both $x_0$ and $- x_0$ to the same point. Similarly, we can use $p_{ee} (- 1)$ and $p_{eo} (-1)$ to reconstruct the evaluations of $p_e (i)$ and $p_e (-i)$ since $i^2 = ( - i)^2 = - 1$. Below we are giving those 8 evaluations, four for $p_e (x)$ and four for $p_o (x)$: - -$$p_{e} (1) = (a_0 + a_4) + (a_2 + a_6)$$ - -$$p_{e} (i) = (a_0 - a_4) + i (a_2 - a_6)$$ - -$$p_{e} (- 1) = (a_0 + a_4) - (a_2 + a_6)$$ - -$$p_{e} (- i) = (a_0 - a_4) - i (a_2 - a_6)$$ - -$$p_{o} (1) = (a_1 + a_5) + (a_3 + a_7)$$ - -$$p_{o} (i ) = (a_1 - a_5) + i (a_3 - a_7)$$ - -$$p_{o} (- 1) = (a_1 + a_5) - (a_3 + a_7)$$ - -$$p_{o} (- i ) = (a_1 - a_5) - i (a_3 - a_7)$$ - -We have written things in this way so that you can clearly see the butterflies on this level. Each term from the level below is in brackets. If you look at the first and third elements, they are of the form: - -$$p_{e} (1) = a_e + b_e$$ - -$$p_{e} (- 1) = a_e - b_e$$ - -Similarly, for the second and fourth, - -$$p_{e} (i) = a_o + i b_o$$ - -$$p_{e} (- i) = a_o - i b_o$$ - -The same structure is followed by $p_o (x)$. Here, there are two different elements when combining the elements: 1 and $i$, which correspond to the $4$-th roots of unity. - -Finally, we can go one step above and combine $p_e (t^2)$ and $p_o (t^2)$ to obtain $p (t)$. We take the first and fifth elements, $p_e (1)$ and $p_o (1)$ and generate $p (1)$ and $p(- 1)$: - -$$p (1) = ((a_0 + a_4) + (a_2 + a_6)) + ((a_1 + a_5) + (a_3 + a_7))$$ - -$$p (\omega ) = ((a_0 - a_4) + i (a_2 - a_6)) + \omega ((a_1 - a_5) + i (a_3 - a_7))$$ - -$$p (i) = ((a_0 + a_4) - (a_2 + a_6)) + i ((a_1 + a_5) - (a_3 + a_7))$$ - -$$p (\omega^3) = ((a_0 - a_4) - i (a_2 - a_6)) + \omega^3 ((a_1 - a_5) - i (a_3 - a_7))$$ - -$$p (- 1) = ((a_0 + a_4) + (a_2 + a_6)) - ((a_1 + a_5) + (a_3 + a_7))$$ - -$$p (- \omega ) = ((a_0 - a_4) + i (a_2 - a_6)) - \omega ((a_1 - a_5) + i (a_3 - a_7))$$ - -$$p (- i) = ((a_0 + a_4) - (a_2 + a_6)) - i ((a_1 + a_5) - (a_3 + a_7))$$ - -$$p (- \omega^3) = ((a_0 - a_4) - i (a_2 - a_6)) - \omega^3 ((a_1 - a_5) - i (a_3 - a_7))$$ - -The terms are organized differently from the direct evaluation, but this is just to show clearly how all the butterflies have been combined at each stage. We can organize all the $a_k$ according to their index: - -$$p(1) = a_0 + a_1 + a_2 + a_3 + a_4 + a_5 + a_6 + a_7$$ - -$$p(\omega) = a_0 + a_1 \omega + a_2 i + a_3 \omega^3 - a_4 - a_5 \omega - a_6 i - a_7 \omega^3$$ - -$$p(i) = a_0 + a_1 i - a_2 - a_3 i + a_4 + a_5 i - a_6 - a_7 i$$ - -$$p (\omega^3) = a_0 + a_1 \omega^3 - a_2 i + a_3 \omega - a_4 - a_5 \omega^3 + a_6 i - a_7 \omega$$ - -$$p(- 1) = a_0 - a_1 + a_2 - a_3 + a_4 - a_5 + a_6 - a_7$$ - -$$p(- \omega) = a_0 - a_1 \omega + a_2 i - a_3 \omega^3 - a_4 + a_5 \omega - a_6 i + a_7 \omega^3$$ - -$$p(- i) = a_0 - a_1 i - a_2 + a_3 i + a_4 - a_5 i - a_6 + a_7 i$$ - -$$p (- \omega^3) = a_0 - a_1 \omega^3 - a_2 i - a_3 \omega - a_4 + a_5 \omega^3 + a_6 i + a_7 \omega$$ - -where we have used that $\omega i = \omega^3$ and $\omega^3 i = -\omega$. For larger FFTs, we just have to continue breaking down and combining the elements using the butterflies with their corresponding twiddle factors. - diff --git a/crates/math/src/fft/cpu/bit_reversing.rs b/crates/math/src/fft/cpu/bit_reversing.rs deleted file mode 100644 index f225dd5e0..000000000 --- a/crates/math/src/fft/cpu/bit_reversing.rs +++ /dev/null @@ -1,51 +0,0 @@ -/// In-place bit-reverse permutation algorithm. Requires input length to be a power of two. -pub fn in_place_bit_reverse_permute(input: &mut [E]) { - for i in 0..input.len() { - let bit_reversed_index = reverse_index(i, input.len() as u64); - if bit_reversed_index > i { - input.swap(i, bit_reversed_index); - } - } -} - -/// Reverses the `log2(size)` first bits of `i` -pub fn reverse_index(i: usize, size: u64) -> usize { - if size == 1 { - i - } else { - i.reverse_bits() >> (usize::BITS - size.trailing_zeros()) - } -} - -#[cfg(all(test, feature = "alloc"))] -mod test { - use super::*; - use alloc::vec::Vec; - - // TODO: proptest would be better. - #[test] - fn bit_reverse_permutation_works() { - let mut reversed: Vec = Vec::with_capacity(16); - for i in 0..reversed.capacity() { - reversed.push(reverse_index(i, reversed.capacity() as u64)); - } - assert_eq!( - reversed[..], - [0, 8, 4, 12, 2, 10, 6, 14, 1, 9, 5, 13, 3, 11, 7, 15] - ); - - in_place_bit_reverse_permute(&mut reversed[..]); - assert_eq!( - reversed[..], - [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15] - ); - } - - #[test] - fn bit_reverse_permutation_edge_case() { - let mut edge_case = [0]; - - in_place_bit_reverse_permute(&mut edge_case[..]); - assert_eq!(edge_case[..], [0]); - } -} diff --git a/crates/math/src/fft/cpu/fft.rs b/crates/math/src/fft/cpu/fft.rs deleted file mode 100644 index e2c732d75..000000000 --- a/crates/math/src/fft/cpu/fft.rs +++ /dev/null @@ -1,260 +0,0 @@ -use crate::field::{ - element::FieldElement, - traits::{IsFFTField, IsField, IsSubFieldOf}, -}; - -/// In-Place Radix-2 NR DIT FFT algorithm over a slice of two-adic field elements. -/// It's required that the twiddle factors are in bit-reverse order. Else this function will not -/// return fourier transformed values. -/// Also the input size needs to be a power of two. -/// It's recommended to use the current safe abstractions instead of this function. -/// -/// Performs a fast fourier transform with the next attributes: -/// - In-Place: an auxiliary vector of data isn't needed for the algorithm. -/// - Radix-2: the algorithm halves the problem size log(n) times. -/// - NR: natural to reverse order, meaning that the input is naturally ordered and the output will -/// be bit-reversed ordered. -/// - DIT: decimation in time -/// -/// It supports values in a field E and domain in a subfield F. -pub fn in_place_nr_2radix_fft(input: &mut [FieldElement], twiddles: &[FieldElement]) -where - F: IsFFTField + IsSubFieldOf, - E: IsField, -{ - // divide input in groups, starting with 1, duplicating the number of groups in each stage. - let mut group_count = 1; - let mut group_size = input.len(); - - // for each group, there'll be group_size / 2 butterflies. - // a butterfly is the atomic operation of a FFT, e.g: (a, b) = (a + wb, a - wb). - // The 0.5 factor is what gives FFT its performance, it recursively halves the problem size - // (group size). - - while group_count < input.len() { - #[allow(clippy::needless_range_loop)] // the suggestion would obfuscate a bit the algorithm - for group in 0..group_count { - let first_in_group = group * group_size; - let first_in_next_group = first_in_group + group_size / 2; - - let w = &twiddles[group]; // a twiddle factor is used per group - - for i in first_in_group..first_in_next_group { - let wi = w * &input[i + group_size / 2]; - - let y0 = &input[i] + &wi; - let y1 = &input[i] - &wi; - - input[i] = y0; - input[i + group_size / 2] = y1; - } - } - group_count *= 2; - group_size /= 2; - } -} - -/// In-Place Radix-2 RN DIT FFT algorithm over a slice of two-adic field elements. -/// It's required that the twiddle factors are naturally ordered (so w[i] = w^i). Else this -/// function will not return fourier transformed values. -/// Also the input size needs to be a power of two. -/// It's recommended to use the current safe abstractions instead of this function. -/// -/// Performs a fast fourier transform with the next attributes: -/// - In-Place: an auxiliary vector of data isn't needed for storing the results. -/// - Radix-2: the algorithm halves the problem size log(n) times. -/// - RN: reverse to natural order, meaning that the input is bit-reversed ordered and the output will -/// be naturally ordered. -/// - DIT: decimation in time -/// -/// It supports values in a field E and domain in a subfield F. -#[allow(dead_code)] -pub fn in_place_rn_2radix_fft(input: &mut [FieldElement], twiddles: &[FieldElement]) -where - F: IsFFTField, -{ - // divide input in groups, starting with 1, duplicating the number of groups in each stage. - let mut group_count = 1; - let mut group_size = input.len(); - - // for each group, there'll be group_size / 2 butterflies. - // a butterfly is the atomic operation of a FFT, e.g: (a, b) = (a + wb, a - wb). - // The 0.5 factor is what gives FFT its performance, it recursively halves the problem size - // (group size). - - while group_count < input.len() { - let step_to_next = 2 * group_count; // next butterfly in the group - let step_to_last = step_to_next * (group_size / 2 - 1); - - for group in 0..group_count { - let w = &twiddles[group * group_size / 2]; - - for i in (group..=group + step_to_last).step_by(step_to_next) { - let wi = w * &input[i + group_count]; - - let y0 = &input[i] + &wi; - let y1 = &input[i] - &wi; - - input[i] = y0; - input[i + group_count] = y1; - } - } - group_count *= 2; - group_size /= 2; - } -} - -/// In-Place Radix-4 NR DIT FFT algorithm over a slice of two-adic field elements. -/// It's required that the twiddle factors are in bit-reverse order. Else this function will not -/// return fourier transformed values. -/// Also the input size needs to be a power of two. -/// It's recommended to use the current safe abstractions instead of this function. -/// -/// Performs a fast fourier transform with the next attributes: -/// - In-Place: an auxiliary vector of data isn't needed for the algorithm. -/// - Radix-4: the algorithm halves the problem size log(n) times. -/// - NR: natural to reverse order, meaning that the input is naturally ordered and the output will -/// be bit-reversed ordered. -/// - DIT: decimation in time -pub fn in_place_nr_4radix_fft(input: &mut [FieldElement], twiddles: &[FieldElement]) -where - F: IsFFTField + IsSubFieldOf, - E: IsField, -{ - debug_assert!(input.len().is_power_of_two()); - debug_assert!(input.len().ilog2().is_multiple_of(2)); // Even power of 2 => x is power of 4 - - // divide input in groups, starting with 1, duplicating the number of groups in each stage. - let mut group_count = 1; - let mut group_size = input.len(); - - // for each group, there'll be group_size / 4 butterflies. - // a butterfly is the atomic operation of a FFT, e.g: - // x' = x + yw2 + zw1 + tw1w2 - // y' = x - yw2 + zw1 - tw1w2 - // z' = x + yw3 - zw1 - tw1w3 - // t' = x - yw3 - zw1 + tw1w3 - // The 0.25 factor is what gives FFT its performance, it recursively divides the problem size - // by 4 (group size). - - while group_count < input.len() { - #[allow(clippy::needless_range_loop)] // the suggestion would obfuscate a bit the algorithm - for group in 0..group_count { - let first_in_group = group * group_size; - let first_in_next_group = first_in_group + group_size / 4; - - let (w1, w2, w3) = ( - &twiddles[group], - &twiddles[2 * group], - &twiddles[2 * group + 1], - ); - - for i in first_in_group..first_in_next_group { - let (j, k, l) = ( - i + group_size / 4, - i + group_size / 2, - i + 3 * group_size / 4, - ); - - let zw1 = w1 * &input[k]; - let tw1 = w1 * &input[l]; - let a = w2 * (&input[j] + &tw1); - let b = w3 * (&input[j] - &tw1); - - let x = &input[i] + &zw1 + &a; - let y = &input[i] + &zw1 - &a; - let z = &input[i] - &zw1 + &b; - let t = &input[i] - &zw1 - &b; - - input[i] = x; - input[j] = y; - input[k] = z; - input[l] = t; - } - } - group_count *= 4; - group_size /= 4; - } -} - -#[cfg(all(test, feature = "alloc"))] -mod tests { - use crate::fft::cpu::bit_reversing::in_place_bit_reverse_permute; - use crate::fft::cpu::roots_of_unity::get_twiddles; - use crate::fft::test_helpers::naive_matrix_dft_test; - use crate::field::{test_fields::u64_test_field::U64TestField, traits::RootsConfig}; - use proptest::{collection, prelude::*}; - - use super::*; - - type F = U64TestField; - type FE = FieldElement; - - prop_compose! { - fn powers_of_two(max_exp: u8)(exp in 1..max_exp) -> usize { 1 << exp } - // max_exp cannot be multiple of the bits that represent a usize, generally 64 or 32. - // also it can't exceed the test field's two-adicity. - } - prop_compose! { - fn field_element()(num in any::().prop_filter("Avoid null coefficients", |x| x != &0)) -> FE { - FE::from(num) - } - } - prop_compose! { - fn field_vec(max_exp: u8)(vec in (1..max_exp).prop_flat_map(|i| collection::vec(field_element(), 1 << i))) -> alloc::vec::Vec { - vec - } - } - prop_compose! { - fn field_vec_r4(max_exp: u8)(vec in (1..max_exp).prop_flat_map(|i| collection::vec(field_element(), 1 << (2 * i)))) -> alloc::vec::Vec { - vec - } - } - - proptest! { - // Property-based test that ensures NR Radix-2 FFT gives the same result as a naive DFT. - #[test] - fn test_nr_2radix_fft_matches_naive_eval(coeffs in field_vec(8)) { - let expected = naive_matrix_dft_test(&coeffs); - - let order = coeffs.len().trailing_zeros(); - let twiddles = get_twiddles(order.into(), RootsConfig::BitReverse).unwrap(); - - let mut result = coeffs; - in_place_nr_2radix_fft::(&mut result, &twiddles); - in_place_bit_reverse_permute(&mut result); - - prop_assert_eq!(expected, result); - } - - // Property-based test that ensures RN Radix-2 FFT gives the same result as a naive DFT. - #[test] - fn test_rn_2radix_fft_matches_naive_eval(coeffs in field_vec(8)) { - let expected = naive_matrix_dft_test(&coeffs); - - let order = coeffs.len().trailing_zeros(); - let twiddles = get_twiddles(order.into(), RootsConfig::Natural).unwrap(); - - let mut result = coeffs; - in_place_bit_reverse_permute(&mut result); - in_place_rn_2radix_fft(&mut result, &twiddles); - - prop_assert_eq!(result, expected); - } - - // Property-based test that ensures NR Radix-2 FFT gives the same result as a naive DFT. - #[test] - fn test_nr_4radix_fft_matches_naive_eval(coeffs in field_vec_r4(5)) { - let expected = naive_matrix_dft_test(&coeffs); - - let order = coeffs.len().trailing_zeros(); - let twiddles = get_twiddles(order.into(), RootsConfig::BitReverse).unwrap(); - - let mut result = coeffs; - in_place_nr_4radix_fft::(&mut result, &twiddles); - in_place_bit_reverse_permute(&mut result); - - prop_assert_eq!(expected, result); - } - } -} diff --git a/crates/math/src/fft/cpu/mod.rs b/crates/math/src/fft/cpu/mod.rs deleted file mode 100644 index 8f296f412..000000000 --- a/crates/math/src/fft/cpu/mod.rs +++ /dev/null @@ -1,6 +0,0 @@ -pub mod bit_reversing; -pub mod fft; -#[cfg(feature = "alloc")] -pub mod ops; -#[cfg(feature = "alloc")] -pub mod roots_of_unity; diff --git a/crates/math/src/fft/cpu/ops.rs b/crates/math/src/fft/cpu/ops.rs deleted file mode 100644 index da887cb70..000000000 --- a/crates/math/src/fft/cpu/ops.rs +++ /dev/null @@ -1,26 +0,0 @@ -use crate::{ - fft::errors::FFTError, - field::{ - element::FieldElement, - traits::{IsFFTField, IsField, IsSubFieldOf}, - }, -}; - -use super::{bit_reversing::in_place_bit_reverse_permute, fft::in_place_nr_2radix_fft}; - -/// Executes Fast Fourier Transform over elements of a two-adic finite field `E` and domain in a -/// subfield `F`. Usually used for fast polynomial evaluation. -pub fn fft, E: IsField>( - input: &[FieldElement], - twiddles: &[FieldElement], -) -> Result>, FFTError> { - if !input.len().is_power_of_two() { - return Err(FFTError::InputError(input.len())); - } - - let mut results = input.to_vec(); - in_place_nr_2radix_fft(&mut results, twiddles); - in_place_bit_reverse_permute(&mut results); - - Ok(results) -} diff --git a/crates/math/src/fft/cpu/roots_of_unity.rs b/crates/math/src/fft/cpu/roots_of_unity.rs deleted file mode 100644 index 02d525ce0..000000000 --- a/crates/math/src/fft/cpu/roots_of_unity.rs +++ /dev/null @@ -1,107 +0,0 @@ -use crate::field::{ - element::FieldElement, - traits::{IsFFTField, RootsConfig}, -}; -use alloc::vec::Vec; - -use crate::fft::errors::FFTError; - -use super::bit_reversing::in_place_bit_reverse_permute; - -/// Returns a `Vec` of the powers of a `2^n`th primitive root of unity in some configuration -/// `config`. For example, in a `Natural` config this would yield: w^0, w^1, w^2... -pub fn get_powers_of_primitive_root( - n: u64, - count: usize, - config: RootsConfig, -) -> Result>, FFTError> { - if count == 0 { - return Ok(Vec::new()); - } - - let root = match config { - RootsConfig::Natural | RootsConfig::BitReverse => F::get_primitive_root_of_unity(n)?, - _ => F::get_primitive_root_of_unity(n)?.inv().unwrap(), - }; - let up_to = match config { - RootsConfig::Natural | RootsConfig::NaturalInversed => count, - // In bit reverse form we could need as many as `(1 << count.bits()) - 1` roots - _ => count.next_power_of_two(), - }; - - let mut results = Vec::with_capacity(up_to); - // NOTE: a nice version would be using `core::iter::successors`. However, this is 10% faster. - results.extend((0..up_to).scan(FieldElement::one(), |state, _| { - let res = state.clone(); - *state = &(*state) * &root; - Some(res) - })); - - if matches!( - config, - RootsConfig::BitReverse | RootsConfig::BitReverseInversed - ) { - in_place_bit_reverse_permute(&mut results); - } - - Ok(results) -} - -/// Returns a `Vec` of the powers of a `2^n`th primitive root of unity, scaled `offset` times, -/// in a Natural configuration. -pub fn get_powers_of_primitive_root_coset( - n: u64, - count: usize, - offset: &FieldElement, -) -> Result>, FFTError> { - let root = F::get_primitive_root_of_unity(n)?; - let results = (0..count).map(|i| offset * root.pow(i)); - - Ok(results.collect()) -} - -/// Returns 2^`order` / 2 twiddle factors for FFT in some configuration `config`. -/// Twiddle factors are powers of a primitive root of unity of the field, used for FFT -/// computations. FFT only requires the first half of all the powers -pub fn get_twiddles( - order: u64, - config: RootsConfig, -) -> Result>, FFTError> { - if order > 63 { - return Err(FFTError::OrderError(order)); - } - - get_powers_of_primitive_root(order, (1 << order) / 2, config) -} - -#[cfg(test)] -mod tests { - use crate::{ - fft::{ - cpu::{bit_reversing::in_place_bit_reverse_permute, roots_of_unity::get_twiddles}, - errors::FFTError, - }, - field::{test_fields::u64_test_field::U64TestField, traits::RootsConfig}, - }; - use proptest::prelude::*; - - type F = U64TestField; - - proptest! { - #[test] - fn test_gen_twiddles_bit_reversed_validity(n in 1..8_u64) { - let twiddles = get_twiddles::(n, RootsConfig::Natural).unwrap(); - let mut twiddles_to_reorder = get_twiddles(n, RootsConfig::BitReverse).unwrap(); - in_place_bit_reverse_permute(&mut twiddles_to_reorder); // so now should be naturally ordered - - prop_assert_eq!(twiddles, twiddles_to_reorder); - } - } - - #[test] - fn gen_twiddles_with_order_greater_than_63_should_fail() { - let twiddles = get_twiddles::(64, RootsConfig::Natural); - - assert!(matches!(twiddles, Err(FFTError::OrderError(_)))); - } -} diff --git a/crates/math/src/fft/errors.rs b/crates/math/src/fft/errors.rs deleted file mode 100644 index 92a122238..000000000 --- a/crates/math/src/fft/errors.rs +++ /dev/null @@ -1,69 +0,0 @@ -use core::fmt::Display; - -use crate::field::errors::FieldError; - -#[cfg(feature = "cuda")] -use lambdaworks_gpu::cuda::abstractions::errors::CudaError; - -#[derive(Debug)] -pub enum FFTError { - RootOfUnityError(u64), - InputError(usize), - OrderError(u64), - DomainSizeError(usize), - #[cfg(feature = "cuda")] - CudaError(CudaError), -} - -impl Display for FFTError { - fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { - match self { - FFTError::RootOfUnityError(_) => write!(f, "Could not calculate root of unity"), - FFTError::InputError(v) => { - write!(f, "Input length is {v}, which is not a power of two") - } - FFTError::OrderError(v) => { - write!(f, "Order should be less than or equal to 63, but is {v}") - } - FFTError::DomainSizeError(_) => { - write!(f, "Domain size exceeds two adicity of the field") - } - #[cfg(feature = "cuda")] - FFTError::CudaError(_) => { - write!(f, "A CUDA related error has ocurred") - } - } - } -} - -#[cfg(feature = "std")] -impl std::error::Error for FFTError { - fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { - match self { - #[cfg(feature = "cuda")] - FFTError::CudaError(_) => Some(e), - _ => None, - } - } -} - -#[cfg(feature = "cuda")] -impl From for FFTError { - fn from(error: CudaError) -> Self { - Self::CudaError(error) - } -} - -impl From for FFTError { - fn from(error: FieldError) -> Self { - match error { - FieldError::DivisionByZero => { - panic!("Can't divide by zero during FFT"); - } - FieldError::InvZeroError => { - panic!("Can't calculate inverse of zero during FFT"); - } - FieldError::RootOfUnityError(order) => FFTError::RootOfUnityError(order), - } - } -} diff --git a/crates/math/src/fft/gpu/cuda/mod.rs b/crates/math/src/fft/gpu/cuda/mod.rs deleted file mode 100644 index 1a3918e8f..000000000 --- a/crates/math/src/fft/gpu/cuda/mod.rs +++ /dev/null @@ -1,3 +0,0 @@ -pub mod ops; -pub mod polynomial; -pub mod state; diff --git a/crates/math/src/fft/gpu/cuda/ops.rs b/crates/math/src/fft/gpu/cuda/ops.rs deleted file mode 100644 index fb2645ab7..000000000 --- a/crates/math/src/fft/gpu/cuda/ops.rs +++ /dev/null @@ -1,145 +0,0 @@ -use crate::{ - fft::gpu::cuda::state::CudaState, - field::{ - element::FieldElement, - traits::{IsFFTField, RootsConfig}, - }, -}; -use lambdaworks_gpu::cuda::abstractions::errors::CudaError; - -/// Executes parallel ordered FFT over a slice of two-adic field elements, in CUDA. -/// Twiddle factors are required to be in bit-reverse order. -/// -/// "Ordered" means that the input is required to be in natural order, and the output will be -/// in this order too. Natural order means that input[i] corresponds to the i-th coefficient, -/// as opposed to bit-reverse order in which input[bit_rev(i)] corresponds to the i-th -/// coefficient. -pub fn fft( - input: &[FieldElement], - twiddles: &[FieldElement], - state: &CudaState, -) -> Result>, CudaError> -where - F: IsFFTField, - F::BaseType: Unpin, -{ - let mut function = state.get_radix2_dit_butterfly(input, twiddles)?; - - const WARP_SIZE: usize = 32; - - let block_size = WARP_SIZE; - let butterfly_count = input.len() / 2; - let block_count = (butterfly_count + block_size - 1) / block_size; - - let order = input.len().trailing_zeros(); - - for stage in 0..order { - function.launch(block_count, block_size, stage, butterfly_count as u32)?; - } - - let output = function.retrieve_result()?; - - bitrev_permutation(output, state) -} - -pub fn gen_twiddles( - order: u64, - config: RootsConfig, - state: &CudaState, -) -> Result>, CudaError> { - if order > 63 { - return Err(CudaError::FunctionError( - "Order should be less than or equal to 63".to_string(), - )); - } - - let count = (1 << order) / 2; - if count == 0 { - return Ok(Vec::new()); - } - - let mut function = state.get_calc_twiddles::(order, config)?; - - function.launch(count)?; - - function.retrieve_result() -} - -pub fn bitrev_permutation( - input: Vec>, - state: &CudaState, -) -> Result>, CudaError> { - let mut function = state.get_bitrev_permutation(&input, &input)?; - - function.launch()?; - - function.retrieve_result() -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::fft::cpu::roots_of_unity::get_twiddles; - use crate::field::{ - element::FieldElement, fields::fft_friendly::stark_252_prime_field::Stark252PrimeField, - traits::RootsConfig, - }; - use proptest::{collection, prelude::*}; - - // FFT related tests - type F = Stark252PrimeField; - type FE = FieldElement; - - prop_compose! { - fn powers_of_two(max_exp: u8)(exp in 1..max_exp) -> usize { 1 << exp } - // max_exp cannot be multiple of the bits that represent a usize, generally 64 or 32. - // also it can't exceed the test field's two-adicity. - } - - prop_compose! { - fn field_element()(num in any::().prop_filter("Avoid null coefficients", |x| x != &0)) -> FE { - FE::from(num) - } - } - - fn field_vec(max_exp: u8) -> impl Strategy> { - powers_of_two(max_exp).prop_flat_map(|size| collection::vec(field_element(), size)) - } - - proptest! { - #[test] - fn test_cuda_fft_matches_sequential_fft(input in field_vec(4)) { - let state = CudaState::new().unwrap(); - let order = input.len().trailing_zeros(); - let twiddles = get_twiddles(order.into(), RootsConfig::BitReverse).unwrap(); - - let cuda_fft = fft(&input, &twiddles, &state).unwrap(); - let fft = crate::fft::cpu::ops::fft(&input, &twiddles).unwrap(); - - prop_assert_eq!(cuda_fft, fft); - } - } - - #[test] - fn test_cuda_fft_matches_sequential_large_input() { - const ORDER: usize = 20; - let input = vec![FE::one(); 1 << ORDER]; - - let state = CudaState::new().unwrap(); - let order = input.len().trailing_zeros(); - let twiddles = get_twiddles(order.into(), RootsConfig::BitReverse).unwrap(); - - let cuda_result = fft(&input, &twiddles, &state).unwrap(); - let sequential_result = crate::fft::cpu::ops::fft(&input, &twiddles).unwrap(); - - assert_eq!(&cuda_result, &sequential_result); - } - - #[test] - fn gen_twiddles_with_order_greater_than_63_should_fail() { - let state = CudaState::new().unwrap(); - let twiddles = gen_twiddles::(64, RootsConfig::Natural, &state); - - assert!(matches!(twiddles, Err(CudaError::FunctionError(_)))); - } -} diff --git a/crates/math/src/fft/gpu/cuda/polynomial.rs b/crates/math/src/fft/gpu/cuda/polynomial.rs deleted file mode 100644 index e27c08ea3..000000000 --- a/crates/math/src/fft/gpu/cuda/polynomial.rs +++ /dev/null @@ -1,57 +0,0 @@ -use crate::{ - fft::{ - errors::FFTError, - gpu::cuda::{ops::gen_twiddles, state::CudaState}, - }, - field::{ - element::FieldElement, - traits::{IsFFTField, RootsConfig}, - }, - polynomial::Polynomial, -}; - -use super::ops::fft; -use lambdaworks_gpu::cuda::abstractions::errors::CudaError; - -pub fn evaluate_fft_cuda(coeffs: &[FieldElement]) -> Result>, CudaError> -where - F: IsFFTField, - F::BaseType: Unpin, -{ - let state = CudaState::new()?; - let order = log2(coeffs.len())?; - let twiddles = gen_twiddles::(order, RootsConfig::BitReverse, &state)?; - - fft(coeffs, &twiddles, &state) -} - -/// Returns a new polynomial that interpolates `fft_evals`, which are evaluations using twiddle -/// factors. This is considered to be the inverse operation of [evaluate_fft_cuda()]. -pub fn interpolate_fft_cuda( - fft_evals: &[FieldElement], -) -> Result>, FFTError> -where - F: IsFFTField, - F::BaseType: Unpin, -{ - let state = CudaState::new()?; - - // fft() can zero-pad the coeffs if there aren't 2^k of them (k being any integer). - // TODO: twiddle factors need to be handled with too much care, the FFT API shouldn't accept - // invalid twiddle factor collections. A better solution is needed. - let order = log2(fft_evals.len())?; - let twiddles = gen_twiddles::(order, RootsConfig::BitReverseInversed, &state)?; - - let coeffs = fft(fft_evals, &twiddles, &state)?; - - let scale_factor = FieldElement::from(fft_evals.len() as u64).inv().unwrap(); - Ok(Polynomial::new(&coeffs).scale_coeffs(&scale_factor)) -} - -// TODO: remove when fft works on non-multiple-of-two input length -fn log2(n: usize) -> Result { - if !n.is_power_of_two() { - return Err(CudaError::InvalidOrder(n)); - } - Ok(n.trailing_zeros() as u64) -} diff --git a/crates/math/src/fft/gpu/cuda/state.rs b/crates/math/src/fft/gpu/cuda/state.rs deleted file mode 100644 index c3c53180b..000000000 --- a/crates/math/src/fft/gpu/cuda/state.rs +++ /dev/null @@ -1,325 +0,0 @@ -use crate::{ - field::{ - element::FieldElement, - fields::fft_friendly::stark_252_prime_field::Stark252PrimeField, - traits::{IsFFTField, IsField, RootsConfig}, - }, - gpu::cuda::field::element::CUDAFieldElement, -}; -use cudarc::{ - driver::{ - safe::CudaSlice, safe::DeviceSlice, CudaDevice, CudaFunction, LaunchAsync, LaunchConfig, - }, - nvrtc::safe::Ptx, -}; -use lambdaworks_gpu::cuda::abstractions::errors::CudaError; -use std::sync::Arc; - -const STARK256_PTX: &str = include_str!("../../../gpu/cuda/shaders/field/stark256.ptx"); -const WARP_SIZE: usize = 32; // the implementation will spawn threadblocks of this size. - -/// Structure for abstracting basic calls to a CUDA device and saving the state. Used for -/// implementing GPU parallel computations in CUDA. -pub struct CudaState { - device: Arc, -} - -impl CudaState { - /// Creates a new CUDA state with the first GPU. - pub fn new() -> Result { - let device = - CudaDevice::new(0).map_err(|err| CudaError::DeviceNotFound(err.to_string()))?; - let state = Self { device }; - - // Load PTX libraries - state.load_library::(STARK256_PTX)?; - - Ok(state) - } - - fn load_library(&self, src: &'static str) -> Result<(), CudaError> { - let mod_name: &'static str = F::field_name(); - let functions = [ - "radix2_dit_butterfly", - "calc_twiddles", - "calc_twiddles_bitrev", - "bitrev_permutation", - ]; - self.device - .load_ptx(Ptx::from_src(src), mod_name, &functions) - .map_err(|err| CudaError::PtxError(err.to_string())) - } - - fn get_function(&self, func_name: &str) -> Result { - let mod_name = F::field_name(); - self.device - .get_func(mod_name, func_name) - .ok_or_else(|| CudaError::FunctionError(func_name.to_string())) - } - - /// Allocates a buffer in the GPU and copies `data` into it. Returns its handle. - fn alloc_buffer_with_data( - &self, - data: &[FieldElement], - ) -> Result>, CudaError> { - self.device - .htod_sync_copy(&data.iter().map(CUDAFieldElement::from).collect::>()) - .map_err(|err| CudaError::AllocateMemory(err.to_string())) - } - - /// Returns a wrapper object over the `radix2_dit_butterfly` function defined in `fft.cu` - pub(crate) fn get_radix2_dit_butterfly( - &self, - input: &[FieldElement], - twiddles: &[FieldElement], - ) -> Result, CudaError> { - let function = self.get_function::("radix2_dit_butterfly")?; - - let input_buffer = self.alloc_buffer_with_data(input)?; - let twiddles_buffer = self.alloc_buffer_with_data(twiddles)?; - - Ok(Radix2DitButterflyFunction::new( - Arc::clone(&self.device), - function, - input_buffer, - twiddles_buffer, - )) - } - - /// Returns a wrapper object over the `calc_twiddles` function defined in `twiddles.cu` - pub(crate) fn get_calc_twiddles( - &self, - order: u64, - config: RootsConfig, - ) -> Result, CudaError> { - let root: FieldElement = F::get_primitive_root_of_unity(order).map_err(|_| { - CudaError::FunctionError(format!( - "Couldn't get primitive root of unity of order {}", - order - )) - })?; - - let (root, function_name) = match config { - RootsConfig::Natural => (root, "calc_twiddles"), - RootsConfig::NaturalInversed => (root.inv(), "calc_twiddles"), - RootsConfig::BitReverse => (root, "calc_twiddles_bitrev"), - RootsConfig::BitReverseInversed => (root.inv(), "calc_twiddles_bitrev"), - }; - - let function = self.get_function::(function_name)?; - - let count = (1 << order) / 2; - let omega_buffer = self.alloc_buffer_with_data(&[root])?; - let twiddles: &[FieldElement] = &(0..count) - .map(|_| FieldElement::one()) - .collect::>>(); - let twiddles_buffer = self.alloc_buffer_with_data(twiddles)?; - - Ok(CalcTwiddlesFunction::new( - Arc::clone(&self.device), - function, - omega_buffer, - twiddles_buffer, - )) - } - - /// Returns a wrapper object over the `bitrev_permutation` function defined in `bitrev_permutation.cu` - pub(crate) fn get_bitrev_permutation( - &self, - input: &[FieldElement], - result: &[FieldElement], - ) -> Result, CudaError> { - let function = self.get_function::("bitrev_permutation")?; - - let input_buffer = self.alloc_buffer_with_data(input)?; - let result_buffer = self.alloc_buffer_with_data(result)?; - - Ok(BitrevPermutationFunction::new( - Arc::clone(&self.device), - function, - input_buffer, - result_buffer, - )) - } -} - -pub(crate) struct Radix2DitButterflyFunction { - device: Arc, - function: CudaFunction, - input: CudaSlice>, - twiddles: CudaSlice>, -} - -impl Radix2DitButterflyFunction { - fn new( - device: Arc, - function: CudaFunction, - input: CudaSlice>, - twiddles: CudaSlice>, - ) -> Self { - Self { - device, - function, - input, - twiddles, - } - } - - pub(crate) fn launch( - &mut self, - block_count: usize, - block_size: usize, - stage: u32, - butterfly_count: u32, - ) -> Result<(), CudaError> { - let grid_dim = (block_count as u32, 1, 1); // in blocks - let block_dim = (block_size as u32, 1, 1); - - let config = LaunchConfig { - grid_dim, - block_dim, - shared_mem_bytes: 0, - }; - // Launching kernels must be done in an unsafe block. - // Calling a kernel is similar to calling a foreign-language function, - // as the kernel itself could be written in C or unsafe Rust. - unsafe { - self.function.clone().launch( - config, - (&mut self.input, &self.twiddles, stage, butterfly_count), - ) - } - .map_err(|err| CudaError::Launch(err.to_string())) - } - - pub(crate) fn retrieve_result(self) -> Result>, CudaError> { - let Self { device, input, .. } = self; - let output = device - .sync_reclaim(input) - .map_err(|err| CudaError::RetrieveMemory(err.to_string()))? - .into_iter() - .map(FieldElement::from) - .collect(); - - Ok(output) - } -} - -pub(crate) struct CalcTwiddlesFunction { - device: Arc, - function: CudaFunction, - omega: CudaSlice>, - twiddles: CudaSlice>, -} - -impl CalcTwiddlesFunction { - fn new( - device: Arc, - function: CudaFunction, - omega: CudaSlice>, - twiddles: CudaSlice>, - ) -> Self { - Self { - device, - function, - omega, - twiddles, - } - } - - pub(crate) fn launch(&mut self, count: usize) -> Result<(), CudaError> { - let block_size = WARP_SIZE; - let block_count = (count + block_size - 1) / block_size; - - let grid_dim = (block_count as u32, 1, 1); // in blocks - let block_dim = (block_size as u32, 1, 1); - - let config = LaunchConfig { - grid_dim, - block_dim, - shared_mem_bytes: 0, - }; - // Launching kernels must be done in an unsafe block. - // Calling a kernel is similar to calling a foreign-language function, - // as the kernel itself could be written in C or unsafe Rust. - unsafe { - self.function - .clone() - .launch(config, (&mut self.twiddles, &self.omega, count as u32)) - } - .map_err(|err| CudaError::Launch(err.to_string())) - } - - pub(crate) fn retrieve_result(self) -> Result>, CudaError> { - let Self { - device, twiddles, .. - } = self; - let output = device - .sync_reclaim(twiddles) - .map_err(|err| CudaError::RetrieveMemory(err.to_string()))? - .into_iter() - .map(FieldElement::from) - .collect(); - - Ok(output) - } -} - -pub(crate) struct BitrevPermutationFunction { - device: Arc, - function: CudaFunction, - input: CudaSlice>, - result: CudaSlice>, -} - -impl BitrevPermutationFunction { - fn new( - device: Arc, - function: CudaFunction, - input: CudaSlice>, - result: CudaSlice>, - ) -> Self { - Self { - device, - function, - input, - result, - } - } - - pub(crate) fn launch(&mut self) -> Result<(), CudaError> { - let len = self.input.len(); - let block_size = WARP_SIZE; - let block_count = (len + block_size - 1) / block_size; - - let grid_dim = (block_count as u32, 1, 1); // in blocks - let block_dim = (block_size as u32, 1, 1); - - let config = LaunchConfig { - grid_dim, - block_dim, - shared_mem_bytes: 0, - }; - // Launching kernels must be done in an unsafe block. - // Calling a kernel is similar to calling a foreign-language function, - // as the kernel itself could be written in C or unsafe Rust. - unsafe { - self.function - .clone() - .launch(config, (&mut self.input, &self.result, len)) - } - .map_err(|err| CudaError::Launch(err.to_string())) - } - - pub(crate) fn retrieve_result(self) -> Result>, CudaError> { - let Self { device, result, .. } = self; - let output = device - .sync_reclaim(result) - .map_err(|err| CudaError::RetrieveMemory(err.to_string()))? - .into_iter() - .map(FieldElement::from) - .collect(); - - Ok(output) - } -} diff --git a/crates/math/src/fft/gpu/mod.rs b/crates/math/src/fft/gpu/mod.rs deleted file mode 100644 index 572db957b..000000000 --- a/crates/math/src/fft/gpu/mod.rs +++ /dev/null @@ -1,2 +0,0 @@ -#[cfg(feature = "cuda")] -pub mod cuda; diff --git a/crates/math/src/fft/mod.rs b/crates/math/src/fft/mod.rs deleted file mode 100644 index 51080aaef..000000000 --- a/crates/math/src/fft/mod.rs +++ /dev/null @@ -1,8 +0,0 @@ -pub mod cpu; -pub mod errors; -pub mod gpu; -#[cfg(feature = "alloc")] -pub mod polynomial; - -#[cfg(all(test, feature = "alloc"))] -pub(crate) mod test_helpers; diff --git a/crates/math/src/fft/polynomial.rs b/crates/math/src/fft/polynomial.rs deleted file mode 100644 index 051f60983..000000000 --- a/crates/math/src/fft/polynomial.rs +++ /dev/null @@ -1,566 +0,0 @@ -use crate::fft::errors::FFTError; - -use crate::field::errors::FieldError; -use crate::field::traits::{IsField, IsSubFieldOf}; -use crate::{ - field::{ - element::FieldElement, - traits::{IsFFTField, RootsConfig}, - }, - polynomial::Polynomial, -}; -use alloc::{vec, vec::Vec}; - -#[cfg(feature = "cuda")] -use crate::fft::gpu::cuda::polynomial::{evaluate_fft_cuda, interpolate_fft_cuda}; - -use super::cpu::{ops, roots_of_unity}; - -impl Polynomial> { - /// Returns `N` evaluations of this polynomial using FFT over a domain in a subfield F of E (so the results - /// are P(w^i), with w being a primitive root of unity). - /// `N = max(self.coeff_len(), domain_size).next_power_of_two() * blowup_factor`. - /// If `domain_size` is `None`, it defaults to 0. - pub fn evaluate_fft>( - poly: &Polynomial>, - blowup_factor: usize, - domain_size: Option, - ) -> Result>, FFTError> { - let domain_size = domain_size.unwrap_or(0); - let len = core::cmp::max(poly.coeff_len(), domain_size).next_power_of_two() * blowup_factor; - if len.trailing_zeros() as u64 > F::TWO_ADICITY { - return Err(FFTError::DomainSizeError(len.trailing_zeros() as usize)); - } - if poly.coefficients().is_empty() { - return Ok(vec![FieldElement::zero(); len]); - } - - let mut coeffs = poly.coefficients().to_vec(); - coeffs.resize(len, FieldElement::zero()); - // padding with zeros will make FFT return more evaluations of the same polynomial. - - #[cfg(feature = "cuda")] - { - // TODO: support multiple fields with CUDA - if F::field_name() == "stark256" { - Ok(evaluate_fft_cuda(&coeffs)?) - } else { - evaluate_fft_cpu::(&coeffs) - } - } - - #[cfg(not(feature = "cuda"))] - { - evaluate_fft_cpu::(&coeffs) - } - } - - /// Returns `N` evaluations with an offset of this polynomial using FFT over a domain in a subfield F of E - /// (so the results are P(w^i), with w being a primitive root of unity). - /// `N = max(self.coeff_len(), domain_size).next_power_of_two() * blowup_factor`. - /// If `domain_size` is `None`, it defaults to 0. - pub fn evaluate_offset_fft>( - poly: &Polynomial>, - blowup_factor: usize, - domain_size: Option, - offset: &FieldElement, - ) -> Result>, FFTError> { - let scaled = poly.scale(offset); - Polynomial::evaluate_fft::(&scaled, blowup_factor, domain_size) - } - - /// Returns a new polynomial that interpolates `(w^i, fft_evals[i])`, with `w` being a - /// Nth primitive root of unity in a subfield F of E, and `i in 0..N`, with `N = fft_evals.len()`. - /// This is considered to be the inverse operation of [Self::evaluate_fft()]. - pub fn interpolate_fft>( - fft_evals: &[FieldElement], - ) -> Result { - #[cfg(feature = "cuda")] - { - if !F::field_name().is_empty() { - Ok(interpolate_fft_cuda(fft_evals)?) - } else { - interpolate_fft_cpu::(fft_evals) - } - } - - #[cfg(not(feature = "cuda"))] - { - interpolate_fft_cpu::(fft_evals) - } - } - - /// Returns a new polynomial that interpolates offset `(w^i, fft_evals[i])`, with `w` being a - /// Nth primitive root of unity in a subfield F of E, and `i in 0..N`, with `N = fft_evals.len()`. - /// This is considered to be the inverse operation of [Self::evaluate_offset_fft()]. - pub fn interpolate_offset_fft>( - fft_evals: &[FieldElement], - offset: &FieldElement, - ) -> Result>, FFTError> { - let scaled = Polynomial::interpolate_fft::(fft_evals)?; - Ok(scaled.scale(&offset.inv().unwrap())) - } - - /// Multiplies two polynomials using FFT. - /// It's faster than naive multiplication when the degree of the polynomials is large enough (>=2**6). - /// This works best with polynomials whose highest degree is equal to a power of 2 - 1. - /// Will return an error if the degree of the resulting polynomial is greater than 2**63. - /// - /// This is an implementation of the fast division algorithm from - /// [Gathen's book](https://www.cambridge.org/core/books/modern-computer-algebra/DB3563D4013401734851CF683D2F03F0) - /// chapter 9 - pub fn fast_fft_multiplication>( - &self, - other: &Self, - ) -> Result { - let domain_size = self.degree() + other.degree() + 1; - let p = Polynomial::evaluate_fft::(self, 1, Some(domain_size))?; - let q = Polynomial::evaluate_fft::(other, 1, Some(domain_size))?; - let r = p.into_iter().zip(q).map(|(a, b)| a * b).collect::>(); - - Polynomial::interpolate_fft::(&r) - } - - /// Divides two polynomials with remainder. - /// This is faster than the naive division if the degree of the divisor - /// is greater than the degree of the dividend and both degrees are large enough. - pub fn fast_division + IsFFTField>( - &self, - divisor: &Self, - ) -> Result<(Self, Self), FFTError> { - let n = self.degree(); - let m = divisor.degree(); - if divisor.coefficients.is_empty() - || divisor - .coefficients - .iter() - .all(|c| c == &FieldElement::zero()) - { - return Err(FieldError::DivisionByZero.into()); - } - if n < m { - return Ok((Self::zero(), self.clone())); - } - let d = n - m; // Degree of the quotient - let a_rev = self.reverse(n); - let b_rev = divisor.reverse(m); - let inv_b_rev = b_rev.invert_polynomial_mod::(d + 1)?; - let q = a_rev - .fast_fft_multiplication::(&inv_b_rev)? - .truncate(d + 1) - .reverse(d); - - let r = self - q.fast_fft_multiplication::(divisor)?; - Ok((q, r)) - } - - /// Computes the inverse of polynomial P modulo x^k using Newton iteration. - /// P must have an invertible constant term. - pub fn invert_polynomial_mod + IsFFTField>( - &self, - k: usize, - ) -> Result { - if self.coefficients.is_empty() - || self.coefficients.iter().all(|c| c == &FieldElement::zero()) - { - return Err(FieldError::DivisionByZero.into()); - } - let mut q = Self::new(&[self.coefficients[0].inv()?]); - let mut current_precision = 1; - - let two = Self::new(&[FieldElement::::one() + FieldElement::one()]); - while current_precision < k { - current_precision *= 2; - let temp = self - .fast_fft_multiplication::(&q)? - .truncate(current_precision); - let correction = &two - temp; - q = q - .fast_fft_multiplication::(&correction)? - .truncate(current_precision); - } - - // Final truncation to desired degree k - Ok(q.truncate(k)) - } -} - -pub fn compose_fft( - poly_1: &Polynomial>, - poly_2: &Polynomial>, -) -> Polynomial> -where - F: IsFFTField + IsSubFieldOf, - E: IsField, -{ - let poly_2_evaluations = Polynomial::evaluate_fft::(poly_2, 1, None).unwrap(); - - let values: Vec<_> = poly_2_evaluations - .iter() - .map(|value| poly_1.evaluate(value)) - .collect(); - - Polynomial::interpolate_fft::(values.as_slice()).unwrap() -} - -pub fn evaluate_fft_cpu(coeffs: &[FieldElement]) -> Result>, FFTError> -where - F: IsFFTField + IsSubFieldOf, - E: IsField, -{ - let order = coeffs.len().trailing_zeros(); - let twiddles = roots_of_unity::get_twiddles::(order.into(), RootsConfig::BitReverse)?; - // Bit reverse order is needed for NR DIT FFT. - ops::fft(coeffs, &twiddles) -} - -pub fn interpolate_fft_cpu( - fft_evals: &[FieldElement], -) -> Result>, FFTError> -where - F: IsFFTField + IsSubFieldOf, - E: IsField, -{ - let order = fft_evals.len().trailing_zeros(); - let twiddles = - roots_of_unity::get_twiddles::(order.into(), RootsConfig::BitReverseInversed)?; - - let coeffs = ops::fft(fft_evals, &twiddles)?; - - let scale_factor = FieldElement::from(fft_evals.len() as u64).inv().unwrap(); - Ok(Polynomial::new(&coeffs).scale_coeffs(&scale_factor)) -} - -#[cfg(test)] -mod tests { - #[cfg(not(feature = "cuda"))] - use crate::field::traits::IsField; - - use crate::field::{ - test_fields::u64_test_field::{U64TestField, U64TestFieldExtension}, - traits::RootsConfig, - }; - use proptest::{collection, prelude::*}; - - use roots_of_unity::{get_powers_of_primitive_root, get_powers_of_primitive_root_coset}; - - use super::*; - - fn gen_fft_and_naive_evaluation( - poly: Polynomial>, - ) -> (Vec>, Vec>) { - let len = poly.coeff_len().next_power_of_two(); - let order = len.trailing_zeros(); - let twiddles = - get_powers_of_primitive_root(order.into(), len, RootsConfig::Natural).unwrap(); - - let fft_eval = Polynomial::evaluate_fft::(&poly, 1, None).unwrap(); - let naive_eval = poly.evaluate_slice(&twiddles); - - (fft_eval, naive_eval) - } - - fn gen_fft_coset_and_naive_evaluation( - poly: Polynomial>, - offset: FieldElement, - blowup_factor: usize, - ) -> (Vec>, Vec>) { - let len = poly.coeff_len().next_power_of_two(); - let order = (len * blowup_factor).trailing_zeros(); - let twiddles = - get_powers_of_primitive_root_coset(order.into(), len * blowup_factor, &offset).unwrap(); - - let fft_eval = - Polynomial::evaluate_offset_fft::(&poly, blowup_factor, None, &offset).unwrap(); - let naive_eval = poly.evaluate_slice(&twiddles); - - (fft_eval, naive_eval) - } - - fn gen_fft_and_naive_interpolate( - fft_evals: &[FieldElement], - ) -> (Polynomial>, Polynomial>) { - let order = fft_evals.len().trailing_zeros() as u64; - let twiddles = - get_powers_of_primitive_root(order, 1 << order, RootsConfig::Natural).unwrap(); - - let naive_poly = Polynomial::interpolate(&twiddles, fft_evals).unwrap(); - let fft_poly = Polynomial::interpolate_fft::(fft_evals).unwrap(); - - (fft_poly, naive_poly) - } - - fn gen_fft_and_naive_coset_interpolate( - fft_evals: &[FieldElement], - offset: &FieldElement, - ) -> (Polynomial>, Polynomial>) { - let order = fft_evals.len().trailing_zeros() as u64; - let twiddles = get_powers_of_primitive_root_coset(order, 1 << order, offset).unwrap(); - - let naive_poly = Polynomial::interpolate(&twiddles, fft_evals).unwrap(); - let fft_poly = Polynomial::interpolate_offset_fft(fft_evals, offset).unwrap(); - - (fft_poly, naive_poly) - } - - fn gen_fft_interpolate_and_evaluate( - poly: Polynomial>, - ) -> (Polynomial>, Polynomial>) { - let eval = Polynomial::evaluate_fft::(&poly, 1, None).unwrap(); - let new_poly = Polynomial::interpolate_fft::(&eval).unwrap(); - - (poly, new_poly) - } - - #[cfg(not(feature = "cuda"))] - mod u64_field_tests { - use super::*; - use crate::field::test_fields::u64_test_field::U64TestField; - - // FFT related tests - type F = U64TestField; - type FE = FieldElement; - - prop_compose! { - fn powers_of_two(max_exp: u8)(exp in 1..max_exp) -> usize { 1 << exp } - // max_exp cannot be multiple of the bits that represent a usize, generally 64 or 32. - // also it can't exceed the test field's two-adicity. - } - prop_compose! { - fn field_element()(num in any::().prop_filter("Avoid null coefficients", |x| x != &0)) -> FE { - FE::from(num) - } - } - prop_compose! { - fn offset()(num in 1..F::neg(&1)) -> FE { FE::from(num) } - } - prop_compose! { - fn field_vec(max_exp: u8)(vec in collection::vec(field_element(), 0..1 << max_exp)) -> Vec { - vec - } - } - prop_compose! { - fn non_empty_field_vec(max_exp: u8)(vec in collection::vec(field_element(), 1 << max_exp)) -> Vec { - vec - } - } - prop_compose! { - fn non_power_of_two_sized_field_vec(max_exp: u8)(vec in collection::vec(field_element(), 2..1< Vec { - vec - } - } - prop_compose! { - fn poly(max_exp: u8)(coeffs in field_vec(max_exp)) -> Polynomial { - Polynomial::new(&coeffs) - } - } - prop_compose! { - fn non_zero_poly(max_exp: u8)(coeffs in non_empty_field_vec(max_exp)) -> Polynomial { - Polynomial::new(&coeffs) - } - } - prop_compose! { - fn poly_with_non_power_of_two_coeffs(max_exp: u8)(coeffs in non_power_of_two_sized_field_vec(max_exp)) -> Polynomial { - Polynomial::new(&coeffs) - } - } - - proptest! { - // Property-based test that ensures FFT eval. gives same result as a naive polynomial evaluation. - #[test] - fn test_fft_matches_naive_evaluation(poly in poly(8)) { - let (fft_eval, naive_eval) = gen_fft_and_naive_evaluation(poly); - prop_assert_eq!(fft_eval, naive_eval); - } - - // Property-based test that ensures FFT eval. with coset gives same result as a naive polynomial evaluation. - #[test] - fn test_fft_coset_matches_naive_evaluation(poly in poly(6), offset in offset(), blowup_factor in powers_of_two(4)) { - let (fft_eval, naive_eval) = gen_fft_coset_and_naive_evaluation(poly, offset, blowup_factor); - prop_assert_eq!(fft_eval, naive_eval); - } - - // Property-based test that ensures FFT interpolation is the same as naive. - #[test] - fn test_fft_interpolate_matches_naive(fft_evals in field_vec(4) - .prop_filter("Avoid polynomials of size not power of two", - |evals| evals.len().is_power_of_two())) { - let (fft_poly, naive_poly) = gen_fft_and_naive_interpolate(&fft_evals); - prop_assert_eq!(fft_poly, naive_poly); - } - - // Property-based test that ensures FFT interpolation with an offset is the same as naive. - #[test] - fn test_fft_interpolate_coset_matches_naive(offset in offset(), fft_evals in field_vec(4) - .prop_filter("Avoid polynomials of size not power of two", - |evals| evals.len().is_power_of_two())) { - let (fft_poly, naive_poly) = gen_fft_and_naive_coset_interpolate(&fft_evals, &offset); - prop_assert_eq!(fft_poly, naive_poly); - } - - // Property-based test that ensures interpolation is the inverse operation of evaluation. - #[test] - fn test_fft_interpolate_is_inverse_of_evaluate(poly in poly(4) - .prop_filter("Avoid polynomials of size not power of two", - |poly| poly.coeff_len().is_power_of_two())) { - let (poly, new_poly) = gen_fft_interpolate_and_evaluate(poly); - - prop_assert_eq!(poly, new_poly); - } - - #[test] - fn test_fft_multiplication_works(poly in poly(7), other in poly(7)) { - prop_assert_eq!(poly.fast_fft_multiplication::(&other).unwrap(), poly * other); - } - - #[test] - fn test_fft_division_works(poly in non_zero_poly(7), other in non_zero_poly(7)) { - prop_assert_eq!(poly.fast_division::(&other).unwrap(), poly.long_division_with_remainder(&other)); - } - - #[test] - fn test_invert_polynomial_mod_works(poly in non_zero_poly(7), k in powers_of_two(4)) { - let inverted_poly = poly.invert_polynomial_mod::(k).unwrap(); - prop_assert_eq!((poly * inverted_poly).truncate(k), Polynomial::new(&[FE::one()])); - } - } - - #[test] - fn composition_fft_works() { - let p = Polynomial::new(&[FE::new(0), FE::new(2)]); - let q = Polynomial::new(&[FE::new(0), FE::new(0), FE::new(0), FE::new(1)]); - assert_eq!( - compose_fft::(&p, &q), - Polynomial::new(&[FE::new(0), FE::new(0), FE::new(0), FE::new(2)]) - ); - } - } - - mod u256_field_tests { - use super::*; - use crate::field::fields::fft_friendly::stark_252_prime_field::Stark252PrimeField; - - prop_compose! { - fn powers_of_two(max_exp: u8)(exp in 1..max_exp) -> usize { 1 << exp } - // max_exp cannot be multiple of the bits that represent a usize, generally 64 or 32. - // also it can't exceed the test field's two-adicity. - } - prop_compose! { - fn field_element()(num in any::().prop_filter("Avoid null coefficients", |x| x != &0)) -> FE { - FE::from(num) - } - } - prop_compose! { - fn offset()(num in any::(), factor in any::()) -> FE { FE::from(num).pow(factor) } - } - prop_compose! { - fn field_vec(max_exp: u8)(vec in collection::vec(field_element(), 0..1 << max_exp)) -> Vec { - vec - } - } - prop_compose! { - fn non_empty_field_vec(max_exp: u8)(vec in collection::vec(field_element(), 1 << max_exp)) -> Vec { - vec - } - } - prop_compose! { - fn non_power_of_two_sized_field_vec(max_exp: u8)(vec in collection::vec(field_element(), 2..1< Vec { - vec - } - } - prop_compose! { - fn poly(max_exp: u8)(coeffs in field_vec(max_exp)) -> Polynomial { - Polynomial::new(&coeffs) - } - } - prop_compose! { - fn non_zero_poly(max_exp: u8)(coeffs in non_empty_field_vec(max_exp)) -> Polynomial { - Polynomial::new(&coeffs) - } - } - prop_compose! { - fn poly_with_non_power_of_two_coeffs(max_exp: u8)(coeffs in non_power_of_two_sized_field_vec(max_exp)) -> Polynomial { - Polynomial::new(&coeffs) - } - } - - // FFT related tests - type F = Stark252PrimeField; - type FE = FieldElement; - - proptest! { - // Property-based test that ensures FFT eval. gives same result as a naive polynomial evaluation. - #[test] - fn test_fft_matches_naive_evaluation(poly in poly(8)) { - let (fft_eval, naive_eval) = gen_fft_and_naive_evaluation(poly); - prop_assert_eq!(fft_eval, naive_eval); - } - - // Property-based test that ensures FFT eval. with coset gives same result as a naive polynomial evaluation. - #[test] - fn test_fft_coset_matches_naive_evaluation(poly in poly(4), offset in offset(), blowup_factor in powers_of_two(4)) { - let (fft_eval, naive_eval) = gen_fft_coset_and_naive_evaluation(poly, offset, blowup_factor); - prop_assert_eq!(fft_eval, naive_eval); - } - - // Property-based test that ensures FFT interpolation is the same as naive.. - #[test] - fn test_fft_interpolate_matches_naive(fft_evals in field_vec(4) - .prop_filter("Avoid polynomials of size not power of two", - |evals| evals.len().is_power_of_two())) { - let (fft_poly, naive_poly) = gen_fft_and_naive_interpolate(&fft_evals); - prop_assert_eq!(fft_poly, naive_poly); - } - - // Property-based test that ensures FFT interpolation with an offset is the same as naive. - #[test] - fn test_fft_interpolate_coset_matches_naive(offset in offset(), fft_evals in field_vec(4) - .prop_filter("Avoid polynomials of size not power of two", - |evals| evals.len().is_power_of_two())) { - let (fft_poly, naive_poly) = gen_fft_and_naive_coset_interpolate(&fft_evals, &offset); - prop_assert_eq!(fft_poly, naive_poly); - } - - // Property-based test that ensures interpolation is the inverse operation of evaluation. - #[test] - fn test_fft_interpolate_is_inverse_of_evaluate( - poly in poly(4).prop_filter("Avoid non pows of two", |poly| poly.coeff_len().is_power_of_two())) { - let (poly, new_poly) = gen_fft_interpolate_and_evaluate(poly); - prop_assert_eq!(poly, new_poly); - } - - #[test] - fn test_fft_multiplication_works(poly in poly(7), other in poly(7)) { - prop_assert_eq!(poly.fast_fft_multiplication::(&other).unwrap(), poly * other); - } - - #[test] - fn test_fft_division_works(poly in poly(7), other in non_zero_poly(7)) { - prop_assert_eq!(poly.fast_division::(&other).unwrap(), poly.long_division_with_remainder(&other)); - } - - #[test] - fn test_invert_polynomial_mod_works(poly in non_zero_poly(7), k in powers_of_two(4)) { - let inverted_poly = poly.invert_polynomial_mod::(k).unwrap(); - prop_assert_eq!((poly * inverted_poly).truncate(k), Polynomial::new(&[FE::one()])); - } - } - } - - #[test] - fn test_fft_with_values_in_field_extension_over_domain_in_prime_field() { - type TF = U64TestField; - type TL = U64TestFieldExtension; - - let a = FieldElement::::from(&[FieldElement::one(), FieldElement::one()]); - let b = FieldElement::::from(&[-FieldElement::from(2), FieldElement::from(17)]); - let c = FieldElement::::one(); - let poly = Polynomial::new(&[a, b, c]); - - let eval = Polynomial::evaluate_offset_fft::(&poly, 8, Some(4), &FieldElement::from(2)) - .unwrap(); - let new_poly = - Polynomial::interpolate_offset_fft::(&eval, &FieldElement::from(2)).unwrap(); - assert_eq!(poly, new_poly); - } -} diff --git a/crates/math/src/fft/test_helpers.rs b/crates/math/src/fft/test_helpers.rs deleted file mode 100644 index 3f083f629..000000000 --- a/crates/math/src/fft/test_helpers.rs +++ /dev/null @@ -1,74 +0,0 @@ -use crate::{ - fft::cpu::roots_of_unity::get_powers_of_primitive_root, - field::{ - element::FieldElement, - traits::{IsFFTField, RootsConfig}, - }, -}; -use alloc::vec::Vec; - -/// Calculates the (non-unitary) Discrete Fourier Transform of `input` via the DFT matrix. -pub fn naive_matrix_dft_test(input: &[FieldElement]) -> Vec> { - let n = input.len(); - assert!(n.is_power_of_two()); - let order = n.trailing_zeros(); - - let twiddles = - get_powers_of_primitive_root::(order.into(), n, RootsConfig::Natural).unwrap(); - - let mut output = Vec::with_capacity(n); - for row in 0..n { - let mut sum = FieldElement::zero(); - - for (col, element) in input.iter().enumerate() { - let i = (row * col) % n; // w^i = w^(i mod n) - sum += element.clone() * twiddles[i].clone(); - } - - output.push(sum); - } - - output -} - -#[cfg(test)] -mod fft_helpers_test { - use super::*; - use crate::{field::test_fields::u64_test_field::U64TestField, polynomial::Polynomial}; - - use proptest::{collection, prelude::*}; - - type F = U64TestField; - type FE = FieldElement; - - prop_compose! { - fn powers_of_two(max_exp: u8)(exp in 1..max_exp) -> usize { 1 << exp } - // max_exp cannot be multiple of the bits that represent a usize, generally 64 or 32. - // also it can't exceed the test field's two-adicity. - } - prop_compose! { - fn field_element()(num in any::().prop_filter("Avoid null coefficients", |x| x != &0)) -> FE { - FE::from(num) - } - } - prop_compose! { - fn field_vec(max_exp: u8)(vec in collection::vec(field_element(), 2..1< Vec { - vec - } - } - - proptest! { - // Property-based test that ensures dft() gives the same result as a naive polynomial evaluation. - #[test] - fn test_dft_same_as_eval(coeffs in field_vec(8)) { - let dft = naive_matrix_dft_test(&coeffs); - - let poly = Polynomial::new(&coeffs); - let order = coeffs.len().trailing_zeros(); - let twiddles = get_powers_of_primitive_root(order.into(), coeffs.len(), RootsConfig::Natural).unwrap(); - let evals: Vec = twiddles.iter().map(|x| poly.evaluate(x)).collect(); - - prop_assert_eq!(evals, dft); - } - } -} diff --git a/crates/math/src/field/README.md b/crates/math/src/field/README.md deleted file mode 100644 index 9f5532721..000000000 --- a/crates/math/src/field/README.md +++ /dev/null @@ -1,372 +0,0 @@ -# lambdaworks Fields - -This folder contains the different field backends, including field extensions. To learn how to use our fields, see the [examples](https://github.com/lambdaclass/lambdaworks/blob/main/examples/README.md) under basic use of finite fields. Below we give a list of currently supported fields; if yours is not on the list, you can add it by implementing the traits and providing the constants. -- [Stark-252](./fields/fft_friendly/stark_252_prime_field.rs): the field currently used by Starknet and STARK Platinum prover. FFT-friendly. -- [Mini-Goldilocks](./fields/fft_friendly/u64_goldilocks.rs), also known as oxfoi prime ($2^{64} - 2^{32} + 1$). FFT-friendly. -- [Pallas base field](./fields/pallas_field.rs): this is also the scalar field of the Vesta elliptic curve. -- [Vesta base field](./fields/vesta_field.rs): this is also the scalar field of the Pallas elliptic curve. -- [Goldilocks-448](./fields/p448_goldilocks_prime_field.rs) -- [Mersenne-31](./fields/mersenne31/): $2^{31} - 1$ and its [quadratic extension](./fields/mersenne31/extensions.rs) -- [Baby Bear](./fields/fft_friendly/babybear_u32.rs) and its [quadratic extension](./fields/fft_friendly/quadratic_babybear.rs): FFT-friendly, $2^{31} - 2^{27} + 1$. -- [Scalar field of BN-254](../elliptic_curve/short_weierstrass/curves/bn_254/default_types.rs), and its quadratic extension, quartic, sextic and twelth degree extensions. This coincides with the base field of [Grumpkin](../elliptic_curve/short_weierstrass/curves/grumpkin/curve.rs) -- [Base field of BN-254](../elliptic_curve/short_weierstrass/curves/bn_254/field_extension.rs) and its quadratic extension. The base field coincides with the scalar field of [Grumpkin](../elliptic_curve/short_weierstrass/curves/grumpkin/curve.rs) -- [Scalar field of BLS12-381](../elliptic_curve/short_weierstrass/curves/bls12_381/default_types.rs), and its quadratic, sextic and twelth degree extensions. FFT-friendly. -- [Base field of BLS12-381](../elliptic_curve/short_weierstrass/curves/bls12_381/field_extension.rs) -- [Scalar field of BLS12-377](../elliptic_curve/short_weierstrass/curves/bls12_377/curve.rs) -- [Base field of BLS12-377](../elliptic_curve/short_weierstrass/curves/bls12_377/field_extension.rs) -- [Base field of secp256k1](./fields/secp256k1_field.rs): the base field of Bitcoin's elliptic curve. -- [Scalar field of secp256k1](./fields/secp256k1_scalarfield.rs): the scalar field of Bitcoin's elliptic curve. - -You also have the tooling to define quadratic and cubic extension fields. - -## 📊 Benchmarks - -Benchmark results are hosted [here](https://lambdaclass.github.io/lambdaworks/bench). - -These are the results of execution of the benchmarks for finite field arithmetic using the STARK field prime (p = 3618502788666131213697322783095070105623107215331596699973092056135872020481). - -Differences of 3% are common for some measurements, so small differences are not statistically relevant. - -ARM - M1 - -| Operation| N | Arkworks | lambdaworks | -| -------- | --- | --------- | ----------- | -| `mul` | 10k | 112 μs | 115 μs | -| `add` | 1M | 8.5 ms | 7.0 ms | -| `sub` | 1M | 7.53 ms | 7.12 ms | -| `pow` | 10k | 11.2 ms | 12.4 ms | -| `invert` | 10k | 30.0 ms | 27.2 ms | - -x86 - AMD Ryzen 7 PRO - -| Operation | N | Arkworks (ASM)* | lambdaworks | -| -------- | --- | --------- | ----------- | -| `mul` | 10k | 118.9 us | 95.7 us | -| `add` | 1M | 6.8 ms | 5.4 ms | -| `sub` | 1M | 6.6 ms | 5.2 ms | -| `pow` | 10k | 10.6 ms | 9.4 ms | -| `invert` | 10k | 34.2 ms | 35.74 ms | - -*assembly feature was enabled manually for that bench, and is not activated by default when running criterion - -To run them locally, you will need `cargo-criterion` and `cargo-flamegraph`. Install it with: - -```bash -cargo install cargo-criterion -``` - -Run the complete benchmark suite with: - -```bash -make benchmarks -``` - -Run a specific benchmark suite with `cargo`, for example to run the one for `field`: - -```bash -make benchmark BENCH=field -``` - -You can check the generated HTML report in `target/criterion/reports/index.html` - -## Background on finite fields - -Finite fields play a fundamental role in Cryptography. They work essentially as the rational or real numbers (where we have the operations of addition, subtraction, multiplication and division), except that the number of elements is finite (for example, 31, 101, but not infinite as real numbers). We will begin this explanation with the simplest types of finite fields, where the number of elements is given by a prime number (a prime number is an integer such that its only divisors are 1 and itself, like 7, 19, 31, but not 8, which is divisible by 1, 2, 4, and 8). We will denote the prime $p$ and the finite field whose size is equal to $p$, $\mathbb{F_p}$. - -The elements of $\mathbb{F_p}$ are given by the possible remainders of the integer division of numbers by $p$. Remember that for any integer $n$, we can express $n = p q + r$, where $q$ is the quotient and $0 \leq r < p$ is the remainder. For example, $8 = 5 \times 1 + 3$. Expressing it in simpler terms, $\mathbb{F_p}$ is the set $\{ 0, 1, 2, 3, \dots , p - 1 \}$. We can define an addition operation $+ : \mathbb{F_p} \times \mathbb{F_p} \rightarrow \mathbb{F_p}$ with the following rule: - -Whenever we add two integers $a, b$ such that $n = a + b$, if the result exceeds $p$, we take the remainder of the division of $n$ by $p$. For example, if $p = 7$, $\mathbb{F_7} = \{ 0, 1, 2, 3, 4, 5, 6 \}$, so -$6 + 5 = 11 \equiv 4 \pmod{7}$ -$2 + 3 = 5 \equiv 5 \pmod{7}$ -$3 + 4 = 7 \equiv 0 \pmod{7}$ - -The notation $a \equiv b \pmod{p}$ means that $a - b$ is divisible by $p$, or that $a$ and $b$ have the same remainder when divided by $p$. We can show that $\mathbb{F_p}$ together with this addition forms an [Abelian group](https://en.wikipedia.org/wiki/Abelian_group). You can check that for every element $k$ there is an element $m$ such that $k + m = 0$. We note that element $m = - k$. You can see that $p - k = - k$, and you can define subtraction. - -We can also define multiplication in a similar way: whenever the product of two integers exceeds the modulus $p$, take the remainder. Except for $0$, we can show that for every element $x$, there is an element $y$, such that $x \times y = 1$ and we denote $y = x^{- 1}$. This allows us to define division as $n / x = n \times x^{- 1}$. We will use $\mathbb{F_p}^\star = \{ 1, 2, \dots p - 1 \}$, that is $\mathbb{F_p}$ without the element $0$. The number of elements in $\mathbb{F_p}^\star$ is $p - 1$ and we can show that is is also an Abelian group. We call it the multiplicative group of $\mathbb{F_p}$. We will say that the field is *FFT friendly* if $p - 1 = 2^m c$, where $c$ is an odd integer, and $m$ is *sufficiently large*. For example, $p = 2^{64} - 2^{32} + 1$ satisfies $p - 1 = 2^{32} (2^{32} - 1)$ is *FFT friendly* with $m = 32$, and this means we can use the radix-2 FFT algorithm for vectors of size up to $2^{32}$. - -If we take a look at $\mathbb{F_p}$ with the operations $+$ and $\times$, we see that it satisfies the axioms for a [field](https://en.wikipedia.org/wiki/Field_(mathematics)). To define a field in lambdaworks, you need to specify the modulus $p$, then the library will handle all the operations. While it is possible to do operations by taking remainder, this is not performant in practice. lambdaworks relies on Montgomery arithmetic for general finite fields (unless they have some faster alternative, such as [Mersenne primes](https://en.wikipedia.org/wiki/Mersenne_prime)). Before jumping into how you define your finite field, we need to see how we are going to represent "big" numbers. Common types to represent integers are `u8`, `u16`, `u32`, `u64`, `u128`. For cryptographic applications, we need to work (in general) with larger integers, taking 256 bits or more. As these numbers do not fit into a single unsigned integer variable, we can express it using several limbs in a given base (in lambdaworks, we use base 64, where each limb is represent by `u64`). Mathematically, - -$$n = \sum_{i = 0}^m a_k b^k$$ - -where $b = 2^{64}$. For a 256 bit number, we need $4$ limbs and the number looks like - -$$n = a_0 + a_1 2^{64} + a_2 2^{128} + a_3 2^{192}$$ - -The number is expressed in little-endian form. Alternatively, we can also write things as - -$$n = a_0 2^{192} + a_1 2^{128} + a_2 2^{64} + a_3$$ - -The number is expressed in big-endian form. This is the form we use in lambdaworks to work with big integers. To work with large integers, we provide `UnsignedInteger` types with variable number of limbs. - -```rust -#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] -pub struct UnsignedInteger { - pub limbs: [u64; NUM_LIMBS], -} -``` - -You can create `UnsignedInteger` types whose size is a multiple of 64, such as 128, 192, 256, 320, 384. We provide `U128`, `U256` `U384` since these are commonly used with elliptic curves. There are several ways of assigning an `UnsignedInteger` a value. Some examples are: -- `UnsignedInteger::from(value: u128)` -- `UnsignedInteger::from(value: u64)` -- `UnsignedInteger::from(value: u16)` -- `UnsignedInteger::from(hex_str: &str)` -- `UnsignedInteger::from_hex_unchecked(hex_str: &str)` - -For example, - -```rust - let modulus: U256 = U256::from_hex_unchecked( - "0x40000000000000000000000000000000224698fc0994a8dd8c46eb2100000001", - ); -``` - -Defines a value, modulus, whose hex representation is given by `0x40000000000000000000000000000000224698fc0994a8dd8c46eb2100000001`. Inside, the function takes chunks of 8 bytes and interprets them as the limb. This represents the prime number of 77 decimal digits `28948022309329048855892746252171976963363056481941647379679742748393362948097`. - -We can now jump to the definition of a finite field: -```rust -use crate::{ - field::fields::montgomery_backed_prime_fields::{IsModulus, MontgomeryBackendPrimeField}, - unsigned_integer::element::U256, -}; - -type VestaMontgomeryBackendPrimeField = MontgomeryBackendPrimeField; - -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct MontgomeryConfigVesta255PrimeField; -impl IsModulus for MontgomeryConfigVesta255PrimeField { - const MODULUS: U256 = U256::from_hex_unchecked( - "0x40000000000000000000000000000000224698fc0994a8dd8c46eb2100000001", - ); -} - -pub type Vesta255PrimeField = VestaMontgomeryBackendPrimeField; -``` - -We give a name to the field `Vesta255PrimeField`, which is going to use a Montgomery representation. Each element is composed of 4 limbs of 64 bits and the modulus is given by `MODULUS`. The compiler resolves all the necessary things to define the different field operations: -- Addition -- Doubling (as an optimization to addition when $a = b$) -- Multiplication -- Square (as an optimization to multiplication when $a = b$) -- Subtraction -- (Multiplicative) inversion/division -- Exponentiation/Power (using a square and multiply algorithm) -- Square root (In a finite field, we can compute the square root of an element if it is a [quadratic residue](https://en.wikipedia.org/wiki/Quadratic_residue) modulo $p$). When the element is a quadratic residue, the function returns the values $y$ and $- y$ such that $y^2 = (- y)^2 = x$. - -It also resolves all the necessary constants. These involve: -- `pub const R2` : The square of the $R$ parameter -- `pub const MU` : $- \text{modulus}^{-1} \pmod{ 2^{64}}$ -- `pub const ZERO` : the value of the neutral element for addition in Montgomery form (it is always 0). -- `pub const ONE` : the value of the unit in Montgomery form. -- `MODULUS_HAS_ONE_SPARE_BIT` : checks whether the highest bit in the modulus is set or not (this is useful for faster modular arithmetic). - -Additionally, you have the following methods: -- `to_bytes_be` : transforms the element to bytes in big-endian form. -- `to_bytes_le` : transforms the element to bytes in little-endian form. -- `from_bytes_be` : creates an element from byte array in big-endian form. -- `from_bytes_le` : creates an element from byte array in little-endian form. -- `representative` : transforms the element from Montgomery form to standard form. -- `to_hex` : transforms the element to a hex string. - -To practice some operations, we are going to define a new field and do some operations. The following is the base field for the `secp256k1` elliptic curve, best known as Bitcoin's curve: - -```rust - #[derive(Clone, Debug)] -struct SecpModulus; -impl IsModulus for SecpModulus { - const MODULUS: U256 = UnsignedInteger::from_hex_unchecked( - "0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F", - ); -} -type SecpMontField = U256PrimeField; -type SecpMontElement = FieldElement; -``` - -We will create some elements and perform operations: - -```rust -let minus_3 = -SecpMontElement::from_hex_unchecked("0x3"); -let three = SecpMontElement::from_hex_unchecked("0x3"); -assert_eq!(three + minus_3, SecpMontElement::zero()); -``` - -```rust -let two = SecpMontElement::from_hex_unchecked("0x2"); -assert_eq!(three - two, SecpMontElement::one()); -``` - -```rust -let minus_3_mul_minus_3 = &minus_3 * &minus_3; -let minus_3_squared = minus_3.square(); -let minus_3_pow_2 = minus_3.pow(2_u32); -let nine = SecpMontElement::from_hex_unchecked("0x9"); - -assert_eq!(minus_3_mul_minus_3, nine); -assert_eq!(minus_3_squared, nine); -assert_eq!(minus_3_pow_2, nine); -``` - -This last part shows that we have three ways of computing the square of a number. The first one is using multiplication, the second is squaring and the third one is using the power/exponentiation function `pow`. The `pow` function needs as exponent an `UnsignedInteger`. The most efficient function in this context is `square()`, followed by `mul`. - -```rust -let three_inv = three.inv(); -assert_eq!(three * three_inv, SecpMontElement::one()); -``` - -Note: if you need to invert several elements, you should use the `inplace_batch_inverse`, since computing field inversion is usually expensive. - -If you print the hex representation of three, `three.to_hex()`, you will get `0x300000B73`. This is the Montgomery representation, which is different from the standard form `0x3`. If you perform `three.representative().to_hex()`, it will transform first to standard form, then give `0x3`. Let's look at the output of several functions: -- `three.to_hex()` : `0x300000B73` -- `three.representative().to_hex()` : `0x3` -- `three.to_bytes_be()`: Returns a vector of 32 bytes (256 bits), all of which are `0x0`, except for the last one, `0x3` -- `three.to_bytes_le()`: Returns a vector of 32 bytes (256 bits), all of which are `0x0`, except for the first one, `0x3` -- `SecpMontElement::from_bytes_be(&three.to_bytes_be())`: will return the Montgomery form of the number 3. -- `SecpMontElement::from_hex(&three.to_hex())`: this will not return the Montgomery form of 3! -- `SecpMontElement::from_hex(&three.representative().to_hex())`: this will return the Montgomery form of 3. - -## Montgomery arithmetic - -Addition and subtraction in Montgomery form follow the same rules as ordinary addition and subtraction over a field. There are different algorithms for multiplication (and squaring): -- Coarsely Integrated Operand Scanning (CIOS) -- Separated Operand Scanning Method (SOS) - -Multiplication follows `cios`, unless there are spare bits in the modulus. For that case, multiplication changes to `cios_optimized_for_moduli_with_one_spare_bit`. Squaring uses the `sos_square` method. - -Inversion is performed using Algorithm 16 (Binary Euclidean Algorithm) from [Guajardo, Kumar, Paar, Perzl](https://www.sandeep.de/my/papers/2006_ActaApplMath_EfficientSoftFiniteF.pdf). - -## Extension fields - -In some applications in Cryptography, it may be necessary to work over an *extension field*. For example, to compute pairings we need to work over a larger field. Similarly, in STARKs, when we need to sample a random number, we want to do it from a large set, and we can do this by working with an extension of the original field. What are extension fields? You may have heard about [complex numbers](https://en.wikipedia.org/wiki/Complex_number). We can view them as a pair of real numbers $c = (a, b)$ with a multiplication operation defined as $(a_0 , b_0 ) \times (a_1 , b_1 ) = (a_0 a_1 - b_0 b_1 , a_0 b_1 + a_1 b_0 )$ and the addition as $(a_0 , b_0 ) + (a_1 , b_1 ) = (a_0 + a_1 , b_0 + b_1 )$. We can see real numbers as a subset of $\mathbb{C}$ of the form $(a , 0)$. It is common to introduce the imaginary unit, $i$, and write them also as $a + b i$, with $i^2 = - 1$ (you can check that $(0 , 1)^2 = - 1$). We see that when work with complex numbers of the form $(a , 0)$ this works as ordinary real numbers, and we see that complex numbers extend the real numbers, allowing us to find solutions to equations that cannot be solved over the real numbers. For example, $x^2 + 1 = 0$ does not have a solution over $\mathbb{R}$, but over $\mathbb{C}$ both $i$ and $- i$ solve the equation. We will focus on how to build the complex numbers from the real numbers, and then explain how to adapt this to the setting of finite fields. - -We will work with univariate polynomials over the real numbers. A univariate polynomial over the real numbers is an expression in an indeterminate, $x$, with coefficients taking their values over the real numbers, with the following form $p(x) = a_0 + a_1 x + a_2 x^2 + \dots + a_n x^n$. For example, $p(x) = \sqrt{2} + \pi x + 2 x^2 + 5x^3 - 56 x^4$. We can add, subtract and multiply polynomials similar to what we do with integers, which means that the polynomials form a ring. We denote the ring of polynomials over real numbers as $\mathbb{R} [x]$, the ring of polynomials over the integers as $\mathbb{Z} [x]$ and the ring of polynomials over a finite field as $\mathbb{F_p} [x]$. The highest $k$ such that $a_k \neq 0$ is called the degree of the polynomial; for our previous example it is 4. Given polynomials $p(x)$ and $d(x)$, there are polynomials $q(x)$ and $r(x)$ such that $p(x) = d(x)q(x) + r(x)$, with the degree of $r(x)$ less than the degree of $d(x)$ (this in analogous to the integer division with remainder). Given a polynomial $p(x)$, we can define the ring of polynomials modulo $p(x)$, denoted by $\mathbb{R} [x] / p(x)$. Operations in this ring work similar to integers: whenever the degree of the result exceeds or equals the degree of $p(x)$, we take the remainder $r(x)$ of the division between the result and $p(x)$ (basically, $p(x)$ acts like the modulus in finite fields). - -Many polynomials can be expressed in terms of lower degree polynomials. For example, $x^2 - 1 = (x + 1) (x - 1)$, $x^3 + 3x^2 + 3x + 1 = (x + 1)(x + 1)(x + 1)$. Over the real numbers, $I(x) = x^2 + 1$ cannot be expressed in terms of lower degree polynomials. We say that $I(x)$ is [irreducible](https://en.wikipedia.org/wiki/Irreducible_polynomial) over $\mathbb{R}$. If we consider the ring modulo an irreducible polynomial, $\mathbb{R} [x] / I(x)$, then $\mathbb{R} [x] / I(x)$ is a field. The degree of $I(x)$ is the degree of the extension. For example, in the case of the real numbers, $\mathbb{R} [x] / (x^2 + 1)$ coincides with our notion of the complex numbers. Every element there has the form $a + b x$. - -Addition (and subtraction) is done the usual way, $(a + b x) + (c + dx) = (a + c) + (b + d)x$. Multiplication is a bit more difficult, $(a + bx) \times (c + dx) = ac + (b c + a d) x + b d x^2$. But this polynomial has equal degree than $x^2 + 1$, so we take the remainder. The result is $ac - bd + (b c + a d)x$ (you can check that $x^2 = - 1$). - -In the case of complex numbers, we don't need to continue extending them further, since we can factor any polynomial over $\mathbb{C}$ (see the [fundamental theorem of algebra](https://en.wikipedia.org/wiki/Fundamental_theorem_of_algebra)). - -The recipe to build extension fields over finite fields is the same. We will start with the simplest case, when $\mathbb{F_p}$ is a prime field. For example, we are working with $p = 2^{31} - 1$, a Mersenne prime (we can see that $p \equiv 3 \pmod{4}$). We consider the polynomials with coefficients over $\mathbb{F_p} [x]$. It can be shown that $- 1$ has no square root over $\mathbb{F_p}$: this means that there is no $x$ such that $x^2 = - 1$ over $\mathbb{F_p}$. The polynomial $I(x) = x^2 + 1$ is, therefore, irreducible over $\mathbb{F_p}$ (note, $x^2 - 1$ is not always irreducible over arbitrary fields!). We can consider then $\mathbb{F_p} [x] / I(x)$ and the elements there are represented by $a + b x$. One caveat with the case of complex numbers is that the operations involving $a$ and $b$ are the operations of $\mathbb{F_p }$. For example, $(5 + 2^{31} x) + ((2^{31} - 6) - x) = 0 + 0x$. Since the degree of $I(x)$ is $2$, we say that we have a quadratic extension of $\mathbb{F_p}$ and will denote it $\mathbb{F_{ p^2 } }$. You could have chosen a different degree $2$ irreducible polynomial, but we can show that the two extensions are isomorphic. - -You can build other extensions looking for higher-degree irreducible polynomials. For example, if you consider the field $\mathbb{F_2} = \{0 , 1 \}$, the polynomial $x^8 + x^4 + x^3 + x + 1$ is irreducible, and you can define a degree $8$ extension of $\mathbb{F_2}$. - -There are different ways in which we can construct higher-degree extensions. For example, we can take our prime field and find an irreducible polynomial of degree $4$ and work with $\mathbb{F_p} / I(x)$. Each element in the field can be represented as $a + b x + c x^2 + dx^3$. We can also use a towered approach: we first find an irreducible polynomial $I(x)$ of degree $2$ and obtain $\mathbb{F_{ p^2 } } = \mathbb{F_p} / I(x)$. Each element is of the form $a + b x$. Since the extension is also a field, we can find an irreducible polynomial over ${F_{ p^2 } }$, $J(y)$, of degree $2$ and consider ${F_{ p^2 } } [y] / J(y)$. Then, each element there is of the form $a^\prime + b^\prime y$, where $a^\prime$ and $b^\prime$ live in ${F_{ p^2 } }$. Since every element in ${F_{ p^2 } }$ is of the form $a_0 + a_1 x$, we get that $a_0 + a_1 x + b_0 y + b_1 x y$. Even though the extensions look different, there is an isomorphism connecting the two. Depending on the application, one form or the other can be more efficient. - -While some of the underlying concepts can be difficult to grasp, defining extension fields is simpler in lambdaworks. While we could allow you to define arbitrary extensions, we provide methods to define quadratic and cubic extensions over fields. To define a quadratic extension, you need to implement the following trait: - -```rust -pub trait HasQuadraticNonResidue { - fn residue() -> FieldElement; -} -``` - -Here, we assume that you want to define a quadratic extension of the form $x^2 - \mathrm{residue}$, where $\mathrm{residue}$ is not a square over your field. In the case of the Mersenne prime $2^{31} - 1$, $\mathrm{residue} = - 1$ (you could use other quadratic non-residues, but this can lead to slower field extension operations). Similarly, for cubic extensions we have - -```rust -pub trait HasCubicNonResidue { - /// This function must return an element that is not a cube in Fp, - /// that is, a cubic non-residue. - fn residue() -> FieldElement; -} -``` - -For example, the following code defines the default quadratic extension for BabyBear: - -```rust -pub type QuadraticBabybearField = - QuadraticExtensionField; - -impl HasQuadraticNonResidue for Babybear31PrimeField { - fn residue() -> FieldElement { - -FieldElement::one() - } -} - -/// Field element type for the quadratic extension of Babybear -pub type QuadraticBabybearFieldElement = - QuadraticExtensionFieldElement; -``` - -You can also create a separate quadratic extension by implementing the `IsField` trait for the quadratic extension, - -```rust -#[derive(Clone, Debug)] -pub struct BLS12381FieldModulus; -impl IsModulus for BLS12381FieldModulus { - const MODULUS: U384 = BLS12381_PRIME_FIELD_ORDER; -} - -pub type BLS12381PrimeField = MontgomeryBackendPrimeField; - -////////////////// -#[derive(Clone, Debug)] -pub struct Degree2ExtensionField; - -impl IsField for Degree2ExtensionField { - type BaseType = [FieldElement; 2]; - /// Returns the component wise addition of `a` and `b` - fn add(a: &Self::BaseType, b: &Self::BaseType) -> Self::BaseType { - [&a[0] + &b[0], &a[1] + &b[1]] - } - - /// Returns the multiplication of `a` and `b` using the following - /// equation: - /// (a0 + a1 * t) * (b0 + b1 * t) = a0 * b0 + a1 * b1 * Self::residue() + (a0 * b1 + a1 * b0) * t - /// where `t.pow(2)` equals `Q::residue()`. - fn mul(a: &Self::BaseType, b: &Self::BaseType) -> Self::BaseType { - let a0b0 = &a[0] * &b[0]; - let a1b1 = &a[1] * &b[1]; - let z = (&a[0] + &a[1]) * (&b[0] + &b[1]); - [&a0b0 - &a1b1, z - a0b0 - a1b1] - } -} -``` - -You should then implement the operations for the field, such as addition, multiplication, subtraction, inversion and so on. This is more convenient if you can avoid doing extra operations and defining the residue. You can also optimize the operations between elements of the base field and the extension field by implementing the trait `IsSubFieldOf`. - -```rust -impl IsSubFieldOf for BLS12381PrimeField { - fn mul( - a: &Self::BaseType, - b: &::BaseType, - ) -> ::BaseType { - let c0 = FieldElement::from_raw(::mul(a, b[0].value())); - let c1 = FieldElement::from_raw(::mul(a, b[1].value())); - [c0, c1] - } -} -``` - -Once you have the quadratic extension, you can build another extension (tower approach). For example, to define a degree 3 extension field over the quadratic extension of the BLS12-381 scalar field, we have - -```rust -#[derive(Debug, Clone)] -pub struct LevelTwoResidue; -impl HasCubicNonResidue for LevelTwoResidue { - fn residue() -> FieldElement { - FieldElement::new([ - FieldElement::new(U384::from("1")), - FieldElement::new(U384::from("1")), - ]) - } -} - -pub type Degree6ExtensionField = CubicExtensionField; -``` - -This defines a 6th degree extension over the scalar field of BLS12-381. We only need to define the cubic (non) residue, which is an element of $\mathbb{F_{ p^2 } }$. - -## Exercises - -- Define the base field of the Ed25519 elliptic curve, defined by the prime $p$. -- Check whether $- 1$ is a quadratic residue. -- Compute $100^{65537} \pmod p$ -- Define a degree 4 extension of the BabyBear field. - -## References - -- [An introduction to mathematical cryptography](https://books.google.com.ar/books/about/An_Introduction_to_Mathematical_Cryptogr.html?id=XLY9AnfDhsYC&source=kp_book_description&redir_esc=y) -- [High-Speed Algorithms & Architectures For Number-Theoretic Cryptosystems](https://www.microsoft.com/en-us/research/wp-content/uploads/1998/06/97Acar.pdf) -- [Developer math survival kit](https://blog.lambdaclass.com/math-survival-kit-for-developers/) -- [Montgomery Arithmetic from a Software Perspective](https://eprint.iacr.org/2017/1057.pdf) -- [Guajardo, Kumar, Paar, Perzl - Efficient software implementation of finite fields with applications to Cryptography](https://www.sandeep.de/my/papers/2006_ActaApplMath_EfficientSoftFiniteF.pdf) -- [Ingonyama - Montgomery-Barrett duality](https://hackmd.io/@Ingonyama/Barret-Montgomery) \ No newline at end of file diff --git a/crates/math/src/field/element.rs b/crates/math/src/field/element.rs deleted file mode 100644 index 2c2f5b27c..000000000 --- a/crates/math/src/field/element.rs +++ /dev/null @@ -1,1139 +0,0 @@ -use crate::errors::{ByteConversionError, CreationError}; -use crate::field::errors::FieldError; -use crate::field::traits::IsField; -use crate::traits::ByteConversion; -use crate::unsigned_integer::element::UnsignedInteger; -use crate::unsigned_integer::montgomery::MontgomeryAlgorithms; -use crate::unsigned_integer::traits::IsUnsignedInteger; -#[cfg(feature = "alloc")] -use alloc::{ - format, - string::{String, ToString}, -}; -use core::fmt; -use core::fmt::Debug; -use core::iter::Sum; -#[cfg(any( - feature = "lambdaworks-serde-binary", - feature = "lambdaworks-serde-string" -))] -use core::marker::PhantomData; -use core::ops::{Add, AddAssign, Div, Mul, MulAssign, Neg, Sub}; -use num_bigint::BigUint; -use num_traits::Num; -#[cfg(any( - feature = "lambdaworks-serde-binary", - feature = "lambdaworks-serde-string" -))] -use serde::de::{self, Deserializer, MapAccess, SeqAccess, Visitor}; -#[cfg(any( - feature = "lambdaworks-serde-binary", - feature = "lambdaworks-serde-string" -))] -use serde::ser::{Serialize, SerializeStruct, Serializer}; -#[cfg(any( - feature = "lambdaworks-serde-binary", - feature = "lambdaworks-serde-string" -))] -use serde::Deserialize; - -use super::fields::montgomery_backed_prime_fields::{IsModulus, MontgomeryBackendPrimeField}; -use super::traits::{IsPrimeField, IsSubFieldOf, LegendreSymbol}; - -/// A field element with operations algorithms defined in `F` -#[allow(clippy::derived_hash_with_manual_eq)] -#[derive(Debug, Clone, Hash, Copy)] -pub struct FieldElement { - value: F::BaseType, -} - -#[cfg(feature = "alloc")] -impl FieldElement { - // Source: https://en.wikipedia.org/wiki/Modular_multiplicative_inverse#Multiple_inverses - /// Computes the multiplicative inverses of a slice of field elements - /// The algorithm just performs one inversion and several multiplications and should be used - /// when wanting to invert several elements together - pub fn inplace_batch_inverse(numbers: &mut [Self]) -> Result<(), FieldError> { - if numbers.is_empty() { - return Ok(()); - } - let count = numbers.len(); - let mut prod_prefix = alloc::vec::Vec::with_capacity(count); - prod_prefix.push(numbers[0].clone()); - for i in 1..count { - prod_prefix.push(&prod_prefix[i - 1] * &numbers[i]); - } - let mut bi_inv = prod_prefix[count - 1].inv()?; - for i in (1..count).rev() { - let ai_inv = &bi_inv * &prod_prefix[i - 1]; - bi_inv = &bi_inv * &numbers[i]; - numbers[i] = ai_inv; - } - numbers[0] = bi_inv; - Ok(()) - } - - #[inline(always)] - pub fn to_subfield_vec(self) -> alloc::vec::Vec> - where - S: IsSubFieldOf, - { - S::to_subfield_vec(self.value) - .into_iter() - .map(|x| FieldElement::from_raw(x)) - .collect() - } -} - -/// From overloading for field elements -impl From<&F::BaseType> for FieldElement -where - F::BaseType: Clone, - F: IsField, -{ - fn from(value: &F::BaseType) -> Self { - Self { - value: F::from_base_type(value.clone()), - } - } -} - -/// From overloading for U64 -impl From for FieldElement -where - F: IsField, -{ - fn from(value: u64) -> Self { - Self { - value: F::from_u64(value), - } - } -} - -#[cfg(feature = "alloc")] -/// From overloading for BigUint. -/// Creates a field element from a BigUint that is smaller than the modulus. -/// Returns error if the BigUint value is bigger than the modulus. -impl TryFrom for FieldElement -where - Self: ByteConversion, - F: IsPrimeField, -{ - type Error = ByteConversionError; - fn try_from(value: BigUint) -> Result { - FieldElement::::from_reduced_big_uint(&value) - } -} - -impl FieldElement -where - F::BaseType: Clone, - F: IsField, -{ - pub fn from_raw(value: F::BaseType) -> Self { - Self { value } - } - - pub const fn const_from_raw(value: F::BaseType) -> Self { - Self { value } - } -} - -/// Equality operator overloading for field elements -impl PartialEq> for FieldElement -where - F: IsField, -{ - fn eq(&self, other: &FieldElement) -> bool { - F::eq(&self.value, &other.value) - } -} - -impl Eq for FieldElement where F: IsField {} - -/// Addition operator overloading for field elements -impl Add<&FieldElement> for &FieldElement -where - F: IsSubFieldOf, - L: IsField, -{ - type Output = FieldElement; - - fn add(self, rhs: &FieldElement) -> Self::Output { - Self::Output { - value: >::add(&self.value, &rhs.value), - } - } -} - -impl Add> for FieldElement -where - F: IsSubFieldOf, - L: IsField, -{ - type Output = FieldElement; - - fn add(self, rhs: FieldElement) -> Self::Output { - &self + &rhs - } -} - -impl Add<&FieldElement> for FieldElement -where - F: IsSubFieldOf, - L: IsField, -{ - type Output = FieldElement; - - fn add(self, rhs: &FieldElement) -> Self::Output { - &self + rhs - } -} - -impl Add> for &FieldElement -where - F: IsSubFieldOf, - L: IsField, -{ - type Output = FieldElement; - - fn add(self, rhs: FieldElement) -> Self::Output { - self + &rhs - } -} - -/// AddAssign operator overloading for field elements -impl AddAssign> for FieldElement -where - F: IsSubFieldOf, - L: IsField, -{ - fn add_assign(&mut self, rhs: FieldElement) { - self.value = >::add(&rhs.value, &self.value); - } -} - -/// Sum operator for field elements -impl Sum> for FieldElement -where - F: IsField, -{ - fn sum>(iter: I) -> Self { - iter.fold(Self::zero(), |augend, addend| augend + addend) - } -} - -/// Subtraction operator overloading for field elements*/ -impl Sub<&FieldElement> for &FieldElement -where - F: IsSubFieldOf, - L: IsField, -{ - type Output = FieldElement; - - fn sub(self, rhs: &FieldElement) -> Self::Output { - Self::Output { - value: >::sub(&self.value, &rhs.value), - } - } -} - -impl Sub> for FieldElement -where - F: IsSubFieldOf, - L: IsField, -{ - type Output = FieldElement; - - fn sub(self, rhs: FieldElement) -> Self::Output { - &self - &rhs - } -} - -impl Sub<&FieldElement> for FieldElement -where - F: IsSubFieldOf, - L: IsField, -{ - type Output = FieldElement; - - fn sub(self, rhs: &FieldElement) -> Self::Output { - &self - rhs - } -} - -impl Sub> for &FieldElement -where - F: IsSubFieldOf, - L: IsField, -{ - type Output = FieldElement; - - fn sub(self, rhs: FieldElement) -> Self::Output { - self - &rhs - } -} - -/// Multiplication operator overloading for field elements*/ -impl Mul<&FieldElement> for &FieldElement -where - F: IsSubFieldOf, - L: IsField, -{ - type Output = FieldElement; - - fn mul(self, rhs: &FieldElement) -> Self::Output { - Self::Output { - value: >::mul(&self.value, &rhs.value), - } - } -} - -impl Mul> for FieldElement -where - F: IsSubFieldOf, - L: IsField, -{ - type Output = FieldElement; - - fn mul(self, rhs: FieldElement) -> Self::Output { - &self * &rhs - } -} - -impl Mul<&FieldElement> for FieldElement -where - F: IsSubFieldOf, - L: IsField, -{ - type Output = FieldElement; - - fn mul(self, rhs: &FieldElement) -> Self::Output { - &self * rhs - } -} - -impl Mul> for &FieldElement -where - F: IsSubFieldOf, - L: IsField, -{ - type Output = FieldElement; - - fn mul(self, rhs: FieldElement) -> Self::Output { - self * &rhs - } -} - -/// MulAssign operator overloading for field elements -impl MulAssign> for FieldElement -where - F: IsSubFieldOf, - L: IsField, -{ - fn mul_assign(&mut self, rhs: FieldElement) { - self.value = >::mul(&rhs.value, &self.value); - } -} - -/// MulAssign operator overloading for field elements -impl MulAssign<&FieldElement> for FieldElement -where - F: IsSubFieldOf, - L: IsField, -{ - fn mul_assign(&mut self, rhs: &FieldElement) { - self.value = >::mul(&rhs.value, &self.value); - } -} - -/// Division operator overloading for field elements*/ -impl Div<&FieldElement> for &FieldElement -where - F: IsSubFieldOf, - L: IsField, -{ - type Output = Result, FieldError>; - - fn div(self, rhs: &FieldElement) -> Self::Output { - let value = >::div(&self.value, &rhs.value)?; - Ok(FieldElement:: { value }) - } -} - -impl Div> for FieldElement -where - F: IsSubFieldOf, - L: IsField, -{ - type Output = Result, FieldError>; - - fn div(self, rhs: FieldElement) -> Self::Output { - &self / &rhs - } -} - -impl Div<&FieldElement> for FieldElement -where - F: IsSubFieldOf, - L: IsField, -{ - type Output = Result, FieldError>; - - fn div(self, rhs: &FieldElement) -> Self::Output { - &self / rhs - } -} - -impl Div> for &FieldElement -where - F: IsSubFieldOf, - L: IsField, -{ - type Output = Result, FieldError>; - - fn div(self, rhs: FieldElement) -> Self::Output { - self / &rhs - } -} - -/// Negation operator overloading for field elements*/ -impl Neg for &FieldElement -where - F: IsField, -{ - type Output = FieldElement; - - fn neg(self) -> Self::Output { - Self::Output { - value: F::neg(&self.value), - } - } -} - -impl Neg for FieldElement -where - F: IsField, -{ - type Output = FieldElement; - - fn neg(self) -> Self::Output { - -&self - } -} - -impl Default for FieldElement -where - F: IsField, -{ - fn default() -> Self { - Self { value: F::zero() } - } -} - -/// FieldElement general implementation -/// Most of this is delegated to the trait `F` that -/// implements the field operations. -impl FieldElement -where - F: IsField, -{ - /// Creates a field element from `value` - #[inline(always)] - pub fn new(value: F::BaseType) -> Self { - Self { - value: F::from_base_type(value), - } - } - - /// Returns the underlying `value` - #[inline(always)] - pub fn value(&self) -> &F::BaseType { - &self.value - } - - /// Returns the multiplicative inverse of `self` - #[inline(always)] - pub fn inv(&self) -> Result { - let value = F::inv(&self.value)?; - Ok(Self { value }) - } - - /// Returns the square of `self` - #[inline(always)] - pub fn square(&self) -> Self { - Self { - value: F::square(&self.value), - } - } - - /// Returns the double of `self` - #[inline(always)] - pub fn double(&self) -> Self { - Self { - value: F::double(&self.value), - } - } - - /// Returns `self` raised to the power of `exponent` - #[inline(always)] - pub fn pow(&self, exponent: T) -> Self - where - T: IsUnsignedInteger, - { - Self { - value: F::pow(&self.value, exponent), - } - } - - /// Returns the multiplicative neutral element of the field. - #[inline(always)] - pub fn one() -> Self { - Self { value: F::one() } - } - - /// Returns the additive neutral element of the field. - #[inline(always)] - pub fn zero() -> Self { - Self { value: F::zero() } - } - - /// Returns the raw base type - pub fn to_raw(self) -> F::BaseType { - self.value - } - - #[inline(always)] - pub fn to_extension(self) -> FieldElement - where - F: IsSubFieldOf, - { - FieldElement { - value: >::embed(self.value), - } - } - - #[cfg(feature = "alloc")] - /// Creates a field element from a BigUint that is smaller than the modulus. - /// Returns error if the value is bigger than the modulus. - pub fn from_reduced_big_uint(value: &BigUint) -> Result - where - Self: ByteConversion, - F: IsPrimeField, - { - let mod_minus_one = F::modulus_minus_one().to_string(); - - // We check if `mod_minus_one` is a hex string or a decimal string. - // In case it is a hex we remove the prefix `0x`. - let (digits, radix) = if let Some(hex) = mod_minus_one - .strip_prefix("0x") - .or_else(|| mod_minus_one.strip_prefix("0X")) - { - (hex, 16) - } else { - (mod_minus_one.as_str(), 10) - }; - - let modulus = - BigUint::from_str_radix(digits, radix).expect("invalid modulus representation") + 1u32; - - if value >= &modulus { - Err(ByteConversionError::ValueNotReduced) - } else { - let mut bytes = value.to_bytes_le(); - // We pad the bytes to the size of the base type to be able to apply `from_bytes_le`. - bytes.resize(core::mem::size_of::(), 0); - Self::from_bytes_le(&bytes) - } - } - - #[cfg(feature = "alloc")] - /// Converts a field element into a BigUint. - pub fn to_big_uint(&self) -> BigUint - where - Self: ByteConversion, - { - BigUint::from_bytes_be(&self.to_bytes_be()) - } - - #[cfg(feature = "alloc")] - /// Converts a hex string into a field element. - /// It returns error if the hex value is larger than the modulus. - pub fn from_hex_str(hex: &str) -> Result - where - Self: ByteConversion, - F: IsPrimeField, - { - let hex_str = hex.strip_prefix("0x").unwrap_or(hex); - if hex_str.is_empty() { - return Err(CreationError::EmptyString); - } - - let value = - BigUint::from_str_radix(hex_str, 16).map_err(|_| CreationError::InvalidHexString)?; - - Self::from_reduced_big_uint(&value).map_err(|_| CreationError::InvalidHexString) - } - - #[cfg(feature = "alloc")] - /// Converts a field element into a hex string. - pub fn to_hex_str(&self) -> String - where - Self: ByteConversion, - { - format!("0x{:02X}", self.to_big_uint()) - } -} - -impl FieldElement { - /// Returns the representative of the value stored - pub fn representative(&self) -> F::RepresentativeType { - F::representative(self.value()) - } - - /// Returns the two square roots of a field element, provided it exists - /// The function returns the roots whenever the field element is a quadratic residue modulo p - pub fn sqrt(&self) -> Option<(Self, Self)> { - let sqrts = F::sqrt(&self.value); - sqrts.map(|(sqrt1, sqrt2)| (Self { value: sqrt1 }, Self { value: sqrt2 })) - } - - /// Returns the Legendre symbol of a field element modulo p - pub fn legendre_symbol(&self) -> LegendreSymbol { - F::legendre_symbol(&self.value) - } - - /// Creates a `FieldElement` from a hexstring. It can contain `0x` or not. - /// Returns an `CreationError::InvalidHexString`if the value is not a hexstring. - /// Returns a `CreationError::EmptyString` if the input string is empty. - /// Returns a `CreationError::HexStringIsTooBig` if the the input hex string is bigger than the - /// maximum amount of characters for this element. - /// Returns a `CreationError::RepresentativeOutOfRange` if the representative of the value is - /// out of the range [0, p-1] where p is the modulus. - pub fn from_hex(hex_string: &str) -> Result { - if hex_string.is_empty() { - return Err(CreationError::EmptyString); - } - let value = F::from_hex(hex_string)?; - Ok(Self { value }) - } - - #[cfg(feature = "std")] - /// Creates a hexstring from a `FieldElement` without `0x`. - pub fn to_hex(&self) -> String { - F::to_hex(&self.value) - } -} - -#[cfg(feature = "lambdaworks-serde-binary")] -impl Serialize for FieldElement -where - F: IsField, - F::BaseType: ByteConversion, -{ - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - let mut state = serializer.serialize_struct("FieldElement", 1)?; - let data = self.value().to_bytes_be(); - state.serialize_field("value", &data)?; - state.end() - } -} - -#[cfg(all( - feature = "lambdaworks-serde-string", - not(feature = "lambdaworks-serde-binary") -))] -impl Serialize for FieldElement { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - use crate::alloc::string::ToString; - let mut state = serializer.serialize_struct("FieldElement", 1)?; - state.serialize_field("value", &F::representative(self.value()).to_string())?; - state.end() - } -} - -#[cfg(feature = "lambdaworks-serde-binary")] -impl<'de, F> Deserialize<'de> for FieldElement -where - F: IsField, - F::BaseType: ByteConversion, -{ - fn deserialize(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - #[derive(Deserialize)] - #[serde(field_identifier, rename_all = "lowercase")] - enum Field { - Value, - } - - struct FieldElementVisitor(PhantomData F>); - - impl<'de, F: IsField> Visitor<'de> for FieldElementVisitor { - type Value = FieldElement; - - fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - formatter.write_str("struct FieldElement") - } - - fn visit_map(self, mut map: M) -> Result, M::Error> - where - M: MapAccess<'de>, - { - let mut value: Option> = None; - while let Some(key) = map.next_key()? { - match key { - Field::Value => { - if value.is_some() { - return Err(de::Error::duplicate_field("value")); - } - value = Some(map.next_value()?); - } - } - } - let value = value.ok_or_else(|| de::Error::missing_field("value"))?; - let val = F::BaseType::from_bytes_be(&value).unwrap(); - Ok(FieldElement::from_raw(val)) - } - - fn visit_seq(self, mut seq: S) -> Result, S::Error> - where - S: SeqAccess<'de>, - { - let mut value: Option> = None; - while let Some(val) = seq.next_element()? { - if value.is_some() { - return Err(de::Error::duplicate_field("value")); - } - value = Some(val); - } - let value = value.ok_or_else(|| de::Error::missing_field("value"))?; - let val = F::BaseType::from_bytes_be(&value).unwrap(); - Ok(FieldElement::from_raw(val)) - } - } - - const FIELDS: &[&str] = &["value"]; - deserializer.deserialize_struct("FieldElement", FIELDS, FieldElementVisitor(PhantomData)) - } -} - -#[cfg(all( - feature = "lambdaworks-serde-string", - not(feature = "lambdaworks-serde-binary") -))] -impl<'de, F: IsPrimeField> Deserialize<'de> for FieldElement { - fn deserialize(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - #[derive(Deserialize)] - #[serde(field_identifier, rename_all = "lowercase")] - enum Field { - Value, - } - - struct FieldElementVisitor(PhantomData F>); - - impl<'de, F: IsPrimeField> Visitor<'de> for FieldElementVisitor { - type Value = FieldElement; - - fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - formatter.write_str("struct FieldElement") - } - - fn visit_map(self, mut map: M) -> Result, M::Error> - where - M: MapAccess<'de>, - { - let mut value: Option<&str> = None; - while let Some(key) = map.next_key()? { - match key { - Field::Value => { - if value.is_some() { - return Err(de::Error::duplicate_field("value")); - } - value = Some(map.next_value()?); - } - } - } - let value = value.ok_or_else(|| de::Error::missing_field("value"))?; - FieldElement::from_hex(&value).map_err(|_| de::Error::custom("invalid hex")) - } - - fn visit_seq(self, mut seq: S) -> Result, S::Error> - where - S: SeqAccess<'de>, - { - let mut value: Option<&str> = None; - while let Some(val) = seq.next_element()? { - if value.is_some() { - return Err(de::Error::duplicate_field("value")); - } - value = Some(val); - } - let value = value.ok_or_else(|| de::Error::missing_field("value"))?; - FieldElement::from_hex(&value).map_err(|_| de::Error::custom("invalid hex")) - } - } - - const FIELDS: &[&str] = &["value"]; - deserializer.deserialize_struct("FieldElement", FIELDS, FieldElementVisitor(PhantomData)) - } -} - -impl fmt::Display - for FieldElement> -where - M: IsModulus> + Clone + Debug, -{ - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let value: UnsignedInteger = self.representative(); - write!(f, "{value}") - } -} - -impl FieldElement> -where - M: IsModulus> + Clone + Debug, -{ - /// Creates a `FieldElement` from a hexstring. It can contain `0x` or not. - /// # Panics - /// Panics if value is not a hexstring - pub const fn from_hex_unchecked(hex: &str) -> Self { - let integer = UnsignedInteger::::from_hex_unchecked(hex); - Self { - value: MontgomeryAlgorithms::cios( - &integer, - &MontgomeryBackendPrimeField::::R2, - &M::MODULUS, - &MontgomeryBackendPrimeField::::MU, - ), - } - } -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::elliptic_curve::short_weierstrass::curves::bn_254::field_extension::BN254PrimeField; - use crate::field::fields::fft_friendly::{ - babybear_u32::Babybear31PrimeField, stark_252_prime_field::Stark252PrimeField, - }; - use crate::field::fields::montgomery_backed_prime_fields::U384PrimeField; - use crate::field::fields::u64_prime_field::U64PrimeField; - use crate::field::test_fields::u64_test_field::U64TestField; - #[cfg(feature = "alloc")] - use crate::unsigned_integer::element::UnsignedInteger; - use crate::unsigned_integer::element::U384; - #[cfg(feature = "alloc")] - use alloc::vec::Vec; - use num_bigint::BigUint; - #[cfg(feature = "alloc")] - use proptest::collection; - use proptest::{prelude::*, prop_compose, proptest, strategy::Strategy}; - - #[test] - fn test_std_iter_sum_field_element() { - let n = 164; - const MODULUS: u64 = 18446744069414584321; - assert_eq!( - (0..n) - .map(|x| { FieldElement::::from(x) }) - .sum::>() - .value, - ((n - 1) as f64 / 2. * ((n - 1) as f64 + 1.)) as u64 % MODULUS - ); - } - - #[test] - fn test_std_iter_sum_field_element_zero_length() { - let n = 0; - assert_eq!( - (0..n) - .map(|x| { FieldElement::::from(x) }) - .sum::>() - .value, - 0 - ); - } - - #[cfg(feature = "alloc")] - #[test] - fn test_display_montgomery_field() { - use alloc::format; - - let zero_field_element = FieldElement::::from(0); - assert_eq!(format!("{zero_field_element}"), "0x0"); - - let some_field_element = - FieldElement::::from(&UnsignedInteger::from_limbs([ - 0x0, 0x1, 0x0, 0x1, - ])); - - // it should start with the first non-zero digit. Each limb has 16 digits in hex. - assert_eq!( - format!("{some_field_element}"), - format!("0x{}{}{}{}", "1", "0".repeat(16), "0".repeat(15), "1") - ); - } - - #[test] - fn one_of_sqrt_roots_for_4_is_2() { - type FrField = Stark252PrimeField; - type FrElement = FieldElement; - - let input = FrElement::from(4); - let sqrt = input.sqrt().unwrap(); - let result = FrElement::from(2); - assert_eq!(sqrt.0, result); - } - - #[test] - fn one_of_sqrt_roots_for_5_is_28_mod_41() { - let input = FieldElement::>::from(5); - let sqrt = input.sqrt().unwrap(); - let result = FieldElement::from(28); - assert_eq!(sqrt.0, result); - assert_eq!(sqrt.1, -result); - } - - #[test] - fn one_of_sqrt_roots_for_25_is_5() { - type FrField = Stark252PrimeField; - type FrElement = FieldElement; - let input = FrElement::from(25); - let sqrt = input.sqrt().unwrap(); - let five = FrElement::from(5); - assert!(sqrt.1 == five || sqrt.0 == five); - } - - #[test] - fn sqrt_works_for_prime_minus_one() { - type FrField = Stark252PrimeField; - type FrElement = FieldElement; - - let input = -FrElement::from(1); - let sqrt = input.sqrt().unwrap(); - assert_eq!(sqrt.0.square(), input); - assert_eq!(sqrt.1.square(), input); - assert_ne!(sqrt.0, sqrt.1); - } - - #[test] - fn one_of_sqrt_roots_for_25_is_5_in_stark_field() { - type FrField = Stark252PrimeField; - type FrElement = FieldElement; - - let input = FrElement::from(25); - let sqrt = input.sqrt().unwrap(); - let result = FrElement::from(5); - assert_eq!(sqrt.0, result); - assert_eq!(sqrt.1, -result); - } - - #[test] - fn sqrt_roots_for_0_are_0_in_stark_field() { - type FrField = Stark252PrimeField; - type FrElement = FieldElement; - - let input = FrElement::from(0); - let sqrt = input.sqrt().unwrap(); - let result = FrElement::from(0); - assert_eq!(sqrt.0, result); - assert_eq!(sqrt.1, result); - } - - #[test] - fn sqrt_of_27_for_stark_field_does_not_exist() { - type FrField = Stark252PrimeField; - type FrElement = FieldElement; - - let input = FrElement::from(27); - let sqrt = input.sqrt(); - assert!(sqrt.is_none()); - } - - #[test] - fn from_hex_1a_is_26_for_stark252_prime_field_element() { - type F = Stark252PrimeField; - type FE = FieldElement; - assert_eq!(FE::from_hex("1a").unwrap(), FE::from(26)) - } - - #[test] - fn from_hex_unchecked_zero_x_1a_is_26_for_stark252_prime_field_element() { - type F = Stark252PrimeField; - type FE = FieldElement; - assert_eq!(FE::from_hex_unchecked("0x1a"), FE::from(26)) - } - - #[test] - fn construct_new_field_element_from_empty_string_errs() { - type F = Stark252PrimeField; - type FE = FieldElement; - assert!(FE::from_hex("").is_err()); - } - - #[test] - fn construct_new_field_element_from_value_bigger_than_modulus() { - type F = Stark252PrimeField; - type FE = FieldElement; - // A number that consists of 255 1s is bigger than the `Stark252PrimeField` modulus - assert!(FE::from_hex(&format!("0x{}", "f".repeat(65))).is_err()); - } - - prop_compose! { - fn field_element()(num in any::().prop_filter("Avoid null coefficients", |x| x != &0)) -> FieldElement:: { - FieldElement::::from(num) - } - } - - prop_compose! { - #[cfg(feature = "alloc")] - fn field_vec(max_exp: u8)(vec in collection::vec(field_element(), 0..1 << max_exp)) -> Vec> { - vec - } - } - - proptest! { - #[cfg(feature = "alloc")] - #[test] - fn test_inplace_batch_inverse_returns_inverses(vec in field_vec(10)) { - let input: Vec<_> = vec.into_iter().filter(|x| x != &FieldElement::::zero()).collect(); - let mut inverses = input.clone(); - FieldElement::inplace_batch_inverse(&mut inverses).unwrap(); - - for (i, x) in inverses.into_iter().enumerate() { - prop_assert_eq!(x * input[i], FieldElement::::one()); - } - } - } - - // Tests for BigUint conversion. - // We define different fields to test the conversion. - - // Prime field with modulus 17 and base type u64. - type U64F17 = U64PrimeField<17>; - type U64F17Element = FieldElement; - - // Baby Bear Prime field with u32 montgomery backend. - type BabyBear = Babybear31PrimeField; - type BabyBearElement = FieldElement; - - // Prime field with modulus 23, using u64 montgomery backend of 6 limbs. - #[derive(Clone, Debug)] - struct U384Modulus23; - impl IsModulus for U384Modulus23 { - const MODULUS: U384 = UnsignedInteger::from_u64(23); - } - type U384F23 = U384PrimeField; - type U384F23Element = FieldElement; - - #[test] - fn test_reduced_biguint_conversion_u64_field() { - let value = BigUint::from(10u32); - let fe = U64F17Element::try_from(value.clone()).unwrap(); - let back_to_biguint = fe.to_big_uint(); - assert_eq!(value, back_to_biguint); - } - - #[test] - fn test_reduced_biguint_conversion_baby_bear() { - let value = BigUint::from(1000u32); - let fe = BabyBearElement::from_reduced_big_uint(&value).unwrap(); - assert_eq!(fe, BabyBearElement::from(1000)); - let back_to_biguint = fe.to_big_uint(); - assert_eq!(value, back_to_biguint); - } - - #[test] - fn test_reduced_biguint_conversion_u384_field() { - let value = BigUint::from(22u32); - let fe = U384F23Element::from_reduced_big_uint(&value).unwrap(); - let back_to_biguint = fe.to_big_uint(); - assert_eq!(value, back_to_biguint); - } - #[test] - fn test_bn254_field_biguint_conversion() { - type BN254Element = FieldElement; - let value = BigUint::from(1001u32); - let fe = BN254Element::from_reduced_big_uint(&value).unwrap(); - let back_to_biguint = fe.to_big_uint(); - assert_eq!(value, back_to_biguint); - } - - #[test] - fn non_reduced_biguint_value_conversion_errors_u64_field() { - let value = BigUint::from(17u32); - let result = U64F17Element::from_reduced_big_uint(&value); - assert_eq!(result, Err(ByteConversionError::ValueNotReduced)); - } - - #[test] - fn non_reduced_biguint_value_conversion_errors_baby_bear() { - let value = BigUint::from(2013265921u32); - let result = BabyBearElement::try_from(value); - assert_eq!(result, Err(ByteConversionError::ValueNotReduced)); - } - - #[test] - fn non_reduced_biguint_value_conversion_errors_u384_field() { - let value = BigUint::from(30u32); - let result = U384F23Element::try_from(value); - assert_eq!(result, Err(ByteConversionError::ValueNotReduced)); - } - - #[test] - fn test_hex_string_conversion_u64_field() { - let hex_str = "0x0a"; - let fe = U64F17Element::from_hex_str(hex_str).unwrap(); - assert_eq!(fe, U64F17Element::from(10)); - assert_eq!(fe.to_hex_str(), "0x0A"); - } - - #[test] - fn test_hex_string_conversion_baby_bear() { - let hex_str = "0x77FFFFFF"; // 2013265919 - let fe = BabyBearElement::from_hex_str(hex_str).unwrap(); - assert_eq!(fe, BabyBearElement::from(2013265919)); - assert_eq!(fe.to_hex_str(), "0x77FFFFFF"); - } - - #[test] - fn test_hex_string_conversion_u384_field() { - let hex_str = "0x14"; // 20 - let fe = U384F23Element::from_hex_str(hex_str).unwrap(); - assert_eq!(fe, U384F23Element::from(20)); - assert_eq!(fe.to_hex_str(), "0x14"); - } - - #[test] - fn test_invalid_hex_string_u64_field() { - let hex_str = "0xzz"; - let result = U64F17Element::from_hex_str(hex_str); - assert!(result.is_err()); - } - - #[test] - fn test_invalid_hex_string_baby_bear() { - // modulus = 0x78000001 - let hex_str = "0x78000001"; - let result = BabyBearElement::from_hex_str(hex_str); - assert!(result.is_err()); - } - - #[test] - fn test_empty_hex_string() { - let hex_str = ""; - let result = U64F17Element::from_hex_str(hex_str); - assert!(result.is_err()); - } -} diff --git a/crates/math/src/field/errors.rs b/crates/math/src/field/errors.rs deleted file mode 100644 index 7156e11c3..000000000 --- a/crates/math/src/field/errors.rs +++ /dev/null @@ -1,8 +0,0 @@ -#[derive(Debug)] -pub enum FieldError { - DivisionByZero, - /// Returns order of the calculated root of unity - RootOfUnityError(u64), - /// Can't calculate inverse of zero - InvZeroError, -} diff --git a/crates/math/src/field/extensions/cubic.rs b/crates/math/src/field/extensions/cubic.rs deleted file mode 100644 index 8350ea5d7..000000000 --- a/crates/math/src/field/extensions/cubic.rs +++ /dev/null @@ -1,396 +0,0 @@ -use crate::field::element::FieldElement; -use crate::field::errors::FieldError; -use crate::field::traits::{IsField, IsSubFieldOf}; -use crate::traits::ByteConversion; -use core::fmt::Debug; -use core::marker::PhantomData; - -/// A general cubic extension field over `F` -/// with cubic non residue `Q::residue()` -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct CubicExtensionField { - field: PhantomData, - non_residue: PhantomData, -} - -pub type CubicExtensionFieldElement = FieldElement>; - -/// Trait to fix a cubic non residue. -/// Used to construct a cubic extension field by adding -/// a square root of `residue()`. -pub trait HasCubicNonResidue { - /// This function must return an element that is not a cube in Fp, - /// that is, a cubic non-residue. - fn residue() -> FieldElement; -} - -impl ByteConversion for [FieldElement; 3] -where - F: IsField, -{ - #[cfg(feature = "alloc")] - fn to_bytes_be(&self) -> alloc::vec::Vec { - unimplemented!() - } - - #[cfg(feature = "alloc")] - fn to_bytes_le(&self) -> alloc::vec::Vec { - unimplemented!() - } - - fn from_bytes_be(_bytes: &[u8]) -> Result - where - Self: Sized, - { - unimplemented!() - } - - fn from_bytes_le(_bytes: &[u8]) -> Result - where - Self: Sized, - { - unimplemented!() - } -} - -impl IsField for CubicExtensionField -where - F: IsField, - Q: Clone + Debug + HasCubicNonResidue, -{ - type BaseType = [FieldElement; 3]; - - /// Returns the component wise addition of `a` and `b` - fn add(a: &[FieldElement; 3], b: &[FieldElement; 3]) -> [FieldElement; 3] { - [&a[0] + &b[0], &a[1] + &b[1], &a[2] + &b[2]] - } - - /// Returns the multiplication of `a` and `b` using the following - /// equation: - /// (a0 + a1 * t) * (b0 + b1 * t) = a0 * b0 + a1 * b1 * Q::residue() + (a0 * b1 + a1 * b0) * t - /// where `t.pow(2)` equals `Q::residue()`. - fn mul(a: &[FieldElement; 3], b: &[FieldElement; 3]) -> [FieldElement; 3] { - let v0 = &a[0] * &b[0]; - let v1 = &a[1] * &b[1]; - let v2 = &a[2] * &b[2]; - - [ - &v0 + Q::residue() * ((&a[1] + &a[2]) * (&b[1] + &b[2]) - &v1 - &v2), - (&a[0] + &a[1]) * (&b[0] + &b[1]) - &v0 - &v1 + Q::residue() * &v2, - (&a[0] + &a[2]) * (&b[0] + &b[2]) - v0 + v1 - v2, - ] - } - - /// Returns the component wise subtraction of `a` and `b` - fn sub(a: &[FieldElement; 3], b: &[FieldElement; 3]) -> [FieldElement; 3] { - [&a[0] - &b[0], &a[1] - &b[1], &a[2] - &b[2]] - } - - /// Returns the component wise negation of `a` - fn neg(a: &[FieldElement; 3]) -> [FieldElement; 3] { - [-&a[0], -&a[1], -&a[2]] - } - - /// Returns the multiplicative inverse of `a` - fn inv(a: &[FieldElement; 3]) -> Result<[FieldElement; 3], FieldError> { - let three = FieldElement::::from(3_u64); - let d = a[0].pow(3_u64) - + a[1].pow(3_u64) * Q::residue() - + a[2].pow(3_u64) * Q::residue().pow(2_u64) - - three * &a[0] * &a[1] * &a[2] * Q::residue(); - let inv = d.inv()?; - Ok([ - (a[0].pow(2_u64) - &a[1] * &a[2] * Q::residue()) * &inv, - (-&a[0] * &a[1] + a[2].pow(2_u64) * Q::residue()) * &inv, - (-&a[0] * &a[2] + a[1].pow(2_u64)) * &inv, - ]) - } - - /// Returns the division of `a` and `b` - fn div( - a: &[FieldElement; 3], - b: &[FieldElement; 3], - ) -> Result<[FieldElement; 3], FieldError> { - let b_inv = &Self::inv(b).map_err(|_| FieldError::DivisionByZero)?; - Ok(::mul(a, b_inv)) - } - - /// Returns a boolean indicating whether `a` and `b` are equal component wise. - fn eq(a: &[FieldElement; 3], b: &[FieldElement; 3]) -> bool { - a[0] == b[0] && a[1] == b[1] && a[2] == b[2] - } - - /// Returns the additive neutral element of the field extension. - fn zero() -> [FieldElement; 3] { - [ - FieldElement::zero(), - FieldElement::zero(), - FieldElement::zero(), - ] - } - - /// Returns the multiplicative neutral element of the field extension. - fn one() -> [FieldElement; 3] { - [ - FieldElement::one(), - FieldElement::zero(), - FieldElement::zero(), - ] - } - - /// Returns the element `x * 1` where 1 is the multiplicative neutral element. - fn from_u64(x: u64) -> Self::BaseType { - [ - FieldElement::from(x), - FieldElement::zero(), - FieldElement::zero(), - ] - } - - /// Takes as input an element of BaseType and returns the internal representation - /// of that element in the field. - /// Note: for this case this is simply the identity, because the components - /// already have correct representations. - fn from_base_type(x: [FieldElement; 3]) -> [FieldElement; 3] { - x - } -} - -impl IsSubFieldOf> for F -where - F: IsField, - Q: Clone + Debug + HasCubicNonResidue, -{ - fn mul( - a: &Self::BaseType, - b: & as IsField>::BaseType, - ) -> as IsField>::BaseType { - let c0 = FieldElement::from_raw(F::mul(a, b[0].value())); - let c1 = FieldElement::from_raw(F::mul(a, b[1].value())); - let c2 = FieldElement::from_raw(F::mul(a, b[2].value())); - [c0, c1, c2] - } - - fn add( - a: &Self::BaseType, - b: & as IsField>::BaseType, - ) -> as IsField>::BaseType { - let c0 = FieldElement::from_raw(F::add(a, b[0].value())); - [c0, b[1].clone(), b[2].clone()] - } - - fn div( - a: &Self::BaseType, - b: & as IsField>::BaseType, - ) -> Result< as IsField>::BaseType, FieldError> { - let b_inv = as IsField>::inv(b)?; - Ok(>>::mul( - a, &b_inv, - )) - } - - fn sub( - a: &Self::BaseType, - b: & as IsField>::BaseType, - ) -> as IsField>::BaseType { - let c0 = FieldElement::from_raw(F::sub(a, b[0].value())); - let c1 = FieldElement::from_raw(F::neg(b[1].value())); - let c2 = FieldElement::from_raw(F::neg(b[2].value())); - [c0, c1, c2] - } - - fn embed(a: Self::BaseType) -> as IsField>::BaseType { - [ - FieldElement::from_raw(a), - FieldElement::zero(), - FieldElement::zero(), - ] - } - - #[cfg(feature = "alloc")] - fn to_subfield_vec( - b: as IsField>::BaseType, - ) -> alloc::vec::Vec { - b.into_iter().map(|x| x.to_raw()).collect() - } -} - -#[cfg(test)] -mod tests { - use crate::field::fields::u64_prime_field::{U64FieldElement, U64PrimeField}; - - const ORDER_P: u64 = 13; - - use super::*; - - #[derive(Debug, Clone)] - struct MyCubicNonResidue; - impl HasCubicNonResidue> for MyCubicNonResidue { - fn residue() -> FieldElement> { - -FieldElement::from(11) - } - } - - type FE = U64FieldElement; - type MyFieldExtensionBackend = CubicExtensionField, MyCubicNonResidue>; - #[allow(clippy::upper_case_acronyms)] - type FEE = FieldElement; - - #[test] - fn test_add_1() { - let a = FEE::new([FE::new(0), FE::new(3), FE::new(5)]); - let b = FEE::new([-FE::new(2), FE::new(8), FE::new(10)]); - let expected_result = FEE::new([FE::new(11), FE::new(11), FE::new(15)]); - assert_eq!(a + b, expected_result); - } - - #[test] - fn test_add_2() { - let a = FEE::new([FE::new(12), FE::new(5), FE::new(3)]); - let b = FEE::new([-FE::new(4), FE::new(2), FE::new(8)]); - let expected_result = FEE::new([FE::new(8), FE::new(7), FE::new(11)]); - assert_eq!(a + b, expected_result); - } - - #[test] - fn test_sub_1() { - let a = FEE::new([FE::new(0), FE::new(3), FE::new(3)]); - let b = FEE::new([-FE::new(2), FE::new(8), FE::new(2)]); - let expected_result = FEE::new([FE::new(2), FE::new(8), FE::new(1)]); - assert_eq!(a - b, expected_result); - } - - #[test] - fn test_sub_2() { - let a = FEE::new([FE::new(12), FE::new(5), FE::new(3)]); - let b = FEE::new([-FE::new(4), FE::new(2), FE::new(8)]); - let expected_result = FEE::new([FE::new(16), FE::new(3), FE::new(8)]); - assert_eq!(a - b, expected_result); - } - - #[test] - fn test_mul_1() { - let a = FEE::new([FE::new(0), FE::new(3), FE::new(5)]); - let b = FEE::new([-FE::new(2), FE::new(8), FE::new(6)]); - let expected_result = FEE::new([FE::new(12), FE::new(2), FE::new(1)]); - assert_eq!(a * b, expected_result); - } - - #[test] - fn test_mul_2() { - let a = FEE::new([FE::new(12), FE::new(5), FE::new(11)]); - let b = FEE::new([-FE::new(4), FE::new(2), FE::new(15)]); - let expected_result = FEE::new([FE::new(3), FE::new(9), FE::new(3)]); - assert_eq!(a * b, expected_result); - } - - #[test] - fn test_div_1() { - let a = FEE::new([FE::new(0), FE::new(3), FE::new(2)]); - let b = FEE::new([-FE::new(2), FE::new(8), FE::new(5)]); - let expected_result = FEE::new([FE::new(12), FE::new(6), FE::new(1)]); - assert_eq!((a / b).unwrap(), expected_result); - } - - #[test] - fn test_div_2() { - let a = FEE::new([FE::new(12), FE::new(5), FE::new(4)]); - let b = FEE::new([-FE::new(4), FE::new(2), FE::new(2)]); - let expected_result = FEE::new([FE::new(3), FE::new(8), FE::new(11)]); - assert_eq!((a / b).unwrap(), expected_result); - } - - #[test] - fn test_pow_1() { - let a = FEE::new([FE::new(0), FE::new(3), FE::new(3)]); - let b: u64 = 5; - let expected_result = FEE::new([FE::new(7), FE::new(3), FE::new(1)]); - assert_eq!(a.pow(b), expected_result); - } - - #[test] - fn test_pow_2() { - let a = FEE::new([FE::new(12), FE::new(5), FE::new(3)]); - let b: u64 = 8; - let expected_result = FEE::new([FE::new(5), FE::new(5), FE::new(12)]); - assert_eq!(a.pow(b), expected_result); - } - - #[test] - fn test_inv() { - let a = FEE::new([FE::new(12), FE::new(5), FE::new(3)]); - let expected_result = FEE::new([FE::new(2), FE::new(2), FE::new(3)]); - assert_eq!(a.inv().unwrap(), expected_result); - } - - #[test] - fn test_inv_1() { - let a = FEE::new([FE::new(1), FE::new(0), FE::new(1)]); - let expected_result = FEE::new([FE::new(8), FE::new(3), FE::new(5)]); - assert_eq!(a.inv().unwrap(), expected_result); - } - - #[test] - fn test_add_as_subfield_1() { - let a = FE::new(5); - let b = FEE::new([-FE::new(2), FE::new(8), FE::new(10)]); - let expected_result = FEE::new([FE::new(3), FE::new(8), FE::new(10)]); - assert_eq!(a + b, expected_result); - } - - #[test] - fn test_add_as_subfield_2() { - let a = FE::new(12); - let b = FEE::new([-FE::new(4), FE::new(2), FE::new(8)]); - let expected_result = FEE::new([FE::new(8), FE::new(2), FE::new(8)]); - assert_eq!(a + b, expected_result); - } - - #[test] - fn test_sub_as_subfield_1() { - let a = FE::new(3); - let b = FEE::new([-FE::new(2), FE::new(8), FE::new(2)]); - let expected_result = FEE::new([FE::new(5), FE::new(5), FE::new(11)]); - assert_eq!(a - b, expected_result); - } - - #[test] - fn test_sub_as_subfield_2() { - let a = FE::new(12); - let b = FEE::new([-FE::new(4), FE::new(2), FE::new(3)]); - let expected_result = FEE::new([FE::new(3), FE::new(11), FE::new(10)]); - assert_eq!(a - b, expected_result); - } - - #[test] - fn test_mul_as_subfield_1() { - let a = FE::new(5); - let b = FEE::new([-FE::new(2), FE::new(8), FE::new(6)]); - let expected_result = FEE::new([FE::new(3), FE::new(1), FE::new(4)]); - assert_eq!(a * b, expected_result); - } - - #[test] - fn test_mul_as_subfield_2() { - let a = FE::new(11); - let b = FEE::new([-FE::new(4), FE::new(2), FE::new(15)]); - let expected_result = FEE::new([FE::new(8), FE::new(9), FE::new(9)]); - assert_eq!(a * b, expected_result); - } - - #[test] - fn test_div_as_subfield_1() { - let a = FE::new(2); - let b = FEE::new([-FE::new(2), FE::new(8), FE::new(5)]); - let expected_result = FEE::new([FE::new(8), FE::new(4), FE::new(10)]); - assert_eq!((a / b).unwrap(), expected_result); - } - - #[test] - fn test_div_as_subfield_2() { - let a = FE::new(4); - let b = FEE::new([-FE::new(4), FE::new(2), FE::new(2)]); - let expected_result = FEE::new([FE::new(3), FE::new(6), FE::new(11)]); - assert_eq!((a / b).unwrap(), expected_result); - } -} diff --git a/crates/math/src/field/extensions/mod.rs b/crates/math/src/field/extensions/mod.rs deleted file mode 100644 index bd5584d7a..000000000 --- a/crates/math/src/field/extensions/mod.rs +++ /dev/null @@ -1,2 +0,0 @@ -pub mod cubic; -pub mod quadratic; diff --git a/crates/math/src/field/extensions/quadratic.rs b/crates/math/src/field/extensions/quadratic.rs deleted file mode 100644 index 626b727bc..000000000 --- a/crates/math/src/field/extensions/quadratic.rs +++ /dev/null @@ -1,402 +0,0 @@ -use crate::field::element::FieldElement; -use crate::field::errors::FieldError; -use crate::field::traits::{IsField, IsSubFieldOf}; -use crate::traits::ByteConversion; -use core::fmt::Debug; -use core::marker::PhantomData; - -/// A general quadratic extension field over `F` -/// with quadratic non residue `Q::residue()` -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct QuadraticExtensionField -where - F: IsField, - T: HasQuadraticNonResidue, -{ - field: PhantomData, - non_residue: PhantomData, -} - -pub type QuadraticExtensionFieldElement = FieldElement>; - -/// Trait to fix a quadratic non residue. -/// Used to construct a quadratic extension field by adding -/// a square root of `residue()`. -/// If p is congruent to 3 modulo 4, then -1 is a quadractic non-residue -/// and can be used here -pub trait HasQuadraticNonResidue { - fn residue() -> FieldElement; -} - -impl FieldElement> -where - F: IsField, - Q: Clone + Debug + HasQuadraticNonResidue, -{ - pub fn conjugate(&self) -> Self { - let [a, b] = self.value(); - Self::new([a.clone(), -b]) - } -} - -impl ByteConversion for [FieldElement; 2] -where - F: IsField, -{ - #[cfg(feature = "alloc")] - fn to_bytes_be(&self) -> alloc::vec::Vec { - unimplemented!() - } - - #[cfg(feature = "alloc")] - fn to_bytes_le(&self) -> alloc::vec::Vec { - unimplemented!() - } - - fn from_bytes_be(_bytes: &[u8]) -> Result - where - Self: Sized, - { - unimplemented!() - } - - fn from_bytes_le(_bytes: &[u8]) -> Result - where - Self: Sized, - { - unimplemented!() - } -} - -impl IsField for QuadraticExtensionField -where - F: IsField, - Q: Clone + Debug + HasQuadraticNonResidue, -{ - type BaseType = [FieldElement; 2]; - - /// Returns the component wise addition of `a` and `b` - fn add(a: &[FieldElement; 2], b: &[FieldElement; 2]) -> [FieldElement; 2] { - [&a[0] + &b[0], &a[1] + &b[1]] - } - - /// Returns the multiplication of `a` and `b` using the following - /// equation: - /// (a0 + a1 * t) * (b0 + b1 * t) = a0 * b0 + a1 * b1 * Q::residue() + (a0 * b1 + a1 * b0) * t - /// where `t.pow(2)` equals `Q::residue()`. - fn mul(a: &[FieldElement; 2], b: &[FieldElement; 2]) -> [FieldElement; 2] { - let q = Q::residue(); - let a0b0 = &a[0] * &b[0]; - let a1b1 = &a[1] * &b[1]; - let z = (&a[0] + &a[1]) * (&b[0] + &b[1]); - [&a0b0 + &a1b1 * q, z - a0b0 - a1b1] - } - - fn square(a: &[FieldElement; 2]) -> [FieldElement; 2] { - let [a0, a1] = a; - let v0 = a0 * a1; - let c0 = (a0 + a1) * (a0 + Q::residue() * a1) - &v0 - Q::residue() * &v0; - let c1 = &v0 + &v0; - [c0, c1] - } - - /// Returns the component wise subtraction of `a` and `b` - fn sub(a: &[FieldElement; 2], b: &[FieldElement; 2]) -> [FieldElement; 2] { - [&a[0] - &b[0], &a[1] - &b[1]] - } - - /// Returns the component wise negation of `a` - fn neg(a: &[FieldElement; 2]) -> [FieldElement; 2] { - [-&a[0], -&a[1]] - } - - /// Returns the multiplicative inverse of `a` - /// This uses the equality `(a0 + a1 * t) * (a0 - a1 * t) = a0.pow(2) - a1.pow(2) * Q::residue()` - fn inv(a: &[FieldElement; 2]) -> Result<[FieldElement; 2], FieldError> { - let inv_norm = (a[0].pow(2_u64) - Q::residue() * a[1].pow(2_u64)).inv()?; - Ok([&a[0] * &inv_norm, -&a[1] * inv_norm]) - } - - /// Returns the division of `a` and `b` - fn div( - a: &[FieldElement; 2], - b: &[FieldElement; 2], - ) -> Result<[FieldElement; 2], FieldError> { - let b_inv = &Self::inv(b).map_err(|_| FieldError::DivisionByZero)?; - Ok(::mul(a, b_inv)) - } - - /// Returns a boolean indicating whether `a` and `b` are equal component wise. - fn eq(a: &[FieldElement; 2], b: &[FieldElement; 2]) -> bool { - a[0] == b[0] && a[1] == b[1] - } - - /// Returns the additive neutral element of the field extension. - fn zero() -> [FieldElement; 2] { - [FieldElement::zero(), FieldElement::zero()] - } - - /// Returns the multiplicative neutral element of the field extension. - fn one() -> [FieldElement; 2] { - [FieldElement::one(), FieldElement::zero()] - } - - /// Returns the element `x * 1` where 1 is the multiplicative neutral element. - fn from_u64(x: u64) -> Self::BaseType { - [FieldElement::from(x), FieldElement::zero()] - } - - /// Takes as input an element of BaseType and returns the internal representation - /// of that element in the field. - /// Note: for this case this is simply the identity, because the components - /// already have correct representations. - fn from_base_type(x: [FieldElement; 2]) -> [FieldElement; 2] { - x - } -} - -impl IsSubFieldOf> for F -where - F: IsField, - Q: Clone + Debug + HasQuadraticNonResidue, -{ - fn mul( - a: &Self::BaseType, - b: & as IsField>::BaseType, - ) -> as IsField>::BaseType { - let c0 = FieldElement::from_raw(F::mul(a, b[0].value())); - let c1 = FieldElement::from_raw(F::mul(a, b[1].value())); - [c0, c1] - } - - fn add( - a: &Self::BaseType, - b: & as IsField>::BaseType, - ) -> as IsField>::BaseType { - let c0 = FieldElement::from_raw(F::add(a, b[0].value())); - [c0, b[1].clone()] - } - - fn div( - a: &Self::BaseType, - b: & as IsField>::BaseType, - ) -> Result< as IsField>::BaseType, FieldError> { - let b_inv = as IsField>::inv(b)?; - Ok(>>::mul( - a, &b_inv, - )) - } - - fn sub( - a: &Self::BaseType, - b: & as IsField>::BaseType, - ) -> as IsField>::BaseType { - let c0 = FieldElement::from_raw(F::sub(a, b[0].value())); - let c1 = FieldElement::from_raw(F::neg(b[1].value())); - [c0, c1] - } - - fn embed(a: Self::BaseType) -> as IsField>::BaseType { - [FieldElement::from_raw(a), FieldElement::zero()] - } - - #[cfg(feature = "alloc")] - fn to_subfield_vec( - b: as IsField>::BaseType, - ) -> alloc::vec::Vec { - b.into_iter().map(|x| x.to_raw()).collect() - } -} - -impl> - FieldElement> -{ -} - -#[cfg(test)] -mod tests { - use crate::field::fields::u64_prime_field::{U64FieldElement, U64PrimeField}; - - const ORDER_P: u64 = 59; - - use super::*; - - #[derive(Debug, Clone)] - struct MyQuadraticNonResidue; - impl HasQuadraticNonResidue> for MyQuadraticNonResidue { - fn residue() -> FieldElement> { - -FieldElement::one() - } - } - - type FE = U64FieldElement; - type MyFieldExtensionBackend = - QuadraticExtensionField, MyQuadraticNonResidue>; - #[allow(clippy::upper_case_acronyms)] - type FEE = FieldElement; - - #[test] - fn test_add_1() { - let a = FEE::new([FE::new(0), FE::new(3)]); - let b = FEE::new([-FE::new(2), FE::new(8)]); - let expected_result = FEE::new([FE::new(57), FE::new(11)]); - assert_eq!(a + b, expected_result); - } - - #[test] - fn test_add_2() { - let a = FEE::new([FE::new(12), FE::new(5)]); - let b = FEE::new([-FE::new(4), FE::new(2)]); - let expected_result = FEE::new([FE::new(8), FE::new(7)]); - assert_eq!(a + b, expected_result); - } - - #[test] - fn test_sub_1() { - let a = FEE::new([FE::new(0), FE::new(3)]); - let b = FEE::new([-FE::new(2), FE::new(8)]); - let expected_result = FEE::new([FE::new(2), FE::new(54)]); - assert_eq!(a - b, expected_result); - } - - #[test] - fn test_sub_2() { - let a = FEE::new([FE::new(12), FE::new(5)]); - let b = FEE::new([-FE::new(4), FE::new(2)]); - let expected_result = FEE::new([FE::new(16), FE::new(3)]); - assert_eq!(a - b, expected_result); - } - - #[test] - fn test_mul_1() { - let a = FEE::new([FE::new(0), FE::new(3)]); - let b = FEE::new([-FE::new(2), FE::new(8)]); - let expected_result = FEE::new([FE::new(35), FE::new(53)]); - assert_eq!(a * b, expected_result); - } - - #[test] - fn test_mul_2() { - let a = FEE::new([FE::new(12), FE::new(5)]); - let b = FEE::new([-FE::new(4), FE::new(2)]); - let expected_result = FEE::new([FE::new(1), FE::new(4)]); - assert_eq!(a * b, expected_result); - } - - #[test] - fn test_div_1() { - let a = FEE::new([FE::new(0), FE::new(3)]); - let b = FEE::new([-FE::new(2), FE::new(8)]); - let expected_result = FEE::new([FE::new(42), FE::new(19)]); - assert_eq!((a / b).unwrap(), expected_result); - } - - #[test] - fn test_div_2() { - let a = FEE::new([FE::new(12), FE::new(5)]); - let b = FEE::new([-FE::new(4), FE::new(2)]); - let expected_result = FEE::new([FE::new(4), FE::new(45)]); - assert_eq!((a / b).unwrap(), expected_result); - } - - #[test] - fn test_pow_1() { - let a = FEE::new([FE::new(0), FE::new(3)]); - let b: u64 = 5; - let expected_result = FEE::new([FE::new(0), FE::new(7)]); - assert_eq!(a.pow(b), expected_result); - } - - #[test] - fn test_pow_2() { - let a = FEE::new([FE::new(12), FE::new(5)]); - let b: u64 = 8; - let expected_result = FEE::new([FE::new(52), FE::new(35)]); - assert_eq!(a.pow(b), expected_result); - } - - #[test] - fn test_inv_1() { - let a = FEE::new([FE::new(0), FE::new(3)]); - let expected_result = FEE::new([FE::new(0), FE::new(39)]); - assert_eq!(a.inv().unwrap(), expected_result); - } - - #[test] - fn test_inv() { - let a = FEE::new([FE::new(12), FE::new(5)]); - let expected_result = FEE::new([FE::new(28), FE::new(8)]); - assert_eq!(a.inv().unwrap(), expected_result); - } - - #[test] - fn test_conjugate() { - let a = FEE::new([FE::new(12), FE::new(5)]); - let expected_result = FEE::new([FE::new(12), -FE::new(5)]); - assert_eq!(a.conjugate(), expected_result); - } - - #[test] - fn test_add_as_subfield_1() { - let a = -FE::new(2); - let b = FEE::new([FE::new(0), FE::new(3)]); - let expected_result = FEE::new([FE::new(57), FE::new(3)]); - assert_eq!(a + b, expected_result); - } - - #[test] - fn test_add_as_subfield_2() { - let a = -FE::new(4); - let b = FEE::new([FE::new(12), FE::new(5)]); - let expected_result = FEE::new([FE::new(8), FE::new(5)]); - assert_eq!(a + b, expected_result); - } - - #[test] - fn test_sub_as_subfield_1() { - let a = FE::new(0); - let b = FEE::new([-FE::new(2), FE::new(8)]); - let expected_result = FEE::new([FE::new(2), FE::new(51)]); - assert_eq!(a - b, expected_result); - } - - #[test] - fn test_sub_a_subfield_2() { - let a = FE::new(12); - let b = FEE::new([-FE::new(4), -FE::new(2)]); - let expected_result = FEE::new([FE::new(16), FE::new(2)]); - assert_eq!(a - b, expected_result); - } - - #[test] - fn test_mul_as_subfield_1() { - let a = FE::new(2); - let b = FEE::new([-FE::new(2), FE::new(8)]); - let expected_result = FEE::new([FE::new(55), FE::new(16)]); - assert_eq!(a * b, expected_result); - } - - #[test] - fn test_mul_as_subfield_2() { - let a = FE::new(12); - let b = FEE::new([-FE::new(4), FE::new(2)]); - let expected_result = FEE::new([FE::new(11), FE::new(24)]); - assert_eq!(a * b, expected_result); - } - - #[test] - fn test_div_as_subfield_1() { - let a = FE::new(3); - let b = FEE::new([-FE::new(2), FE::new(8)]); - let expected_result = FEE::new([FE::new(19), FE::new(17)]); - assert_eq!((a / b).unwrap(), expected_result); - } - - #[test] - fn test_div_as_subfield_2() { - let a = FE::new(22); - let b = FEE::new([FE::new(4), FE::new(2)]); - let expected_result = FEE::new([FE::new(28), FE::new(45)]); - assert_eq!((a / b).unwrap(), expected_result); - } -} diff --git a/crates/math/src/field/fields/binary/README.md b/crates/math/src/field/fields/binary/README.md deleted file mode 100644 index e6d5bcef4..000000000 --- a/crates/math/src/field/fields/binary/README.md +++ /dev/null @@ -1,165 +0,0 @@ -# Binary Fields - -This module implements binary fields of the form $GF(2^{2^n})$ (i.e. a finite field with $2^{2^n}$ elements) by constructing a tower of field extensions. It doesn't implement `IsField` or `FieldElement`, because we wanted elements from different field extensions to coexist within the same struct, allowing us to optimize each operation to work simultaneously across all levels. - -Binary fields are particularly useful for verifiable computing applications, including SNARKs and STARKs, due to their efficient implementation and favorable properties. - -## Overview - -The tower of binary fields provides a powerful alternative to prime fields for cryptographic applications. This implementation represents field elements as multivariable polynomials with binary coefficients in $GF(2) = \{0, 1\}$, where these coefficients are stored as bits in a `u128` integer. The tower structure is built recursively, with each level representing an extension of the previous field. - -Key features of this implementation: - -- Supports field extensions from level 0 $(GF(2))$ to level 7 $(GF(2^{128}))$. -- Efficient arithmetic operations optimized for binary fields. -- Karatsuba optimization for multiplication. - -## Implementation Details - -### Tower Construction - -Let's explain the theory behind. To expand the binary field $GF(2) = \{0, 1\}$, we construct a tower of field extensions in the following way: - -**Level 0:** The tower starts at level 0 with the base field of two elements $GF(2^{2^0}) = \{0, 1\}$. - -**Level 1:** Then at level 1, we define the field extension $GF(2^{2^1})$ whose elements are univariate polynomials with binary coefficients and variable $x_0$ such that ${x_0}^2 = x_0 + 1$. Note that this means that the polynomials are lineal (have degree at most 1). Therefore, this field extension has $2^{2^1}$ elements. We represent them as the binary expression of integers: - -$$\begin{aligned} -00 &= 0 \\ -01 &= 1 \\ -10 &= x_0 \\ -11 &= x_0 + 1 -\end{aligned}$$ - -**Level 2:** At level 2, we define the field extension $GF(2^{2^2})$. In this case the elements are polynomials with binary coefficients and two variables, $x_0$ and $x_1$. The first one keeps satisfying ${x_0}^2 = x_0 + 1$ and in addition the second one satisfies ${x_1}^2 = x_1 \cdot x_0 + 1$. This means that the polynomials are lineal in each variable. Therefore, this field extension has $2^{2^2}$ elements: - -$\begin{array}{llll} -0000 = 0 & 0100 = x_1 & 1000 = x_1x_0 & 1100 = x_1x_0 + x_1 \\ -0001 = 1 & 0101 = x_1 + 1 & 1001 = x_1x_0 + 1 & 1101 = x_1x_0 + x_1 + 1 \\ -0010 = x_0 & 0110 = x_1 + x_0 & 1010 = x_1x_0 + x_0 & 1110 = x_1x_0 + x_1 + x_0 \\ -0011 = x_0 + 1 & 0111 = x_1 + x_0 + 1 & 1011 = x_1x_0 + x_0 + 1 & 1111 = x_1x_0 + x_1 + x_0 + 1 -\end{array}$ - -**Level 3:** At level 3, we define $GF(2^{2^3})$ in the same way. This time the polynomials have three variables $x_0$, $x_1$ and $x_2$. The first two variables satisfy the equations mentioned before and in addition the last one satisfies ${x_2}^2 = x_2 \cdot x_1 + 1$. This field extension has $2^{2^3}$ elements. - -**Level $n$:** Continuing this argument, in each level $n$ we define the field extension $GF(2^{2^n})$ using polynomials of $n$ variables with ${x_i}^2 = x_i \cdot x_{i-1} + 1$. - -Our implementation admits until level $n = 7$. - - - -### Element Representation - -A `TowerFieldElement` is represented by: - -- `value`: A `u128` integer where the bits represent the coefficients of the polynomial. -- `num_level`: The level in the tower (0-7). - -For example, if `value = 0b1101` and `num_level = 2`, this represents the polynomial $x_0\cdot x_1 + x_1 + 1$ in $GF(2^4)$. - - -### Field Operations - -The implementation provides efficient algorithms for: - -- Addition and subtraction (XOR operations). -- Multiplication using a recursive tower approach with Karatsuba optimization. -- Inversion using Fermat's Little Theorem. -- Exponentiation using square-and-multiply. - -## API Usage - -### Creating Field Elements - -The method `new` handles possible overflows in this way: -- If the level input is greater than 7, then the element's `num_level` is set as 7. -- If the value input doesn't fit in the level given, then we take just the less significant bits that fit in that level and set it as the element's `value`. - -```rust -use lambdaworks_math::field::fields::binary::field::TowerFieldElement; - -// Create elements at different tower levels -let element_level_0 = TowerFieldElement::new(1, 0); // Element '1' in GF(2) -let element_level_1 = TowerFieldElement::new(3, 1); // Element '11' in GF(2^2) -let element_level_2 = TowerFieldElement::new(123, 2); // Element '1011' in GF(2^4) -let element_level_3 = TowerFieldElement::new(123, 3); // Element '01111011'in GF(2^8) -let element_level_7 = TowerFieldElement::new(123, 25); // Element '0..01111011'in GF(2^128) - -// Create zero and one -let zero = TowerFieldElement::zero(); -let one = TowerFieldElement::one(); - -// Create from integer values -let from_u64 = TowerFieldElement::from(42u64); -``` - -### Basic Operations - -```rust -use lambdaworks_math::field::fields::binary::field::TowerFieldElement; - -// Create two elements -let a = TowerFieldElement::new(5, 2); // '0101' in GF(2^4) -let b = TowerFieldElement::new(3, 2); // '0011' in GF(2^4) - -// Addition (XOR operation) -let sum = a + b; // '0110' = 6 - -// Subtraction (same as addition in binary fields) -let difference = a - b; // '0110' = 6 - -// Multiplication -let product = a * b; // '1111' = 15 - -// Inversion -let a_inverse = a.inv().unwrap(); -assert_eq!(a * a_inverse, TowerFieldElement::one()); - -// Exponentiation -let a_cubed = a.pow(3); -``` - -### Working with Different Levels - -```rust -use lambdaworks_math::field::fields::binary::field::TowerFieldElement; - -// Elements at different levels -let a = TowerFieldElement::new(3, 1); // Level 1: GF(2^2) -let b = TowerFieldElement::new(5, 2); // Level 2: GF(2^4) - -// Operations automatically promote to the higher level -let sum = a + b; // Result is at level 2 -assert_eq!(sum.num_level(), 2); - -// Splitting and joining elements -let element = TowerFieldElement::new(0b1010, 2); // Level 2 -let (hi, lo) = element.split(); // Split into two level 1 elements -assert_eq!(hi.value(), 0b10); // High part: '10' -assert_eq!(lo.value(), 0b10); // Low part: '10' - -// Join back -let rejoined = hi.join(&lo); -assert_eq!(rejoined, element); -``` - -## Applications - -Binary tower fields are particularly useful for: - -1. **SNARKs and STARKs**: These fields enable efficient proof systems, especially when working with binary operations. - -2. **Binius**: A SNARK system that leverages binary fields for improved performance and simplicity. - - -## Performance Considerations - -- Operations in binary fields are generally faster than in prime fields for many applications. -- XOR-based addition/subtraction is extremely efficient. -- The tower structure enables optimized implementations of multiplication and other operations. - -## References - -- [SNARKs on Binary Fields: Binius](https://blog.lambdaclass.com/snarks-on-binary-fields-binius/) - LambdaClass Blog -- [Binius: SNARKs on Binary Fields](https://vitalik.eth.limo/general/2024/04/29/binius.html) - Vitalik Buterin's explanation -- [Binary Tower Fields are the Future of Verifiable Computing](https://www.irreducible.com/posts/binary-tower-fields-are-the-future-of-verifiable-computing) - Irreducible diff --git a/crates/math/src/field/fields/binary/field.rs b/crates/math/src/field/fields/binary/field.rs deleted file mode 100644 index 6f9562e93..000000000 --- a/crates/math/src/field/fields/binary/field.rs +++ /dev/null @@ -1,595 +0,0 @@ -use core::cmp::Ordering; -use core::fmt; -use core::iter::{Product, Sum}; -use core::ops::{Add, AddAssign, Mul, MulAssign, Neg, Sub}; - -// Implementation of binary fields of the form GF(2^{2^n}) (i.e. a finite field of 2^{2^n} elements) by constructing a tower of field extensions. -// The basic idea is to represent an element of each field as a multi-variable polynomial with binary coefficients in GF(2) = {0, 1}. -// The coefficients of each polynomial are stored as bits in a `u128` integer. -// The tower structure is built recursively, with each level representing an extension of the previous field. -// In each level n, polynomials have n variables that satisfy: -// (x_i)² = x_i * x_{i-1} + 1 - -// For more details, see: -// - Lambdaclass blog post about the use of binary fields in SNARKs: https://blog.lambdaclass.com/snarks-on-binary-fields-binius/ -// - Vitalik Buterin's Binius: https://vitalik.eth.limo/general/2024/04/29/binius.html - -#[derive(Debug)] -pub enum BinaryFieldError { - /// Attempt to compute inverse of zero - InverseOfZero, -} - -#[derive(Clone, Copy, Debug)] -/// An element in the tower of binary field extensions from level 0 to level 7. -/// -/// Implements arithmetic in finite fields GF(2^{2^n}) where n is the level of the field extension in the tower. -/// -/// The internal representation stores polynomial coefficients as bits in a u128 integer. -#[derive(Default)] -pub struct TowerFieldElement { - /// The value of the element. - /// The binary expression of this value represents the coefficients of the corresponding polynomial of the element. - /// For example, if value = 0b1101, then p = xy + y + 1. If value = 0b0110, then p = y + x. - pub value: u128, - /// Number of the level in the tower. - /// It tells us to which field extension the element belongs. - /// It goes from 0 (representing the base field of two elements) to 7 (representing the field extension of 2^128 elements). - pub num_level: usize, -} - -impl TowerFieldElement { - /// Constructor that always succeeds by masking the value if it is too big for the given - /// num_level, and limiting the level so that is not greater than 7. - pub fn new(val: u128, num_level: usize) -> Self { - // Limit num_level to a maximum valid value for u128. - let safe_level = if num_level > 7 { 7 } else { num_level }; - - // The number of bits needed for the given level - let bits = 1 << safe_level; - let mask = if bits >= 128 { - u128::MAX - } else { - (1 << bits) - 1 - }; - - Self { - // We take just the lsb of val that fit in the extension field we are. - value: val & mask, - num_level: safe_level, - } - } - - /// Returns true if the element is zero - pub fn is_zero(&self) -> bool { - self.value == 0 - } - - /// Returns true if this element is one - #[inline] - pub fn is_one(&self) -> bool { - self.value == 1 - } - - /// Returns the underlying value - #[inline] - pub fn value(&self) -> u128 { - self.value - } - - /// Returns level number in the tower. - #[inline] - pub fn num_level(&self) -> usize { - self.num_level - } - - /// Returns the number of bits needed for that level (2^num_levels). - /// Note that the order of the extension field in that level is 2^num_bits. - #[inline] - pub fn num_bits(&self) -> usize { - 1 << self.num_level() - } - - /// Returns binary string representation - #[cfg(feature = "std")] - pub fn to_binary_string(&self) -> String { - format!("{:0width$b}", self.value, width = self.num_bits()) - } - - /// Splits element into high and low parts. - /// For example, if a = xy + y + x, then a = (x + 1)y + x and - /// therefore, a_hi = x + 1 and a_lo = x. - pub fn split(&self) -> (Self, Self) { - let half_bits = self.num_bits() / 2; - let mask = (1 << half_bits) - 1; - let lo = self.value() & mask; - let hi = (self.value() >> half_bits) & mask; - - ( - Self::new(hi, self.num_level() - 1), - Self::new(lo, self.num_level() - 1), - ) - } - - /// Joins the hi and low part making a new element of a bigger level. - /// For example, if a_hi = x and a_low = 1 - /// then a = xy + 1. - pub fn join(&self, low: &Self) -> Self { - let joined = (self.value() << self.num_bits()) | low.value(); - Self::new(joined, self.num_level() + 1) - } - - // It embeds an element in an extension changing the level number. - pub fn extend_num_level(&mut self, new_level: usize) { - if self.num_level() < new_level { - self.num_level = new_level; - } - } - - /// Create a zero element - pub fn zero() -> Self { - Self::new(0, 0) - } - - /// Create a one element - pub fn one() -> Self { - Self::new(1, 0) - } - - /// Addition between elements of same or different levels. - fn add_elements(&self, other: &Self) -> Self { - let num_level = self.num_level().max(other.num_level()); - Self::new(self.value() ^ other.value(), num_level) - } - - // Multiplies a and b in the following way: - // - // - If a and b are from the same level: - // a = a_hi * x_n + a_lo - // b = b_hi * x_n + b_lo - // Then a * b = (b_hi * a_hi * x_{n-1} + b_hi * a_lo + a_hi * b_lo ) * x_n + b_hi * a_hi + a_lo * b_lo. - // We calculate each product in the equation below using recursion. - // - // - if a's level is larger than b's level, we partition a until we have parts of the size of b and - // multiply each part by b. - fn mul(self, other: Self) -> Self { - match self.num_level().cmp(&other.num_level()) { - Ordering::Greater => { - // We split a into two parts and call the same method to multiply each part by b. - let (a_hi, a_lo) = self.split(); - // Join a_hi * b and a_lo * b. - a_hi.mul(other).join(&a_lo.mul(other)) - } - Ordering::Less => { - // If b is larger than a, we swap the arguments and call the same method. - other.mul(self) - } - Ordering::Equal => { - // Base case: - if self.num_level() == 0 { - // In the binary base field, multiplication is the same as AND operation. - return Self::new(self.value() & other.value(), 0); - } - - // Split both elements into high and low parts - let (a_high, a_low) = self.split(); - let (b_high, b_low) = other.split(); - - // Step 1: Compute sub-products - let low_product = a_low.mul(b_low); // a_low * b_low - let high_product = a_high.mul(b_high); // a_high * b_high - - // Step 2: Get the polynomial x_{n-1} value - let x_value = if self.num_level() == 1 { - Self::new(1, 0) - } else { - Self::new(1 << (self.num_bits() / 4), self.num_level() - 1) - }; - - // Step 3: Compute high_product * x_{n-1} - let shifted_high_product = high_product.mul(x_value); - - // Step 4: Karatsuba optimization for middle term - // Instead of computing a_high * b_low + a_low * b_high directly, - // we use (a_low + a_high) * (b_low + b_high) - low_product - high_product - let sum_product = (a_low + a_high).mul(b_low + b_high); - let middle_term = sum_product - low_product - high_product; - - // Step 5: Join the parts according to the tower field multiplication formula - (shifted_high_product + middle_term).join(&(high_product + low_product)) - } - } - } - - /// Computes the multiplicative inverse using Fermat's little theorem. - /// Returns an error if the element is zero. - // Based on Ingoyama's implementation - // https://github.com/ingonyama-zk/smallfield-super-sumcheck/blob/a8c61beef39bc0c10a8f68d25eeac0a7190a7289/src/tower_fields/binius.rs#L116C5-L116C6 - pub fn inv(&self) -> Result { - if self.is_zero() { - return Err(BinaryFieldError::InverseOfZero); - } - if self.num_level() <= 1 || self.num_bits() <= 4 { - let exponent = (1 << self.num_bits()) - 2; - Ok(Self::pow(self, exponent as u32)) - } else { - let (a_hi, a_lo) = self.split(); - let two_pow_k_minus_one = Self::new(1 << (self.num_bits() / 4), self.num_level() - 1); - // a = a_hi * x^k + a_lo - // a_lo_next = a_hi * x^(k-1) + a_lo - let a_lo_next = a_lo + a_hi * two_pow_k_minus_one; - - // Δ = a_lo * a_lo_next + a_hi^2 - let delta = a_lo * a_lo_next + a_hi * a_hi; - - // Compute inverse of delta recursively - let delta_inverse = delta.inv()?; - - // Compute parts of the inverse - let out_hi = delta_inverse * a_hi; - let out_lo = delta_inverse * a_lo_next; - - // Join the parts to get the final inverse - Ok(out_hi.join(&out_lo)) - } - } - - /// Calculate power. - pub fn pow(&self, exp: u32) -> Self { - let mut result = Self::one(); - let mut base = *self; - let mut exp_val = exp; - - while exp_val > 0 { - if exp_val & 1 == 1 { - result *= base; - } - base = base * base; - exp_val >>= 1; - } - - result - } -} - -impl PartialEq for TowerFieldElement { - fn eq(&self, other: &Self) -> bool { - self.value() == other.value() - } -} - -impl Eq for TowerFieldElement {} - -impl Add for TowerFieldElement { - type Output = Self; - - fn add(self, other: Self) -> Self { - // Use the helper method that takes references - self.add_elements(&other) - } -} - -impl<'a> Add<&'a TowerFieldElement> for &'a TowerFieldElement { - type Output = TowerFieldElement; - - fn add(self, other: &'a TowerFieldElement) -> TowerFieldElement { - // Directly use the helper method - self.add_elements(other) - } -} - -impl AddAssign for TowerFieldElement { - fn add_assign(&mut self, other: Self) { - *self = *self + other; - } -} -#[allow(clippy::suspicious_arithmetic_impl)] -impl Sub for TowerFieldElement { - type Output = Self; - - fn sub(self, other: Self) -> Self { - // In binary fields, subtraction is the same as addition - self + other - } -} - -impl Neg for TowerFieldElement { - type Output = Self; - - fn neg(self) -> Self { - // In binary fields, negation is the identity - self - } -} - -impl Mul for TowerFieldElement { - type Output = Self; - - fn mul(self, other: Self) -> Self { - self.mul(other) - } -} - -impl Mul<&TowerFieldElement> for &TowerFieldElement { - type Output = TowerFieldElement; - - fn mul(self, other: &TowerFieldElement) -> TowerFieldElement { - >::mul(*self, *other) - } -} - -impl MulAssign for TowerFieldElement { - fn mul_assign(&mut self, other: Self) { - *self = *self * other; - } -} - -impl Product for TowerFieldElement { - fn product(iter: I) -> Self - where - I: Iterator, - { - iter.fold(Self::one(), |acc, x| acc * x) - } -} - -impl Sum for TowerFieldElement { - fn sum(iter: I) -> Self - where - I: Iterator, - { - iter.fold(Self::zero(), |acc, x| acc + x) - } -} - -impl fmt::Display for TowerFieldElement { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{}", self.value) - } -} - -impl From for TowerFieldElement { - fn from(val: u128) -> Self { - TowerFieldElement::new(val, 7) - } -} - -impl From for TowerFieldElement { - fn from(val: u64) -> Self { - TowerFieldElement::new(val as u128, 6) - } -} - -impl From for TowerFieldElement { - fn from(val: u32) -> Self { - TowerFieldElement::new(val as u128, 5) - } -} - -impl From for TowerFieldElement { - fn from(val: u16) -> Self { - TowerFieldElement::new(val as u128, 4) - } -} - -impl From for TowerFieldElement { - fn from(val: u8) -> Self { - TowerFieldElement::new(val as u128, 3) - } -} - -#[cfg(test)] -mod tests { - use super::*; - use proptest::prelude::*; - - #[test] - fn test_new_safe() { - // Test with level too large - let elem = TowerFieldElement::new(0, 8); - assert_eq!(elem.num_level, 7); // Should be capped at 7 - - // Test with value too large for level - let elem = TowerFieldElement::new(4, 1); // Level 1 can only store 0-3 - assert_eq!(elem.value, 0); // Should mask to 0 (100 & 11 = 00) - } - - #[test] - fn test_addition() { - let a = TowerFieldElement::new(5, 9); // 8 bits - let b = TowerFieldElement::new(3, 2); // 4 bits - - let c = a + b; - // 5 (0101) + 3 (0011) should be 6 (0110) at level 3 - assert_eq!(c.value, 6); - assert_eq!(c.num_level, 7); - - // Test commutative property - let d = b + a; - assert_eq!(d, c); - } - - #[test] - fn mul_in_level_0() { - let a = TowerFieldElement::new(0, 0); - let b = TowerFieldElement::new(1, 0); - assert_eq!(a * a, a); - assert_eq!(a * b, a); - assert_eq!(b * b, b); - } - - #[test] - fn mul_in_level_1() { - let a = TowerFieldElement::new(0b00, 1); // 0 - let b = TowerFieldElement::new(0b01, 1); // 1 - let c = TowerFieldElement::new(0b10, 1); // x - let d = TowerFieldElement::new(0b11, 1); // x + 1 - assert_eq!(a * a, a); - assert_eq!(a * b, a); - assert_eq!(b * c, c); - assert_eq!(c * d, b); - } - - #[test] - fn mul_in_level_2() { - let a = TowerFieldElement::new(0b0000, 2); // 0 - let b = TowerFieldElement::new(0b0001, 2); // 1 - let c = TowerFieldElement::new(0b0010, 2); // x - let d = TowerFieldElement::new(0b0011, 2); // x + 1 - let e = TowerFieldElement::new(0b0100, 2); // y - let f = TowerFieldElement::new(0b0101, 2); // y + 1 - let g = TowerFieldElement::new(0b0110, 2); // y + x - let h = TowerFieldElement::new(0b0111, 2); // y + x + 1 - let i = TowerFieldElement::new(0b1000, 2); // yx - let j = TowerFieldElement::new(0b1001, 2); // yx + 1 - let k = TowerFieldElement::new(0b1010, 2); // yx + x - let l = TowerFieldElement::new(0b1011, 2); // yx + x + 1 - let n = TowerFieldElement::new(0b1100, 2); // yx + y - let m = TowerFieldElement::new(0b1101, 2); // yx + y + 1 - let o = TowerFieldElement::new(0b1110, 2); // yx + y + x - let p = TowerFieldElement::new(0b1111, 2); // yx + y + x + 1 - - assert_eq!(a * p, a); // 0 * (yx + y + x + 1) = 0 - assert_eq!(a * l, a); // 0 * (yx + x + 1) = 0 - assert_eq!(b * m, m); // 1 * 1 = 1 - assert_eq!(c * e, i); // x * y = xy - assert_eq!(c * c, d); // x * x = x + 1 - assert_eq!(g * h, n); //(y + x)(y + x + 1) = yx + y - assert_eq!(k * j, b); // (yx + x)(yx + 1) = 1 - assert_eq!(j * f, d); // (yx + 1)(y + 1) = x + 1 - assert_eq!(e * e, j); // y * y = yx + 1 - assert_eq!(n * o, k); // (yx + y)(yx + y + x) = yx + x - } - - #[test] - fn mul_between_different_levels() { - let a = TowerFieldElement::new(0b10, 1); // x - let b = TowerFieldElement::new(0b0100, 2); // y - let c = TowerFieldElement::new(0b1000, 2); // yx - assert_eq!(a * b, c); - } - - #[test] - fn test_correct_level_mul() { - let a = TowerFieldElement::new(0b1111, 5); - let b = TowerFieldElement::new(0b1010, 2); - assert_eq!((a * b).num_level, 5); - } - - #[test] - fn mul_is_asociative() { - let a = TowerFieldElement::new(83, 7); - let b = TowerFieldElement::new(31, 5); - let c = TowerFieldElement::new(3, 2); - let ab = a * b; - let bc = b * c; - assert_eq!(ab * c, a * bc); - } - - #[test] - fn mul_is_conmutative() { - let a = TowerFieldElement::new(127, 7); - let b = TowerFieldElement::new(6, 3); - let ab = a * b; - let ba = b * a; - assert_eq!(ab, ba); - } - - #[test] - fn test_inverse() { - let a0 = TowerFieldElement::new(1, 0); - let inv_a0 = a0.inv().unwrap(); - assert_eq!(inv_a0.value, 1); - assert_eq!(inv_a0.num_level, 0); - - let a1 = TowerFieldElement::new(2, 1); - let inv_a1 = a1.inv().unwrap(); - assert_eq!(inv_a1.value, 3); // because 10 * 11 = 01. - assert_eq!(inv_a1.num_level, 1); - - // Verify a * a^(-1) = 1 - let a2 = TowerFieldElement::new(15, 4); - let inv_a2 = a2.inv().unwrap(); - let one = TowerFieldElement::new(1, 4); - assert_eq!(a2 * inv_a2, one); - - let a3 = TowerFieldElement::new(30, 5); - let inv_a3 = a3.inv().unwrap(); - let one = TowerFieldElement::new(1, 5); - assert_eq!(a3 * inv_a3, one); - - let zero = TowerFieldElement::zero(); - assert!(matches!(zero.inv(), Err(BinaryFieldError::InverseOfZero))); - } - - #[test] - fn test_multiplication_overflow() { - for level in 0..7 { - let max_value = (1u128 << (1 << level)) - 1; // Maximum value for this level - let a = TowerFieldElement::new(max_value, level); - let b = TowerFieldElement::new(max_value, level); - - let result = a * b; - - // Result should be properly reduced - assert!(result.value < (1u128 << result.num_bits())); - } - } - - #[test] - fn test_split_join_consistency() { - // Test that join and split are consistent operations - for i in 0..20 { - let original = TowerFieldElement::new(i, 3); - let (hi, lo) = original.split(); - let rejoined = hi.join(&lo); - - assert_eq!(rejoined, original); - } - } - #[cfg(feature = "std")] - #[test] - fn test_bin_representation() { - let a = TowerFieldElement::new(0b1010, 5); - assert_eq!(a.to_binary_string(), "00000000000000000000000000001010"); - let b = TowerFieldElement::new(0b1010, 4); - assert_eq!(b.to_binary_string(), "0000000000001010"); - } - - // Strategy to generate a TowerFieldElement with a random level between 0 and 7. - // For a given level: - // - The number of bits is computed as 1 << level. - // - For level 0, valid values are 0 to (1 << 1) - 1 = 1. - // - For level > 0, valid values are 0 to (1 << (1 << level)) - 1. - fn arb_tower_element_any() -> impl Strategy { - (0usize..=7) - .prop_flat_map(|level| { - let max_val = if level == 0 { - 1 - } else if (1usize << level) >= 128 { - u128::MAX - } else { - (1u128 << (1 << level)) - 1 - }; - (Just(level), 0u128..=max_val) - }) - .prop_map(|(level, val)| TowerFieldElement::new(val, level)) - } - - #[cfg(feature = "std")] - proptest! { - // Test that multiplication is commutative: - // For any two randomly generated elements, a * b should equal b * a. - #[test] - fn test_mul_commutative(a in arb_tower_element_any(), b in arb_tower_element_any()) { - prop_assert_eq!(a * b, b * a); - } - - // Test that multiplication is associative: - // For any three randomly generated elements, (a * b) * c should equal a * (b * c). - #[test] - fn test_mul_associative(a in arb_tower_element_any(), b in arb_tower_element_any(), c in arb_tower_element_any()) { - prop_assert_eq!((a * b) * c, a * (b * c)); - } - } -} diff --git a/crates/math/src/field/fields/binary/mod.rs b/crates/math/src/field/fields/binary/mod.rs deleted file mode 100644 index 5b8f2df9f..000000000 --- a/crates/math/src/field/fields/binary/mod.rs +++ /dev/null @@ -1 +0,0 @@ -pub mod field; diff --git a/crates/math/src/field/fields/fft_friendly/babybear.rs b/crates/math/src/field/fields/fft_friendly/babybear.rs deleted file mode 100644 index acb2af2f4..000000000 --- a/crates/math/src/field/fields/fft_friendly/babybear.rs +++ /dev/null @@ -1,264 +0,0 @@ -use crate::{ - field::{ - element::FieldElement, - fields::montgomery_backed_prime_fields::{IsModulus, MontgomeryBackendPrimeField}, - traits::IsFFTField, - }, - unsigned_integer::element::{UnsignedInteger, U64}, -}; - -pub type U64MontgomeryBackendPrimeField = MontgomeryBackendPrimeField; - -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct MontgomeryConfigBabybear31PrimeField; -impl IsModulus for MontgomeryConfigBabybear31PrimeField { - //Babybear Prime p = 2^31 - 2^27 + 1 = 0x78000001 - const MODULUS: U64 = U64::from_u64(2013265921); -} - -pub type Babybear31PrimeField = - U64MontgomeryBackendPrimeField; - -//a two-adic primitive root of unity is 21^(2^24) -// 21^(2^24)=1 mod 2013265921 -// 2^27(2^4-1)+1 where n=27 (two-adicity) and k=2^4+1 - -//In the future we should allow this with cuda feature, and just dispatch it to the CPU until the implementation is done -#[cfg(not(feature = "cuda"))] -impl IsFFTField for Babybear31PrimeField { - const TWO_ADICITY: u64 = 24; - - const TWO_ADIC_PRIMITVE_ROOT_OF_UNITY: Self::BaseType = UnsignedInteger { limbs: [21] }; - - fn field_name() -> &'static str { - "babybear31" - } -} - -impl FieldElement { - pub fn to_bytes_le(&self) -> [u8; 8] { - let limbs = self.representative().limbs; - limbs[0].to_le_bytes() - } - - pub fn to_bytes_be(&self) -> [u8; 8] { - let limbs = self.representative().limbs; - limbs[0].to_be_bytes() - } -} - -#[cfg(test)] -mod tests { - use super::*; - - mod test_babybear_31_bytes_ops { - use super::*; - use crate::{field::element::FieldElement, traits::ByteConversion}; - - #[test] - #[cfg(feature = "alloc")] - fn byte_serialization_for_a_number_matches_with_byte_conversion_implementation_le() { - let element = - FieldElement::::from_hex_unchecked("0123456701234567"); - let bytes = element.to_bytes_le(); - let expected_bytes: [u8; 8] = ByteConversion::to_bytes_le(&element).try_into().unwrap(); - assert_eq!(bytes, expected_bytes); - } - - #[test] - #[cfg(feature = "alloc")] - fn byte_serialization_for_a_number_matches_with_byte_conversion_implementation_be() { - let element = - FieldElement::::from_hex_unchecked("0123456701234567"); - let bytes = element.to_bytes_be(); - let expected_bytes: [u8; 8] = ByteConversion::to_bytes_be(&element).try_into().unwrap(); - assert_eq!(bytes, expected_bytes); - } - - #[test] - fn byte_serialization_and_deserialization_works_le() { - let element = - FieldElement::::from_hex_unchecked("7654321076543210"); - let bytes = element.to_bytes_le(); - let from_bytes = FieldElement::::from_bytes_le(&bytes).unwrap(); - assert_eq!(element, from_bytes); - } - - #[test] - fn byte_serialization_and_deserialization_works_be() { - let element = - FieldElement::::from_hex_unchecked("7654321076543210"); - let bytes = element.to_bytes_be(); - let from_bytes = FieldElement::::from_bytes_be(&bytes).unwrap(); - assert_eq!(element, from_bytes); - } - } - - #[cfg(all(feature = "std", not(feature = "instruments")))] - mod test_babybear_31_fft { - use super::*; - #[cfg(not(feature = "cuda"))] - use crate::fft::cpu::roots_of_unity::{ - get_powers_of_primitive_root, get_powers_of_primitive_root_coset, - }; - #[cfg(not(feature = "cuda"))] - use crate::field::element::FieldElement; - #[cfg(not(feature = "cuda"))] - use crate::field::traits::{IsFFTField, RootsConfig}; - use crate::polynomial::Polynomial; - use proptest::{collection, prelude::*, std_facade::Vec}; - - #[cfg(not(feature = "cuda"))] - fn gen_fft_and_naive_evaluation( - poly: Polynomial>, - ) -> (Vec>, Vec>) { - let len = poly.coeff_len().next_power_of_two(); - let order = len.trailing_zeros(); - let twiddles = - get_powers_of_primitive_root(order.into(), len, RootsConfig::Natural).unwrap(); - - let fft_eval = Polynomial::evaluate_fft::(&poly, 1, None).unwrap(); - let naive_eval = poly.evaluate_slice(&twiddles); - - (fft_eval, naive_eval) - } - - #[cfg(not(feature = "cuda"))] - fn gen_fft_coset_and_naive_evaluation( - poly: Polynomial>, - offset: FieldElement, - blowup_factor: usize, - ) -> (Vec>, Vec>) { - let len = poly.coeff_len().next_power_of_two(); - let order = (len * blowup_factor).trailing_zeros(); - let twiddles = - get_powers_of_primitive_root_coset(order.into(), len * blowup_factor, &offset) - .unwrap(); - - let fft_eval = - Polynomial::evaluate_offset_fft::(&poly, blowup_factor, None, &offset).unwrap(); - let naive_eval = poly.evaluate_slice(&twiddles); - - (fft_eval, naive_eval) - } - - #[cfg(not(feature = "cuda"))] - fn gen_fft_and_naive_interpolate( - fft_evals: &[FieldElement], - ) -> (Polynomial>, Polynomial>) { - let order = fft_evals.len().trailing_zeros() as u64; - let twiddles = - get_powers_of_primitive_root(order, 1 << order, RootsConfig::Natural).unwrap(); - - let naive_poly = Polynomial::interpolate(&twiddles, fft_evals).unwrap(); - let fft_poly = Polynomial::interpolate_fft::(fft_evals).unwrap(); - - (fft_poly, naive_poly) - } - - #[cfg(not(feature = "cuda"))] - fn gen_fft_and_naive_coset_interpolate( - fft_evals: &[FieldElement], - offset: &FieldElement, - ) -> (Polynomial>, Polynomial>) { - let order = fft_evals.len().trailing_zeros() as u64; - let twiddles = get_powers_of_primitive_root_coset(order, 1 << order, offset).unwrap(); - - let naive_poly = Polynomial::interpolate(&twiddles, fft_evals).unwrap(); - let fft_poly = Polynomial::interpolate_offset_fft(fft_evals, offset).unwrap(); - - (fft_poly, naive_poly) - } - - #[cfg(not(feature = "cuda"))] - fn gen_fft_interpolate_and_evaluate( - poly: Polynomial>, - ) -> (Polynomial>, Polynomial>) { - let eval = Polynomial::evaluate_fft::(&poly, 1, None).unwrap(); - let new_poly = Polynomial::interpolate_fft::(&eval).unwrap(); - - (poly, new_poly) - } - - prop_compose! { - fn powers_of_two(max_exp: u8)(exp in 1..max_exp) -> usize { 1 << exp } - // max_exp cannot be multiple of the bits that represent a usize, generally 64 or 32. - // also it can't exceed the test field's two-adicity. - } - prop_compose! { - fn field_element()(num in any::().prop_filter("Avoid null coefficients", |x| x != &0)) -> FieldElement { - FieldElement::::from(num) - } - } - prop_compose! { - fn offset()(num in any::(), factor in any::()) -> FieldElement { FieldElement::::from(num).pow(factor) } - } - prop_compose! { - fn field_vec(max_exp: u8)(vec in collection::vec(field_element(), 0..1 << max_exp)) -> Vec> { - vec - } - } - prop_compose! { - fn non_power_of_two_sized_field_vec(max_exp: u8)(vec in collection::vec(field_element(), 2..1< Vec> { - vec - } - } - prop_compose! { - fn poly(max_exp: u8)(coeffs in field_vec(max_exp)) -> Polynomial> { - Polynomial::new(&coeffs) - } - } - prop_compose! { - fn poly_with_non_power_of_two_coeffs(max_exp: u8)(coeffs in non_power_of_two_sized_field_vec(max_exp)) -> Polynomial> { - Polynomial::new(&coeffs) - } - } - - proptest! { - // Property-based test that ensures FFT eval. gives same result as a naive polynomial evaluation. - #[test] - #[cfg(not(feature = "cuda"))] - fn test_fft_matches_naive_evaluation(poly in poly(8)) { - let (fft_eval, naive_eval) = gen_fft_and_naive_evaluation(poly); - prop_assert_eq!(fft_eval, naive_eval); - } - - // Property-based test that ensures FFT eval. with coset gives same result as a naive polynomial evaluation. - #[test] - #[cfg(not(feature = "cuda"))] - fn test_fft_coset_matches_naive_evaluation(poly in poly(4), offset in offset(), blowup_factor in powers_of_two(4)) { - let (fft_eval, naive_eval) = gen_fft_coset_and_naive_evaluation(poly, offset, blowup_factor); - prop_assert_eq!(fft_eval, naive_eval); - } - - // Property-based test that ensures FFT interpolation is the same as naive.. - #[test] - #[cfg(not(feature = "cuda"))] - fn test_fft_interpolate_matches_naive(fft_evals in field_vec(4) - .prop_filter("Avoid polynomials of size not power of two", - |evals| evals.len().is_power_of_two())) { - let (fft_poly, naive_poly) = gen_fft_and_naive_interpolate(&fft_evals); - prop_assert_eq!(fft_poly, naive_poly); - } - - // Property-based test that ensures FFT interpolation with an offset is the same as naive. - #[test] - #[cfg(not(feature = "cuda"))] - fn test_fft_interpolate_coset_matches_naive(offset in offset(), fft_evals in field_vec(4) - .prop_filter("Avoid polynomials of size not power of two", - |evals| evals.len().is_power_of_two())) { - let (fft_poly, naive_poly) = gen_fft_and_naive_coset_interpolate(&fft_evals, &offset); - prop_assert_eq!(fft_poly, naive_poly); - } - - // Property-based test that ensures interpolation is the inverse operation of evaluation. - #[test] - #[cfg(not(feature = "cuda"))] - fn test_fft_interpolate_is_inverse_of_evaluate( - poly in poly(4).prop_filter("Avoid non pows of two", |poly| poly.coeff_len().is_power_of_two())) { - let (poly, new_poly) = gen_fft_interpolate_and_evaluate(poly); - prop_assert_eq!(poly, new_poly); - } - } - } -} diff --git a/crates/math/src/field/fields/fft_friendly/babybear_u32.rs b/crates/math/src/field/fields/fft_friendly/babybear_u32.rs deleted file mode 100644 index 7bd11e2c1..000000000 --- a/crates/math/src/field/fields/fft_friendly/babybear_u32.rs +++ /dev/null @@ -1,429 +0,0 @@ -use crate::field::{ - fields::u32_montgomery_backend_prime_field::U32MontgomeryBackendPrimeField, traits::IsFFTField, -}; - -// Babybear Prime p = 2^31 - 2^27 + 1 = 0x78000001 = 2013265921 -pub type Babybear31PrimeField = U32MontgomeryBackendPrimeField<2013265921>; - -// p = 2^31 - 2^27 + 1 = 2^27 * (2^4-1) + 1, then -// there is a gruop in the field of order 2^27. -// Since we want to have margin to be able to define a bigger group (blow-up group), -// we define TWO_ADICITY as 24 (so the blow-up factor can be 2^3 = 8). -// A two-adic primitive root of unity is 21^(2^24) because -// 21^(2^24)=1 mod 2013265921. -// In the future we should allow this with cuda feature, and just dispatch it to the CPU until the implementation is done -#[cfg(not(feature = "cuda"))] -impl IsFFTField for Babybear31PrimeField { - const TWO_ADICITY: u64 = 24; - - const TWO_ADIC_PRIMITVE_ROOT_OF_UNITY: Self::BaseType = 21; - - fn field_name() -> &'static str { - "babybear31" - } -} - -#[cfg(test)] -mod tests { - use super::*; - mod test_babybear_31_ops { - use super::*; - use crate::{ - errors::CreationError, - field::{element::FieldElement, errors::FieldError, traits::IsPrimeField}, - traits::ByteConversion, - }; - type FE = FieldElement; - - #[test] - fn two_plus_one_is_three() { - let a = FE::from(2); - let b = FE::one(); - let res = FE::from(3); - - assert_eq!(a + b, res) - } - - #[test] - fn one_minus_two_is_minus_one() { - let a = FE::from(2); - let b = FE::one(); - let res = FE::from(2013265920); - assert_eq!(b - a, res) - } - - #[test] - fn mul_by_zero_is_zero() { - let a = FE::from(2); - let b = FE::zero(); - assert_eq!(a * b, b) - } - - #[test] - fn neg_zero_is_zero() { - let zero = FE::from(0); - - assert_eq!(-&zero, zero); - } - - #[test] - fn doubling() { - assert_eq!(FE::from(2).double(), FE::from(2) + FE::from(2),); - } - - const ORDER: usize = 2013265921; - - #[test] - fn order_is_0() { - assert_eq!(FE::from((ORDER - 1) as u64) + FE::from(1), FE::from(0)); - } - - #[test] - fn when_comparing_13_and_13_they_are_equal() { - let a: FE = FE::from(13); - let b: FE = FE::from(13); - assert_eq!(a, b); - } - - #[test] - fn when_comparing_13_and_8_they_are_different() { - let a: FE = FE::from(13); - let b: FE = FE::from(8); - assert_ne!(a, b); - } - - #[test] - fn mul_neutral_element() { - let a: FE = FE::from(1); - let b: FE = FE::from(2); - assert_eq!(a * b, FE::from(2)); - } - - #[test] - fn mul_2_3_is_6() { - let a: FE = FE::from(2); - let b: FE = FE::from(3); - assert_eq!(a * b, FE::from(6)); - } - - #[test] - fn mul_order_minus_1() { - let a: FE = FE::from((ORDER - 1) as u64); - let b: FE = FE::from((ORDER - 1) as u64); - assert_eq!(a * b, FE::from(1)); - } - - #[test] - fn inv_0_error() { - let result = FE::from(0).inv(); - assert!(matches!(result, Err(FieldError::InvZeroError))) - } - - #[test] - fn inv_2_mul_2_is_1() { - let a: FE = FE::from(2); - assert_eq!(a * a.inv().unwrap(), FE::from(1)); - } - - #[test] - fn square_2_is_4() { - assert_eq!(FE::from(2).square(), FE::from(4)) - } - - #[test] - fn pow_2_3_is_8() { - assert_eq!(FE::from(2).pow(3_u64), FE::from(8)) - } - - #[test] - fn pow_p_minus_1() { - assert_eq!(FE::from(2).pow(ORDER - 1), FE::from(1)) - } - - #[test] - fn div_1() { - assert_eq!((FE::from(2) / FE::from(1)).unwrap(), FE::from(2)) - } - - #[test] - fn div_4_2() { - assert_eq!((FE::from(4) / FE::from(2)).unwrap(), FE::from(2)) - } - - #[test] - fn two_plus_its_additive_inv_is_0() { - let two = FE::from(2); - - assert_eq!(two + (-&two), FE::from(0)) - } - - #[test] - fn four_minus_three_is_1() { - let four = FE::from(4); - let three = FE::from(3); - - assert_eq!(four - three, FE::from(1)) - } - - #[test] - fn zero_minus_1_is_order_minus_1() { - let zero = FE::from(0); - let one = FE::from(1); - - assert_eq!(zero - one, FE::from((ORDER - 1) as u64)) - } - - #[test] - fn babybear_uses_31_bits() { - assert_eq!(Babybear31PrimeField::field_bit_size(), 31); - } - - #[test] - fn montgomery_backend_prime_field_compute_mu_parameter() { - let mu_expected: u32 = 2281701377; - assert_eq!(Babybear31PrimeField::MU, mu_expected); - } - - #[test] - fn montgomery_backend_prime_field_compute_r2_parameter() { - let r2_expected: u32 = 1172168163; - assert_eq!(Babybear31PrimeField::R2, r2_expected); - } - - #[test] - #[cfg(feature = "alloc")] - fn from_hex_bigger_than_u64_returns_error() { - let x = FE::from_hex("5f103b0bd4397d4df560eb559f38353f80eeb6"); - assert!(matches!(x, Err(CreationError::InvalidHexString))) - } - - #[test] - #[cfg(feature = "alloc")] - fn to_bytes_from_bytes_be_is_the_identity() { - let x = FE::from_hex("5f103b").unwrap(); - assert_eq!(FE::from_bytes_be(&x.to_bytes_be()).unwrap(), x); - } - - #[test] - #[cfg(feature = "alloc")] - fn from_bytes_to_bytes_be_is_the_identity() { - let bytes = [0, 0, 0, 1]; - assert_eq!(FE::from_bytes_be(&bytes).unwrap().to_bytes_be(), bytes); - } - - #[test] - #[cfg(feature = "alloc")] - fn to_bytes_from_bytes_le_is_the_identity() { - let x = FE::from_hex("5f103b").unwrap(); - assert_eq!(FE::from_bytes_le(&x.to_bytes_le()).unwrap(), x); - } - - #[test] - #[cfg(feature = "alloc")] - fn from_bytes_to_bytes_le_is_the_identity_4_bytes() { - let bytes = [1, 0, 0, 0]; - assert_eq!(FE::from_bytes_le(&bytes).unwrap().to_bytes_le(), bytes); - } - - #[test] - #[cfg(feature = "alloc")] - fn byte_serialization_for_a_number_matches_with_byte_conversion_implementation_le() { - let element = FE::from_hex("0123456701234567").unwrap(); - let bytes = element.to_bytes_le(); - let expected_bytes: [u8; 4] = ByteConversion::to_bytes_le(&element).try_into().unwrap(); - assert_eq!(bytes, expected_bytes); - } - - #[test] - #[cfg(feature = "alloc")] - fn byte_serialization_for_a_number_matches_with_byte_conversion_implementation_be() { - let element = FE::from_hex("0123456701234567").unwrap(); - let bytes = element.to_bytes_be(); - let expected_bytes: [u8; 4] = ByteConversion::to_bytes_be(&element).try_into().unwrap(); - assert_eq!(bytes, expected_bytes); - } - - #[test] - fn byte_serialization_and_deserialization_works_le() { - let element = FE::from_hex("0x7654321076543210").unwrap(); - let bytes = element.to_bytes_le(); - let from_bytes = FE::from_bytes_le(&bytes).unwrap(); - assert_eq!(element, from_bytes); - } - - #[test] - fn byte_serialization_and_deserialization_works_be() { - let element = FE::from_hex("7654321076543210").unwrap(); - let bytes = element.to_bytes_be(); - let from_bytes = FE::from_bytes_be(&bytes).unwrap(); - assert_eq!(element, from_bytes); - } - } - - #[cfg(all(feature = "std", not(feature = "instruments")))] - mod test_babybear_31_fft { - use super::*; - #[cfg(not(feature = "cuda"))] - use crate::fft::cpu::roots_of_unity::{ - get_powers_of_primitive_root, get_powers_of_primitive_root_coset, - }; - use crate::field::element::FieldElement; - #[cfg(not(feature = "cuda"))] - use crate::field::traits::{IsFFTField, RootsConfig}; - use crate::polynomial::Polynomial; - use proptest::{collection, prelude::*, std_facade::Vec}; - - #[cfg(not(feature = "cuda"))] - fn gen_fft_and_naive_evaluation( - poly: Polynomial>, - ) -> (Vec>, Vec>) { - let len = poly.coeff_len().next_power_of_two(); - let order = len.trailing_zeros(); - let twiddles = - get_powers_of_primitive_root(order.into(), len, RootsConfig::Natural).unwrap(); - - let fft_eval = Polynomial::evaluate_fft::(&poly, 1, None).unwrap(); - let naive_eval = poly.evaluate_slice(&twiddles); - - (fft_eval, naive_eval) - } - - #[cfg(not(feature = "cuda"))] - fn gen_fft_coset_and_naive_evaluation( - poly: Polynomial>, - offset: FieldElement, - blowup_factor: usize, - ) -> (Vec>, Vec>) { - let len = poly.coeff_len().next_power_of_two(); - let order = (len * blowup_factor).trailing_zeros(); - let twiddles = - get_powers_of_primitive_root_coset(order.into(), len * blowup_factor, &offset) - .unwrap(); - - let fft_eval = - Polynomial::evaluate_offset_fft::(&poly, blowup_factor, None, &offset).unwrap(); - let naive_eval = poly.evaluate_slice(&twiddles); - - (fft_eval, naive_eval) - } - - #[cfg(not(feature = "cuda"))] - fn gen_fft_and_naive_interpolate( - fft_evals: &[FieldElement], - ) -> (Polynomial>, Polynomial>) { - let order = fft_evals.len().trailing_zeros() as u64; - let twiddles = - get_powers_of_primitive_root(order, 1 << order, RootsConfig::Natural).unwrap(); - - let naive_poly = Polynomial::interpolate(&twiddles, fft_evals).unwrap(); - let fft_poly = Polynomial::interpolate_fft::(fft_evals).unwrap(); - - (fft_poly, naive_poly) - } - - #[cfg(not(feature = "cuda"))] - fn gen_fft_and_naive_coset_interpolate( - fft_evals: &[FieldElement], - offset: &FieldElement, - ) -> (Polynomial>, Polynomial>) { - let order = fft_evals.len().trailing_zeros() as u64; - let twiddles = get_powers_of_primitive_root_coset(order, 1 << order, offset).unwrap(); - - let naive_poly = Polynomial::interpolate(&twiddles, fft_evals).unwrap(); - let fft_poly = Polynomial::interpolate_offset_fft(fft_evals, offset).unwrap(); - - (fft_poly, naive_poly) - } - - #[cfg(not(feature = "cuda"))] - fn gen_fft_interpolate_and_evaluate( - poly: Polynomial>, - ) -> (Polynomial>, Polynomial>) { - let eval = Polynomial::evaluate_fft::(&poly, 1, None).unwrap(); - let new_poly = Polynomial::interpolate_fft::(&eval).unwrap(); - - (poly, new_poly) - } - - prop_compose! { - fn powers_of_two(max_exp: u8)(exp in 1..max_exp) -> usize { 1 << exp } - // max_exp cannot be multiple of the bits that represent a usize, generally 64 or 32. - // also it can't exceed the test field's two-adicity. - } - prop_compose! { - fn field_element()(num in any::().prop_filter("Avoid null coefficients", |x| x != &0)) -> FieldElement { - FieldElement::::from(num) - } - } - prop_compose! { - fn offset()(num in any::(), factor in any::()) -> FieldElement { FieldElement::::from(num).pow(factor) } - } - prop_compose! { - fn field_vec(max_exp: u8)(vec in collection::vec(field_element(), 0..1 << max_exp)) -> Vec> { - vec - } - } - prop_compose! { - fn non_power_of_two_sized_field_vec(max_exp: u8)(vec in collection::vec(field_element(), 2..1< Vec> { - vec - } - } - prop_compose! { - fn poly(max_exp: u8)(coeffs in field_vec(max_exp)) -> Polynomial> { - Polynomial::new(&coeffs) - } - } - prop_compose! { - fn poly_with_non_power_of_two_coeffs(max_exp: u8)(coeffs in non_power_of_two_sized_field_vec(max_exp)) -> Polynomial> { - Polynomial::new(&coeffs) - } - } - - proptest! { - // Property-based test that ensures FFT eval. gives same result as a naive polynomial evaluation. - #[test] - #[cfg(not(feature = "cuda"))] - fn test_fft_matches_naive_evaluation(poly in poly(8)) { - let (fft_eval, naive_eval) = gen_fft_and_naive_evaluation(poly); - prop_assert_eq!(fft_eval, naive_eval); - } - - // Property-based test that ensures FFT eval. with coset gives same result as a naive polynomial evaluation. - #[test] - #[cfg(not(feature = "cuda"))] - fn test_fft_coset_matches_naive_evaluation(poly in poly(4), offset in offset(), blowup_factor in powers_of_two(4)) { - let (fft_eval, naive_eval) = gen_fft_coset_and_naive_evaluation(poly, offset, blowup_factor); - prop_assert_eq!(fft_eval, naive_eval); - } - - // Property-based test that ensures FFT interpolation is the same as naive.. - #[test] - #[cfg(not(feature = "cuda"))] - fn test_fft_interpolate_matches_naive(fft_evals in field_vec(4) - .prop_filter("Avoid polynomials of size not power of two", - |evals| evals.len().is_power_of_two())) { - let (fft_poly, naive_poly) = gen_fft_and_naive_interpolate(&fft_evals); - prop_assert_eq!(fft_poly, naive_poly); - } - - // Property-based test that ensures FFT interpolation with an offset is the same as naive. - #[test] - #[cfg(not(feature = "cuda"))] - fn test_fft_interpolate_coset_matches_naive(offset in offset(), fft_evals in field_vec(4) - .prop_filter("Avoid polynomials of size not power of two", - |evals| evals.len().is_power_of_two())) { - let (fft_poly, naive_poly) = gen_fft_and_naive_coset_interpolate(&fft_evals, &offset); - prop_assert_eq!(fft_poly, naive_poly); - } - - // Property-based test that ensures interpolation is the inverse operation of evaluation. - #[test] - #[cfg(not(feature = "cuda"))] - fn test_fft_interpolate_is_inverse_of_evaluate( - poly in poly(4).prop_filter("Avoid non pows of two", |poly| poly.coeff_len().is_power_of_two())) { - let (poly, new_poly) = gen_fft_interpolate_and_evaluate(poly); - prop_assert_eq!(poly, new_poly); - } - } - } -} diff --git a/crates/math/src/field/fields/fft_friendly/mod.rs b/crates/math/src/field/fields/fft_friendly/mod.rs deleted file mode 100644 index 1bd50c19b..000000000 --- a/crates/math/src/field/fields/fft_friendly/mod.rs +++ /dev/null @@ -1,20 +0,0 @@ -/// Implemenation of the Babybear Prime Field p = 2^31 - 2^27 + 1 -pub mod babybear; -/// Implementation of the quadratic extension of the babybear field -pub mod quadratic_babybear; -/// Implementation of the extension of degree 4 of the babybear field using u64. -pub mod quartic_babybear; -/// Implementation of the prime field used in [Stark101](https://starkware.co/stark-101/) tutorial, p = 3 * 2^30 + 1 -pub mod stark_101_prime_field; -/// Implementation of two-adic prime field over 256 bit unsigned integers. -pub mod stark_252_prime_field; -/// Implemenation of the Goldilocks Prime Field p = 2^64 - 2^32 + 1 -pub mod u64_goldilocks; -/// Implemenation of the Mersenne Prime field p = 2^31 - 1 -pub mod u64_mersenne_montgomery_field; - -/// Inmplementation of the Babybear Prime Field p = 2^31 - 2^27 + 1 using u32 -pub mod babybear_u32; - -/// Implementation of the extension of degree 4 of the babybear field using u32. -pub mod quartic_babybear_u32; diff --git a/crates/math/src/field/fields/fft_friendly/quadratic_babybear.rs b/crates/math/src/field/fields/fft_friendly/quadratic_babybear.rs deleted file mode 100644 index 81a1630f0..000000000 --- a/crates/math/src/field/fields/fft_friendly/quadratic_babybear.rs +++ /dev/null @@ -1,72 +0,0 @@ -use crate::field::{ - element::FieldElement, extensions::quadratic::*, - fields::fft_friendly::babybear::Babybear31PrimeField, -}; - -/// Quadratic field extension of Babybear -pub type QuadraticBabybearField = - QuadraticExtensionField; - -impl HasQuadraticNonResidue for Babybear31PrimeField { - fn residue() -> FieldElement { - -FieldElement::one() - } -} - -/// Field element type for the quadratic extension of Babybear -pub type QuadraticBabybearFieldElement = - QuadraticExtensionFieldElement; - -#[cfg(test)] -mod tests { - use super::*; - - type FE = FieldElement; - type Fee = QuadraticBabybearFieldElement; - - #[test] - fn test_add_quadratic() { - let a = Fee::new([FE::from(0), FE::from(3)]); - let b = Fee::new([-FE::from(2), FE::from(8)]); - let expected_result = Fee::new([FE::from(0) - FE::from(2), FE::from(3) + FE::from(8)]); - assert_eq!(a + b, expected_result); - } - - #[test] - fn test_sub_quadratic() { - let a = Fee::new([FE::from(0), FE::from(3)]); - let b = Fee::new([-FE::from(2), FE::from(8)]); - let expected_result = Fee::new([FE::from(0) + FE::from(2), FE::from(3) - FE::from(8)]); - assert_eq!(a - b, expected_result); - } - - #[test] - fn test_mul_quadratic() { - let a = Fee::new([FE::from(12), FE::from(5)]); - let b = Fee::new([-FE::from(4), FE::from(2)]); - let expected_result = Fee::new([ - FE::from(12) * (-FE::from(4)) - + FE::from(5) * FE::from(2) * Babybear31PrimeField::residue(), - FE::from(12) * FE::from(2) + FE::from(5) * (-FE::from(4)), - ]); - assert_eq!(a * b, expected_result); - } - - #[test] - fn test_inv_quadratic() { - let a = Fee::new([FE::from(12), FE::from(5)]); - let inv_norm = (FE::from(12).pow(2_u64) - - Babybear31PrimeField::residue() * FE::from(5).pow(2_u64)) - .inv() - .unwrap(); - let expected_result = Fee::new([FE::from(12) * &inv_norm, -&FE::from(5) * inv_norm]); - assert_eq!(a.inv().unwrap(), expected_result); - } - - #[test] - fn test_conjugate_quadratic() { - let a = Fee::new([FE::from(12), FE::from(5)]); - let expected_result = Fee::new([FE::from(12), -FE::from(5)]); - assert_eq!(a.conjugate(), expected_result); - } -} diff --git a/crates/math/src/field/fields/fft_friendly/quartic_babybear.rs b/crates/math/src/field/fields/fft_friendly/quartic_babybear.rs deleted file mode 100644 index f695879a4..000000000 --- a/crates/math/src/field/fields/fft_friendly/quartic_babybear.rs +++ /dev/null @@ -1,660 +0,0 @@ -use crate::field::{ - element::FieldElement, - errors::FieldError, - fields::fft_friendly::babybear::Babybear31PrimeField, - traits::{HasDefaultTranscript, IsFFTField, IsField, IsSubFieldOf}, -}; - -use crate::traits::ByteConversion; - -#[cfg(feature = "alloc")] -use crate::traits::AsBytes; - -/// We are implementig the extension of Baby Bear of degree 4 using the irreducible polynomial x^4 + 11. -/// BETA = 11 and -BETA = -11 is the non-residue. -pub const BETA: FieldElement = - FieldElement::::from_hex_unchecked("b"); - -#[derive(Clone, Debug)] -pub struct Degree4BabyBearExtensionField; - -impl IsField for Degree4BabyBearExtensionField { - type BaseType = [FieldElement; 4]; - - fn add(a: &Self::BaseType, b: &Self::BaseType) -> Self::BaseType { - [&a[0] + &b[0], &a[1] + &b[1], &a[2] + &b[2], &a[3] + &b[3]] - } - - /// Result of multiplying two polynomials a = a0 + a1 * x + a2 * x^2 + a3 * x^3 and - /// b = b0 + b1 * x + b2 * x^2 + b3 * x^3 by applying distribution and taking - /// the remainder of the division by x^4 + 11. - fn mul(a: &Self::BaseType, b: &Self::BaseType) -> Self::BaseType { - [ - &a[0] * &b[0] - BETA * (&a[1] * &b[3] + &a[3] * &b[1] + &a[2] * &b[2]), - &a[0] * &b[1] + &a[1] * &b[0] - BETA * (&a[2] * &b[3] + &a[3] * &b[2]), - &a[0] * &b[2] + &a[2] * &b[0] + &a[1] * &b[1] - BETA * (&a[3] * &b[3]), - &a[0] * &b[3] + &a[3] * &b[0] + &a[1] * &b[2] + &a[2] * &b[1], - ] - } - - fn square(a: &Self::BaseType) -> Self::BaseType { - [ - &a[0].square() - BETA * ((&a[1] * &a[3]).double() + &a[2].square()), - (&a[0] * &a[1] - BETA * (&a[2] * &a[3])).double(), - (&a[0] * &a[2]).double() + &a[1].square() - BETA * (&a[3].square()), - (&a[0] * &a[3] + &a[1] * &a[2]).double(), - ] - } - - fn sub(a: &Self::BaseType, b: &Self::BaseType) -> Self::BaseType { - [&a[0] - &b[0], &a[1] - &b[1], &a[2] - &b[2], &a[3] - &b[3]] - } - - fn neg(a: &Self::BaseType) -> Self::BaseType { - [-&a[0], -&a[1], -&a[2], -&a[3]] - } - - /// Return te inverse of a fp4 element if exist. - /// This algorithm is inspired by Risc0 implementation: - /// - fn inv(a: &Self::BaseType) -> Result { - let mut b0 = &a[0] * &a[0] + BETA * (&a[1] * (&a[3] + &a[3]) - &a[2] * &a[2]); - let mut b2 = &a[0] * (&a[2] + &a[2]) - &a[1] * &a[1] + BETA * (&a[3] * &a[3]); - let c = &b0.square() + BETA * b2.square(); - let c_inv = c.inv()?; - b0 *= &c_inv; - b2 *= &c_inv; - Ok([ - &a[0] * &b0 + BETA * &a[2] * &b2, - -&a[1] * &b0 - BETA * &a[3] * &b2, - -&a[0] * &b2 + &a[2] * &b0, - &a[1] * &b2 - &a[3] * &b0, - ]) - } - - fn div(a: &Self::BaseType, b: &Self::BaseType) -> Result { - let b_inv = &Self::inv(b).map_err(|_| FieldError::DivisionByZero)?; - Ok(::mul(a, b_inv)) - } - - fn eq(a: &Self::BaseType, b: &Self::BaseType) -> bool { - a[0] == b[0] && a[1] == b[1] && a[2] == b[2] && a[3] == b[3] - } - - fn zero() -> Self::BaseType { - Self::BaseType::default() - } - - fn one() -> Self::BaseType { - [ - FieldElement::one(), - FieldElement::zero(), - FieldElement::zero(), - FieldElement::zero(), - ] - } - - fn from_u64(x: u64) -> Self::BaseType { - [ - FieldElement::from(x), - FieldElement::zero(), - FieldElement::zero(), - FieldElement::zero(), - ] - } - - /// Takes as input an element of BaseType and returns the internal representation - /// of that element in the field. - /// Note: for this case this is simply the identity, because the components - /// already have correct representations. - fn from_base_type(x: Self::BaseType) -> Self::BaseType { - x - } - - fn double(a: &Self::BaseType) -> Self::BaseType { - ::add(a, a) - } - - fn pow(a: &Self::BaseType, mut exponent: T) -> Self::BaseType - where - T: crate::unsigned_integer::traits::IsUnsignedInteger, - { - let zero = T::from(0); - let one = T::from(1); - - if exponent == zero { - return Self::one(); - } - if exponent == one { - return a.clone(); - } - - let mut result = a.clone(); - - // Fast path for powers of 2 - while exponent & one == zero { - result = Self::square(&result); - exponent >>= 1; - if exponent == zero { - return result; - } - } - - let mut base = result.clone(); - exponent >>= 1; - - while exponent != zero { - base = Self::square(&base); - if exponent & one == one { - result = ::mul(&result, &base); - } - exponent >>= 1; - } - - result - } -} - -impl IsSubFieldOf for Babybear31PrimeField { - fn mul( - a: &Self::BaseType, - b: &::BaseType, - ) -> ::BaseType { - let c0 = FieldElement::from_raw(::mul(a, b[0].value())); - let c1 = FieldElement::from_raw(::mul(a, b[1].value())); - let c2 = FieldElement::from_raw(::mul(a, b[2].value())); - let c3 = FieldElement::from_raw(::mul(a, b[3].value())); - - [c0, c1, c2, c3] - } - - fn add( - a: &Self::BaseType, - b: &::BaseType, - ) -> ::BaseType { - let c0 = FieldElement::from_raw(::add(a, b[0].value())); - let c1 = FieldElement::from_raw(*b[1].value()); - let c2 = FieldElement::from_raw(*b[2].value()); - let c3 = FieldElement::from_raw(*b[3].value()); - - [c0, c1, c2, c3] - } - - fn div( - a: &Self::BaseType, - b: &::BaseType, - ) -> Result<::BaseType, FieldError> { - let b_inv = - Degree4BabyBearExtensionField::inv(b).map_err(|_| FieldError::DivisionByZero)?; - Ok(>::mul( - a, &b_inv, - )) - } - - fn sub( - a: &Self::BaseType, - b: &::BaseType, - ) -> ::BaseType { - let c0 = FieldElement::from_raw(::sub(a, b[0].value())); - let c1 = FieldElement::from_raw(::neg(b[1].value())); - let c2 = FieldElement::from_raw(::neg(b[2].value())); - let c3 = FieldElement::from_raw(::neg(b[3].value())); - [c0, c1, c2, c3] - } - - fn embed(a: Self::BaseType) -> ::BaseType { - [ - FieldElement::from_raw(a), - FieldElement::zero(), - FieldElement::zero(), - FieldElement::zero(), - ] - } - - #[cfg(feature = "alloc")] - fn to_subfield_vec( - b: ::BaseType, - ) -> alloc::vec::Vec { - b.into_iter().map(|x| x.to_raw()).collect() - } -} - -impl ByteConversion for [FieldElement; 4] { - #[cfg(feature = "alloc")] - fn to_bytes_be(&self) -> alloc::vec::Vec { - let mut byte_slice = ByteConversion::to_bytes_be(&self[0]); - byte_slice.extend(ByteConversion::to_bytes_be(&self[1])); - byte_slice.extend(ByteConversion::to_bytes_be(&self[2])); - byte_slice.extend(ByteConversion::to_bytes_be(&self[3])); - byte_slice - } - - #[cfg(feature = "alloc")] - fn to_bytes_le(&self) -> alloc::vec::Vec { - let mut byte_slice = ByteConversion::to_bytes_le(&self[0]); - byte_slice.extend(ByteConversion::to_bytes_le(&self[1])); - byte_slice.extend(ByteConversion::to_bytes_le(&self[2])); - byte_slice.extend(ByteConversion::to_bytes_le(&self[3])); - byte_slice - } - - fn from_bytes_be(bytes: &[u8]) -> Result - where - Self: Sized, - { - const BYTES_PER_FIELD: usize = 64; - - let x0 = FieldElement::from_bytes_be(&bytes[0..BYTES_PER_FIELD])?; - let x1 = FieldElement::from_bytes_be(&bytes[BYTES_PER_FIELD..BYTES_PER_FIELD * 2])?; - let x2 = FieldElement::from_bytes_be(&bytes[BYTES_PER_FIELD * 2..BYTES_PER_FIELD * 3])?; - let x3 = FieldElement::from_bytes_be(&bytes[BYTES_PER_FIELD * 3..BYTES_PER_FIELD * 4])?; - - Ok([x0, x1, x2, x3]) - } - - fn from_bytes_le(bytes: &[u8]) -> Result - where - Self: Sized, - { - const BYTES_PER_FIELD: usize = 64; - - let x0 = FieldElement::from_bytes_le(&bytes[0..BYTES_PER_FIELD])?; - let x1 = FieldElement::from_bytes_le(&bytes[BYTES_PER_FIELD..BYTES_PER_FIELD * 2])?; - let x2 = FieldElement::from_bytes_le(&bytes[BYTES_PER_FIELD * 2..BYTES_PER_FIELD * 3])?; - let x3 = FieldElement::from_bytes_le(&bytes[BYTES_PER_FIELD * 3..BYTES_PER_FIELD * 4])?; - - Ok([x0, x1, x2, x3]) - } -} - -impl ByteConversion for FieldElement { - #[cfg(feature = "alloc")] - fn to_bytes_be(&self) -> alloc::vec::Vec { - let mut byte_slice = ByteConversion::to_bytes_be(&self.value()[0]); - byte_slice.extend(ByteConversion::to_bytes_be(&self.value()[1])); - byte_slice.extend(ByteConversion::to_bytes_be(&self.value()[2])); - byte_slice.extend(ByteConversion::to_bytes_be(&self.value()[3])); - byte_slice - } - - #[cfg(feature = "alloc")] - fn to_bytes_le(&self) -> alloc::vec::Vec { - let mut byte_slice = ByteConversion::to_bytes_le(&self.value()[0]); - byte_slice.extend(ByteConversion::to_bytes_le(&self.value()[1])); - byte_slice.extend(ByteConversion::to_bytes_le(&self.value()[2])); - byte_slice.extend(ByteConversion::to_bytes_le(&self.value()[3])); - byte_slice - } - - fn from_bytes_be(bytes: &[u8]) -> Result - where - Self: Sized, - { - const BYTES_PER_FIELD: usize = 8; - let x0 = FieldElement::from_bytes_be(&bytes[0..BYTES_PER_FIELD])?; - let x1 = FieldElement::from_bytes_be(&bytes[BYTES_PER_FIELD..BYTES_PER_FIELD * 2])?; - let x2 = FieldElement::from_bytes_be(&bytes[BYTES_PER_FIELD * 2..BYTES_PER_FIELD * 3])?; - let x3 = FieldElement::from_bytes_be(&bytes[BYTES_PER_FIELD * 3..BYTES_PER_FIELD * 4])?; - - Ok(Self::new([x0, x1, x2, x3])) - } - - fn from_bytes_le(bytes: &[u8]) -> Result - where - Self: Sized, - { - const BYTES_PER_FIELD: usize = 8; - let x0 = FieldElement::from_bytes_le(&bytes[0..BYTES_PER_FIELD])?; - let x1 = FieldElement::from_bytes_le(&bytes[BYTES_PER_FIELD..BYTES_PER_FIELD * 2])?; - let x2 = FieldElement::from_bytes_le(&bytes[BYTES_PER_FIELD * 2..BYTES_PER_FIELD * 3])?; - let x3 = FieldElement::from_bytes_le(&bytes[BYTES_PER_FIELD * 3..BYTES_PER_FIELD * 4])?; - - Ok(Self::new([x0, x1, x2, x3])) - } -} - -#[cfg(feature = "alloc")] -impl AsBytes for FieldElement { - fn as_bytes(&self) -> alloc::vec::Vec { - self.to_bytes_be() - } -} - -impl IsFFTField for Degree4BabyBearExtensionField { - const TWO_ADICITY: u64 = 29; - const TWO_ADIC_PRIMITVE_ROOT_OF_UNITY: Self::BaseType = [ - FieldElement::from_hex_unchecked("0"), - FieldElement::from_hex_unchecked("0"), - FieldElement::from_hex_unchecked("0"), - FieldElement::from_hex_unchecked("771F1C8"), - ]; -} - -impl HasDefaultTranscript for Degree4BabyBearExtensionField { - fn get_random_field_element_from_rng(rng: &mut impl rand::Rng) -> FieldElement { - //Babybear Prime p = 2^31 - 2^27 + 1 - const MODULUS: u64 = 2013265921; - - //Babybear prime needs 31 bits and is represented with 32 bits. - //The mask is used to remove the first bit. - const MASK: u64 = 0x7FFF_FFFF; - - let mut sample = [0u8; 8]; - - let mut coeffs = [ - FieldElement::from(0u64), - FieldElement::from(0u64), - FieldElement::from(0u64), - FieldElement::from(0u64), - ]; - - for coeff in &mut coeffs { - loop { - rng.fill(&mut sample); - let int_sample = u64::from_be_bytes(sample) & MASK; - if int_sample < MODULUS { - *coeff = FieldElement::from(int_sample); - break; - } - } - } - - FieldElement::::new(coeffs) - } -} - -#[cfg(test)] -mod tests { - use super::*; - - type FpE = FieldElement; - type Fp4E = FieldElement; - - #[test] - fn test_add() { - let a = Fp4E::new([FpE::from(0), FpE::from(1), FpE::from(2), FpE::from(3)]); - let b = Fp4E::new([-FpE::from(2), FpE::from(4), FpE::from(6), -FpE::from(8)]); - let expected_result = Fp4E::new([ - FpE::from(0) - FpE::from(2), - FpE::from(1) + FpE::from(4), - FpE::from(2) + FpE::from(6), - FpE::from(3) - FpE::from(8), - ]); - assert_eq!(a + b, expected_result); - } - - #[test] - fn test_sub() { - let a = Fp4E::new([FpE::from(0), FpE::from(1), FpE::from(2), FpE::from(3)]); - let b = Fp4E::new([-FpE::from(2), FpE::from(4), FpE::from(6), -FpE::from(8)]); - let expected_result = Fp4E::new([ - FpE::from(0) + FpE::from(2), - FpE::from(1) - FpE::from(4), - FpE::from(2) - FpE::from(6), - FpE::from(3) + FpE::from(8), - ]); - assert_eq!(a - b, expected_result); - } - - #[test] - fn test_mul_by_0() { - let a = Fp4E::new([FpE::from(4), FpE::from(1), FpE::from(2), FpE::from(3)]); - let b = Fp4E::new([FpE::zero(), FpE::zero(), FpE::zero(), FpE::zero()]); - assert_eq!(&a * &b, b); - } - - #[test] - fn test_mul_by_1() { - let a = Fp4E::new([FpE::from(4), FpE::from(1), FpE::from(2), FpE::from(3)]); - let b = Fp4E::new([FpE::one(), FpE::zero(), FpE::zero(), FpE::zero()]); - assert_eq!(&a * b, a); - } - - #[test] - fn test_mul() { - let a = Fp4E::new([FpE::from(0), FpE::from(1), FpE::from(2), FpE::from(3)]); - let b = Fp4E::new([FpE::from(2), FpE::from(4), FpE::from(6), FpE::from(8)]); - let expected_result = Fp4E::new([ - -FpE::from(352), - -FpE::from(372), - -FpE::from(256), - FpE::from(20), - ]); - assert_eq!(a * b, expected_result); - } - - #[test] - fn test_pow() { - let a = Fp4E::new([FpE::from(0), FpE::from(1), FpE::from(2), FpE::from(3)]); - let expected_result = &a * &a * &a; - assert_eq!(a.pow(3u64), expected_result); - } - - #[test] - fn test_inv_of_one_is_one() { - let a = Fp4E::one(); - assert_eq!(a.inv().unwrap(), a); - } - - #[test] - fn test_inv_of_zero_error() { - let result = Fp4E::zero().inv(); - assert!(result.is_err()); - } - - #[test] - fn test_mul_by_inv_is_identity() { - let a = Fp4E::from(123456); - assert_eq!(&a * a.inv().unwrap(), Fp4E::one()); - } - - #[test] - fn test_mul_as_subfield() { - let a = FpE::from(2); - let b = Fp4E::new([FpE::from(2), FpE::from(4), FpE::from(6), FpE::from(8)]); - let expected_result = Fp4E::new([ - FpE::from(2) * FpE::from(2), - FpE::from(4) * FpE::from(2), - FpE::from(6) * FpE::from(2), - FpE::from(8) * FpE::from(2), - ]); - assert_eq!(a * b, expected_result); - } - - #[test] - fn test_double_equals_sum_two_times() { - let a = Fp4E::new([FpE::from(2), FpE::from(4), FpE::from(6), FpE::from(8)]); - - assert_eq!(a.double(), &a + &a); - } - - #[test] - fn test_mul_group_generator_pow_order_is_one() { - let generator = Fp4E::new([FpE::from(8), FpE::from(1), FpE::zero(), FpE::zero()]); - let extension_order: u128 = 2013265921_u128.pow(4); - assert_eq!(generator.pow(extension_order), generator); - } - - #[test] - fn test_two_adic_primitve_root_of_unity() { - let generator = Fp4E::new(Degree4BabyBearExtensionField::TWO_ADIC_PRIMITVE_ROOT_OF_UNITY); - assert_eq!( - generator.pow(2u64.pow(Degree4BabyBearExtensionField::TWO_ADICITY as u32)), - Fp4E::one() - ); - } - - #[cfg(all(feature = "std", not(feature = "instruments")))] - mod test_babybear_31_fft { - use super::*; - #[cfg(not(feature = "cuda"))] - use crate::fft::cpu::roots_of_unity::{ - get_powers_of_primitive_root, get_powers_of_primitive_root_coset, - }; - #[cfg(not(feature = "cuda"))] - use crate::field::element::FieldElement; - #[cfg(not(feature = "cuda"))] - use crate::field::traits::{IsFFTField, RootsConfig}; - use crate::polynomial::Polynomial; - use proptest::{collection, prelude::*, std_facade::Vec}; - - #[cfg(not(feature = "cuda"))] - fn gen_fft_and_naive_evaluation( - poly: Polynomial>, - ) -> (Vec>, Vec>) { - let len = poly.coeff_len().next_power_of_two(); - let order = len.trailing_zeros(); - let twiddles = - get_powers_of_primitive_root(order.into(), len, RootsConfig::Natural).unwrap(); - - let fft_eval = Polynomial::evaluate_fft::(&poly, 1, None).unwrap(); - let naive_eval = poly.evaluate_slice(&twiddles); - - (fft_eval, naive_eval) - } - - #[cfg(not(feature = "cuda"))] - fn gen_fft_coset_and_naive_evaluation( - poly: Polynomial>, - offset: FieldElement, - blowup_factor: usize, - ) -> (Vec>, Vec>) { - let len = poly.coeff_len().next_power_of_two(); - let order = (len * blowup_factor).trailing_zeros(); - let twiddles = - get_powers_of_primitive_root_coset(order.into(), len * blowup_factor, &offset) - .unwrap(); - - let fft_eval = - Polynomial::evaluate_offset_fft::(&poly, blowup_factor, None, &offset).unwrap(); - let naive_eval = poly.evaluate_slice(&twiddles); - - (fft_eval, naive_eval) - } - - #[cfg(not(feature = "cuda"))] - fn gen_fft_and_naive_interpolate( - fft_evals: &[FieldElement], - ) -> (Polynomial>, Polynomial>) { - let order = fft_evals.len().trailing_zeros() as u64; - let twiddles = - get_powers_of_primitive_root(order, 1 << order, RootsConfig::Natural).unwrap(); - - let naive_poly = Polynomial::interpolate(&twiddles, fft_evals).unwrap(); - let fft_poly = Polynomial::interpolate_fft::(fft_evals).unwrap(); - - (fft_poly, naive_poly) - } - - #[cfg(not(feature = "cuda"))] - fn gen_fft_and_naive_coset_interpolate( - fft_evals: &[FieldElement], - offset: &FieldElement, - ) -> (Polynomial>, Polynomial>) { - let order = fft_evals.len().trailing_zeros() as u64; - let twiddles = get_powers_of_primitive_root_coset(order, 1 << order, offset).unwrap(); - - let naive_poly = Polynomial::interpolate(&twiddles, fft_evals).unwrap(); - let fft_poly = Polynomial::interpolate_offset_fft(fft_evals, offset).unwrap(); - - (fft_poly, naive_poly) - } - - #[cfg(not(feature = "cuda"))] - fn gen_fft_interpolate_and_evaluate( - poly: Polynomial>, - ) -> (Polynomial>, Polynomial>) { - let eval = Polynomial::evaluate_fft::(&poly, 1, None).unwrap(); - let new_poly = Polynomial::interpolate_fft::(&eval).unwrap(); - - (poly, new_poly) - } - - prop_compose! { - fn powers_of_two(max_exp: u8)(exp in 1..max_exp) -> usize { 1 << exp } - // max_exp cannot be multiple of the bits that represent a usize, generally 64 or 32. - // also it can't exceed the test field's two-adicity. - } - prop_compose! { - fn field_element()(coeffs in [any::(); 4]) -> Fp4E { - Fp4E::new([ - FpE::from(coeffs[0]), - FpE::from(coeffs[1]), - FpE::from(coeffs[2]), - FpE::from(coeffs[3])] - ) - } - } - prop_compose! { - fn offset()(num in field_element(), factor in any::()) -> Fp4E { num.pow(factor) } - } - - prop_compose! { - fn field_vec(max_exp: u8)(vec in collection::vec(field_element(), 0..1 << max_exp)) -> Vec { - vec - } - } - prop_compose! { - fn non_power_of_two_sized_field_vec(max_exp: u8)(vec in collection::vec(field_element(), 2..1< Vec { - vec - } - } - prop_compose! { - fn poly(max_exp: u8)(coeffs in field_vec(max_exp)) -> Polynomial { - Polynomial::new(&coeffs) - } - } - prop_compose! { - fn poly_with_non_power_of_two_coeffs(max_exp: u8)(coeffs in non_power_of_two_sized_field_vec(max_exp)) -> Polynomial { - Polynomial::new(&coeffs) - } - } - - proptest! { - // Property-based test that ensures FFT eval. gives same result as a naive polynomial evaluation. - #[test] - #[cfg(not(feature = "cuda"))] - fn test_fft_matches_naive_evaluation(poly in poly(8)) { - let (fft_eval, naive_eval) = gen_fft_and_naive_evaluation(poly); - prop_assert_eq!(fft_eval, naive_eval); - } - - // Property-based test that ensures FFT eval. with coset gives same result as a naive polynomial evaluation. - #[test] - #[cfg(not(feature = "cuda"))] - fn test_fft_coset_matches_naive_evaluation(poly in poly(4), offset in offset(), blowup_factor in powers_of_two(4)) { - let (fft_eval, naive_eval) = gen_fft_coset_and_naive_evaluation(poly, offset, blowup_factor); - prop_assert_eq!(fft_eval, naive_eval); - } - - // Property-based test that ensures FFT interpolation is the same as naive.. - #[test] - #[cfg(not(feature = "cuda"))] - fn test_fft_interpolate_matches_naive(fft_evals in field_vec(4) - .prop_filter("Avoid polynomials of size not power of two", - |evals| evals.len().is_power_of_two())) { - let (fft_poly, naive_poly) = gen_fft_and_naive_interpolate(&fft_evals); - prop_assert_eq!(fft_poly, naive_poly); - } - - // Property-based test that ensures FFT interpolation with an offset is the same as naive. - #[test] - #[cfg(not(feature = "cuda"))] - fn test_fft_interpolate_coset_matches_naive(offset in offset(), fft_evals in field_vec(4) - .prop_filter("Avoid polynomials of size not power of two", - |evals| evals.len().is_power_of_two())) { - let (fft_poly, naive_poly) = gen_fft_and_naive_coset_interpolate(&fft_evals, &offset); - prop_assert_eq!(fft_poly, naive_poly); - } - - // Property-based test that ensures interpolation is the inverse operation of evaluation. - #[test] - #[cfg(not(feature = "cuda"))] - fn test_fft_interpolate_is_inverse_of_evaluate( - poly in poly(4).prop_filter("Avoid non pows of two", |poly| poly.coeff_len().is_power_of_two())) { - let (poly, new_poly) = gen_fft_interpolate_and_evaluate(poly); - prop_assert_eq!(poly, new_poly); - } - } - } -} diff --git a/crates/math/src/field/fields/fft_friendly/quartic_babybear_u32.rs b/crates/math/src/field/fields/fft_friendly/quartic_babybear_u32.rs deleted file mode 100644 index 81640818f..000000000 --- a/crates/math/src/field/fields/fft_friendly/quartic_babybear_u32.rs +++ /dev/null @@ -1,669 +0,0 @@ -use crate::field::{ - element::FieldElement, - errors::FieldError, - fields::fft_friendly::babybear_u32::Babybear31PrimeField, - traits::{IsFFTField, IsField, IsSubFieldOf}, -}; - -use crate::traits::ByteConversion; - -#[cfg(feature = "alloc")] -use crate::traits::AsBytes; - -/// We are implementig the extension of Baby Bear of degree 4 using the irreducible polynomial x^4 + 11. -/// BETA = 11 and -BETA = -11 is the non-residue. -/// Since `const_from_raw()` doesn't make the montgomery conversion, we calculated it. -/// The montgomery form of a number "a" is a * R mod p. -/// In Baby Bear field, R = 2^32 and p = 2013265921. -/// Then, 939524073 = 11 * 2^32 mod 2013265921. -pub const BETA: FieldElement = - FieldElement::::const_from_raw(939524073); - -#[derive(Clone, Debug)] -pub struct Degree4BabyBearU32ExtensionField; - -/// We implement directly the degree four extension for performance reasons, instead of using -/// the default quadratic extension provided by the library -impl IsField for Degree4BabyBearU32ExtensionField { - type BaseType = [FieldElement; 4]; - - /// Addition of degree four field extension elements - fn add(a: &Self::BaseType, b: &Self::BaseType) -> Self::BaseType { - [a[0] + b[0], a[1] + b[1], a[2] + b[2], a[3] + b[3]] - } - - /// Result of multiplying two polynomials a = a0 + a1 * x + a2 * x^2 + a3 * x^3 and - /// b = b0 + b1 * x + b2 * x^2 + b3 * x^3 by applying distribution and taking - /// the remainder of the division by x^4 + 11. - /// Multiplication of two degree four field extension elements - fn mul(a: &Self::BaseType, b: &Self::BaseType) -> Self::BaseType { - [ - a[0] * b[0] - BETA * (a[1] * b[3] + a[3] * b[1] + a[2] * b[2]), - a[0] * b[1] + a[1] * b[0] - BETA * (a[2] * b[3] + a[3] * b[2]), - a[0] * b[2] + a[2] * b[0] + a[1] * b[1] - BETA * (a[3] * b[3]), - a[0] * b[3] + a[3] * b[0] + a[1] * b[2] + a[2] * b[1], - ] - } - - /// Returns the square of a degree four field extension element - /// More efficient to use instead of multiplying the element with itself - fn square(a: &Self::BaseType) -> Self::BaseType { - [ - a[0].square() - BETA * ((a[1] * a[3]).double() + a[2].square()), - (a[0] * a[1] - BETA * (a[2] * a[3])).double(), - (a[0] * a[2]).double() + a[1].square() - BETA * (a[3].square()), - (a[0] * a[3] + a[1] * a[2]).double(), - ] - } - - /// Subtraction of degree four field extension elements - fn sub(a: &Self::BaseType, b: &Self::BaseType) -> Self::BaseType { - [a[0] - b[0], a[1] - b[1], a[2] - b[2], a[3] - b[3]] - } - - /// Additive inverse of degree four field extension element - fn neg(a: &Self::BaseType) -> Self::BaseType { - [-&a[0], -&a[1], -&a[2], -&a[3]] - } - - /// Return te inverse of a fp4 element if exist. - /// This algorithm is inspired by Risc0 implementation: - /// - fn inv(a: &Self::BaseType) -> Result { - let mut b0 = a[0] * a[0] + BETA * (a[1] * (a[3] + a[3]) - a[2] * a[2]); - let mut b2 = a[0] * (a[2] + a[2]) - a[1] * a[1] + BETA * (a[3] * a[3]); - let c = b0.square() + BETA * b2.square(); - let c_inv = c.inv()?; - b0 *= &c_inv; - b2 *= &c_inv; - Ok([ - a[0] * b0 + BETA * a[2] * b2, - -a[1] * b0 - BETA * a[3] * b2, - -a[0] * b2 + a[2] * b0, - a[1] * b2 - a[3] * b0, - ]) - } - - fn div(a: &Self::BaseType, b: &Self::BaseType) -> Result { - let b_inv = &Self::inv(b).map_err(|_| FieldError::DivisionByZero)?; - Ok(::mul(a, b_inv)) - } - - fn eq(a: &Self::BaseType, b: &Self::BaseType) -> bool { - a[0] == b[0] && a[1] == b[1] && a[2] == b[2] && a[3] == b[3] - } - - fn zero() -> Self::BaseType { - Self::BaseType::default() - } - - fn one() -> Self::BaseType { - [ - FieldElement::one(), - FieldElement::zero(), - FieldElement::zero(), - FieldElement::zero(), - ] - } - - fn from_u64(x: u64) -> Self::BaseType { - [ - FieldElement::from(x), - FieldElement::zero(), - FieldElement::zero(), - FieldElement::zero(), - ] - } - - /// It takes as input an element of BaseType and returns the internal representation - /// of that element in the field. - /// Note: for this case this is simply the identity, because the components - /// already have correct representations. - fn from_base_type(x: Self::BaseType) -> Self::BaseType { - x - } - - fn double(a: &Self::BaseType) -> Self::BaseType { - ::add(a, a) - } - - fn pow(a: &Self::BaseType, mut exponent: T) -> Self::BaseType - where - T: crate::unsigned_integer::traits::IsUnsignedInteger, - { - let zero = T::from(0); - let one = T::from(1); - - if exponent == zero { - return Self::one(); - } - if exponent == one { - return *a; - } - - let mut result = *a; - - // Fast path for powers of 2 - while exponent & one == zero { - result = Self::square(&result); - exponent >>= 1; - if exponent == zero { - return result; - } - } - - let mut base = result; - exponent >>= 1; - - while exponent != zero { - base = Self::square(&base); - if exponent & one == one { - result = ::mul(&result, &base); - } - exponent >>= 1; - } - - result - } -} - -/// Implements efficient operations between a BabyBear element and a degree four extension element -impl IsSubFieldOf for Babybear31PrimeField { - fn mul( - a: &Self::BaseType, - b: &::BaseType, - ) -> ::BaseType { - let c0 = FieldElement::from_raw(::mul(a, b[0].value())); - let c1 = FieldElement::from_raw(::mul(a, b[1].value())); - let c2 = FieldElement::from_raw(::mul(a, b[2].value())); - let c3 = FieldElement::from_raw(::mul(a, b[3].value())); - - [c0, c1, c2, c3] - } - - fn add( - a: &Self::BaseType, - b: &::BaseType, - ) -> ::BaseType { - let c0 = FieldElement::from_raw(::add(a, b[0].value())); - let c1 = FieldElement::from_raw(*b[1].value()); - let c2 = FieldElement::from_raw(*b[2].value()); - let c3 = FieldElement::from_raw(*b[3].value()); - - [c0, c1, c2, c3] - } - - fn div( - a: &Self::BaseType, - b: &::BaseType, - ) -> Result<::BaseType, FieldError> { - let b_inv = Degree4BabyBearU32ExtensionField::inv(b)?; - Ok(>::mul(a, &b_inv)) - } - - fn sub( - a: &Self::BaseType, - b: &::BaseType, - ) -> ::BaseType { - let c0 = FieldElement::from_raw(::sub(a, b[0].value())); - let c1 = FieldElement::from_raw(::neg(b[1].value())); - let c2 = FieldElement::from_raw(::neg(b[2].value())); - let c3 = FieldElement::from_raw(::neg(b[3].value())); - [c0, c1, c2, c3] - } - - fn embed(a: Self::BaseType) -> ::BaseType { - [ - FieldElement::from_raw(a), - FieldElement::zero(), - FieldElement::zero(), - FieldElement::zero(), - ] - } - - #[cfg(feature = "alloc")] - fn to_subfield_vec( - b: ::BaseType, - ) -> alloc::vec::Vec { - b.into_iter().map(|x| x.to_raw()).collect() - } -} - -impl ByteConversion for [FieldElement; 4] { - #[cfg(feature = "alloc")] - fn to_bytes_be(&self) -> alloc::vec::Vec { - let mut byte_slice = ByteConversion::to_bytes_be(&self[0]); - byte_slice.extend(ByteConversion::to_bytes_be(&self[1])); - byte_slice.extend(ByteConversion::to_bytes_be(&self[2])); - byte_slice.extend(ByteConversion::to_bytes_be(&self[3])); - byte_slice - } - - #[cfg(feature = "alloc")] - fn to_bytes_le(&self) -> alloc::vec::Vec { - let mut byte_slice = ByteConversion::to_bytes_le(&self[0]); - byte_slice.extend(ByteConversion::to_bytes_le(&self[1])); - byte_slice.extend(ByteConversion::to_bytes_le(&self[2])); - byte_slice.extend(ByteConversion::to_bytes_le(&self[3])); - byte_slice - } - - fn from_bytes_be(bytes: &[u8]) -> Result - where - Self: Sized, - { - const BYTES_PER_FIELD: usize = 32; - - let x0 = FieldElement::from_bytes_be(&bytes[0..BYTES_PER_FIELD])?; - let x1 = FieldElement::from_bytes_be(&bytes[BYTES_PER_FIELD..BYTES_PER_FIELD * 2])?; - let x2 = FieldElement::from_bytes_be(&bytes[BYTES_PER_FIELD * 2..BYTES_PER_FIELD * 3])?; - let x3 = FieldElement::from_bytes_be(&bytes[BYTES_PER_FIELD * 3..BYTES_PER_FIELD * 4])?; - - Ok([x0, x1, x2, x3]) - } - - fn from_bytes_le(bytes: &[u8]) -> Result - where - Self: Sized, - { - const BYTES_PER_FIELD: usize = 32; - - let x0 = FieldElement::from_bytes_le(&bytes[0..BYTES_PER_FIELD])?; - let x1 = FieldElement::from_bytes_le(&bytes[BYTES_PER_FIELD..BYTES_PER_FIELD * 2])?; - let x2 = FieldElement::from_bytes_le(&bytes[BYTES_PER_FIELD * 2..BYTES_PER_FIELD * 3])?; - let x3 = FieldElement::from_bytes_le(&bytes[BYTES_PER_FIELD * 3..BYTES_PER_FIELD * 4])?; - - Ok([x0, x1, x2, x3]) - } -} - -impl ByteConversion for FieldElement { - #[cfg(feature = "alloc")] - fn to_bytes_be(&self) -> alloc::vec::Vec { - let mut byte_slice = ByteConversion::to_bytes_be(&self.value()[0]); - byte_slice.extend(ByteConversion::to_bytes_be(&self.value()[1])); - byte_slice.extend(ByteConversion::to_bytes_be(&self.value()[2])); - byte_slice.extend(ByteConversion::to_bytes_be(&self.value()[3])); - byte_slice - } - #[cfg(feature = "alloc")] - fn to_bytes_le(&self) -> alloc::vec::Vec { - let mut byte_slice = ByteConversion::to_bytes_le(&self.value()[0]); - byte_slice.extend(ByteConversion::to_bytes_le(&self.value()[1])); - byte_slice.extend(ByteConversion::to_bytes_le(&self.value()[2])); - byte_slice.extend(ByteConversion::to_bytes_le(&self.value()[3])); - byte_slice - } - - fn from_bytes_be(bytes: &[u8]) -> Result - where - Self: Sized, - { - const BYTES_PER_FIELD: usize = 4; - let x0 = FieldElement::from_bytes_be(&bytes[0..BYTES_PER_FIELD])?; - let x1 = FieldElement::from_bytes_be(&bytes[BYTES_PER_FIELD..BYTES_PER_FIELD * 2])?; - let x2 = FieldElement::from_bytes_be(&bytes[BYTES_PER_FIELD * 2..BYTES_PER_FIELD * 3])?; - let x3 = FieldElement::from_bytes_be(&bytes[BYTES_PER_FIELD * 3..BYTES_PER_FIELD * 4])?; - - Ok(Self::new([x0, x1, x2, x3])) - } - - fn from_bytes_le(bytes: &[u8]) -> Result - where - Self: Sized, - { - const BYTES_PER_FIELD: usize = 4; - let x0 = FieldElement::from_bytes_le(&bytes[0..BYTES_PER_FIELD])?; - let x1 = FieldElement::from_bytes_le(&bytes[BYTES_PER_FIELD..BYTES_PER_FIELD * 2])?; - let x2 = FieldElement::from_bytes_le(&bytes[BYTES_PER_FIELD * 2..BYTES_PER_FIELD * 3])?; - let x3 = FieldElement::from_bytes_le(&bytes[BYTES_PER_FIELD * 3..BYTES_PER_FIELD * 4])?; - - Ok(Self::new([x0, x1, x2, x3])) - } -} - -#[cfg(feature = "alloc")] -impl AsBytes for FieldElement { - fn as_bytes(&self) -> alloc::vec::Vec { - self.to_bytes_be() - } -} - -impl IsFFTField for Degree4BabyBearU32ExtensionField { - const TWO_ADICITY: u64 = 29; - const TWO_ADIC_PRIMITVE_ROOT_OF_UNITY: Self::BaseType = [ - FieldElement::const_from_raw(0), - FieldElement::const_from_raw(0), - FieldElement::const_from_raw(0), - // We are using the montgomery form of 124907976. - // That is: 1344142388 = 124907976 * R mod p, - // where R = 2^32 and p = 2013265921. - FieldElement::const_from_raw(1344142388), - ]; -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::field::element::FieldElement; - - type FpE = FieldElement; - type Fp4E = FieldElement; - - #[test] - fn test_add() { - let a = Fp4E::new([FpE::from(0), FpE::from(1), FpE::from(2), FpE::from(3)]); - let b = Fp4E::new([-FpE::from(2), FpE::from(4), FpE::from(6), -FpE::from(8)]); - let expected_result = Fp4E::new([ - FpE::from(0) - FpE::from(2), - FpE::from(1) + FpE::from(4), - FpE::from(2) + FpE::from(6), - FpE::from(3) - FpE::from(8), - ]); - assert_eq!(a + b, expected_result); - } - - #[test] - fn test_sub() { - let a = Fp4E::new([FpE::from(0), FpE::from(1), FpE::from(2), FpE::from(3)]); - let b = Fp4E::new([-FpE::from(2), FpE::from(4), FpE::from(6), -FpE::from(8)]); - let expected_result = Fp4E::new([ - FpE::from(0) + FpE::from(2), - FpE::from(1) - FpE::from(4), - FpE::from(2) - FpE::from(6), - FpE::from(3) + FpE::from(8), - ]); - assert_eq!(a - b, expected_result); - } - - #[test] - fn test_mul_by_0() { - let a = Fp4E::new([FpE::from(4), FpE::from(1), FpE::from(2), FpE::from(3)]); - let b = Fp4E::new([FpE::zero(), FpE::zero(), FpE::zero(), FpE::zero()]); - assert_eq!(&a * &b, b); - } - - #[test] - fn test_mul_by_1() { - let a = Fp4E::new([FpE::from(4), FpE::from(1), FpE::from(2), FpE::from(3)]); - let b = Fp4E::new([FpE::one(), FpE::zero(), FpE::zero(), FpE::zero()]); - assert_eq!(&a * b, a); - } - - #[test] - fn test_mul() { - let a = Fp4E::new([FpE::from(0), FpE::from(1), FpE::from(2), FpE::from(3)]); - let b = Fp4E::new([FpE::from(2), FpE::from(4), FpE::from(6), FpE::from(8)]); - let expected_result = Fp4E::new([ - -FpE::from(352), - -FpE::from(372), - -FpE::from(256), - FpE::from(20), - ]); - assert_eq!(a * b, expected_result); - } - - #[test] - fn test_pow() { - let a = Fp4E::new([FpE::from(0), FpE::from(1), FpE::from(2), FpE::from(3)]); - let expected_result = &a * &a * &a; - assert_eq!(a.pow(3u64), expected_result); - } - - #[test] - fn test_inv_of_one_is_one() { - let a = Fp4E::one(); - assert_eq!(a.inv().unwrap(), a); - } - - #[test] - fn test_inv_of_zero_error() { - let result = Fp4E::zero().inv(); - assert!(result.is_err()); - } - - #[test] - fn test_mul_by_inv_is_identity() { - let a = Fp4E::from(123456); - assert_eq!(&a * a.inv().unwrap(), Fp4E::one()); - } - - #[test] - fn test_mul_as_subfield() { - let a = FpE::from(2); - let b = Fp4E::new([FpE::from(2), FpE::from(4), FpE::from(6), FpE::from(8)]); - let expected_result = Fp4E::new([ - FpE::from(2) * FpE::from(2), - FpE::from(4) * FpE::from(2), - FpE::from(6) * FpE::from(2), - FpE::from(8) * FpE::from(2), - ]); - assert_eq!(a * b, expected_result); - } - - #[test] - fn test_double_equals_sum_two_times() { - let a = Fp4E::new([FpE::from(2), FpE::from(4), FpE::from(6), FpE::from(8)]); - - assert_eq!(a.double(), &a + &a); - } - - #[test] - fn test_mul_group_generator_pow_order_is_one() { - let generator = Fp4E::new([FpE::from(8), FpE::from(1), FpE::zero(), FpE::zero()]); - let extension_order: u128 = 2013265921_u128.pow(4); - assert_eq!(generator.pow(extension_order), generator); - } - - #[test] - fn test_two_adic_primitve_root_of_unity() { - let generator = - Fp4E::new(Degree4BabyBearU32ExtensionField::TWO_ADIC_PRIMITVE_ROOT_OF_UNITY); - assert_eq!( - generator.pow(2u64.pow(Degree4BabyBearU32ExtensionField::TWO_ADICITY as u32)), - Fp4E::one() - ); - } - - #[test] - #[cfg(feature = "alloc")] - fn to_bytes_from_bytes_be_is_the_identity() { - let x = Fp4E::new([FpE::from(2), FpE::from(4), FpE::from(6), FpE::from(8)]); - assert_eq!(Fp4E::from_bytes_be(&x.to_bytes_be()).unwrap(), x); - } - - #[test] - #[cfg(feature = "alloc")] - fn from_bytes_to_bytes_be_is_the_identity() { - let bytes = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]; - assert_eq!(Fp4E::from_bytes_be(&bytes).unwrap().to_bytes_be(), bytes); - } - - #[test] - #[cfg(feature = "alloc")] - fn to_bytes_from_bytes_le_is_the_identity() { - let x = Fp4E::new([FpE::from(2), FpE::from(4), FpE::from(6), FpE::from(8)]); - assert_eq!(Fp4E::from_bytes_le(&x.to_bytes_le()).unwrap(), x); - } - - #[test] - #[cfg(feature = "alloc")] - fn from_bytes_to_bytes_le_is_the_identity() { - let bytes = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]; - assert_eq!(Fp4E::from_bytes_le(&bytes).unwrap().to_bytes_le(), bytes); - } - - #[cfg(all(feature = "std", not(feature = "instruments")))] - mod test_babybear_31_fft { - use super::*; - #[cfg(not(feature = "cuda"))] - use crate::fft::cpu::roots_of_unity::{ - get_powers_of_primitive_root, get_powers_of_primitive_root_coset, - }; - #[cfg(not(feature = "cuda"))] - use crate::field::element::FieldElement; - #[cfg(not(feature = "cuda"))] - use crate::field::traits::{IsFFTField, RootsConfig}; - use crate::polynomial::Polynomial; - use proptest::{collection, prelude::*, std_facade::Vec}; - - #[cfg(not(feature = "cuda"))] - fn gen_fft_and_naive_evaluation( - poly: Polynomial>, - ) -> (Vec>, Vec>) { - let len = poly.coeff_len().next_power_of_two(); - let order = len.trailing_zeros(); - let twiddles = - get_powers_of_primitive_root(order.into(), len, RootsConfig::Natural).unwrap(); - - let fft_eval = Polynomial::evaluate_fft::(&poly, 1, None).unwrap(); - let naive_eval = poly.evaluate_slice(&twiddles); - - (fft_eval, naive_eval) - } - - #[cfg(not(feature = "cuda"))] - fn gen_fft_coset_and_naive_evaluation( - poly: Polynomial>, - offset: FieldElement, - blowup_factor: usize, - ) -> (Vec>, Vec>) { - let len = poly.coeff_len().next_power_of_two(); - let order = (len * blowup_factor).trailing_zeros(); - let twiddles = - get_powers_of_primitive_root_coset(order.into(), len * blowup_factor, &offset) - .unwrap(); - - let fft_eval = - Polynomial::evaluate_offset_fft::(&poly, blowup_factor, None, &offset).unwrap(); - let naive_eval = poly.evaluate_slice(&twiddles); - - (fft_eval, naive_eval) - } - - #[cfg(not(feature = "cuda"))] - fn gen_fft_and_naive_interpolate( - fft_evals: &[FieldElement], - ) -> (Polynomial>, Polynomial>) { - let order = fft_evals.len().trailing_zeros() as u64; - let twiddles = - get_powers_of_primitive_root(order, 1 << order, RootsConfig::Natural).unwrap(); - - let naive_poly = Polynomial::interpolate(&twiddles, fft_evals).unwrap(); - let fft_poly = Polynomial::interpolate_fft::(fft_evals).unwrap(); - - (fft_poly, naive_poly) - } - - #[cfg(not(feature = "cuda"))] - fn gen_fft_and_naive_coset_interpolate( - fft_evals: &[FieldElement], - offset: &FieldElement, - ) -> (Polynomial>, Polynomial>) { - let order = fft_evals.len().trailing_zeros() as u64; - let twiddles = get_powers_of_primitive_root_coset(order, 1 << order, offset).unwrap(); - - let naive_poly = Polynomial::interpolate(&twiddles, fft_evals).unwrap(); - let fft_poly = Polynomial::interpolate_offset_fft(fft_evals, offset).unwrap(); - - (fft_poly, naive_poly) - } - - #[cfg(not(feature = "cuda"))] - fn gen_fft_interpolate_and_evaluate( - poly: Polynomial>, - ) -> (Polynomial>, Polynomial>) { - let eval = Polynomial::evaluate_fft::(&poly, 1, None).unwrap(); - let new_poly = Polynomial::interpolate_fft::(&eval).unwrap(); - - (poly, new_poly) - } - - prop_compose! { - fn powers_of_two(max_exp: u8)(exp in 1..max_exp) -> usize { 1 << exp } - // max_exp cannot be multiple of the bits that represent a usize, generally 64 or 32. - // also it can't exceed the test field's two-adicity. - } - prop_compose! { - fn field_element()(coeffs in [any::(); 4]) -> Fp4E { - Fp4E::new([ - FpE::from(coeffs[0]), - FpE::from(coeffs[1]), - FpE::from(coeffs[2]), - FpE::from(coeffs[3])] - ) - } - } - prop_compose! { - fn offset()(num in field_element(), factor in any::()) -> Fp4E { num.pow(factor) } - } - - prop_compose! { - fn field_vec(max_exp: u8)(vec in collection::vec(field_element(), 0..1 << max_exp)) -> Vec { - vec - } - } - prop_compose! { - fn non_power_of_two_sized_field_vec(max_exp: u8)(vec in collection::vec(field_element(), 2..1< Vec { - vec - } - } - prop_compose! { - fn poly(max_exp: u8)(coeffs in field_vec(max_exp)) -> Polynomial { - Polynomial::new(&coeffs) - } - } - prop_compose! { - fn poly_with_non_power_of_two_coeffs(max_exp: u8)(coeffs in non_power_of_two_sized_field_vec(max_exp)) -> Polynomial { - Polynomial::new(&coeffs) - } - } - - proptest! { - // Property-based test that ensures FFT eval. gives same result as a naive polynomial evaluation. - #[test] - #[cfg(not(feature = "cuda"))] - fn test_fft_matches_naive_evaluation(poly in poly(8)) { - let (fft_eval, naive_eval) = gen_fft_and_naive_evaluation(poly); - prop_assert_eq!(fft_eval, naive_eval); - } - - // Property-based test that ensures FFT eval. with coset gives same result as a naive polynomial evaluation. - #[test] - #[cfg(not(feature = "cuda"))] - fn test_fft_coset_matches_naive_evaluation(poly in poly(4), offset in offset(), blowup_factor in powers_of_two(4)) { - let (fft_eval, naive_eval) = gen_fft_coset_and_naive_evaluation(poly, offset, blowup_factor); - prop_assert_eq!(fft_eval, naive_eval); - } - - // Property-based test that ensures FFT interpolation is the same as naive.. - #[test] - #[cfg(not(feature = "cuda"))] - fn test_fft_interpolate_matches_naive(fft_evals in field_vec(4) - .prop_filter("Avoid polynomials of size not power of two", - |evals| evals.len().is_power_of_two())) { - let (fft_poly, naive_poly) = gen_fft_and_naive_interpolate(&fft_evals); - prop_assert_eq!(fft_poly, naive_poly); - } - - // Property-based test that ensures FFT interpolation with an offset is the same as naive. - #[test] - #[cfg(not(feature = "cuda"))] - fn test_fft_interpolate_coset_matches_naive(offset in offset(), fft_evals in field_vec(4) - .prop_filter("Avoid polynomials of size not power of two", - |evals| evals.len().is_power_of_two())) { - let (fft_poly, naive_poly) = gen_fft_and_naive_coset_interpolate(&fft_evals, &offset); - prop_assert_eq!(fft_poly, naive_poly); - } - - // Property-based test that ensures interpolation is the inverse operation of evaluation. - #[test] - #[cfg(not(feature = "cuda"))] - fn test_fft_interpolate_is_inverse_of_evaluate( - poly in poly(4).prop_filter("Avoid non pows of two", |poly| poly.coeff_len().is_power_of_two())) { - let (poly, new_poly) = gen_fft_interpolate_and_evaluate(poly); - prop_assert_eq!(poly, new_poly); - } - } - } -} diff --git a/crates/math/src/field/fields/fft_friendly/stark_101_prime_field.rs b/crates/math/src/field/fields/fft_friendly/stark_101_prime_field.rs deleted file mode 100644 index d6232a414..000000000 --- a/crates/math/src/field/fields/fft_friendly/stark_101_prime_field.rs +++ /dev/null @@ -1,113 +0,0 @@ -use crate::{ - field::{ - element::FieldElement, - fields::montgomery_backed_prime_fields::{IsModulus, U64PrimeField}, - traits::IsFFTField, - }, - unsigned_integer::element::{UnsignedInteger, U64}, -}; - -#[derive(Clone, Debug, Hash, Copy)] -pub struct MontgomeryConfigStark101PrimeField; -impl IsModulus for MontgomeryConfigStark101PrimeField { - /// 3 * 2^30 + 1 - const MODULUS: U64 = U64::from_hex_unchecked("c0000001"); -} - -pub type Stark101PrimeField = U64PrimeField; - -impl IsFFTField for Stark101PrimeField { - const TWO_ADICITY: u64 = 30; - - const TWO_ADIC_PRIMITVE_ROOT_OF_UNITY: U64 = UnsignedInteger::from_hex_unchecked("bb6e79d"); - - fn field_name() -> &'static str { - "stark101" - } -} - -impl FieldElement { - pub fn to_bytes_le(&self) -> [u8; 8] { - let limbs = self.representative().limbs; - limbs[0].to_le_bytes() - } - - pub fn to_bytes_be(&self) -> [u8; 8] { - let limbs = self.representative().limbs; - limbs[0].to_be_bytes() - } -} - -#[allow(clippy::non_canonical_partial_ord_impl)] -impl PartialOrd for FieldElement { - fn partial_cmp(&self, other: &Self) -> Option { - self.representative().partial_cmp(&other.representative()) - } -} - -impl Ord for FieldElement { - fn cmp(&self, other: &Self) -> core::cmp::Ordering { - self.representative().cmp(&other.representative()) - } -} - -#[cfg(test)] -mod test_stark101_prime_field { - use super::Stark101PrimeField; - use crate::{ - field::{element::FieldElement, traits::IsFFTField}, - traits::ByteConversion, - }; - - #[test] - fn two_adic_order() { - let w = FieldElement::::from( - Stark101PrimeField::TWO_ADIC_PRIMITVE_ROOT_OF_UNITY.limbs[0], - ); - - assert_eq!( - w.pow(1u64 << Stark101PrimeField::TWO_ADICITY), - FieldElement::one() - ); - assert_ne!( - w.pow(1u64 << (Stark101PrimeField::TWO_ADICITY >> 1)), - FieldElement::one() - ); - } - - #[test] - #[cfg(feature = "alloc")] - fn byte_serialization_for_a_number_matches_with_byte_conversion_implementation_le() { - let element = FieldElement::::from_hex_unchecked("0123456701234567"); - let bytes = element.to_bytes_le(); - let expected_bytes: [u8; 8] = ByteConversion::to_bytes_le(&element).try_into().unwrap(); - assert_eq!(bytes, expected_bytes); - } - - #[test] - #[cfg(feature = "alloc")] - fn byte_serialization_for_a_number_matches_with_byte_conversion_implementation_be() { - let element = FieldElement::::from_hex_unchecked("0123456701234567"); - let bytes = element.to_bytes_be(); - let expected_bytes: [u8; 8] = ByteConversion::to_bytes_be(&element).try_into().unwrap(); - assert_eq!(bytes, expected_bytes); - } - - #[test] - - fn byte_serialization_and_deserialization_works_le() { - let element = FieldElement::::from_hex_unchecked("7654321076543210"); - let bytes = element.to_bytes_le(); - let from_bytes = FieldElement::::from_bytes_le(&bytes).unwrap(); - assert_eq!(element, from_bytes); - } - - #[test] - - fn byte_serialization_and_deserialization_works_be() { - let element = FieldElement::::from_hex_unchecked("7654321076543210"); - let bytes = element.to_bytes_be(); - let from_bytes = FieldElement::::from_bytes_be(&bytes).unwrap(); - assert_eq!(element, from_bytes); - } -} diff --git a/crates/math/src/field/fields/fft_friendly/stark_252_prime_field.rs b/crates/math/src/field/fields/fft_friendly/stark_252_prime_field.rs deleted file mode 100644 index 999c1afae..000000000 --- a/crates/math/src/field/fields/fft_friendly/stark_252_prime_field.rs +++ /dev/null @@ -1,167 +0,0 @@ -use crate::{ - field::{ - element::FieldElement, - fields::montgomery_backed_prime_fields::{IsModulus, U256PrimeField}, - traits::IsFFTField, - }, - unsigned_integer::element::{UnsignedInteger, U256}, -}; - -#[derive(Clone, Debug, Hash, Copy)] -pub struct MontgomeryConfigStark252PrimeField; -impl IsModulus for MontgomeryConfigStark252PrimeField { - const MODULUS: U256 = - U256::from_hex_unchecked("800000000000011000000000000000000000000000000000000000000000001"); -} - -pub type Stark252PrimeField = U256PrimeField; - -impl IsFFTField for Stark252PrimeField { - const TWO_ADICITY: u64 = 192; - // Change this line for a new function like `from_limbs`. - const TWO_ADIC_PRIMITVE_ROOT_OF_UNITY: U256 = UnsignedInteger::from_hex_unchecked( - "5282db87529cfa3f0464519c8b0fa5ad187148e11a61616070024f42f8ef94", - ); - - fn field_name() -> &'static str { - "stark256" - } -} - -impl FieldElement { - /// No std version of `to_bytes_le` from `ByteConversion` trait - /// This follows the convention used by - /// Starkware and Lambdaclass Cairo VM It's the same as ByteConversion to_bytes_le. - pub fn to_bytes_le(&self) -> [u8; 32] { - let limbs = self.representative().limbs; - let mut bytes: [u8; 32] = [0; 32]; - - for i in (0..4).rev() { - let limb_bytes = limbs[i].to_le_bytes(); - for j in 0..8 { - // i = 3 -> - bytes[(3 - i) * 8 + j] = limb_bytes[j] - } - } - bytes - } - - /// This follows the convention used by starknet-rs - pub fn to_bits_le(&self) -> [bool; 256] { - let limbs = self.representative().limbs; - let mut bits = [false; 256]; - - for i in (0..4).rev() { - let limb_bytes = limbs[i].to_le_bytes(); - let limb_bytes_starting_index = (3 - i) * 8; - for (j, byte) in limb_bytes.iter().enumerate() { - let byte_index = (limb_bytes_starting_index + j) * 8; - for k in 0..8 { - let bit_index = byte_index + k; - let bit_value = (byte >> k) & 1 == 1; - bits[bit_index] = bit_value; - } - } - } - bits - } - - /// No std version of `to_bytes_be` from `ByteConversion` trait - /// This follows the convention used by - /// Starkware and Lambdaclass Cairo VM It's the same as ByteConversion to_bytes_be. - pub fn to_bytes_be(&self) -> [u8; 32] { - let limbs = self.representative().limbs; - let mut bytes: [u8; 32] = [0; 32]; - - for i in 0..4 { - let limb_bytes = limbs[i].to_be_bytes(); - for j in 0..8 { - bytes[i * 8 + j] = limb_bytes[j] - } - } - bytes - } -} - -#[allow(clippy::non_canonical_partial_ord_impl)] -impl PartialOrd for FieldElement { - fn partial_cmp(&self, other: &Self) -> Option { - self.representative().partial_cmp(&other.representative()) - } -} - -impl Ord for FieldElement { - fn cmp(&self, other: &Self) -> core::cmp::Ordering { - self.representative().cmp(&other.representative()) - } -} - -#[cfg(test)] -mod test_stark_252_bytes_ops { - use super::Stark252PrimeField; - use crate::{field::element::FieldElement, traits::ByteConversion}; - - #[test] - #[cfg(feature = "alloc")] - fn byte_serialization_for_a_number_matches_with_byte_conversion_implementation_le() { - let element = FieldElement::::from_hex_unchecked( - "\ - 0123456701234567\ - 0123456701234567\ - 0123456701234567\ - 0123456701234567\ - ", - ); - let bytes = element.to_bytes_le(); - let expected_bytes: [u8; 32] = ByteConversion::to_bytes_le(&element).try_into().unwrap(); - assert_eq!(bytes, expected_bytes); - } - - #[test] - #[cfg(feature = "alloc")] - fn byte_serialization_for_a_number_matches_with_byte_conversion_implementation_be() { - let element = FieldElement::::from_hex_unchecked( - "\ - 0123456701234567\ - 0123456701234567\ - 0123456701234567\ - 0123456701234567\ - ", - ); - let bytes = element.to_bytes_be(); - let expected_bytes: [u8; 32] = ByteConversion::to_bytes_be(&element).try_into().unwrap(); - assert_eq!(bytes, expected_bytes); - } - - #[test] - - fn byte_serialization_and_deserialization_works_le() { - let element = FieldElement::::from_hex_unchecked( - "\ - 0123456701234567\ - 7654321076543210\ - 7654321076543210\ - 7654321076543210\ - ", - ); - let bytes = element.to_bytes_le(); - let from_bytes = FieldElement::::from_bytes_le(&bytes).unwrap(); - assert_eq!(element, from_bytes); - } - - #[test] - - fn byte_serialization_and_deserialization_works_be() { - let element = FieldElement::::from_hex_unchecked( - "\ - 0123456701234567\ - 7654321076543210\ - 7654321076543210\ - 7654321076543210\ - ", - ); - let bytes = element.to_bytes_be(); - let from_bytes = FieldElement::::from_bytes_be(&bytes).unwrap(); - assert_eq!(element, from_bytes); - } -} diff --git a/crates/math/src/field/fields/fft_friendly/u64_goldilocks.rs b/crates/math/src/field/fields/fft_friendly/u64_goldilocks.rs deleted file mode 100644 index ca2cec138..000000000 --- a/crates/math/src/field/fields/fft_friendly/u64_goldilocks.rs +++ /dev/null @@ -1,102 +0,0 @@ -use crate::{ - field::{ - element::FieldElement, - fields::montgomery_backed_prime_fields::{IsModulus, MontgomeryBackendPrimeField}, - }, - unsigned_integer::element::U64, -}; - -pub type U64MontgomeryBackendPrimeField = MontgomeryBackendPrimeField; - -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub struct MontgomeryConfigU64GoldilocksPrimeField; -impl IsModulus for MontgomeryConfigU64GoldilocksPrimeField { - //Babybear Prime p = 2^64 - 2^32 + 1 - const MODULUS: U64 = U64::from_u64(18446744069414584321); -} - -pub type U64GoldilocksPrimeField = - U64MontgomeryBackendPrimeField; - -impl FieldElement { - pub fn to_bytes_le(&self) -> [u8; 8] { - let limbs = self.representative().limbs; - limbs[0].to_le_bytes() - } - - pub fn to_bytes_be(&self) -> [u8; 8] { - let limbs = self.representative().limbs; - limbs[0].to_be_bytes() - } -} - -#[allow(clippy::non_canonical_partial_ord_impl)] -impl PartialOrd for FieldElement { - fn partial_cmp(&self, other: &Self) -> Option { - self.representative().partial_cmp(&other.representative()) - } -} - -impl Ord for FieldElement { - fn cmp(&self, other: &Self) -> core::cmp::Ordering { - self.representative().cmp(&other.representative()) - } -} - -#[cfg(test)] -mod test_u64_goldilocks_bytes_ops { - use super::U64GoldilocksPrimeField; - use crate::{field::element::FieldElement, traits::ByteConversion}; - - #[test] - #[cfg(feature = "alloc")] - fn byte_serialization_for_a_number_matches_with_byte_conversion_implementation_le() { - let element = FieldElement::::from_hex_unchecked( - "\ - 0123456701234567\ - ", - ); - let bytes = element.to_bytes_le(); - let expected_bytes: [u8; 8] = ByteConversion::to_bytes_le(&element).try_into().unwrap(); - assert_eq!(bytes, expected_bytes); - } - - #[test] - #[cfg(feature = "alloc")] - fn byte_serialization_for_a_number_matches_with_byte_conversion_implementation_be() { - let element = FieldElement::::from_hex_unchecked( - "\ - 0123456701234567\ - ", - ); - let bytes = element.to_bytes_be(); - let expected_bytes: [u8; 8] = ByteConversion::to_bytes_be(&element).try_into().unwrap(); - assert_eq!(bytes, expected_bytes); - } - - #[test] - - fn byte_serialization_and_deserialization_works_le() { - let element = FieldElement::::from_hex_unchecked( - "\ - 7654321076543210\ - ", - ); - let bytes = element.to_bytes_le(); - let from_bytes = FieldElement::::from_bytes_le(&bytes).unwrap(); - assert_eq!(element, from_bytes); - } - - #[test] - - fn byte_serialization_and_deserialization_works_be() { - let element = FieldElement::::from_hex_unchecked( - "\ - 7654321076543210\ - ", - ); - let bytes = element.to_bytes_be(); - let from_bytes = FieldElement::::from_bytes_be(&bytes).unwrap(); - assert_eq!(element, from_bytes); - } -} diff --git a/crates/math/src/field/fields/fft_friendly/u64_mersenne_montgomery_field.rs b/crates/math/src/field/fields/fft_friendly/u64_mersenne_montgomery_field.rs deleted file mode 100644 index a7521e4a6..000000000 --- a/crates/math/src/field/fields/fft_friendly/u64_mersenne_montgomery_field.rs +++ /dev/null @@ -1,44 +0,0 @@ -use crate::{ - field::{ - element::FieldElement, - fields::montgomery_backed_prime_fields::{IsModulus, MontgomeryBackendPrimeField}, - }, - unsigned_integer::element::U64, -}; - -pub type U64MontgomeryBackendPrimeField = MontgomeryBackendPrimeField; - -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub struct MontgomeryConfigMersenne31PrimeField; -impl IsModulus for MontgomeryConfigMersenne31PrimeField { - //Mersenne Prime p = 2^31 - 1 - const MODULUS: U64 = U64::from_u64(2147483647); -} - -pub type Mersenne31MontgomeryPrimeField = - U64MontgomeryBackendPrimeField; - -impl FieldElement { - pub fn to_bytes_le(&self) -> [u8; 8] { - let limbs = self.representative().limbs; - limbs[0].to_le_bytes() - } - - pub fn to_bytes_be(&self) -> [u8; 8] { - let limbs = self.representative().limbs; - limbs[0].to_be_bytes() - } -} - -#[allow(clippy::non_canonical_partial_ord_impl)] -impl PartialOrd for FieldElement { - fn partial_cmp(&self, other: &Self) -> Option { - self.representative().partial_cmp(&other.representative()) - } -} - -impl Ord for FieldElement { - fn cmp(&self, other: &Self) -> core::cmp::Ordering { - self.representative().cmp(&other.representative()) - } -} diff --git a/crates/math/src/field/fields/mersenne31/extensions.rs b/crates/math/src/field/fields/mersenne31/extensions.rs deleted file mode 100644 index 1ad7bf95d..000000000 --- a/crates/math/src/field/fields/mersenne31/extensions.rs +++ /dev/null @@ -1,650 +0,0 @@ -use super::field::Mersenne31Field; -use crate::field::{ - element::FieldElement, - errors::FieldError, - traits::{IsFFTField, IsField, IsSubFieldOf}, -}; -#[cfg(feature = "alloc")] -use alloc::vec::Vec; - -type FpE = FieldElement; -type Fp2E = FieldElement; -type Fp4E = FieldElement; - -#[derive(Clone, Debug)] -pub struct Degree2ExtensionField; - -impl Degree2ExtensionField { - pub fn mul_fp2_by_nonresidue(a: &Fp2E) -> Fp2E { - Fp2E::new([ - a.value()[0].double() - a.value()[1], - a.value()[1].double() + a.value()[0], - ]) - } -} - -impl IsField for Degree2ExtensionField { - //Element representation: a[0] = real part, a[1] = imaginary part - type BaseType = [FpE; 2]; - - /// Returns the component wise addition of `a` and `b` - fn add(a: &Self::BaseType, b: &Self::BaseType) -> Self::BaseType { - [a[0] + b[0], a[1] + b[1]] - } - - /// Returns the multiplication of `a` and `b`. - fn mul(a: &Self::BaseType, b: &Self::BaseType) -> Self::BaseType { - let a0b0 = a[0] * b[0]; - let a1b1 = a[1] * b[1]; - let z = (a[0] + a[1]) * (b[0] + b[1]); - [a0b0 - a1b1, z - a0b0 - a1b1] - } - - fn square(a: &Self::BaseType) -> Self::BaseType { - let [a0, a1] = a; - let v0 = a0 * a1; - let c0 = (a0 + a1) * (a0 - a1); - let c1 = v0.double(); - [c0, c1] - } - /// Returns the component wise subtraction of `a` and `b` - fn sub(a: &Self::BaseType, b: &Self::BaseType) -> Self::BaseType { - [a[0] - b[0], a[1] - b[1]] - } - - /// Returns the component wise negation of `a` - fn neg(a: &Self::BaseType) -> Self::BaseType { - [-a[0], -a[1]] - } - - /// Returns the multiplicative inverse of `a` - fn inv(a: &Self::BaseType) -> Result { - let inv_norm = (a[0].square() + a[1].square()).inv()?; - Ok([a[0] * inv_norm, -a[1] * inv_norm]) - } - - /// Returns the division of `a` and `b` - fn div(a: &Self::BaseType, b: &Self::BaseType) -> Result { - let b_inv = &Self::inv(b).map_err(|_| FieldError::DivisionByZero)?; - Ok(::mul(a, b_inv)) - } - - /// Returns a boolean indicating whether `a` and `b` are equal component wise. - fn eq(a: &Self::BaseType, b: &Self::BaseType) -> bool { - a[0] == b[0] && a[1] == b[1] - } - - /// Returns the multiplicative neutral element of the field extension. - fn one() -> Self::BaseType { - [FpE::one(), FpE::zero()] - } - - /// Returns the element `x * 1` where 1 is the multiplicative neutral element. - fn from_u64(x: u64) -> Self::BaseType { - [FpE::from(x), FpE::zero()] - } - - /// Takes as input an element of BaseType and returns the internal representation - /// of that element in the field. - /// Note: for this case this is simply the identity, because the components - /// already have correct representations. - fn from_base_type(x: Self::BaseType) -> Self::BaseType { - x - } -} - -impl IsFFTField for Degree2ExtensionField { - // Values taken from stwo - // https://github.com/starkware-libs/stwo/blob/dev/crates/prover/src/core/circle.rs#L203-L209 - const TWO_ADICITY: u64 = 31; - const TWO_ADIC_PRIMITVE_ROOT_OF_UNITY: Self::BaseType = - [FpE::const_from_raw(2), FpE::const_from_raw(1268011823)]; -} - -impl IsSubFieldOf for Mersenne31Field { - fn add( - a: &Self::BaseType, - b: &::BaseType, - ) -> ::BaseType { - [FpE::from(a) + b[0], b[1]] - } - - fn sub( - a: &Self::BaseType, - b: &::BaseType, - ) -> ::BaseType { - [FpE::from(a) - b[0], -b[1]] - } - - fn mul( - a: &Self::BaseType, - b: &::BaseType, - ) -> ::BaseType { - [FpE::from(a) * b[0], FpE::from(a) * b[1]] - } - - fn div( - a: &Self::BaseType, - b: &::BaseType, - ) -> Result<::BaseType, FieldError> { - let b_inv = Degree2ExtensionField::inv(b).map_err(|_| FieldError::DivisionByZero)?; - Ok(>::mul( - a, &b_inv, - )) - } - - fn embed(a: Self::BaseType) -> ::BaseType { - [FieldElement::from_raw(a), FieldElement::zero()] - } - - #[cfg(feature = "alloc")] - fn to_subfield_vec( - b: ::BaseType, - ) -> alloc::vec::Vec { - b.into_iter().map(|x| x.to_raw()).collect() - } -} - -#[derive(Clone, Debug)] -pub struct Degree4ExtensionField; - -impl Degree4ExtensionField { - pub const fn const_from_coefficients(a: u32, b: u32, c: u32, d: u32) -> Fp4E { - Fp4E::const_from_raw([ - Fp2E::const_from_raw([FpE::const_from_raw(a), FpE::const_from_raw(b)]), - Fp2E::const_from_raw([FpE::const_from_raw(c), FpE::const_from_raw(d)]), - ]) - } -} - -impl IsField for Degree4ExtensionField { - type BaseType = [Fp2E; 2]; - - fn add(a: &Self::BaseType, b: &Self::BaseType) -> Self::BaseType { - [&a[0] + &b[0], &a[1] + &b[1]] - } - - fn sub(a: &Self::BaseType, b: &Self::BaseType) -> Self::BaseType { - [&a[0] - &b[0], &a[1] - &b[1]] - } - - fn neg(a: &Self::BaseType) -> Self::BaseType { - [-&a[0], -&a[1]] - } - - fn mul(a: &Self::BaseType, b: &Self::BaseType) -> Self::BaseType { - // Algorithm from: https://github.com/ingonyama-zk/papers/blob/main/Mersenne31_polynomial_arithmetic.pdf (page 5): - let a0b0 = &a[0] * &b[0]; - let a1b1 = &a[1] * &b[1]; - [ - &a0b0 + Degree2ExtensionField::mul_fp2_by_nonresidue(&a1b1), - (&a[0] + &a[1]) * (&b[0] + &b[1]) - a0b0 - a1b1, - ] - } - - fn square(a: &Self::BaseType) -> Self::BaseType { - let a0_square = &a[0].square(); - let a1_square = &a[1].square(); - [ - a0_square + Degree2ExtensionField::mul_fp2_by_nonresidue(a1_square), - (&a[0] + &a[1]).square() - a0_square - a1_square, - ] - } - - fn inv(a: &Self::BaseType) -> Result { - let inv_norm = - (a[0].square() - Degree2ExtensionField::mul_fp2_by_nonresidue(&a[1].square())).inv()?; - Ok([&a[0] * &inv_norm, -&a[1] * &inv_norm]) - } - - fn div(a: &Self::BaseType, b: &Self::BaseType) -> Result { - let b_inv = &Self::inv(b).map_err(|_| FieldError::DivisionByZero)?; - Ok(::mul(a, b_inv)) - } - - fn eq(a: &Self::BaseType, b: &Self::BaseType) -> bool { - a[0] == b[0] && a[1] == b[1] - } - - fn zero() -> Self::BaseType { - [Fp2E::zero(), Fp2E::zero()] - } - - fn one() -> Self::BaseType { - [Fp2E::one(), Fp2E::zero()] - } - - fn from_u64(x: u64) -> Self::BaseType { - [Fp2E::from(x), Fp2E::zero()] - } - - fn from_base_type(x: Self::BaseType) -> Self::BaseType { - x - } -} - -impl IsSubFieldOf for Mersenne31Field { - fn add( - a: &Self::BaseType, - b: &::BaseType, - ) -> ::BaseType { - [FpE::from(a) + &b[0], b[1].clone()] - } - - fn sub( - a: &Self::BaseType, - b: &::BaseType, - ) -> ::BaseType { - [FpE::from(a) - &b[0], -&b[1]] - } - - fn mul( - a: &Self::BaseType, - b: &::BaseType, - ) -> ::BaseType { - let c0 = FpE::from(a) * &b[0]; - let c1 = FpE::from(a) * &b[1]; - [c0, c1] - } - - fn div( - a: &Self::BaseType, - b: &::BaseType, - ) -> Result<::BaseType, FieldError> { - let b_inv = Degree4ExtensionField::inv(b).map_err(|_| FieldError::DivisionByZero)?; - Ok(>::mul( - a, &b_inv, - )) - } - - fn embed(a: Self::BaseType) -> ::BaseType { - [ - Fp2E::from_raw(>::embed(a)), - Fp2E::zero(), - ] - } - - #[cfg(feature = "alloc")] - fn to_subfield_vec( - b: ::BaseType, - ) -> alloc::vec::Vec { - // TODO: Repace this for with a map similarly to this: - // b.into_iter().map(|x| x.to_raw()).collect() - let mut result = Vec::new(); - for fp2e in b { - result.push(fp2e.value()[0].to_raw()); - result.push(fp2e.value()[1].to_raw()); - } - result - } -} - -#[cfg(test)] -mod tests { - use core::ops::Neg; - - use crate::field::fields::mersenne31::field::MERSENNE_31_PRIME_FIELD_ORDER; - - use super::*; - - type FpE = FieldElement; - type Fp2E = FieldElement; - type Fp4E = FieldElement; - - #[test] - fn add_real_one_plus_one_is_two() { - assert_eq!(Fp2E::one() + Fp2E::one(), Fp2E::from(2)) - } - - #[test] - fn add_real_neg_one_plus_one_is_zero() { - assert_eq!(Fp2E::one() + Fp2E::one().neg(), Fp2E::zero()) - } - - #[test] - fn add_real_neg_one_plus_two_is_one() { - assert_eq!(Fp2E::one().neg() + Fp2E::from(2), Fp2E::one()) - } - - #[test] - fn add_real_neg_one_plus_neg_one_is_order_sub_two() { - assert_eq!( - Fp2E::one().neg() + Fp2E::one().neg(), - Fp2E::new([FpE::from(&(MERSENNE_31_PRIME_FIELD_ORDER - 2)), FpE::zero()]) - ) - } - - #[test] - fn add_complex_one_plus_one_two() { - let one_i = Fp2E::new([FpE::zero(), FpE::one()]); - let two_i = Fp2E::new([FpE::zero(), FpE::from(2)]); - assert_eq!(&one_i + &one_i, two_i) - } - - #[test] - fn add_complex_neg_one_plus_one_is_zero() { - //Manually declare the complex part to one - let neg_one_i = Fp2E::new([FpE::zero(), -FpE::one()]); - let one_i = Fp2E::new([FpE::zero(), FpE::one()]); - assert_eq!(neg_one_i + one_i, Fp2E::zero()) - } - - #[test] - fn add_complex_neg_one_plus_two_is_one() { - let neg_one_i = Fp2E::new([FpE::zero(), -FpE::one()]); - let two_i = Fp2E::new([FpE::zero(), FpE::from(2)]); - let one_i = Fp2E::new([FpE::zero(), FpE::one()]); - assert_eq!(&neg_one_i + &two_i, one_i) - } - - #[test] - fn add_complex_neg_one_plus_neg_one_imag_is_order_sub_two() { - let neg_one_i = Fp2E::new([FpE::zero(), -FpE::one()]); - assert_eq!( - (&neg_one_i + &neg_one_i).value()[1], - FpE::from(&(MERSENNE_31_PRIME_FIELD_ORDER - 2)) - ) - } - - #[test] - fn add_order() { - let a = Fp2E::new([-FpE::one(), FpE::one()]); - let b = Fp2E::new([ - FpE::from(2), - FpE::from(&(MERSENNE_31_PRIME_FIELD_ORDER - 2)), - ]); - let c = Fp2E::new([FpE::one(), -FpE::one()]); - assert_eq!(&a + &b, c) - } - - #[test] - fn add_equal_zero() { - let a = Fp2E::new([-FpE::one(), -FpE::one()]); - let b = Fp2E::new([FpE::one(), FpE::one()]); - assert_eq!(&a + &b, Fp2E::zero()) - } - - #[test] - fn add_plus_one() { - let a = Fp2E::new([FpE::one(), FpE::from(2)]); - let b = Fp2E::new([FpE::one(), FpE::one()]); - let c = Fp2E::new([FpE::from(2), FpE::from(3)]); - assert_eq!(&a + &b, c) - } - - #[test] - fn sub_real_one_sub_one_is_zero() { - assert_eq!(&Fp2E::one() - &Fp2E::one(), Fp2E::zero()) - } - - #[test] - fn sub_real_two_sub_two_is_zero() { - assert_eq!(&Fp2E::from(2) - &Fp2E::from(2), Fp2E::zero()) - } - - #[test] - fn sub_real_neg_one_sub_neg_one_is_zero() { - assert_eq!(Fp2E::one().neg() - Fp2E::one().neg(), Fp2E::zero()) - } - - #[test] - fn sub_real_two_sub_one_is_one() { - assert_eq!(Fp2E::from(2) - Fp2E::one(), Fp2E::one()) - } - - #[test] - fn sub_real_neg_one_sub_zero_is_neg_one() { - assert_eq!(Fp2E::one().neg() - Fp2E::zero(), Fp2E::one().neg()) - } - - #[test] - fn sub_complex_one_sub_one_is_zero() { - let one = Fp2E::new([FpE::zero(), FpE::one()]); - assert_eq!(&one - &one, Fp2E::zero()) - } - - #[test] - fn sub_complex_two_sub_two_is_zero() { - let two = Fp2E::new([FpE::zero(), FpE::from(2)]); - assert_eq!(&two - &two, Fp2E::zero()) - } - - #[test] - fn sub_complex_neg_one_sub_neg_one_is_zero() { - let neg_one = Fp2E::new([FpE::zero(), -FpE::one()]); - assert_eq!(&neg_one - &neg_one, Fp2E::zero()) - } - - #[test] - fn sub_complex_two_sub_one_is_one() { - let two = Fp2E::new([FpE::zero(), FpE::from(2)]); - let one = Fp2E::new([FpE::zero(), FpE::one()]); - assert_eq!(&two - &one, one) - } - - #[test] - fn sub_complex_neg_one_sub_zero_is_neg_one() { - let neg_one = Fp2E::new([FpE::zero(), -FpE::one()]); - assert_eq!(&neg_one - &Fp2E::zero(), neg_one) - } - - #[test] - fn mul_fp2_is_correct() { - let a = Fp2E::new([FpE::from(2), FpE::from(2)]); - let b = Fp2E::new([FpE::from(4), FpE::from(5)]); - let c = Fp2E::new([-FpE::from(2), FpE::from(18)]); - assert_eq!(&a * &b, c) - } - - #[test] - fn square_equals_mul_by_itself() { - let a = Fp2E::new([FpE::from(2), FpE::from(3)]); - assert_eq!(a.square(), &a * &a) - } - - #[test] - fn test_fp2_add() { - let a = Fp2E::new([FpE::from(0), FpE::from(3)]); - let b = Fp2E::new([-FpE::from(2), FpE::from(8)]); - let expected_result = Fp2E::new([FpE::from(0) - FpE::from(2), FpE::from(3) + FpE::from(8)]); - assert_eq!(a + b, expected_result); - } - - #[test] - fn test_fp2_add_2() { - let a = Fp2E::new([FpE::from(2), FpE::from(4)]); - let b = Fp2E::new([-FpE::from(2), -FpE::from(4)]); - let expected_result = Fp2E::new([FpE::from(2) - FpE::from(2), FpE::from(4) - FpE::from(4)]); - assert_eq!(a + b, expected_result); - } - - #[test] - fn test_fp2_add_3() { - let a = Fp2E::new([FpE::from(&MERSENNE_31_PRIME_FIELD_ORDER), FpE::from(1)]); - let b = Fp2E::new([FpE::from(1), FpE::from(&MERSENNE_31_PRIME_FIELD_ORDER)]); - let expected_result = Fp2E::new([FpE::from(1), FpE::from(1)]); - assert_eq!(a + b, expected_result); - } - - #[test] - fn test_fp2_sub() { - let a = Fp2E::new([FpE::from(0), FpE::from(3)]); - let b = Fp2E::new([-FpE::from(2), FpE::from(8)]); - let expected_result = Fp2E::new([FpE::from(0) + FpE::from(2), FpE::from(3) - FpE::from(8)]); - assert_eq!(a - b, expected_result); - } - - #[test] - fn test_fp2_sub_2() { - let a = Fp2E::new([FpE::zero(), FpE::from(&MERSENNE_31_PRIME_FIELD_ORDER)]); - let b = Fp2E::new([FpE::one(), -FpE::one()]); - let expected_result = - Fp2E::new([FpE::from(&(MERSENNE_31_PRIME_FIELD_ORDER - 1)), FpE::one()]); - assert_eq!(a - b, expected_result); - } - - #[test] - fn test_fp2_sub_3() { - let a = Fp2E::new([FpE::from(5), FpE::from(&MERSENNE_31_PRIME_FIELD_ORDER)]); - let b = Fp2E::new([FpE::from(5), FpE::from(&MERSENNE_31_PRIME_FIELD_ORDER)]); - let expected_result = Fp2E::new([FpE::zero(), FpE::zero()]); - assert_eq!(a - b, expected_result); - } - - #[test] - fn test_fp2_mul() { - let a = Fp2E::new([FpE::from(12), FpE::from(5)]); - let b = Fp2E::new([-FpE::from(4), FpE::from(2)]); - let expected_result = Fp2E::new([-FpE::from(58), FpE::new(4)]); - assert_eq!(a * b, expected_result); - } - - #[test] - fn test_fp2_mul_2() { - let a = Fp2E::new([FpE::one(), FpE::zero()]); - let b = Fp2E::new([FpE::from(12), -FpE::from(8)]); - let expected_result = Fp2E::new([FpE::from(12), -FpE::new(8)]); - assert_eq!(a * b, expected_result); - } - - #[test] - fn test_fp2_mul_3() { - let a = Fp2E::new([FpE::zero(), FpE::zero()]); - let b = Fp2E::new([FpE::from(2), FpE::from(7)]); - let expected_result = Fp2E::new([FpE::zero(), FpE::zero()]); - assert_eq!(a * b, expected_result); - } - - #[test] - fn test_fp2_mul_4() { - let a = Fp2E::new([FpE::from(2), FpE::from(7)]); - let b = Fp2E::new([FpE::zero(), FpE::zero()]); - let expected_result = Fp2E::new([FpE::zero(), FpE::zero()]); - assert_eq!(a * b, expected_result); - } - - #[test] - fn test_fp2_mul_5() { - let a = Fp2E::new([FpE::from(&MERSENNE_31_PRIME_FIELD_ORDER), FpE::one()]); - let b = Fp2E::new([FpE::from(2), FpE::from(&MERSENNE_31_PRIME_FIELD_ORDER)]); - let expected_result = Fp2E::new([FpE::zero(), FpE::from(2)]); - assert_eq!(a * b, expected_result); - } - - #[test] - fn test_fp2_inv() { - let a = Fp2E::new([FpE::one(), FpE::zero()]); - let expected_result = Fp2E::new([FpE::one(), FpE::zero()]); - assert_eq!(a.inv().unwrap(), expected_result); - } - - #[test] - fn test_fp2_inv_2() { - let a = Fp2E::new([FpE::from(&(MERSENNE_31_PRIME_FIELD_ORDER - 1)), FpE::one()]); - let expected_result = Fp2E::new([FpE::from(1073741823), FpE::from(1073741823)]); - assert_eq!(a.inv().unwrap(), expected_result); - } - - #[test] - fn test_fp2_inv_3() { - let a = Fp2E::new([FpE::from(2063384121), FpE::from(1232183486)]); - let expected_result = Fp2E::new([FpE::from(1244288232), FpE::from(1321511038)]); - assert_eq!(a.inv().unwrap(), expected_result); - } - - #[test] - fn test_fp2_mul_inv() { - let a = Fp2E::new([FpE::from(12), FpE::from(5)]); - let b = a.inv().unwrap(); - let expected_result = Fp2E::new([FpE::one(), FpE::zero()]); - assert_eq!(a * b, expected_result); - } - - #[test] - fn test_fp2_div() { - let a = Fp2E::new([FpE::from(12), FpE::from(5)]); - let b = Fp2E::new([FpE::from(4), FpE::from(2)]); - let expected_result = Fp2E::new([FpE::from(644245097), FpE::from(1288490188)]); - assert_eq!((a / b).unwrap(), expected_result); - } - - #[test] - fn test_fp2_div_2() { - let a = Fp2E::new([FpE::from(4), FpE::from(7)]); - let b = Fp2E::new([FpE::one(), FpE::zero()]); - let expected_result = Fp2E::new([FpE::from(4), FpE::from(7)]); - assert_eq!((a / b).unwrap(), expected_result); - } - - #[test] - fn test_fp2_div_3() { - let a = Fp2E::new([FpE::zero(), FpE::zero()]); - let b = Fp2E::new([FpE::from(3), FpE::from(12)]); - let expected_result = Fp2E::new([FpE::zero(), FpE::zero()]); - assert_eq!((a / b).unwrap(), expected_result); - } - - #[test] - fn mul_fp4_by_zero_is_zero() { - let a = Fp4E::new([ - Fp2E::new([FpE::from(2), FpE::from(3)]), - Fp2E::new([FpE::from(4), FpE::from(5)]), - ]); - assert_eq!(Fp4E::zero(), a * Fp4E::zero()) - } - - #[test] - fn mul_fp4_by_one_is_identity() { - let a = Fp4E::new([ - Fp2E::new([FpE::from(2), FpE::from(3)]), - Fp2E::new([FpE::from(4), FpE::from(5)]), - ]); - assert_eq!(a, a.clone() * Fp4E::one()) - } - - #[test] - fn square_fp4_equals_mul_two_times() { - let a = Fp4E::new([ - Fp2E::new([FpE::from(3), FpE::from(4)]), - Fp2E::new([FpE::from(5), FpE::from(6)]), - ]); - - assert_eq!(a.square(), &a * &a) - } - - #[test] - fn fp4_mul_by_inv_is_one() { - let a = Fp4E::new([ - Fp2E::new([FpE::from(2147483647), FpE::from(2147483648)]), - Fp2E::new([FpE::from(2147483649), FpE::from(2147483650)]), - ]); - - assert_eq!(&a * a.inv().unwrap(), Fp4E::one()) - } - - #[test] - fn embed_fp_with_fp4() { - let a = FpE::from(3); - let a_extension = Fp4E::from(3); - assert_eq!(a.to_extension::(), a_extension); - } - - #[test] - fn add_fp_and_fp4() { - let a = FpE::from(3); - let a_extension = Fp4E::from(3); - let b = Fp4E::from(2); - assert_eq!(a + &b, a_extension + b); - } - - #[test] - fn mul_fp_by_fp4() { - let a = FpE::from(30000000000); - let a_extension = a.to_extension::(); - let b = Fp4E::new([ - Fp2E::new([FpE::from(1), FpE::from(2)]), - Fp2E::new([FpE::from(3), FpE::from(4)]), - ]); - assert_eq!(a * &b, a_extension * b); - } -} diff --git a/crates/math/src/field/fields/mersenne31/field.rs b/crates/math/src/field/fields/mersenne31/field.rs deleted file mode 100644 index b8b624811..000000000 --- a/crates/math/src/field/fields/mersenne31/field.rs +++ /dev/null @@ -1,474 +0,0 @@ -use crate::{ - errors::CreationError, - field::{ - element::FieldElement, - errors::FieldError, - traits::{IsField, IsPrimeField}, - }, -}; -use core::fmt::{self, Display}; - -/// Represents a 31 bit integer value -/// Invariants: -/// 31st bit is clear -/// n < MODULUS -#[derive(Debug, Clone, Copy, Hash, PartialOrd, Ord, PartialEq, Eq)] -pub struct Mersenne31Field; - -impl Mersenne31Field { - fn weak_reduce(n: u32) -> u32 { - // To reduce 'n' to 31 bits we clear its MSB, then add it back in its reduced form. - let msb = n & (1 << 31); - let msb_reduced = msb >> 31; - let res = msb ^ n; - - // assert msb_reduced fits within 31 bits - debug_assert!((res >> 31) == 0 && (msb_reduced >> 1) == 0); - res + msb_reduced - } - - fn as_representative(n: &u32) -> u32 { - if *n == MERSENNE_31_PRIME_FIELD_ORDER { - 0 - } else { - *n - } - } - - #[inline] - pub fn sum::BaseType>>( - iter: I, - ) -> ::BaseType { - // Delayed reduction - Self::from_u64(iter.map(|x| x as u64).sum::()) - } - - /// Computes a * 2^k, with 0 < k < 31 - pub fn mul_power_two(a: u32, k: u32) -> u32 { - let msb = (a & (u32::MAX << (31 - k))) >> (31 - k); // The k + 1 msf shifted right . - let lsb = (a & (u32::MAX >> (k + 1))) << k; // The 31 - k lsb shifted left. - Self::weak_reduce(msb + lsb) - } - - pub fn pow_2(a: &u32, order: u32) -> u32 { - let mut res = *a; - (0..order).for_each(|_| res = Self::square(&res)); - res - } - - /// TODO: See if we can optimize this function. - /// Computes 2a^2 - 1 - pub fn two_square_minus_one(a: &u32) -> u32 { - if *a == 0 { - MERSENNE_31_PRIME_FIELD_ORDER - 1 - } else { - Self::from_u64(((u64::from(*a) * u64::from(*a)) << 1) - 1) - } - } -} - -pub const MERSENNE_31_PRIME_FIELD_ORDER: u32 = (1 << 31) - 1; - -//NOTE: This implementation was inspired by and borrows from the work done by the Plonky3 team -// https://github.com/Plonky3/Plonky3/blob/main/mersenne-31/src/lib.rs -// Thank you for pushing this technology forward. -impl IsField for Mersenne31Field { - type BaseType = u32; - - /// Returns the sum of `a` and `b`. - fn add(a: &u32, b: &u32) -> u32 { - // We are using that if a and b are field elements of Mersenne31, then - // a + b has at most 32 bits, so we can use the weak_reduce function to take mudulus p. - Self::weak_reduce(a + b) - } - - /// Returns the multiplication of `a` and `b`. - // Note: for powers of 2 we can perform bit shifting this would involve overriding the trait implementation - fn mul(a: &u32, b: &u32) -> u32 { - Self::from_u64(u64::from(*a) * u64::from(*b)) - } - - fn sub(a: &u32, b: &u32) -> u32 { - Self::weak_reduce(a + MERSENNE_31_PRIME_FIELD_ORDER - b) - } - - /// Returns the additive inverse of `a`. - fn neg(a: &u32) -> u32 { - // NOTE: MODULUS known to have 31 bit clear - MERSENNE_31_PRIME_FIELD_ORDER - a - } - - /// Returns the multiplicative inverse of `a`. - fn inv(x: &u32) -> Result { - if *x == Self::zero() || *x == MERSENNE_31_PRIME_FIELD_ORDER { - return Err(FieldError::InvZeroError); - } - let p101 = Self::mul(&Self::pow_2(x, 2), x); - let p1111 = Self::mul(&Self::square(&p101), &p101); - let p11111111 = Self::mul(&Self::pow_2(&p1111, 4u32), &p1111); - let p111111110000 = Self::pow_2(&p11111111, 4u32); - let p111111111111 = Self::mul(&p111111110000, &p1111); - let p1111111111111111 = Self::mul(&Self::pow_2(&p111111110000, 4u32), &p11111111); - let p1111111111111111111111111111 = - Self::mul(&Self::pow_2(&p1111111111111111, 12u32), &p111111111111); - let p1111111111111111111111111111101 = - Self::mul(&Self::pow_2(&p1111111111111111111111111111, 3u32), &p101); - Ok(p1111111111111111111111111111101) - } - - /// Returns the division of `a` and `b`. - fn div(a: &u32, b: &u32) -> Result { - let b_inv = Self::inv(b).map_err(|_| FieldError::DivisionByZero)?; - Ok(Self::mul(a, &b_inv)) - } - - /// Returns a boolean indicating whether `a` and `b` are equal or not. - fn eq(a: &u32, b: &u32) -> bool { - Self::as_representative(a) == Self::representative(b) - } - - /// Returns the additive neutral element. - fn zero() -> u32 { - 0u32 - } - - /// Returns the multiplicative neutral element. - fn one() -> u32 { - 1u32 - } - - /// Returns the element `x * 1` where 1 is the multiplicative neutral element. - fn from_u64(x: u64) -> u32 { - (((((x >> 31) + x + 1) >> 31) + x) & (MERSENNE_31_PRIME_FIELD_ORDER as u64)) as u32 - } - - /// Takes as input an element of BaseType and returns the internal representation - /// of that element in the field. - fn from_base_type(x: u32) -> u32 { - Self::weak_reduce(x) - } - fn double(a: &u32) -> u32 { - Self::weak_reduce(a << 1) - } -} - -impl IsPrimeField for Mersenne31Field { - type RepresentativeType = u32; - - // Since our invariant guarantees that `value` fits in 31 bits, there is only one possible value - // `value` that is not canonical, namely 2^31 - 1 = p = 0. - fn representative(x: &u32) -> u32 { - debug_assert!((x >> 31) == 0); - Self::as_representative(x) - } - - fn field_bit_size() -> usize { - ((MERSENNE_31_PRIME_FIELD_ORDER - 1).ilog2() + 1) as usize - } - - fn from_hex(hex_string: &str) -> Result { - let mut hex_string = hex_string; - // Remove 0x if it's on the string - let mut char_iterator = hex_string.chars(); - if hex_string.len() > 2 - && char_iterator.next().unwrap() == '0' - && char_iterator.next().unwrap() == 'x' - { - hex_string = &hex_string[2..]; - } - u32::from_str_radix(hex_string, 16).map_err(|_| CreationError::InvalidHexString) - } - - #[cfg(feature = "std")] - fn to_hex(x: &u32) -> String { - format!("{x:X}") - } -} - -impl FieldElement { - #[cfg(feature = "alloc")] - pub fn to_bytes_le(&self) -> alloc::vec::Vec { - self.representative().to_le_bytes().to_vec() - } - - #[cfg(feature = "alloc")] - pub fn to_bytes_be(&self) -> alloc::vec::Vec { - self.representative().to_be_bytes().to_vec() - } -} - -impl Display for FieldElement { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{:x}", self.representative()) - } -} - -#[cfg(test)] -mod tests { - use super::*; - type F = Mersenne31Field; - type FE = FieldElement; - - #[test] - fn mul_power_two_is_correct() { - let a = 3u32; - let k = 2; - let expected_result = FE::from(&a) * FE::from(2).pow(k as u16); - let result = F::mul_power_two(a, k); - assert_eq!(FE::from(&result), expected_result) - } - - #[test] - fn mul_power_two_is_correct_2() { - let a = 229287u32; - let k = 4; - let expected_result = FE::from(&a) * FE::from(2).pow(k as u16); - let result = F::mul_power_two(a, k); - assert_eq!(FE::from(&result), expected_result) - } - - #[test] - fn pow_2_is_correct() { - let a = 3u32; - let order = 12; - let result = F::pow_2(&a, order); - let expected_result = FE::pow(&FE::from(&a), 4096u32); - assert_eq!(FE::from(&result), expected_result) - } - - #[test] - fn from_hex_for_b_is_11() { - assert_eq!(F::from_hex("B").unwrap(), 11); - } - - #[test] - fn from_hex_for_b_is_11_v2() { - assert_eq!(FE::from_hex("B").unwrap(), FE::from(11)); - } - - #[test] - fn sum_delayed_reduction() { - let up_to = u32::pow(2, 16); - let pow = u64::pow(2, 60); - - let iter = (0..up_to).map(F::weak_reduce).map(|e| F::pow(&e, pow)); - - assert_eq!(F::from_u64(2142542785), F::sum(iter)); - } - - #[test] - fn from_hex_for_0x1_a_is_26() { - assert_eq!(F::from_hex("0x1a").unwrap(), 26); - } - - #[test] - fn bit_size_of_field_is_31() { - assert_eq!( - ::field_bit_size(), - 31 - ); - } - - #[test] - fn one_plus_1_is_2() { - assert_eq!(FE::one() + FE::one(), FE::from(&2u32)); - } - - #[test] - fn neg_1_plus_1_is_0() { - assert_eq!(-FE::one() + FE::one(), FE::zero()); - } - - #[test] - fn neg_1_plus_2_is_1() { - assert_eq!(-FE::one() + FE::from(&2u32), FE::one()); - } - - #[test] - fn max_order_plus_1_is_0() { - assert_eq!( - FE::from(&(MERSENNE_31_PRIME_FIELD_ORDER - 1)) + FE::from(1), - FE::from(0) - ); - } - - #[test] - fn comparing_13_and_13_are_equal() { - assert_eq!(FE::from(&13u32), FE::from(13)); - } - - #[test] - fn comparing_13_and_8_they_are_not_equal() { - assert_ne!(FE::from(&13u32), FE::from(8)); - } - - #[test] - fn one_sub_1_is_0() { - assert_eq!(FE::one() - FE::one(), FE::zero()); - } - - #[test] - fn zero_sub_1_is_order_minus_1() { - assert_eq!( - FE::zero() - FE::one(), - FE::from(&(MERSENNE_31_PRIME_FIELD_ORDER - 1)) - ); - } - - #[test] - fn neg_1_sub_neg_1_is_0() { - assert_eq!(-FE::one() - (-FE::one()), FE::zero()); - } - - #[test] - fn neg_1_sub_0_is_neg_1() { - assert_eq!(-FE::one() - FE::zero(), -FE::one()); - } - - #[test] - fn mul_neutral_element() { - assert_eq!(FE::one() * FE::from(&2u32), FE::from(&2u32)); - } - - #[test] - fn mul_2_3_is_6() { - assert_eq!(FE::from(&2u32) * FE::from(&3u32), FE::from(&6u32)); - } - - #[test] - fn mul_order_neg_1() { - assert_eq!( - FE::from(MERSENNE_31_PRIME_FIELD_ORDER as u64 - 1) - * FE::from(MERSENNE_31_PRIME_FIELD_ORDER as u64 - 1), - FE::one() - ); - } - - #[test] - fn pow_p_neg_1() { - assert_eq!( - FE::pow(&FE::from(&2u32), MERSENNE_31_PRIME_FIELD_ORDER - 1), - FE::one() - ) - } - - #[test] - fn inv_0_error() { - let result = FE::inv(&FE::zero()); - assert!(matches!(result, Err(FieldError::InvZeroError))); - } - - #[test] - fn inv_2() { - let result = FE::inv(&FE::from(&2u32)).unwrap(); - // sage: 1 / F(2) = 1073741824 - assert_eq!(result, FE::from(1073741824)); - } - - #[test] - fn pow_2_3() { - assert_eq!(FE::pow(&FE::from(&2u32), 3u64), FE::from(8)); - } - - #[test] - fn div_1() { - assert_eq!( - (FE::from(&2u32) / FE::from(&1u32)).unwrap(), - FE::from(&2u32) - ); - } - - #[test] - fn div_4_2() { - assert_eq!( - (FE::from(&4u32) / FE::from(&2u32)).unwrap(), - FE::from(&2u32) - ); - } - - #[test] - fn div_4_3() { - // sage: F(4) / F(3) = 1431655766 - assert_eq!( - (FE::from(&4u32) / FE::from(&3u32)).unwrap(), - FE::from(1431655766) - ); - } - - #[test] - fn two_plus_its_additive_inv_is_0() { - assert_eq!(FE::from(&2u32) + (-FE::from(&2u32)), FE::zero()); - } - - #[test] - fn from_u64_test() { - assert_eq!(FE::from(1u64), FE::one()); - } - - #[test] - fn creating_a_field_element_from_its_representative_returns_the_same_element_1() { - let change: u32 = MERSENNE_31_PRIME_FIELD_ORDER + 1; - let f1 = FE::from(&change); - let f2 = FE::from(&FE::representative(&f1)); - assert_eq!(f1, f2); - } - - #[test] - fn creating_a_field_element_from_its_representative_returns_the_same_element_2() { - let change: u32 = MERSENNE_31_PRIME_FIELD_ORDER + 8; - let f1 = FE::from(&change); - let f2 = FE::from(&FE::representative(&f1)); - assert_eq!(f1, f2); - } - - #[test] - fn from_base_type_test() { - assert_eq!(FE::from(&1u32), FE::one()); - } - - #[cfg(feature = "std")] - #[test] - fn to_hex_test() { - let num = FE::from_hex("B").unwrap(); - assert_eq!(FE::to_hex(&num), "B"); - } - - #[test] - fn double_equals_add_itself() { - let a = FE::from(1234); - assert_eq!(a + a, a.double()) - } - - #[test] - fn two_square_minus_one_is_correct() { - let a = FE::from(2147483650); - assert_eq!( - FE::from(&F::two_square_minus_one(a.value())), - a.square().double() - FE::one() - ) - } - - #[test] - fn two_square_zero_minus_one_is_minus_one() { - let a = FE::from(0); - assert_eq!( - FE::from(&F::two_square_minus_one(a.value())), - a.square().double() - FE::one() - ) - } - - #[test] - fn two_square_p_minus_one_is_minus_one() { - let a = FE::from(&MERSENNE_31_PRIME_FIELD_ORDER); - assert_eq!( - FE::from(&F::two_square_minus_one(a.value())), - a.square().double() - FE::one() - ) - } - - #[test] - fn mul_by_inv() { - let x = 3476715743_u32; - assert_eq!(FE::from(&x).inv().unwrap() * FE::from(&x), FE::one()); - } -} diff --git a/crates/math/src/field/fields/mersenne31/mod.rs b/crates/math/src/field/fields/mersenne31/mod.rs deleted file mode 100644 index 2272e7d5e..000000000 --- a/crates/math/src/field/fields/mersenne31/mod.rs +++ /dev/null @@ -1,2 +0,0 @@ -pub mod extensions; -pub mod field; diff --git a/crates/math/src/field/fields/mod.rs b/crates/math/src/field/fields/mod.rs deleted file mode 100644 index 6f9b26179..000000000 --- a/crates/math/src/field/fields/mod.rs +++ /dev/null @@ -1,29 +0,0 @@ -/// Implementation of two-adic prime fields to use with the Fast Fourier Transform (FFT). -pub mod fft_friendly; -/// Implementation of the 32-bit Mersenne Prime field (p = 2^31 - 1) -pub mod mersenne31; -pub mod montgomery_backed_prime_fields; -/// Implementation of the Goldilocks Prime field (p = 2^448 - 2^224 - 1) -pub mod p448_goldilocks_prime_field; -/// Implemenation of Pallas field -pub mod pallas_field; -/// Implementation of secp256k1 base field. -pub mod secp256k1_field; -/// Implementation of secp256k1 scalar field. -pub mod secp256k1_scalarfield; -/// Implementation of secp256r1 base field. -pub mod secp256r1_field; -pub mod u32_montgomery_backend_prime_field; -/// Implementation of the u64 Goldilocks Prime field (p = 2^64 - 2^32 + 1) -pub mod u64_goldilocks_field; -/// Implementation of prime fields over 64 bit unsigned integers. -pub mod u64_prime_field; - -/// Winterfell and miden field compatibility -#[cfg(feature = "winter_compatibility")] -pub mod winterfell; - -/// Implemenation of Vesta Prime field (p = 2^254 + 45560315531506369815346746415080538113) -pub mod vesta_field; - -pub mod binary; diff --git a/crates/math/src/field/fields/montgomery_backed_prime_fields.rs b/crates/math/src/field/fields/montgomery_backed_prime_fields.rs deleted file mode 100644 index 4710ed00a..000000000 --- a/crates/math/src/field/fields/montgomery_backed_prime_fields.rs +++ /dev/null @@ -1,1405 +0,0 @@ -use crate::errors::CreationError; -use crate::field::element::FieldElement; -use crate::field::errors::FieldError; -use crate::field::traits::{HasDefaultTranscript, IsPrimeField}; -#[cfg(feature = "alloc")] -use crate::traits::AsBytes; -use crate::traits::ByteConversion; -use crate::{ - field::traits::IsField, unsigned_integer::element::UnsignedInteger, - unsigned_integer::montgomery::MontgomeryAlgorithms, -}; - -use core::fmt::Debug; -use core::marker::PhantomData; - -pub type U384PrimeField = MontgomeryBackendPrimeField; -pub type U256PrimeField = MontgomeryBackendPrimeField; -pub type U64PrimeField = MontgomeryBackendPrimeField; - -/// This trait is necessary for us to be able to use unsigned integer types bigger than -/// `u128` (the biggest native `unit`) as constant generics. -/// This trait should be removed when Rust supports this feature. -pub trait IsModulus: Debug { - const MODULUS: U; -} - -#[cfg_attr( - any( - feature = "lambdaworks-serde-binary", - feature = "lambdaworks-serde-string" - ), - derive(serde::Serialize, serde::Deserialize) -)] -#[derive(Clone, Debug, Hash, Copy)] -pub struct MontgomeryBackendPrimeField { - phantom: PhantomData, -} - -impl MontgomeryBackendPrimeField -where - M: IsModulus>, -{ - pub const R2: UnsignedInteger = Self::compute_r2_parameter(&M::MODULUS); - pub const MU: u64 = Self::compute_mu_parameter(&M::MODULUS); - pub const ZERO: UnsignedInteger = UnsignedInteger::from_u64(0); - pub const ONE: UnsignedInteger = MontgomeryAlgorithms::cios( - &UnsignedInteger::from_u64(1), - &Self::R2, - &M::MODULUS, - &Self::MU, - ); - const MODULUS_HAS_ONE_SPARE_BIT: bool = Self::modulus_has_one_spare_bit(); - - /// Computes `- modulus^{-1} mod 2^{64}` - /// This algorithm is given by Dussé and Kaliski Jr. in - /// "S. R. Dussé and B. S. Kaliski Jr. A cryptographic library for the Motorola - /// DSP56000. In I. Damgård, editor, Advances in Cryptology – EUROCRYPT’90, - /// volume 473 of Lecture Notes in Computer Science, pages 230–244. Springer, - /// Heidelberg, May 1991." - const fn compute_mu_parameter(modulus: &UnsignedInteger) -> u64 { - let mut y = 1; - let word_size = 64; - let mut i: usize = 2; - while i <= word_size { - let (_, lo) = UnsignedInteger::mul(modulus, &UnsignedInteger::from_u64(y)); - let least_significant_limb = lo.limbs[NUM_LIMBS - 1]; - if (least_significant_limb << (word_size - i)) >> (word_size - i) != 1 { - y += 1 << (i - 1); - } - i += 1; - } - y.wrapping_neg() - } - - /// Computes 2^{384 * 2} modulo `modulus` - const fn compute_r2_parameter( - modulus: &UnsignedInteger, - ) -> UnsignedInteger { - let word_size = 64; - let mut l: usize = 0; - let zero = UnsignedInteger::from_u64(0); - // Define `c` as the largest power of 2 smaller than `modulus` - while l < NUM_LIMBS * word_size { - if UnsignedInteger::const_ne(&modulus.const_shr(l), &zero) { - break; - } - l += 1; - } - let mut c = UnsignedInteger::from_u64(1).const_shl(l); - - // Double `c` and reduce modulo `modulus` until getting - // `2^{2 * number_limbs * word_size}` mod `modulus` - let mut i: usize = 1; - while i <= 2 * NUM_LIMBS * word_size - l { - let (double_c, overflow) = UnsignedInteger::add(&c, &c); - c = if UnsignedInteger::const_le(modulus, &double_c) || overflow { - UnsignedInteger::sub(&double_c, modulus).0 - } else { - double_c - }; - i += 1; - } - c - } - - /// Checks whether the most significant limb of the modulus is at - /// most `0x7FFFFFFFFFFFFFFE`. This check is useful since special - /// optimizations exist for this kind of moduli. - #[inline(always)] - const fn modulus_has_one_spare_bit() -> bool { - M::MODULUS.limbs[0] < (1u64 << 63) - 1 - } -} - -impl IsField for MontgomeryBackendPrimeField -where - M: IsModulus> + Clone + Debug, -{ - type BaseType = UnsignedInteger; - - #[inline(always)] - fn add(a: &Self::BaseType, b: &Self::BaseType) -> Self::BaseType { - let (sum, overflow) = UnsignedInteger::add(a, b); - if Self::MODULUS_HAS_ONE_SPARE_BIT { - if sum >= M::MODULUS { - sum - M::MODULUS - } else { - sum - } - } else if overflow || sum >= M::MODULUS { - let (diff, _) = UnsignedInteger::sub(&sum, &M::MODULUS); - diff - } else { - sum - } - } - - #[inline(always)] - fn mul(a: &Self::BaseType, b: &Self::BaseType) -> Self::BaseType { - if Self::MODULUS_HAS_ONE_SPARE_BIT { - MontgomeryAlgorithms::cios_optimized_for_moduli_with_one_spare_bit( - a, - b, - &M::MODULUS, - &Self::MU, - ) - } else { - MontgomeryAlgorithms::cios(a, b, &M::MODULUS, &Self::MU) - } - } - - #[inline(always)] - fn square(a: &UnsignedInteger) -> UnsignedInteger { - MontgomeryAlgorithms::sos_square(a, &M::MODULUS, &Self::MU) - } - - #[inline(always)] - fn sub(a: &Self::BaseType, b: &Self::BaseType) -> Self::BaseType { - if b <= a { - a - b - } else { - M::MODULUS - (b - a) - } - } - - #[inline(always)] - fn neg(a: &Self::BaseType) -> Self::BaseType { - if a == &Self::ZERO { - *a - } else { - M::MODULUS - a - } - } - - #[inline(always)] - fn inv(a: &Self::BaseType) -> Result { - if a == &Self::ZERO { - Err(FieldError::InvZeroError) - } else { - // Guajardo Kumar Paar Pelzl - // Efficient Software-Implementation of Finite Fields with Applications to - // Cryptography - // Algorithm 16 (BEA for Inversion in Fp) - - //These can be done with const functions - let one: UnsignedInteger = UnsignedInteger::from_u64(1); - let modulus: UnsignedInteger = M::MODULUS; - let modulus_has_spare_bits = M::MODULUS.limbs[0] >> 63 == 0; - - let mut u: UnsignedInteger = *a; - let mut v = M::MODULUS; - let mut b = Self::R2; // Avoids unnecessary reduction step. - let mut c = Self::zero(); - - while u != one && v != one { - while u.limbs[NUM_LIMBS - 1] & 1 == 0 { - u >>= 1; - if b.limbs[NUM_LIMBS - 1] & 1 == 0 { - b >>= 1; - } else { - let carry; - (b, carry) = UnsignedInteger::::add(&b, &modulus); - b >>= 1; - if !modulus_has_spare_bits && carry { - b.limbs[0] |= 1 << 63; - } - } - } - - while v.limbs[NUM_LIMBS - 1] & 1 == 0 { - v >>= 1; - - if c.limbs[NUM_LIMBS - 1] & 1 == 0 { - c >>= 1; - } else { - let carry; - (c, carry) = UnsignedInteger::::add(&c, &modulus); - c >>= 1; - if !modulus_has_spare_bits && carry { - c.limbs[0] |= 1 << 63; - } - } - } - - if v <= u { - u = u - v; - if b < c { - b = modulus - c + b; - } else { - b = b - c; - } - } else { - v = v - u; - if c < b { - c = modulus - b + c; - } else { - c = c - b; - } - } - } - - if u == one { - Ok(b) - } else { - Ok(c) - } - } - } - - #[inline(always)] - fn div(a: &Self::BaseType, b: &Self::BaseType) -> Result { - let b_inv = &Self::inv(b)?; - Ok(Self::mul(a, b_inv)) - } - - #[inline(always)] - fn eq(a: &Self::BaseType, b: &Self::BaseType) -> bool { - a == b - } - - #[inline(always)] - fn zero() -> Self::BaseType { - Self::ZERO - } - - #[inline(always)] - fn one() -> Self::BaseType { - Self::ONE - } - - #[inline(always)] - fn from_u64(x: u64) -> Self::BaseType { - MontgomeryAlgorithms::cios( - &UnsignedInteger::from_u64(x), - &Self::R2, - &M::MODULUS, - &Self::MU, - ) - } - - #[inline(always)] - fn from_base_type(x: Self::BaseType) -> Self::BaseType { - MontgomeryAlgorithms::cios(&x, &Self::R2, &M::MODULUS, &Self::MU) - } -} - -impl IsPrimeField for MontgomeryBackendPrimeField -where - M: IsModulus> + Clone + Debug, -{ - type RepresentativeType = Self::BaseType; - - fn representative(x: &Self::BaseType) -> Self::RepresentativeType { - MontgomeryAlgorithms::cios(x, &UnsignedInteger::from_u64(1), &M::MODULUS, &Self::MU) - } - - fn field_bit_size() -> usize { - let mut evaluated_bit = NUM_LIMBS * 64 - 1; - let max_element = M::MODULUS - UnsignedInteger::::from_u64(1); - let one = UnsignedInteger::from_u64(1); - - while ((max_element >> evaluated_bit) & one) != one { - evaluated_bit -= 1; - } - - evaluated_bit + 1 - } - - fn from_hex(hex_string: &str) -> Result { - let integer = Self::BaseType::from_hex(hex_string)?; - if integer > M::MODULUS { - return Err(CreationError::RepresentativeOutOfRange); - } - - Ok(MontgomeryAlgorithms::cios( - &integer, - &MontgomeryBackendPrimeField::::R2, - &M::MODULUS, - &MontgomeryBackendPrimeField::::MU, - )) - } - - #[cfg(feature = "std")] - fn to_hex(x: &Self::BaseType) -> String { - Self::BaseType::to_hex(x) - } -} - -impl FieldElement> where - M: IsModulus> + Clone + Debug -{ -} - -impl ByteConversion - for FieldElement> -where - M: IsModulus> + Clone + Debug, -{ - #[cfg(feature = "alloc")] - fn to_bytes_be(&self) -> alloc::vec::Vec { - MontgomeryAlgorithms::cios( - self.value(), - &UnsignedInteger::from_u64(1), - &M::MODULUS, - &MontgomeryBackendPrimeField::::MU, - ) - .to_bytes_be() - } - - #[cfg(feature = "alloc")] - fn to_bytes_le(&self) -> alloc::vec::Vec { - MontgomeryAlgorithms::cios( - self.value(), - &UnsignedInteger::from_u64(1), - &M::MODULUS, - &MontgomeryBackendPrimeField::::MU, - ) - .to_bytes_le() - } - - fn from_bytes_be(bytes: &[u8]) -> Result { - let value = UnsignedInteger::from_bytes_be(bytes)?; - Ok(Self::new(value)) - } - - fn from_bytes_le(bytes: &[u8]) -> Result { - let value = UnsignedInteger::from_bytes_le(bytes)?; - Ok(Self::new(value)) - } -} - -#[cfg(feature = "alloc")] -impl AsBytes for FieldElement> -where - M: IsModulus> + Clone + Debug, -{ - fn as_bytes(&self) -> alloc::vec::Vec { - self.value().to_bytes_be() - } -} - -#[cfg(feature = "alloc")] -impl From>> - for alloc::vec::Vec -where - M: IsModulus> + Clone + Debug, -{ - fn from(value: FieldElement>) -> alloc::vec::Vec { - value.value().to_bytes_be() - } -} - -impl HasDefaultTranscript for MontgomeryBackendPrimeField -where - M: IsModulus> + Clone + Debug, -{ - /// # Panics - /// - /// This function will panic if NUM_LIMBS is greater than 6. - fn get_random_field_element_from_rng( - rng: &mut impl rand::Rng, - ) -> FieldElement> { - let mut buffer = [0u8; 6 * 8]; - let first_non_zero_limb_index = M::MODULUS - .limbs - .iter() - .position(|&x| x != 0) - .expect("modulus should be non-zero"); - let mask = u64::MAX >> M::MODULUS.limbs[first_non_zero_limb_index].leading_zeros(); - - let bits_start_idx = first_non_zero_limb_index * 8; - let bits_end_idx = NUM_LIMBS * 8; - let mut uint_sample; - - loop { - let sample_bytes = &mut buffer[bits_start_idx..bits_end_idx]; - rng.fill(sample_bytes); - - uint_sample = UnsignedInteger::from_bytes_be(&buffer).unwrap(); - - uint_sample.limbs[first_non_zero_limb_index] &= mask; - - if uint_sample < M::MODULUS { - break; - } - } - - FieldElement::new(MontgomeryBackendPrimeField::::from_base_type( - uint_sample, - )) - } -} - -#[cfg(test)] -mod tests_u384_prime_fields { - use crate::field::element::FieldElement; - use crate::field::errors::FieldError; - use crate::field::fields::fft_friendly::stark_252_prime_field::Stark252PrimeField; - use crate::field::fields::montgomery_backed_prime_fields::{ - IsModulus, U256PrimeField, U384PrimeField, - }; - use crate::field::traits::HasDefaultTranscript; - use crate::field::traits::IsField; - use crate::field::traits::IsPrimeField; - #[cfg(feature = "alloc")] - use crate::traits::ByteConversion; - use crate::unsigned_integer::element::U384; - use crate::unsigned_integer::element::{UnsignedInteger, U256}; - - use rand::Rng; - use rand_chacha::{rand_core::SeedableRng, ChaCha20Rng}; - - #[derive(Clone, Debug)] - struct U384Modulus23; - impl IsModulus for U384Modulus23 { - const MODULUS: U384 = UnsignedInteger::from_u64(23); - } - - type U384F23 = U384PrimeField; - type U384F23Element = FieldElement; - - #[test] - fn u384_mod_23_uses_5_bits() { - assert_eq!(U384F23::field_bit_size(), 5); - } - - #[test] - fn stark_252_prime_field_uses_252_bits() { - assert_eq!(Stark252PrimeField::field_bit_size(), 252); - } - - #[test] - fn u256_mod_2_uses_1_bit() { - #[derive(Clone, Debug)] - struct U256Modulus1; - impl IsModulus for U256Modulus1 { - const MODULUS: U256 = UnsignedInteger::from_u64(2); - } - type U256OneField = U256PrimeField; - assert_eq!(U256OneField::field_bit_size(), 1); - } - - #[test] - fn u256_with_first_bit_set_uses_256_bit() { - #[derive(Clone, Debug)] - struct U256ModulusBig; - impl IsModulus for U256ModulusBig { - const MODULUS: U256 = UnsignedInteger::from_hex_unchecked( - "F0000000F0000000F0000000F0000000F0000000F0000000F0000000F0000000", - ); - } - type U256OneField = U256PrimeField; - assert_eq!(U256OneField::field_bit_size(), 256); - } - - #[test] - fn montgomery_backend_primefield_compute_r2_parameter() { - let r2: U384 = UnsignedInteger { - limbs: [0, 0, 0, 0, 0, 6], - }; - assert_eq!(U384F23::R2, r2); - } - - #[test] - fn montgomery_backend_primefield_compute_mu_parameter() { - assert_eq!(U384F23::MU, 3208129404123400281); - } - - #[test] - fn montgomery_backend_primefield_compute_zero_parameter() { - let zero: U384 = UnsignedInteger { - limbs: [0, 0, 0, 0, 0, 0], - }; - assert_eq!(U384F23::ZERO, zero); - } - - #[test] - fn montgomery_backend_primefield_from_u64() { - let a: U384 = UnsignedInteger { - limbs: [0, 0, 0, 0, 0, 17], - }; - assert_eq!(U384F23::from_u64(770_u64), a); - } - - #[test] - fn montgomery_backend_primefield_representative() { - let a: U384 = UnsignedInteger { - limbs: [0, 0, 0, 0, 0, 11], - }; - assert_eq!(U384F23::representative(&U384F23::from_u64(770_u64)), a); - } - - #[test] - fn montgomery_backend_multiplication_works_0() { - let x = U384F23Element::from(11_u64); - let y = U384F23Element::from(10_u64); - let c = U384F23Element::from(110_u64); - assert_eq!(x * y, c); - } - - #[test] - #[cfg(feature = "lambdaworks-serde-string")] - fn montgomery_backend_serialization_deserialization() { - let x = U384F23Element::from(11_u64); - let x_serialized = serde_json::to_string(&x).unwrap(); - let x_deserialized: U384F23Element = serde_json::from_str(&x_serialized).unwrap(); - // assert_eq!(x_serialized, "{\"value\":\"0xb\"}"); // serialization is no longer as hex string - assert_eq!(x_deserialized, x); - } - - #[test] - fn doubling() { - assert_eq!( - U384F23Element::from(2).double(), - U384F23Element::from(2) + U384F23Element::from(2), - ); - } - - const ORDER: usize = 23; - #[test] - fn two_plus_one_is_three() { - assert_eq!( - U384F23Element::from(2) + U384F23Element::from(1), - U384F23Element::from(3) - ); - } - - #[test] - fn max_order_plus_1_is_0() { - assert_eq!( - U384F23Element::from((ORDER - 1) as u64) + U384F23Element::from(1), - U384F23Element::from(0) - ); - } - - #[test] - fn when_comparing_13_and_13_they_are_equal() { - let a: U384F23Element = U384F23Element::from(13); - let b: U384F23Element = U384F23Element::from(13); - assert_eq!(a, b); - } - - #[test] - fn when_comparing_13_and_8_they_are_different() { - let a: U384F23Element = U384F23Element::from(13); - let b: U384F23Element = U384F23Element::from(8); - assert_ne!(a, b); - } - - #[test] - fn mul_neutral_element() { - let a: U384F23Element = U384F23Element::from(1); - let b: U384F23Element = U384F23Element::from(2); - assert_eq!(a * b, U384F23Element::from(2)); - } - - #[test] - fn mul_2_3_is_6() { - let a: U384F23Element = U384F23Element::from(2); - let b: U384F23Element = U384F23Element::from(3); - assert_eq!(a * b, U384F23Element::from(6)); - } - - #[test] - fn mul_order_minus_1() { - let a: U384F23Element = U384F23Element::from((ORDER - 1) as u64); - let b: U384F23Element = U384F23Element::from((ORDER - 1) as u64); - assert_eq!(a * b, U384F23Element::from(1)); - } - - #[test] - fn inv_0_error() { - let result = U384F23Element::from(0).inv(); - assert!(matches!(result, Err(FieldError::InvZeroError))) - } - - #[test] - fn inv_2() { - let a: U384F23Element = U384F23Element::from(2); - assert_eq!(&a * a.inv().unwrap(), U384F23Element::from(1)); - } - - #[test] - fn pow_2_3() { - assert_eq!(U384F23Element::from(2).pow(3_u64), U384F23Element::from(8)) - } - - #[test] - fn pow_p_minus_1() { - assert_eq!( - U384F23Element::from(2).pow(ORDER - 1), - U384F23Element::from(1) - ) - } - - #[test] - fn div_1() { - assert_eq!( - (U384F23Element::from(2) / U384F23Element::from(1)).unwrap(), - U384F23Element::from(2) - ) - } - - #[test] - fn div_4_2() { - assert_eq!( - (U384F23Element::from(4) / U384F23Element::from(2)).unwrap(), - U384F23Element::from(2) - ) - } - - #[test] - fn three_inverse() { - let a = U384F23Element::from(3); - let expected = U384F23Element::from(8); - assert_eq!(a.inv().unwrap(), expected) - } - - #[test] - fn div_4_3() { - assert_eq!( - (U384F23Element::from(4) / U384F23Element::from(3)).unwrap() * U384F23Element::from(3), - U384F23Element::from(4) - ) - } - - #[test] - fn two_plus_its_additive_inv_is_0() { - let two = U384F23Element::from(2); - - assert_eq!(&two + (-&two), U384F23Element::from(0)) - } - - #[test] - fn four_minus_three_is_1() { - let four = U384F23Element::from(4); - let three = U384F23Element::from(3); - - assert_eq!(four - three, U384F23Element::from(1)) - } - - #[test] - fn zero_minus_1_is_order_minus_1() { - let zero = U384F23Element::from(0); - let one = U384F23Element::from(1); - - assert_eq!(zero - one, U384F23Element::from((ORDER - 1) as u64)) - } - - #[test] - fn neg_zero_is_zero() { - let zero = U384F23Element::from(0); - - assert_eq!(-&zero, zero); - } - - #[test] - fn test_random_field_element_from_rng_0() { - // This seed generates a sample that is less than the modulus; - let mut rng = ::from_seed([1; 32]); - let mut expected_rng = rng.clone(); - - let mut buffer = [0u8; 48]; - let sample_bytes = &mut buffer[40..]; - expected_rng.fill(sample_bytes); - - let mut expected_uint = UnsignedInteger::from_bytes_be(&buffer).unwrap(); - - expected_uint.limbs[5] &= 31_u64; - - let expected = FieldElement::new(U384F23::from_base_type(expected_uint)); - - let result = U384F23::get_random_field_element_from_rng(&mut rng); - - assert_eq!(result, expected); - } - - #[test] - fn test_random_field_element_from_rng_1() { - // This seed generates a sample that is grater than the modulus; - let mut rng = ::from_seed([5; 32]); - let mut expected_rng = rng.clone(); - - let mut buffer = [0u8; 48]; - let sample_bytes = &mut buffer[40..]; - expected_rng.fill(sample_bytes); - - let mut expected_uint = UnsignedInteger::from_bytes_be(&buffer).unwrap(); - - expected_uint.limbs[5] &= 31_u64; - - let expected = FieldElement::new(U384F23::from_base_type(expected_uint)); - - let result = U384F23::get_random_field_element_from_rng(&mut rng); - - assert_ne!(result, expected); - } - - // FP1 - #[derive(Clone, Debug)] - struct U384ModulusP1; - impl IsModulus for U384ModulusP1 { - const MODULUS: U384 = UnsignedInteger { - limbs: [ - 0, - 0, - 0, - 3450888597, - 5754816256417943771, - 15923941673896418529, - ], - }; - } - - type U384FP1 = U384PrimeField; - type U384FP1Element = FieldElement; - - #[test] - fn montgomery_prime_field_from_bad_hex_errs() { - assert!(U384FP1Element::from_hex("0xTEST").is_err()); - } - - #[test] - fn montgomery_prime_field_addition_works_0() { - let x = U384FP1Element::new(UnsignedInteger::from_hex_unchecked( - "05ed176deb0e80b4deb7718cdaa075165f149c", - )); - let y = U384FP1Element::new(UnsignedInteger::from_hex_unchecked( - "5f103b0bd4397d4df560eb559f38353f80eeb6", - )); - let c = U384FP1Element::new(UnsignedInteger::from_hex_unchecked( - "64fd5279bf47fe02d4185ce279d8aa55e00352", - )); - assert_eq!(x + y, c); - } - - #[test] - fn montgomery_prime_field_multiplication_works_0() { - let x = U384FP1Element::new(UnsignedInteger::from_hex_unchecked( - "05ed176deb0e80b4deb7718cdaa075165f149c", - )); - let y = U384FP1Element::new(UnsignedInteger::from_hex_unchecked( - "5f103b0bd4397d4df560eb559f38353f80eeb6", - )); - let c = U384FP1Element::new(UnsignedInteger::from_hex_unchecked( - "73d23e8d462060dc23d5c15c00fc432d95621a3c", - )); - assert_eq!(x * y, c); - } - - // FP2 - #[derive(Clone, Debug)] - struct U384ModulusP2; - impl IsModulus for U384ModulusP2 { - const MODULUS: U384 = UnsignedInteger { - limbs: [ - 18446744073709551615, - 18446744073709551615, - 18446744073709551615, - 18446744073709551615, - 18446744073709551615, - 18446744073709551275, - ], - }; - } - - type U384FP2 = U384PrimeField; - type U384FP2Element = FieldElement; - - #[test] - fn montgomery_prime_field_addition_works_1() { - let x = U384FP2Element::new(UnsignedInteger::from_hex_unchecked( - "05ed176deb0e80b4deb7718cdaa075165f149c", - )); - let y = U384FP2Element::new(UnsignedInteger::from_hex_unchecked( - "5f103b0bd4397d4df560eb559f38353f80eeb6", - )); - let c = U384FP2Element::new(UnsignedInteger::from_hex_unchecked( - "64fd5279bf47fe02d4185ce279d8aa55e00352", - )); - assert_eq!(x + y, c); - } - - #[test] - fn montgomery_prime_field_multiplication_works_1() { - let x = U384FP2Element::one(); - let y = U384FP2Element::new(UnsignedInteger::from_hex_unchecked( - "5f103b0bd4397d4df560eb559f38353f80eeb6", - )); - assert_eq!(&y * x, y); - } - - #[test] - #[cfg(feature = "alloc")] - fn to_bytes_from_bytes_be_is_the_identity() { - let x = U384FP2Element::new(UnsignedInteger::from_hex_unchecked( - "5f103b0bd4397d4df560eb559f38353f80eeb6", - )); - assert_eq!(U384FP2Element::from_bytes_be(&x.to_bytes_be()).unwrap(), x); - } - - #[test] - #[cfg(feature = "alloc")] - fn from_bytes_to_bytes_be_is_the_identity_for_one() { - let bytes = [ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, - ]; - assert_eq!( - U384FP2Element::from_bytes_be(&bytes).unwrap().to_bytes_be(), - bytes - ); - } - - #[test] - #[cfg(feature = "alloc")] - fn to_bytes_from_bytes_le_is_the_identity() { - let x = U384FP2Element::new(UnsignedInteger::from_hex_unchecked( - "5f103b0bd4397d4df560eb559f38353f80eeb6", - )); - assert_eq!(U384FP2Element::from_bytes_le(&x.to_bytes_le()).unwrap(), x); - } - - #[test] - #[cfg(feature = "alloc")] - fn from_bytes_to_bytes_le_is_the_identity_for_one() { - let bytes = [ - 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - ]; - assert_eq!( - U384FP2Element::from_bytes_le(&bytes).unwrap().to_bytes_le(), - bytes - ); - } -} - -#[cfg(test)] -mod tests_u256_prime_fields { - use crate::field::element::FieldElement; - use crate::field::errors::FieldError; - use crate::field::fields::montgomery_backed_prime_fields::{IsModulus, U256PrimeField}; - use crate::field::traits::HasDefaultTranscript; - use crate::field::traits::IsField; - use crate::field::traits::IsPrimeField; - #[cfg(feature = "alloc")] - use crate::traits::ByteConversion; - use crate::unsigned_integer::element::U256; - use crate::unsigned_integer::element::{UnsignedInteger, U64}; - use rand::Rng; - use rand_chacha::{rand_core::SeedableRng, ChaCha20Rng}; - - use super::U64PrimeField; - - #[derive(Clone, Debug)] - struct U256Modulus29; - impl IsModulus for U256Modulus29 { - const MODULUS: U256 = UnsignedInteger::from_u64(29); - } - - type U256F29 = U256PrimeField; - type U256F29Element = FieldElement; - - #[test] - fn montgomery_backend_primefield_compute_r2_parameter() { - let r2: U256 = UnsignedInteger { - limbs: [0, 0, 0, 24], - }; - assert_eq!(U256F29::R2, r2); - } - - #[test] - fn montgomery_backend_primefield_compute_mu_parameter() { - // modular multiplicative inverse - assert_eq!(U256F29::MU, 14630176334321368523); - } - - #[test] - fn montgomery_backend_primefield_compute_zero_parameter() { - let zero: U256 = UnsignedInteger { - limbs: [0, 0, 0, 0], - }; - assert_eq!(U256F29::ZERO, zero); - } - - #[test] - fn montgomery_backend_primefield_from_u64() { - // (770*2**(256))%29 - let a: U256 = UnsignedInteger { - limbs: [0, 0, 0, 24], - }; - assert_eq!(U256F29::from_u64(770_u64), a); - } - - #[test] - fn montgomery_backend_primefield_representative() { - // 770%29 - let a: U256 = UnsignedInteger { - limbs: [0, 0, 0, 16], - }; - assert_eq!(U256F29::representative(&U256F29::from_u64(770_u64)), a); - } - - #[test] - fn montgomery_backend_multiplication_works_0() { - let x = U256F29Element::from(11_u64); - let y = U256F29Element::from(10_u64); - let c = U256F29Element::from(110_u64); - assert_eq!(x * y, c); - } - - #[test] - fn doubling() { - assert_eq!( - U256F29Element::from(2).double(), - U256F29Element::from(2) + U256F29Element::from(2), - ); - } - - const ORDER: usize = 29; - #[test] - fn two_plus_one_is_three() { - assert_eq!( - U256F29Element::from(2) + U256F29Element::from(1), - U256F29Element::from(3) - ); - } - - #[test] - fn max_order_plus_1_is_0() { - assert_eq!( - U256F29Element::from((ORDER - 1) as u64) + U256F29Element::from(1), - U256F29Element::from(0) - ); - } - - #[test] - fn when_comparing_13_and_13_they_are_equal() { - let a: U256F29Element = U256F29Element::from(13); - let b: U256F29Element = U256F29Element::from(13); - assert_eq!(a, b); - } - - #[test] - fn when_comparing_13_and_8_they_are_different() { - let a: U256F29Element = U256F29Element::from(13); - let b: U256F29Element = U256F29Element::from(8); - assert_ne!(a, b); - } - - #[test] - fn mul_neutral_element() { - let a: U256F29Element = U256F29Element::from(1); - let b: U256F29Element = U256F29Element::from(2); - assert_eq!(a * b, U256F29Element::from(2)); - } - - #[test] - fn mul_2_3_is_6() { - let a: U256F29Element = U256F29Element::from(2); - let b: U256F29Element = U256F29Element::from(3); - assert_eq!(a * b, U256F29Element::from(6)); - } - - #[test] - fn mul_order_minus_1() { - let a: U256F29Element = U256F29Element::from((ORDER - 1) as u64); - let b: U256F29Element = U256F29Element::from((ORDER - 1) as u64); - assert_eq!(a * b, U256F29Element::from(1)); - } - - #[test] - fn inv_0_error() { - let result = U256F29Element::from(0).inv(); - assert!(matches!(result, Err(FieldError::InvZeroError))); - } - - #[test] - fn inv_2() { - let a: U256F29Element = U256F29Element::from(2); - assert_eq!(&a * a.inv().unwrap(), U256F29Element::from(1)); - } - - #[test] - fn pow_2_3() { - assert_eq!(U256F29Element::from(2).pow(3_u64), U256F29Element::from(8)) - } - - #[test] - fn pow_p_minus_1() { - assert_eq!( - U256F29Element::from(2).pow(ORDER - 1), - U256F29Element::from(1) - ) - } - - #[test] - fn div_1() { - assert_eq!( - (U256F29Element::from(2) / U256F29Element::from(1)).unwrap(), - U256F29Element::from(2) - ) - } - - #[test] - fn div_4_2() { - let a = U256F29Element::from(4); - let b = U256F29Element::from(2); - assert_eq!((a / &b).unwrap(), b) - } - - #[test] - fn div_4_3() { - assert_eq!( - (U256F29Element::from(4) / U256F29Element::from(3)).unwrap() * U256F29Element::from(3), - U256F29Element::from(4) - ) - } - - #[test] - fn two_plus_its_additive_inv_is_0() { - let two = U256F29Element::from(2); - - assert_eq!(&two + (-&two), U256F29Element::from(0)) - } - - #[test] - fn four_minus_three_is_1() { - let four = U256F29Element::from(4); - let three = U256F29Element::from(3); - - assert_eq!(four - three, U256F29Element::from(1)) - } - - #[test] - fn zero_minus_1_is_order_minus_1() { - let zero = U256F29Element::from(0); - let one = U256F29Element::from(1); - - assert_eq!(zero - one, U256F29Element::from((ORDER - 1) as u64)) - } - - #[test] - fn neg_zero_is_zero() { - let zero = U256F29Element::from(0); - - assert_eq!(-&zero, zero); - } - - #[test] - fn test_random_field_element_from_rng_0() { - // This seed generates a sample that is less than the modulus; - let mut rng = ::from_seed([1; 32]); - let mut expected_rng = rng.clone(); - - let mut buffer = [0u8; 48]; - let sample_bytes = &mut buffer[24..32]; - expected_rng.fill(sample_bytes); - - let mut expected_uint = UnsignedInteger::from_bytes_be(&buffer).unwrap(); - - expected_uint.limbs[3] &= 31_u64; - - let expected = FieldElement::new(U256F29::from_base_type(expected_uint)); - - let result = U256F29::get_random_field_element_from_rng(&mut rng); - - assert_eq!(result, expected); - } - - #[test] - fn test_random_field_element_from_rng_1() { - // This seed generates a sample that is grater than the modulus; - let mut rng = ::from_seed([5; 32]); - let mut expected_rng = rng.clone(); - - let mut buffer = [0u8; 48]; - let sample_bytes = &mut buffer[24..32]; - expected_rng.fill(sample_bytes); - - let mut expected_uint = UnsignedInteger::from_bytes_be(&buffer).unwrap(); - - expected_uint.limbs[3] &= 31_u64; - - let expected = FieldElement::new(U256F29::from_base_type(expected_uint)); - - let result = U256F29::get_random_field_element_from_rng(&mut rng); - - assert_ne!(result, expected); - } - - #[derive(Clone, Debug)] - struct U256ModulusP1; - impl IsModulus for U256ModulusP1 { - const MODULUS: U256 = UnsignedInteger { - limbs: [ - 8366, - 8155137382671976874, - 227688614771682406, - 15723111795979912613, - ], - }; - } - - type U256FP1 = U256PrimeField; - type U256FP1Element = FieldElement; - - #[test] - fn montgomery_prime_field_addition_works_0() { - let x = U256FP1Element::new(UnsignedInteger::from_hex_unchecked( - "93e712950bf3fe589aa030562a44b1cec66b09192c4bcf705a5", - )); - let y = U256FP1Element::new(UnsignedInteger::from_hex_unchecked( - "10a712235c1f6b4172a1e35da6aef1a7ec6b09192c4bb88cfa5", - )); - let c = U256FP1Element::new(UnsignedInteger::from_hex_unchecked( - "a48e24b86813699a0d4213b3d0f3a376b2d61232589787fd54a", - )); - assert_eq!(x + y, c); - } - - #[test] - fn montgomery_prime_field_multiplication_works_0() { - let x = U256FP1Element::new(UnsignedInteger::from_hex_unchecked( - "93e712950bf3fe589aa030562a44b1cec66b09192c4bcf705a5", - )); - let y = U256FP1Element::new(UnsignedInteger::from_hex_unchecked( - "10a712235c1f6b4172a1e35da6aef1a7ec6b09192c4bb88cfa5", - )); - let c = U256FP1Element::new(UnsignedInteger::from_hex_unchecked( - "7808e74c3208d9a66791ef9cc15a46acc9951ee312102684021", - )); - assert_eq!(x * y, c); - } - - // FP2 - #[derive(Clone, Debug)] - struct ModulusP2; - impl IsModulus for ModulusP2 { - const MODULUS: U256 = UnsignedInteger { - limbs: [ - 18446744073709551615, - 18446744073709551615, - 18446744073709551615, - 18446744073709551427, - ], - }; - } - - type FP2 = U256PrimeField; - type FP2Element = FieldElement; - - #[test] - fn montgomery_prime_field_addition_works_1() { - let x = FP2Element::new(UnsignedInteger::from_hex_unchecked( - "acbbb7ca01c65cfffffc72815b397fff9ab130ad53a5ffffffb8f21b207dfedf", - )); - let y = FP2Element::new(UnsignedInteger::from_hex_unchecked( - "d65ddbe509d3fffff21f494c588cbdbfe43e929b0543e3ffffffffffffffff43", - )); - let c = FP2Element::new(UnsignedInteger::from_hex_unchecked( - "831993af0b9a5cfff21bbbcdb3c63dbf7eefc34858e9e3ffffb8f21b207dfedf", - )); - assert_eq!(x + y, c); - } - - #[test] - fn montgomery_prime_field_multiplication_works_1() { - let x = FP2Element::new(UnsignedInteger::from_hex_unchecked( - "acbbb7ca01c65cfffffc72815b397fff9ab130ad53a5ffffffb8f21b207dfedf", - )); - let y = FP2Element::new(UnsignedInteger::from_hex_unchecked( - "d65ddbe509d3fffff21f494c588cbdbfe43e929b0543e3ffffffffffffffff43", - )); - let c = FP2Element::new(UnsignedInteger::from_hex_unchecked( - "2b1e80d553ecab2e4d41eb53c4c8ad89ebacac6cf6b91dcf2213f311093aa05d", - )); - assert_eq!(&y * x, c); - } - - #[test] - #[cfg(feature = "alloc")] - fn to_bytes_from_bytes_be_is_the_identity() { - let x = FP2Element::new(UnsignedInteger::from_hex_unchecked( - "5f103b0bd4397d4df560eb559f38353f80eeb6", - )); - assert_eq!(FP2Element::from_bytes_be(&x.to_bytes_be()).unwrap(), x); - } - - #[test] - #[cfg(feature = "alloc")] - fn from_bytes_to_bytes_be_is_the_identity_for_one() { - let bytes = [ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 1, - ]; - assert_eq!( - FP2Element::from_bytes_be(&bytes).unwrap().to_bytes_be(), - bytes - ); - } - - #[test] - #[cfg(feature = "alloc")] - fn to_bytes_from_bytes_le_is_the_identity() { - let x = FP2Element::new(UnsignedInteger::from_hex_unchecked( - "5f103b0bd4397d4df560eb559f38353f80eeb6", - )); - assert_eq!(FP2Element::from_bytes_le(&x.to_bytes_le()).unwrap(), x); - } - - #[test] - #[cfg(feature = "alloc")] - fn from_bytes_to_bytes_le_is_the_identity_for_one() { - let bytes = [ - 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, - ]; - assert_eq!( - FP2Element::from_bytes_le(&bytes).unwrap().to_bytes_le(), - bytes - ); - } - - #[test] - #[cfg(feature = "alloc")] - fn creating_a_field_element_from_its_representative_returns_the_same_element_1() { - let change = U256::from_u64(1); - let f1 = U256FP1Element::new(U256ModulusP1::MODULUS + change); - let f2 = U256FP1Element::new(f1.representative()); - assert_eq!(f1, f2); - } - - #[test] - fn creating_a_field_element_from_its_representative_returns_the_same_element_2() { - let change = U256::from_u64(27); - let f1 = U256F29Element::new(U256Modulus29::MODULUS + change); - let f2 = U256F29Element::new(f1.representative()); - assert_eq!(f1, f2); - } - - #[test] - fn creating_a_field_element_from_hex_works_1() { - let a = U256FP1Element::from_hex_unchecked("eb235f6144d9e91f4b14"); - let b = U256FP1Element::new(U256 { - limbs: [0, 0, 60195, 6872850209053821716], - }); - assert_eq!(a, b); - } - - #[test] - fn creating_a_field_element_from_hex_too_big_errors() { - let a = U256FP1Element::from_hex(&"f".repeat(65)); - assert!(a.is_err()); - assert_eq!( - a.unwrap_err(), - crate::errors::CreationError::HexStringIsTooBig - ) - } - - #[test] - fn creating_a_field_element_from_hex_bigger_than_modulus_errors() { - // A number that consists of 255 1s is bigger than the `U256FP1` modulus - let a = U256FP1Element::from_hex(&"f".repeat(64)); - assert!(a.is_err()); - assert_eq!( - a.unwrap_err(), - crate::errors::CreationError::RepresentativeOutOfRange - ) - } - - #[test] - fn creating_a_field_element_from_hex_works_2() { - let a = U256F29Element::from_hex_unchecked("aa"); - let b = U256F29Element::from(25); - assert_eq!(a, b); - } - - #[test] - fn creating_a_field_element_from_hex_works_3() { - let a = U256F29Element::from_hex_unchecked("1d"); - let b = U256F29Element::zero(); - assert_eq!(a, b); - } - - #[cfg(feature = "std")] - #[test] - fn to_hex_test_works_1() { - let a = U256FP1Element::from_hex_unchecked("eb235f6144d9e91f4b14"); - let b = U256FP1Element::new(U256 { - limbs: [0, 0, 60195, 6872850209053821716], - }); - - assert_eq!(U256FP1Element::to_hex(&a), U256FP1Element::to_hex(&b)); - } - - #[cfg(feature = "std")] - #[test] - fn to_hex_test_works_2() { - let a = U256F29Element::from_hex_unchecked("1d"); - let b = U256F29Element::zero(); - - assert_eq!(U256F29Element::to_hex(&a), U256F29Element::to_hex(&b)); - } - - #[cfg(feature = "std")] - #[test] - fn to_hex_test_works_3() { - let a = U256F29Element::from_hex_unchecked("aa"); - let b = U256F29Element::from(25); - - assert_eq!(U256F29Element::to_hex(&a), U256F29Element::to_hex(&b)); - } - - // Goldilocks - #[derive(Clone, Debug)] - struct GoldilocksModulus; - impl IsModulus for GoldilocksModulus { - const MODULUS: U64 = UnsignedInteger { - limbs: [18446744069414584321], - }; - } - - type GoldilocksField = U64PrimeField; - type GoldilocksElement = FieldElement; - - #[derive(Clone, Debug)] - struct SecpModulus; - impl IsModulus for SecpModulus { - const MODULUS: U256 = UnsignedInteger::from_hex_unchecked( - "0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F", - ); - } - type SecpMontField = U256PrimeField; - type SecpMontElement = FieldElement; - - #[test] - fn secp256k1_minus_three_pow_2_is_9_with_all_operations() { - let minus_3 = -SecpMontElement::from_hex_unchecked("0x3"); - let minus_3_mul_minus_3 = &minus_3 * &minus_3; - let minus_3_squared = minus_3.square(); - let minus_3_pow_2 = minus_3.pow(2_u32); - let nine = SecpMontElement::from_hex_unchecked("0x9"); - - assert_eq!(minus_3_mul_minus_3, nine); - assert_eq!(minus_3_squared, nine); - assert_eq!(minus_3_pow_2, nine); - } - - #[test] - fn secp256k1_inv_works() { - let a = SecpMontElement::from_hex_unchecked("0x456"); - let a_inv = a.inv().unwrap(); - - assert_eq!(a * a_inv, SecpMontElement::one()); - } - - #[test] - fn test_cios_overflow_case() { - let a = GoldilocksElement::from(732582227915286439); - let b = GoldilocksElement::from(3906369333256140342); - let expected_sum = GoldilocksElement::from(4638951561171426781); - assert_eq!(a + b, expected_sum); - } -} diff --git a/crates/math/src/field/fields/p448_goldilocks_prime_field.rs b/crates/math/src/field/fields/p448_goldilocks_prime_field.rs deleted file mode 100644 index 46b7a23d9..000000000 --- a/crates/math/src/field/fields/p448_goldilocks_prime_field.rs +++ /dev/null @@ -1,460 +0,0 @@ -use crate::errors::CreationError; -use crate::field::errors::FieldError; -use crate::field::traits::{IsField, IsPrimeField}; -use crate::traits::ByteConversion; -use crate::unsigned_integer::element::UnsignedInteger; - -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct P448GoldilocksPrimeField; -pub type U448 = UnsignedInteger<7>; - -/// Goldilocks Prime p = 2^448 - 2^224 - 1 -pub const P448_GOLDILOCKS_PRIME_FIELD_ORDER: U448 = - U448::from_hex_unchecked("fffffffffffffffffffffffffffffffffffffffffffffffffffffffeffffffffffffffffffffffffffffffffffffffffffffffffffffffff"); - -/// 448-bit unsigned integer represented as -/// a size 8 `u64` array `limbs` of 56-bit words. -/// The least significant word is in the left most position. -#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Default)] -pub struct U56x8 { - limbs: [u64; 8], -} - -impl ByteConversion for U56x8 { - #[cfg(feature = "alloc")] - fn to_bytes_be(&self) -> alloc::vec::Vec { - unimplemented!() - } - - #[cfg(feature = "alloc")] - fn to_bytes_le(&self) -> alloc::vec::Vec { - unimplemented!() - } - - fn from_bytes_be(_bytes: &[u8]) -> Result - where - Self: Sized, - { - unimplemented!() - } - - fn from_bytes_le(_bytes: &[u8]) -> Result - where - Self: Sized, - { - unimplemented!() - } -} - -impl IsField for P448GoldilocksPrimeField { - type BaseType = U56x8; - - fn add(a: &U56x8, b: &U56x8) -> U56x8 { - let mut limbs = [0u64; 8]; - for (i, limb) in limbs.iter_mut().enumerate() { - *limb = a.limbs[i] + b.limbs[i]; - } - - let mut sum = U56x8 { limbs }; - Self::weak_reduce(&mut sum); - sum - } - - /// Implements fast Karatsuba Multiplication optimized for the - /// Godilocks Prime field. Taken from Mike Hamburg's implemenation: - /// https://sourceforge.net/p/ed448goldilocks/code/ci/master/tree/src/p448/arch_ref64/f_impl.c - fn mul(a: &U56x8, b: &U56x8) -> U56x8 { - let (a, b) = (&a.limbs, &b.limbs); - let mut c = [0u64; 8]; - - let mut accum0 = 0u128; - let mut accum1 = 0u128; - let mut accum2: u128; - - let mask = (1u64 << 56) - 1; - - let mut aa = [0u64; 4]; - let mut bb = [0u64; 4]; - let mut bbb = [0u64; 4]; - - for i in 0..4 { - aa[i] = a[i] + a[i + 4]; - bb[i] = b[i] + b[i + 4]; - bbb[i] = bb[i] + b[i + 4]; - } - - let widemul = |a: u64, b: u64| -> u128 { (a as u128) * (b as u128) }; - - for i in 0..4 { - accum2 = 0; - - for j in 0..=i { - accum2 += widemul(a[j], b[i - j]); - accum1 += widemul(aa[j], bb[i - j]); - accum0 += widemul(a[j + 4], b[i - j + 4]); - } - for j in (i + 1)..4 { - accum2 += widemul(a[j], b[8 - (j - i)]); - accum1 += widemul(aa[j], bbb[4 - (j - i)]); - accum0 += widemul(a[j + 4], bb[4 - (j - i)]); - } - - accum1 -= accum2; - accum0 += accum2; - - c[i] = (accum0 as u64) & mask; - c[i + 4] = (accum1 as u64) & mask; - - accum0 >>= 56; - accum1 >>= 56; - } - - accum0 += accum1; - accum0 += c[4] as u128; - accum1 += c[0] as u128; - c[4] = (accum0 as u64) & mask; - c[0] = (accum1 as u64) & mask; - - accum0 >>= 56; - accum1 >>= 56; - - c[5] += accum0 as u64; - c[1] += accum1 as u64; - - U56x8 { limbs: c } - } - - fn sub(a: &U56x8, b: &U56x8) -> U56x8 { - let co1 = ((1u64 << 56) - 1) * 2; - let co2 = co1 - 2; - - let mut limbs = [0u64; 8]; - for (i, limb) in limbs.iter_mut().enumerate() { - *limb = - a.limbs[i] - .wrapping_sub(b.limbs[i]) - .wrapping_add(if i == 4 { co2 } else { co1 }); - } - - let mut res = U56x8 { limbs }; - Self::weak_reduce(&mut res); - res - } - - fn neg(a: &U56x8) -> U56x8 { - let zero = Self::zero(); - Self::sub(&zero, a) - } - - fn inv(a: &U56x8) -> Result { - if *a == Self::zero() { - return Err(FieldError::InvZeroError); - } - Ok(Self::pow( - a, - P448_GOLDILOCKS_PRIME_FIELD_ORDER - U448::from_u64(2), - )) - } - - fn div(a: &U56x8, b: &U56x8) -> Result { - let b_inv = &Self::inv(b)?; - Ok(Self::mul(a, b_inv)) - } - - /// Taken from https://sourceforge.net/p/ed448goldilocks/code/ci/master/tree/src/per_field/f_generic.tmpl.c - fn eq(a: &U56x8, b: &U56x8) -> bool { - let mut c = Self::sub(a, b); - Self::strong_reduce(&mut c); - let mut ret = 0u64; - for limb in c.limbs.iter() { - ret |= limb; - } - ret == 0 - } - - fn zero() -> U56x8 { - U56x8 { limbs: [0u64; 8] } - } - - fn one() -> U56x8 { - let mut limbs = [0u64; 8]; - limbs[0] = 1; - U56x8 { limbs } - } - - fn from_u64(x: u64) -> U56x8 { - let mut limbs = [0u64; 8]; - limbs[0] = x & ((1u64 << 56) - 1); - limbs[1] = x >> 56; - U56x8 { limbs } - } - - fn from_base_type(x: U56x8) -> U56x8 { - let mut x = x; - Self::strong_reduce(&mut x); - x - } -} - -impl IsPrimeField for P448GoldilocksPrimeField { - type RepresentativeType = U448; - - fn representative(a: &U56x8) -> U448 { - let mut a = *a; - Self::strong_reduce(&mut a); - - let mut r = U448::from_u64(0); - for i in (0..7).rev() { - r = r << 56; - r = r + U448::from_u64(a.limbs[i]); - } - r - } - - fn from_hex(hex_string: &str) -> Result { - U56x8::from_hex(hex_string) - } - - #[cfg(feature = "std")] - fn to_hex(x: &U56x8) -> String { - U56x8::to_hex(x) - } - - fn field_bit_size() -> usize { - 448 - } -} - -impl P448GoldilocksPrimeField { - /// Reduces the value in each limb to less than 2^57 (2^56 + 2^8 - 2 is the largest possible value in a limb after this reduction) - /// Taken from https://sourceforge.net/p/ed448goldilocks/code/ci/master/tree/src/p448/arch_ref64/f_impl.h - fn weak_reduce(a: &mut U56x8) { - let a = &mut a.limbs; - - let mask = (1u64 << 56) - 1; - let tmp = a[7] >> 56; - a[4] += tmp; - - for i in (1..8).rev() { - a[i] = (a[i] & mask) + (a[i - 1] >> 56); - } - - a[0] = (a[0] & mask) + tmp; - } - - /// Reduces the number to its canonical form - /// Taken from https://sourceforge.net/p/ed448goldilocks/code/ci/master/tree/src/per_field/f_generic.tmpl.c - fn strong_reduce(a: &mut U56x8) { - P448GoldilocksPrimeField::weak_reduce(a); - - const MODULUS: U56x8 = U56x8 { - limbs: [ - 0xffffffffffffff, - 0xffffffffffffff, - 0xffffffffffffff, - 0xffffffffffffff, - 0xfffffffffffffe, - 0xffffffffffffff, - 0xffffffffffffff, - 0xffffffffffffff, - ], - }; - let mask = (1u128 << 56) - 1; - - let mut scarry = 0i128; - for i in 0..8 { - scarry = scarry + (a.limbs[i] as i128) - (MODULUS.limbs[i] as i128); - a.limbs[i] = ((scarry as u128) & mask) as u64; - scarry >>= 56; - } - - assert!((scarry as u64) == 0 || (scarry as u64).wrapping_add(1) == 0); - - let scarry_0 = scarry as u64; - let mut carry = 0u128; - - for i in 0..8 { - carry = carry + (a.limbs[i] as u128) + ((scarry_0 & MODULUS.limbs[i]) as u128); - a.limbs[i] = (carry & mask) as u64; - carry >>= 56; - } - - assert!((carry as u64).wrapping_add(scarry_0) == 0); - } -} - -impl U56x8 { - pub const fn from_hex(hex_string: &str) -> Result { - let mut result = [0u64; 8]; - let mut limb = 0; - let mut limb_index = 0; - let mut shift = 0; - let value = hex_string.as_bytes(); - let mut i: usize = value.len(); - while i > 0 { - i -= 1; - limb |= match value[i] { - c @ b'0'..=b'9' => (c as u64 - '0' as u64) << shift, - c @ b'a'..=b'f' => (c as u64 - 'a' as u64 + 10) << shift, - c @ b'A'..=b'F' => (c as u64 - 'A' as u64 + 10) << shift, - _ => { - return Err(CreationError::InvalidHexString); - } - }; - shift += 4; - if shift == 56 && limb_index < 7 { - result[limb_index] = limb; - limb = 0; - limb_index += 1; - shift = 0; - } - } - result[limb_index] = limb; - - Ok(U56x8 { limbs: result }) - } - - #[cfg(feature = "std")] - pub fn to_hex(&self) -> String { - let mut hex_string = String::new(); - for &limb in self.limbs.iter().rev() { - hex_string.push_str(&format!("{limb:014X}")); - } - hex_string.trim_start_matches('0').to_string() - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn construct_u56x8_from_hex_string_1() { - let hex_str = "1"; - let num = U56x8::from_hex(hex_str).unwrap(); - assert_eq!(num.limbs, [1, 0, 0, 0, 0, 0, 0, 0]); - } - - #[test] - fn construct_u56x8_from_hex_string_2() { - let hex_str = "49bbeeaa7102b38a0cfba4634f64a288bcb9b1366599f7afcb5453567ef7c34cce0f7139c6dea4841497172f637c7bbbf3ca1990ad88381e"; - let num = U56x8::from_hex(hex_str).unwrap(); - assert_eq!( - num.limbs, - [ - 56886054472923166, - 6526028801096691, - 16262733217666199, - 35738265244798833, - 43338005839369046, - 45749290377754213, - 38857821720366948, - 20754307036021427 - ] - ); - } - - #[test] - fn strong_reduce_test1() { - let mut num = U56x8::from_hex("fffffffffffffffffffffffffffffffffffffffffffffffffffffffeffffffffffffffffffffffffffffffffffffffffffffffffffffffff").unwrap(); - P448GoldilocksPrimeField::strong_reduce(&mut num); - assert_eq!(num.limbs, [0, 0, 0, 0, 0, 0, 0, 0]); - } - - #[test] - fn strong_reduce_test2() { - let mut num = U56x8::from_hex("ffffffffffffffffffffffffffffffffffffffffffffffffffffffff00000000000000000000000000000000000000000000000000000000").unwrap(); - P448GoldilocksPrimeField::strong_reduce(&mut num); - assert_eq!(num.limbs, [1, 0, 0, 0, 0, 0, 0, 0]); - } - - #[test] - fn representative_test() { - let num = U56x8::from_hex("ffffffffffffffffffffffffffffffffffffffffffffffffffffffff00000000000000000000000000000000000000000000000000000029").unwrap(); - let r = P448GoldilocksPrimeField::representative(&num); - assert_eq!(r, U448::from_u64(42)); - } - - #[test] - fn p448_add_test_1() { - let num1 = U56x8::from_hex("73c7941e36ee1e12b2105fb96634848d62def10bc1782576cfa7f54486820202847bbfb2e8f89ff7707f9913b8cf9b9efaf2029cfd6d3fa9").unwrap(); - let num2 = U56x8::from_hex("f3ef02193a11b6ea80be4bd2944d32c4674456a888b470b14e0cf223bed114bb28146d967f0d220cf20be2016dc84f51e5d5e29a71751f06").unwrap(); - let num3 = P448GoldilocksPrimeField::add(&num1, &num2); - assert_eq!(num3, U56x8::from_hex("67b6963770ffd4fd32ceab8bfa81b751ca2347b44a2c96281db4e769455316bdac902d496805c204628b7b152697eaf0e0c7e5376ee25eb0").unwrap()); - } - - #[test] - fn p448_sub_test_1() { - let num1 = U56x8::from_hex("22264a9d5272984a996cc5eef6bd165e63bc70f2050bbd5bc24343df9cc25f826cef7bff7466963a82cd59f36671c724c53b8b27330ea076").unwrap(); - let num2 = U56x8::from_hex("7a0063b5cd729df62c0e77071727639e06d0892eacb505569e8b47a99175d1d09a4bd7c22a2168c1fb9f3de31d9633d92341f84d000633b1").unwrap(); - let num3 = P448GoldilocksPrimeField::sub(&num1, &num2); - assert_eq!(num3, U56x8::from_hex("a825e6e784fffa546d5e4ee7df95b2c05cebe7c35856b80523b7fc350b4c8db1d2a3a43d4a452d78872e1c1048db934ba1f992da33086cc4").unwrap()); - } - - #[test] - fn p448_neg_test_1() { - let num1 = U56x8::from_hex("21183d1faa857cd3f08d54871837b06d70af4e6b85173c0ff02685147f38e8b9af3141baad0067f3514a527bd3e7405a953c3a8fa9a15bb3").unwrap(); - let num2 = P448GoldilocksPrimeField::neg(&num1); - assert_eq!(num2, U56x8::from_hex("dee7c2e0557a832c0f72ab78e7c84f928f50b1947ae8c3f00fd97aea80c7174650cebe4552ff980caeb5ad842c18bfa56ac3c570565ea44c").unwrap()); - } - - #[test] - fn p448_mul_test_1() { - let num1 = U56x8::from_hex("a").unwrap(); - let num2 = U56x8::from_hex("b").unwrap(); - let num3 = P448GoldilocksPrimeField::mul(&num1, &num2); - assert_eq!(num3, U56x8::from_hex("6e").unwrap()); - } - - #[test] - fn p448_mul_test_2() { - let num1 = U56x8::from_hex("b7aa542ac8824fbf654ee0ab4ea5eb3b0ad65b48bfef5e4d8b84ab5737e9283c06ecbadd799688cdf73cd7d077d53b5e6f738b264086d034").unwrap(); - let num2 = U56x8::from_hex("89a36d8b491f5a9af136a35061a59aa2c65353a3c99bb205a53c7ae2f37e6ae492f24248fc549344ba2f203c6d5b2b5dab216fdd1a7dcf87").unwrap(); - let num3 = P448GoldilocksPrimeField::mul(&num1, &num2); - assert_eq!(num3, U56x8::from_hex("f61c57f70d8a1eaf261907d08eb1086c2289f7bbb6ff6a0dfd016f91ac9eda658879b52a654a10b2ce123717fad3ab15b1e77ce643683886").unwrap()); - } - - #[test] - fn p448_pow_test_1() { - let num1 = U56x8::from_hex("6b1b1d952930ee34fb6ed3521f7653293fd7e01de2027673d3d5a0bf3dc0688530bec50b3dfca4df28cc432bec1198e17fde3e1cc79e5732").unwrap(); - let num2 = P448GoldilocksPrimeField::pow(&num1, 65537u64); - assert_eq!(num2, U56x8::from_hex("ec48eda1579a0879c01e8853e4a718ede9cd6bcf88d6696b47dc4dce7d2acdd1a37674aa455d84126800893975c95bb47c40b098a9e30836").unwrap()); - } - - #[test] - fn p448_inv_test_1() { - let num1 = U56x8::from_hex("b86e226f5ac29af28c74e272fc129ab167798f70dedd2ce76aa76204a23beb74c8ddba2a643196c62ee35a18472d6de7d82b6af4b2fc5e58").unwrap(); - let num2 = U56x8::from_hex("bb2bd89a1297c7a6052b41be503aa7de2cd6e6775396e76bf995f27f1dccf69131067824ded693bdd6e58fe7c2276fa92ec1d9a0048b9be6").unwrap(); - let num3 = P448GoldilocksPrimeField::div(&num1, &num2); - assert_eq!(num3.unwrap(), U56x8::from_hex("707b5cc75967b58ebd28d14d4ed7ed9eaae1187d0b359c7733cf61b1a5c87fc88228ca532c50f19d1ba57146ca2e38417922033f647c8d9").unwrap()); - } - - #[test] - fn p448_from_u64_test_1() { - let num = P448GoldilocksPrimeField::from_u64(2012613457133209520u64); - assert_eq!(num, U56x8::from_hex("1bee3d46a69887b0").unwrap()); - } - - #[test] - fn p448_from_base_type_test_1() { - let mut limbs = [0u64; 8]; - limbs[0] = 15372427657916355716u64; - limbs[1] = 6217911673150459564u64; - let num1 = U56x8 { limbs }; - let num2 = P448GoldilocksPrimeField::from_base_type(num1); - assert_eq!( - num2, - U56x8::from_hex("564a75b90ae34f8155d5821d7e9484").unwrap() - ); - } - - #[cfg(feature = "std")] - #[test] - fn to_hex_test() { - let mut limbs = [0u64; 8]; - limbs[0] = 15372427657916355716u64; - limbs[1] = 6217911673150459564u64; - let num = U56x8::from_hex("564A75B90AE34F8155D5821D7E9484").unwrap(); - assert_eq!(U56x8::to_hex(&num), "564A75B90AE34F8155D5821D7E9484") - } -} diff --git a/crates/math/src/field/fields/pallas_field.rs b/crates/math/src/field/fields/pallas_field.rs deleted file mode 100644 index aeee1e74e..000000000 --- a/crates/math/src/field/fields/pallas_field.rs +++ /dev/null @@ -1,17 +0,0 @@ -use crate::{ - field::fields::montgomery_backed_prime_fields::{IsModulus, MontgomeryBackendPrimeField}, - unsigned_integer::element::U256, -}; - -type PallasMontgomeryBackendPrimeField = MontgomeryBackendPrimeField; - -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct MontgomeryConfigPallas255PrimeField; -impl IsModulus for MontgomeryConfigPallas255PrimeField { - const MODULUS: U256 = U256::from_hex_unchecked( - "40000000000000000000000000000000224698fc094cf91b992d30ed00000001", - ); -} - -pub type Pallas255PrimeField = - PallasMontgomeryBackendPrimeField; diff --git a/crates/math/src/field/fields/secp256k1_field.rs b/crates/math/src/field/fields/secp256k1_field.rs deleted file mode 100644 index 7cff0e2eb..000000000 --- a/crates/math/src/field/fields/secp256k1_field.rs +++ /dev/null @@ -1,17 +0,0 @@ -use crate::{ - field::fields::montgomery_backed_prime_fields::{IsModulus, MontgomeryBackendPrimeField}, - unsigned_integer::element::U256, -}; - -type Secp256k1MontgomeryBackendPrimeField = MontgomeryBackendPrimeField; - -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct MontgomeryConfigSecp256k1PrimeField; -impl IsModulus for MontgomeryConfigSecp256k1PrimeField { - const MODULUS: U256 = U256::from_hex_unchecked( - "0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F", - ); -} - -pub type Secp256k1PrimeField = - Secp256k1MontgomeryBackendPrimeField; diff --git a/crates/math/src/field/fields/secp256k1_scalarfield.rs b/crates/math/src/field/fields/secp256k1_scalarfield.rs deleted file mode 100644 index d1c9bfb1b..000000000 --- a/crates/math/src/field/fields/secp256k1_scalarfield.rs +++ /dev/null @@ -1,17 +0,0 @@ -use crate::{ - field::fields::montgomery_backed_prime_fields::{IsModulus, MontgomeryBackendPrimeField}, - unsigned_integer::element::U256, -}; - -type Secp256k1MontgomeryBackendScalarField = MontgomeryBackendPrimeField; - -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct MontgomeryConfigSecp256k1ScalarField; -impl IsModulus for MontgomeryConfigSecp256k1ScalarField { - const MODULUS: U256 = U256::from_hex_unchecked( - "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141", - ); -} - -pub type Secp256k1ScalarField = - Secp256k1MontgomeryBackendScalarField; diff --git a/crates/math/src/field/fields/secp256r1_field.rs b/crates/math/src/field/fields/secp256r1_field.rs deleted file mode 100644 index 481e2ac2d..000000000 --- a/crates/math/src/field/fields/secp256r1_field.rs +++ /dev/null @@ -1,17 +0,0 @@ -use crate::{ - field::fields::montgomery_backed_prime_fields::{IsModulus, MontgomeryBackendPrimeField}, - unsigned_integer::element::U256, -}; - -type Secp256r1MontgomeryBackendPrimeField = MontgomeryBackendPrimeField; - -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct MontgomeryConfigSecp256r1PrimeField; -impl IsModulus for MontgomeryConfigSecp256r1PrimeField { - const MODULUS: U256 = U256::from_hex_unchecked( - "0xffffffff00000001000000000000000000000000ffffffffffffffffffffffff", - ); -} - -pub type Secp256r1PrimeField = - Secp256r1MontgomeryBackendPrimeField; diff --git a/crates/math/src/field/fields/u32_montgomery_backend_prime_field.rs b/crates/math/src/field/fields/u32_montgomery_backend_prime_field.rs deleted file mode 100644 index 8dcab5004..000000000 --- a/crates/math/src/field/fields/u32_montgomery_backend_prime_field.rs +++ /dev/null @@ -1,310 +0,0 @@ -use crate::errors::CreationError; -use crate::field::element::FieldElement; -use crate::field::errors::FieldError; -use crate::field::traits::IsField; -use crate::field::traits::IsPrimeField; -#[cfg(feature = "alloc")] -use crate::traits::AsBytes; -use crate::traits::ByteConversion; - -use core::fmt::Debug; -#[cfg_attr( - any( - feature = "lambdaworks-serde-binary", - feature = "lambdaworks-serde-string" - ), - derive(serde::Serialize, serde::Deserialize) -)] -#[derive(Clone, Debug, Hash, Copy)] -pub struct U32MontgomeryBackendPrimeField; - -impl U32MontgomeryBackendPrimeField { - pub const R2: u32 = match Self::compute_r2_parameter() { - Ok(value) => value, - Err(_) => panic!("Failed to compute R2 parameter"), - }; - pub const MU: u32 = match Self::compute_mu_parameter() { - Ok(value) => value, - Err(_) => panic!("Failed to compute MU parameter"), - }; - pub const ZERO: u32 = 0; - pub const ONE: u32 = MontgomeryAlgorithms::mul(&1, &Self::R2, &MODULUS, &Self::MU); - - // Compute `modulus^{-1} mod 2^{32}`. - // Algorithm adapted from `compute_mu_parameter()` from `montgomery_backed_prime_fields.rs` in Lambdaworks. - // E.g, in Baby Bear field MU = 2281701377. - const fn compute_mu_parameter() -> Result { - let mut y = 1; - let word_size = 32; - let mut i: usize = 2; - while i <= word_size { - let mul_result = (MODULUS as u64 * y as u64) as u32; - if (mul_result << (word_size - i)) >> (word_size - i) != 1 { - let (shifted, overflowed) = 1u32.overflowing_shl((i - 1) as u32); - if overflowed { - return Err("Overflow occurred while computing mu parameter"); - } - y += shifted; - } - i += 1; - } - Ok(y) - } - - // Compute `2^{2 * 32} mod modulus`. - // Algorithm adapted from `compute_r2_parameter()` from `montgomery_backed_prime_fields.rs` in Lambdaworks. - // E.g, in Baby Bear field R2 = 1172168163. - const fn compute_r2_parameter() -> Result { - let word_size = 32; - let mut l: usize = 0; - - // Find the largest power of 2 smaller than modulus - while l < word_size && (MODULUS >> l) == 0 { - l += 1; - } - let (initial_shifted, overflowed) = 1u32.overflowing_shl(l as u32); - if overflowed { - return Err("Overflow occurred during initial shift in compute_r2_parameter"); - } - let mut c: u32 = initial_shifted; - - // Double c and reduce modulo `MODULUS` until getting - // `2^{2 * word_size}` mod `MODULUS`. - let mut i: usize = 1; - while i <= 2 * word_size - l { - let (double_c, overflowed) = c.overflowing_shl(1); - if overflowed { - return Err("Overflow occurred while doubling in compute_r2_parameter"); - } - c = if double_c >= MODULUS { - double_c - MODULUS - } else { - double_c - }; - i += 1; - } - Ok(c) - } -} - -impl IsField for U32MontgomeryBackendPrimeField { - type BaseType = u32; - - #[inline(always)] - fn add(a: &Self::BaseType, b: &Self::BaseType) -> Self::BaseType { - let mut sum = a + b; - let (corr_sum, over) = sum.overflowing_sub(MODULUS); - if !over { - sum = corr_sum; - } - sum - } - - #[inline(always)] - fn mul(a: &Self::BaseType, b: &Self::BaseType) -> Self::BaseType { - MontgomeryAlgorithms::mul(a, b, &MODULUS, &Self::MU) - } - - #[inline(always)] - fn sub(a: &Self::BaseType, b: &Self::BaseType) -> Self::BaseType { - if b <= a { - a - b - } else { - MODULUS - (b - a) - } - } - - #[inline(always)] - fn neg(a: &Self::BaseType) -> Self::BaseType { - if a == &Self::ZERO { - *a - } else { - MODULUS - a - } - } - - /// Computes multiplicative inverse using Fermat's Little Theorem - /// It states that for any non-zero element a in field F_p: a^(p-1) ≡ 1 (mod p) - /// Therefore: a^(p-2) * a ≡ 1 (mod p), so a^(p-2) is the multiplicative inverse - /// Implementation inspired by Plonky3's work. - /// - #[inline(always)] - fn inv(a: &Self::BaseType) -> Result { - if *a == Self::ZERO { - return Err(FieldError::InvZeroError); - } - let p100000000 = MontgomeryAlgorithms::exp_power_of_2(a, 8, &MODULUS, &Self::MU); - let p100000001 = Self::mul(&p100000000, a); - let p10000000000000000 = - MontgomeryAlgorithms::exp_power_of_2(&p100000000, 8, &MODULUS, &Self::MU); - let p10000000100000001 = Self::mul(&p10000000000000000, &p100000001); - let p10000000100000001000 = - MontgomeryAlgorithms::exp_power_of_2(&p10000000100000001, 3, &MODULUS, &Self::MU); - let p1000000010000000100000000 = - MontgomeryAlgorithms::exp_power_of_2(&p10000000100000001000, 5, &MODULUS, &Self::MU); - let p1000000010000000100000001 = Self::mul(&p1000000010000000100000000, a); - let p1000010010000100100001001 = - Self::mul(&p1000000010000000100000001, &p10000000100000001000); - let p10000000100000001000000010 = Self::square(&p1000000010000000100000001); - - let p11000010110000101100001011 = - Self::mul(&p10000000100000001000000010, &p1000010010000100100001001); - let p100000001000000010000000100 = Self::square(&p10000000100000001000000010); - let p111000011110000111100001111 = - Self::mul(&p100000001000000010000000100, &p11000010110000101100001011); - let p1110000111100001111000011110000 = MontgomeryAlgorithms::exp_power_of_2( - &p111000011110000111100001111, - 4, - &MODULUS, - &Self::MU, - ); - let p1110111111111111111111111111111 = Self::mul( - &p1110000111100001111000011110000, - &p111000011110000111100001111, - ); - Ok(p1110111111111111111111111111111) - } - - #[inline(always)] - fn div(a: &Self::BaseType, b: &Self::BaseType) -> Result { - let b_inv = &Self::inv(b)?; - Ok(Self::mul(a, b_inv)) - } - - #[inline(always)] - fn eq(a: &Self::BaseType, b: &Self::BaseType) -> bool { - a == b - } - - #[inline(always)] - fn zero() -> Self::BaseType { - Self::ZERO - } - - #[inline(always)] - fn one() -> Self::BaseType { - Self::ONE - } - - #[inline(always)] - fn from_u64(x: u64) -> Self::BaseType { - let x_u32 = x as u32; - MontgomeryAlgorithms::mul(&x_u32, &Self::R2, &MODULUS, &Self::MU) - } - - #[inline(always)] - fn from_base_type(x: Self::BaseType) -> Self::BaseType { - MontgomeryAlgorithms::mul(&x, &Self::R2, &MODULUS, &Self::MU) - } -} - -impl IsPrimeField for U32MontgomeryBackendPrimeField { - type RepresentativeType = Self::BaseType; - - fn representative(x: &Self::BaseType) -> Self::RepresentativeType { - MontgomeryAlgorithms::mul(x, &1u32, &MODULUS, &Self::MU) - } - - fn field_bit_size() -> usize { - 32 - (MODULUS - 1).leading_zeros() as usize - } - - fn from_hex(hex_string: &str) -> Result { - let hex = hex_string.strip_prefix("0x").unwrap_or(hex_string); - - u64::from_str_radix(hex, 16) - .map_err(|_| CreationError::InvalidHexString) - .map(|value| (value % MODULUS as u64) as u32) - } - - #[cfg(feature = "std")] - fn to_hex(x: &Self::BaseType) -> String { - format!("{x:x}") - } -} - -impl FieldElement> {} - -impl ByteConversion for FieldElement> { - #[cfg(feature = "alloc")] - fn to_bytes_be(&self) -> alloc::vec::Vec { - MontgomeryAlgorithms::mul( - self.value(), - &1, - &MODULUS, - &U32MontgomeryBackendPrimeField::::MU, - ) - .to_be_bytes() - .to_vec() - } - - #[cfg(feature = "alloc")] - fn to_bytes_le(&self) -> alloc::vec::Vec { - MontgomeryAlgorithms::mul( - self.value(), - &1u32, - &MODULUS, - &U32MontgomeryBackendPrimeField::::MU, - ) - .to_le_bytes() - .to_vec() - } - - fn from_bytes_be(bytes: &[u8]) -> Result { - let value = u32::from_be_bytes(bytes.try_into().unwrap()); - Ok(Self::new(value)) - } - - fn from_bytes_le(bytes: &[u8]) -> Result { - let value = u32::from_le_bytes(bytes.try_into().unwrap()); - Ok(Self::new(value)) - } -} - -#[cfg(feature = "alloc")] -impl AsBytes for FieldElement> { - fn as_bytes(&self) -> alloc::vec::Vec { - self.value().to_be_bytes().to_vec() - } -} - -#[cfg(feature = "alloc")] -impl From>> - for alloc::vec::Vec -{ - fn from(value: FieldElement>) -> alloc::vec::Vec { - value.value().to_be_bytes().to_vec() - } -} - -pub struct MontgomeryAlgorithms; -impl MontgomeryAlgorithms { - /// Montgomery reduction based on Plonky3's implementation. - /// It converts a value from Montgomery domain using reductions mod p. - #[inline(always)] - const fn montgomery_reduction(x: u64, mu: &u32, q: &u32) -> u32 { - let t = x.wrapping_mul(*mu as u64) & (u32::MAX as u64); - let u = t * (*q as u64); - let (x_sub_u, over) = x.overflowing_sub(u); - let x_sub_u_bytes = x_sub_u.to_be_bytes(); - // We take the four most significant bytes of `x_sub_u` and convert them into an u32. - let x_sub_u_hi = u32::from_be_bytes([ - x_sub_u_bytes[0], - x_sub_u_bytes[1], - x_sub_u_bytes[2], - x_sub_u_bytes[3], - ]); - let corr = if over { q } else { &0 }; - x_sub_u_hi.wrapping_add(*corr) - } - - #[inline(always)] - pub const fn mul(a: &u32, b: &u32, q: &u32, mu: &u32) -> u32 { - let x = (*a as u64) * (*b as u64); - Self::montgomery_reduction(x, mu, q) - } - - pub fn exp_power_of_2(a: &u32, power_log: usize, q: &u32, mu: &u32) -> u32 { - (0..power_log).fold(*a, |res, _| Self::mul(&res, &res, q, mu)) - } -} diff --git a/crates/math/src/field/fields/u64_goldilocks_field.rs b/crates/math/src/field/fields/u64_goldilocks_field.rs deleted file mode 100644 index ecdca841d..000000000 --- a/crates/math/src/field/fields/u64_goldilocks_field.rs +++ /dev/null @@ -1,492 +0,0 @@ -use core::fmt::{self, Display}; - -use crate::traits::ByteConversion; -use crate::{ - errors::CreationError, - field::{ - element::FieldElement, - errors::FieldError, - extensions::quadratic::{HasQuadraticNonResidue, QuadraticExtensionField}, - traits::{IsField, IsPrimeField}, - }, -}; - -/// Goldilocks Prime Field F_p where p = 2^64 - 2^32 + 1; -#[derive(Debug, Clone, Copy, Hash, PartialOrd, Ord, PartialEq, Eq)] -pub struct Goldilocks64Field; - -impl Goldilocks64Field { - pub const ORDER: u64 = 0xFFFF_FFFF_0000_0001; - // Two's complement of `ORDER` i.e. `2^64 - ORDER = 2^32 - 1` - pub const NEG_ORDER: u64 = Self::ORDER.wrapping_neg(); -} - -impl ByteConversion for u64 { - #[cfg(feature = "alloc")] - fn to_bytes_be(&self) -> alloc::vec::Vec { - unimplemented!() - } - - #[cfg(feature = "alloc")] - fn to_bytes_le(&self) -> alloc::vec::Vec { - unimplemented!() - } - - fn from_bytes_be(_bytes: &[u8]) -> Result - where - Self: Sized, - { - unimplemented!() - } - - fn from_bytes_le(_bytes: &[u8]) -> Result - where - Self: Sized, - { - unimplemented!() - } -} - -//NOTE: This implementation was inspired by and borrows from the work done by the Plonky3 team -//https://github.com/Plonky3/Plonky3/blob/main/goldilocks/src/lib.rs -// Thank you for pushing this technology forward. -impl IsField for Goldilocks64Field { - type BaseType = u64; - - fn add(a: &u64, b: &u64) -> u64 { - let (sum, over) = a.overflowing_add(*b); - let (mut sum, over) = sum.overflowing_add(u64::from(over) * Self::NEG_ORDER); - if over { - sum += Self::NEG_ORDER - } - Self::representative(&sum) - } - - fn mul(a: &u64, b: &u64) -> u64 { - Self::representative(&reduce_128(u128::from(*a) * u128::from(*b))) - } - - fn sub(a: &u64, b: &u64) -> u64 { - let (diff, under) = a.overflowing_sub(*b); - let (mut diff, under) = diff.overflowing_sub(u64::from(under) * Self::NEG_ORDER); - if under { - diff -= Self::NEG_ORDER; - } - Self::representative(&diff) - } - - fn neg(a: &u64) -> u64 { - Self::sub(&Self::ORDER, &Self::representative(a)) - } - - /// Returns the multiplicative inverse of `a`. - fn inv(a: &u64) -> Result { - if *a == Self::zero() || *a == Self::ORDER { - return Err(FieldError::InvZeroError); - } - - // a^11 - let t2 = Self::mul(&Self::square(a), a); - - // a^111 - let t3 = Self::mul(&Self::square(&t2), a); - - // compute base^111111 (6 ones) by repeatedly squaring t3 3 times and multiplying by t3 - let t6 = exp_acc::<3>(&t3, &t3); - let t60 = Self::square(&t6); - let t7 = Self::mul(&t60, a); - - // compute base^111111111111 (12 ones) - // repeatedly square t6 6 times and multiply by t6 - let t12 = exp_acc::<5>(&t60, &t6); - - // compute base^111111111111111111111111 (24 ones) - // repeatedly square t12 12 times and multiply by t12 - let t24 = exp_acc::<12>(&t12, &t12); - - // compute base^1111111111111111111111111111111 (31 ones) - // repeatedly square t24 6 times and multiply by t6 first. then square t30 and multiply by base - let t31 = exp_acc::<7>(&t24, &t7); - - // compute base^111111111111111111111111111111101111111111111111111111111111111 - // repeatedly square t31 32 times and multiply by t31 - let t63 = exp_acc::<32>(&t31, &t31); - - Ok(Self::mul(&Self::square(&t63), a)) - } - - /// Returns the division of `a` and `b`. - fn div(a: &u64, b: &u64) -> Result { - let b_inv = &Self::inv(b)?; - Ok(Self::mul(a, b_inv)) - } - - /// Returns a boolean indicating whether `a` and `b` are equal or not. - fn eq(a: &u64, b: &u64) -> bool { - Self::representative(a) == Self::representative(b) - } - - /// Returns the additive neutral element. - fn zero() -> u64 { - 0u64 - } - - /// Returns the multiplicative neutral element. - fn one() -> u64 { - 1u64 - } - - /// Returns the element `x * 1` where 1 is the multiplicative neutral element. - fn from_u64(x: u64) -> u64 { - Self::representative(&x) - } - - /// Takes as input an element of BaseType and returns the internal representation - /// of that element in the field. - fn from_base_type(x: u64) -> u64 { - Self::representative(&x) - } -} - -impl IsPrimeField for Goldilocks64Field { - type RepresentativeType = u64; - - fn representative(x: &u64) -> u64 { - let mut u = *x; - if u >= Self::ORDER { - u -= Self::ORDER; - } - u - } - - fn field_bit_size() -> usize { - ((self::Goldilocks64Field::ORDER - 1).ilog2() + 1) as usize - } - - fn from_hex(hex_string: &str) -> Result { - let mut hex_string = hex_string; - // Remove 0x if it's on the string - let mut char_iterator = hex_string.chars(); - if hex_string.len() > 2 - && char_iterator.next().unwrap() == '0' - && char_iterator.next().unwrap() == 'x' - { - hex_string = &hex_string[2..]; - } - u64::from_str_radix(hex_string, 16).map_err(|_| CreationError::InvalidHexString) - } - - #[cfg(feature = "std")] - fn to_hex(x: &u64) -> String { - format!("{x:X}") - } -} - -#[inline(always)] -fn reduce_128(x: u128) -> u64 { - //possibly split apart into separate function to ensure inline - let (x_lo, x_hi) = (x as u64, (x >> 64) as u64); - let x_hi_hi = x_hi >> 32; - let x_hi_lo = x_hi & Goldilocks64Field::NEG_ORDER; - - let (mut t0, borrow) = x_lo.overflowing_sub(x_hi_hi); - if borrow { - t0 -= Goldilocks64Field::NEG_ORDER // Cannot underflow - } - - let t1 = x_hi_lo * Goldilocks64Field::NEG_ORDER; - let (res_wrapped, carry) = t0.overflowing_add(t1); - // Below cannot overflow unless the assumption if x + y < 2**64 + ORDER is incorrect. - res_wrapped + Goldilocks64Field::NEG_ORDER * u64::from(carry) -} - -#[inline(always)] -fn exp_acc(base: &u64, tail: &u64) -> u64 { - Goldilocks64Field::mul(&exp_power_of_2::(base), tail) -} - -#[must_use] -fn exp_power_of_2(base: &u64) -> u64 { - let mut res = *base; - for _ in 0..POWER_LOG { - res = Goldilocks64Field::square(&res); - } - res -} - -pub type Goldilocks64ExtensionField = QuadraticExtensionField; - -impl HasQuadraticNonResidue for Goldilocks64Field { - // Verifiable in Sage with - // `R. = GF(p)[]; assert (x^2 - 7).is_irreducible()` - fn residue() -> FieldElement { - FieldElement::from(Goldilocks64Field::from_u64(7u64)) - } -} - -impl Display for FieldElement { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{:x}", self.representative())?; - Ok(()) - } -} - -#[cfg(test)] -mod tests { - use super::*; - type F = Goldilocks64Field; - - // Over the Goldilocks field, the following set of equations hold - // p = 0 - // 2^64 - 2^32 + 1 = 0 - // 2^64 = 2^32 - 1 - #[test] - fn from_hex_for_b_is_11() { - assert_eq!(F::from_hex("B").unwrap(), 11); - } - - #[test] - fn from_hex_for_0x1_a_is_26() { - assert_eq!(F::from_hex("0x1a").unwrap(), 26); - } - - #[test] - fn bit_size_of_field_is_64() { - assert_eq!( - ::field_bit_size(), - 64 - ); - } - - #[test] - fn one_plus_one_is_two() { - let a = F::one(); - let b = F::one(); - let c = F::add(&a, &b); - assert_eq!(c, 2u64); - } - - #[test] - fn neg_one_plus_one_is_zero() { - let a = F::neg(&F::one()); - let b = F::one(); - let c = F::add(&a, &b); - assert_eq!(c, F::zero()); - } - - #[test] - fn neg_one_plus_two_is_one() { - let a = F::neg(&F::one()); - let b = F::from_base_type(2u64); - let c = F::add(&a, &b); - assert_eq!(c, F::one()); - } - - #[test] - fn max_order_plus_one_is_zero() { - let a = F::from_base_type(F::ORDER - 1); - let b = F::one(); - let c = F::add(&a, &b); - assert_eq!(c, F::zero()); - } - - #[test] - fn comparing_13_and_13_are_equal() { - let a = F::from_base_type(13); - let b = F::from_base_type(13); - assert_eq!(a, b); - } - - #[test] - fn comparing_13_and_8_they_are_not_equal() { - let a = F::from_base_type(13); - let b = F::from_base_type(8); - assert_ne!(a, b); - } - - #[test] - fn one_sub_one_is_zero() { - let a = F::one(); - let b = F::one(); - let c = F::sub(&a, &b); - assert_eq!(c, F::zero()); - } - - #[test] - fn zero_sub_one_is_order_minus_1() { - let a = F::zero(); - let b = F::one(); - let c = F::sub(&a, &b); - assert_eq!(c, F::ORDER - 1); - } - - #[test] - fn neg_one_sub_neg_one_is_zero() { - let a = F::neg(&F::one()); - let b = F::neg(&F::one()); - let c = F::sub(&a, &b); - assert_eq!(c, F::zero()); - } - - #[test] - fn neg_one_sub_one_is_neg_one() { - let a = F::neg(&F::one()); - let b = F::zero(); - let c = F::sub(&a, &b); - assert_eq!(c, F::neg(&F::one())); - } - - #[test] - fn mul_neutral_element() { - let a = F::from_base_type(1); - let b = F::from_base_type(2); - let c = F::mul(&a, &b); - assert_eq!(c, F::from_base_type(2)); - } - - #[test] - fn mul_two_three_is_six() { - let a = F::from_base_type(2); - let b = F::from_base_type(3); - assert_eq!(a * b, F::from_base_type(6)); - } - - #[test] - fn mul_order_neg_one() { - let a = F::from_base_type(F::ORDER - 1); - let b = F::from_base_type(F::ORDER - 1); - let c = F::mul(&a, &b); - assert_eq!(c, F::from_base_type(1)); - } - - #[test] - fn pow_p_neg_one() { - assert_eq!(F::pow(&F::from_base_type(2), F::ORDER - 1), F::one()) - } - - #[test] - fn inv_zero_error() { - let result = F::inv(&F::zero()); - assert!(matches!(result, Err(FieldError::InvZeroError))); - } - - #[test] - fn inv_two() { - let result = F::inv(&F::from_base_type(2u64)).unwrap(); - // sage: 1 / F(2) = 9223372034707292161 - assert_eq!(result, 9223372034707292161); - } - - #[test] - fn pow_two_three() { - assert_eq!(F::pow(&F::from_base_type(2), 3_u64), 8) - } - - #[test] - fn div_one() { - assert_eq!( - F::div(&F::from_base_type(2), &F::from_base_type(1)).unwrap(), - 2 - ) - } - - #[test] - fn div_4_2() { - assert_eq!( - F::div(&F::from_base_type(4), &F::from_base_type(2)).unwrap(), - 2 - ) - } - - // 1431655766 - #[test] - fn div_4_3() { - // sage: F(4) / F(3) = 12297829379609722882 - assert_eq!( - F::div(&F::from_base_type(4), &F::from_base_type(3)).unwrap(), - 12297829379609722882 - ) - } - - #[test] - fn two_plus_its_additive_inv_is_0() { - let two = F::from_base_type(2); - - assert_eq!(F::add(&two, &F::neg(&two)), F::zero()) - } - - #[test] - fn from_u64_test() { - let num = F::from_u64(1u64); - assert_eq!(num, F::one()); - } - - #[test] - fn from_u64_zero_test() { - let num = F::from_u64(0); - assert_eq!(num, F::zero()); - } - - #[test] - fn from_u64_max_test() { - let num = F::from_u64(u64::MAX); - assert_eq!(num, u32::MAX as u64 - 1); - } - - #[test] - fn from_u64_order_test() { - let num = F::from_u64(F::ORDER); - assert_eq!(num, F::zero()); - } - - #[test] - fn creating_a_field_element_from_its_representative_returns_the_same_element_1() { - let change = 1; - let f1 = F::from_base_type(F::ORDER + change); - let f2 = F::from_base_type(F::representative(&f1)); - assert_eq!(f1, f2); - } - - #[test] - fn reduct_128() { - let x = u128::MAX; - let y = reduce_128(x); - // The following equalitiy sequence holds, modulo p = 2^64 - 2^32 + 1 - // 2^128 - 1 = (2^64 - 1) * (2^64 + 1) - // = (2^32 - 1 - 1) * (2^32 - 1 + 1) - // = (2^32 - 2) * (2^32) - // = 2^64 - 2 * 2^32 - // = 2^64 - 2^33 - // = 2^32 - 1 - 2^33 - // = - 2^32 - 1 - let expected_result = F::neg(&F::add(&F::from_base_type(2_u64.pow(32)), &F::one())); - assert_eq!(y, expected_result); - } - - #[test] - fn u64_max_as_representative_less_than_u32_max_sub_1() { - let f = F::from_base_type(u64::MAX); - assert_eq!(F::representative(&f), u32::MAX as u64 - 1) - } - - #[test] - fn creating_a_field_element_from_its_representative_returns_the_same_element_2() { - let change = 8; - let f1 = F::from_base_type(F::ORDER + change); - let f2 = F::from_base_type(F::representative(&f1)); - assert_eq!(f1, f2); - } - - #[test] - fn from_base_type_test() { - let b = F::from_base_type(1u64); - assert_eq!(b, F::one()); - } - - #[cfg(feature = "std")] - #[test] - fn to_hex_test() { - let num = F::from_hex("B").unwrap(); - assert_eq!(F::to_hex(&num), "B"); - } -} diff --git a/crates/math/src/field/fields/u64_prime_field.rs b/crates/math/src/field/fields/u64_prime_field.rs deleted file mode 100644 index 68e4425db..000000000 --- a/crates/math/src/field/fields/u64_prime_field.rs +++ /dev/null @@ -1,401 +0,0 @@ -use crate::cyclic_group::IsGroup; -use crate::errors::ByteConversionError::{FromBEBytesError, FromLEBytesError}; -use crate::errors::CreationError; -use crate::errors::DeserializationError; -use crate::field::element::FieldElement; -use crate::field::errors::FieldError; -use crate::field::traits::{HasDefaultTranscript, IsFFTField, IsField, IsPrimeField}; -use crate::traits::{ByteConversion, Deserializable}; - -/// Type representing prime fields over unsigned 64-bit integers. -#[derive(Debug, Clone, PartialEq, Eq, Hash)] -pub struct U64PrimeField; -pub type U64FieldElement = FieldElement>; - -pub type F17 = U64PrimeField<17>; -pub type FE17 = U64FieldElement<17>; - -impl IsFFTField for F17 { - const TWO_ADICITY: u64 = 4; - const TWO_ADIC_PRIMITVE_ROOT_OF_UNITY: u64 = 3; -} - -impl IsField for U64PrimeField { - type BaseType = u64; - - fn add(a: &u64, b: &u64) -> u64 { - ((*a as u128 + *b as u128) % MODULUS as u128) as u64 - } - - fn sub(a: &u64, b: &u64) -> u64 { - (((*a as u128 + MODULUS as u128) - *b as u128) % MODULUS as u128) as u64 - } - - fn neg(a: &u64) -> u64 { - MODULUS - a - } - - fn mul(a: &u64, b: &u64) -> u64 { - ((*a as u128 * *b as u128) % MODULUS as u128) as u64 - } - - fn div(a: &u64, b: &u64) -> Result { - let b_inv = &Self::inv(b)?; - Ok(Self::mul(a, b_inv)) - } - - fn inv(a: &u64) -> Result { - if *a == 0 { - return Err(FieldError::InvZeroError); - } - Ok(Self::pow(a, MODULUS - 2)) - } - - fn eq(a: &u64, b: &u64) -> bool { - Self::from_u64(*a) == Self::from_u64(*b) - } - - fn zero() -> u64 { - 0 - } - - fn one() -> u64 { - 1 - } - - fn from_u64(x: u64) -> u64 { - x % MODULUS - } - - fn from_base_type(x: u64) -> u64 { - Self::from_u64(x) - } -} - -impl Copy for U64FieldElement {} - -impl IsPrimeField for U64PrimeField { - type RepresentativeType = u64; - - fn representative(x: &u64) -> u64 { - *x - } - - /// Returns how many bits do you need to represent the biggest field element - /// It expects the MODULUS to be a Prime - fn field_bit_size() -> usize { - ((MODULUS - 1).ilog2() + 1) as usize - } - - fn from_hex(hex_string: &str) -> Result { - let mut hex_string = hex_string; - // Remove 0x if it's on the string - let mut char_iterator = hex_string.chars(); - if hex_string.len() > 2 - && char_iterator.next().unwrap() == '0' - && char_iterator.next().unwrap() == 'x' - { - hex_string = &hex_string[2..]; - } - - u64::from_str_radix(hex_string, 16).map_err(|_| CreationError::InvalidHexString) - } - - #[cfg(feature = "std")] - fn to_hex(x: &u64) -> String { - format!("{x:X}") - } -} - -/// Represents an element in Fp. (E.g: 0, 1, 2 are the elements of F3) -impl IsGroup for U64FieldElement { - fn neutral_element() -> U64FieldElement { - U64FieldElement::zero() - } - - fn operate_with(&self, other: &Self) -> Self { - *self + *other - } - - fn neg(&self) -> Self { - -self - } -} - -impl ByteConversion for U64FieldElement { - #[cfg(feature = "alloc")] - fn to_bytes_be(&self) -> alloc::vec::Vec { - u64::to_be_bytes(*self.value()).into() - } - - #[cfg(feature = "alloc")] - fn to_bytes_le(&self) -> alloc::vec::Vec { - u64::to_le_bytes(*self.value()).into() - } - - fn from_bytes_be(bytes: &[u8]) -> Result { - let bytes: [u8; 8] = bytes[0..8].try_into().map_err(|_| FromBEBytesError)?; - Ok(Self::from(u64::from_be_bytes(bytes))) - } - - fn from_bytes_le(bytes: &[u8]) -> Result { - let bytes: [u8; 8] = bytes[0..8].try_into().map_err(|_| FromLEBytesError)?; - Ok(Self::from(u64::from_le_bytes(bytes))) - } -} - -impl Deserializable for FieldElement> { - fn deserialize(bytes: &[u8]) -> Result - where - Self: Sized, - { - Self::from_bytes_be(bytes).map_err(|x| x.into()) - } -} - -impl HasDefaultTranscript for U64PrimeField { - fn get_random_field_element_from_rng(rng: &mut impl rand::Rng) -> FieldElement { - let mask = u64::MAX >> MODULUS.leading_zeros(); - let mut sample = [0u8; 8]; - let field; - loop { - rng.fill(&mut sample); - let int_sample = u64::from_be_bytes(sample) & mask; - if int_sample < MODULUS { - field = FieldElement::from(int_sample); - break; - } - } - field - } -} - -#[cfg(test)] -mod tests { - use super::*; - const MODULUS: u64 = 13; - type F = U64PrimeField; - type FE = FieldElement; - - #[test] - fn from_hex_for_b_is_11() { - assert_eq!(F::from_hex("B").unwrap(), 11); - } - - #[test] - fn from_hex_for_0x1_a_is_26() { - assert_eq!(F::from_hex("0x1a").unwrap(), 26); - } - - #[cfg(feature = "std")] - #[test] - fn to_hex_test_works_1() { - let num = F::from_hex("B").unwrap(); - assert_eq!(F::to_hex(&num), "B"); - } - - #[cfg(feature = "std")] - #[test] - fn to_hex_test_works_2() { - let num = F::from_hex("0x1a").unwrap(); - assert_eq!(F::to_hex(&num), "1A"); - } - - #[test] - fn bit_size_of_mod_13_field_is_4() { - assert_eq!( - as crate::field::traits::IsPrimeField>::field_bit_size(), - 4 - ); - } - - #[test] - fn bit_size_of_big_mod_field_is_64() { - const MODULUS: u64 = 10000000000000000000; - assert_eq!( - as crate::field::traits::IsPrimeField>::field_bit_size(), - 64 - ); - } - - #[test] - fn bit_size_of_63_bit_mod_field_is_63() { - const MODULUS: u64 = 9000000000000000000; - assert_eq!( - as crate::field::traits::IsPrimeField>::field_bit_size(), - 63 - ); - } - - #[test] - fn two_plus_one_is_three() { - assert_eq!(FE::new(2) + FE::new(1), FE::new(3)); - } - - #[test] - fn max_order_plus_1_is_0() { - assert_eq!(FE::new(MODULUS - 1) + FE::new(1), FE::new(0)); - } - - #[test] - fn when_comparing_13_and_13_they_are_equal() { - let a: FE = FE::new(13); - let b: FE = FE::new(13); - assert_eq!(a, b); - } - - #[test] - fn when_comparing_13_and_8_they_are_different() { - let a: FE = FE::new(13); - let b: FE = FE::new(8); - assert_ne!(a, b); - } - - #[test] - fn mul_neutral_element() { - let a: FE = FE::new(1); - let b: FE = FE::new(2); - assert_eq!(a * b, FE::new(2)); - } - - #[test] - fn mul_2_3_is_6() { - let a: FE = FE::new(2); - let b: FE = FE::new(3); - assert_eq!(a * b, FE::new(6)); - } - - #[test] - fn mul_order_minus_1() { - let a: FE = FE::new(MODULUS - 1); - let b: FE = FE::new(MODULUS - 1); - assert_eq!(a * b, FE::new(1)); - } - - #[test] - fn inv_0_error() { - let result = FE::new(0).inv(); - assert!(matches!(result, Err(FieldError::InvZeroError))); - } - - #[test] - fn inv_2() { - let a: FE = FE::new(2); - assert_eq!(a * a.inv().unwrap(), FE::new(1)); - } - - #[test] - fn pow_2_3() { - assert_eq!(FE::new(2).pow(3_u64), FE::new(8)) - } - - #[test] - fn pow_p_minus_1() { - assert_eq!(FE::new(2).pow(MODULUS - 1), FE::new(1)) - } - - #[test] - fn div_1() { - assert_eq!(FE::new(2) * FE::new(1).inv().unwrap(), FE::new(2)) - } - - #[test] - fn div_4_2() { - assert_eq!(FE::new(4) * FE::new(2).inv().unwrap(), FE::new(2)) - } - - #[test] - fn div_4_3() { - assert_eq!( - FE::new(4) * FE::new(3).inv().unwrap() * FE::new(3), - FE::new(4) - ) - } - - #[test] - fn two_plus_its_additive_inv_is_0() { - let two = FE::new(2); - - assert_eq!(two + (-two), FE::new(0)) - } - - #[test] - fn four_minus_three_is_1() { - let four = FE::new(4); - let three = FE::new(3); - - assert_eq!(four - three, FE::new(1)) - } - - #[test] - fn zero_minus_1_is_order_minus_1() { - let zero = FE::new(0); - let one = FE::new(1); - - assert_eq!(zero - one, FE::new(MODULUS - 1)) - } - - #[test] - fn neg_zero_is_zero() { - let zero = FE::new(0); - - assert_eq!(-zero, zero); - } - - #[test] - fn zero_constructor_returns_zero() { - assert_eq!(FE::new(0), FE::new(0)); - } - - #[test] - fn field_element_as_group_element_multiplication_by_scalar_works_as_multiplication_in_finite_fields( - ) { - let a = FE::new(3); - let b = FE::new(12); - assert_eq!(a * b, a.operate_with_self(12_u16)); - } - - #[test] - #[cfg(feature = "alloc")] - fn to_bytes_from_bytes_be_is_the_identity() { - let x = FE::new(12345); - assert_eq!(FE::from_bytes_be(&x.to_bytes_be()).unwrap(), x); - } - - #[test] - #[cfg(feature = "alloc")] - fn from_bytes_to_bytes_be_is_the_identity_for_one() { - let bytes = [0, 0, 0, 0, 0, 0, 0, 1]; - assert_eq!(FE::from_bytes_be(&bytes).unwrap().to_bytes_be(), bytes); - } - - #[test] - #[cfg(feature = "alloc")] - fn to_bytes_from_bytes_le_is_the_identity() { - let x = FE::new(12345); - assert_eq!(FE::from_bytes_le(&x.to_bytes_le()).unwrap(), x); - } - - #[test] - #[cfg(feature = "alloc")] - fn from_bytes_to_bytes_le_is_the_identity_for_one() { - let bytes = [1, 0, 0, 0, 0, 0, 0, 0]; - assert_eq!(FE::from_bytes_le(&bytes).unwrap().to_bytes_le(), bytes); - } - - #[test] - fn creating_a_field_element_from_its_representative_returns_the_same_element_1() { - let change = 1; - let f1 = FE::new(MODULUS + change); - let f2 = FE::new(f1.representative()); - assert_eq!(f1, f2); - } - - #[test] - fn creating_a_field_element_from_its_representative_returns_the_same_element_2() { - let change = 8; - let f1 = FE::new(MODULUS + change); - let f2 = FE::new(f1.representative()); - assert_eq!(f1, f2); - } -} diff --git a/crates/math/src/field/fields/vesta_field.rs b/crates/math/src/field/fields/vesta_field.rs deleted file mode 100644 index c880bceba..000000000 --- a/crates/math/src/field/fields/vesta_field.rs +++ /dev/null @@ -1,16 +0,0 @@ -use crate::{ - field::fields::montgomery_backed_prime_fields::{IsModulus, MontgomeryBackendPrimeField}, - unsigned_integer::element::U256, -}; - -type VestaMontgomeryBackendPrimeField = MontgomeryBackendPrimeField; - -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct MontgomeryConfigVesta255PrimeField; -impl IsModulus for MontgomeryConfigVesta255PrimeField { - const MODULUS: U256 = U256::from_hex_unchecked( - "0x40000000000000000000000000000000224698fc0994a8dd8c46eb2100000001", - ); -} - -pub type Vesta255PrimeField = VestaMontgomeryBackendPrimeField; diff --git a/crates/math/src/field/fields/winterfell.rs b/crates/math/src/field/fields/winterfell.rs deleted file mode 100644 index 6e3f6ca6c..000000000 --- a/crates/math/src/field/fields/winterfell.rs +++ /dev/null @@ -1,280 +0,0 @@ -use core::ops::Add; - -use crate::{ - errors::ByteConversionError, - field::{ - element::FieldElement, - errors::FieldError, - traits::{IsFFTField, IsField, IsPrimeField, IsSubFieldOf}, - }, - traits::{AsBytes, ByteConversion}, - unsigned_integer::element::U256, -}; -pub use miden_core::Felt; -use miden_core::QuadExtension; -pub use winter_math::fields::f128::BaseElement; -use winter_math::{ExtensionOf, FieldElement as IsWinterfellFieldElement, StarkField}; - -// Implementation of Lambdaworks' different field traits for Miden's base field element `Felt` and -// its quadratic extension `QuadFelt`. - -impl IsFFTField for Felt { - const TWO_ADICITY: u64 = ::TWO_ADICITY as u64; - const TWO_ADIC_PRIMITVE_ROOT_OF_UNITY: Self::BaseType = Felt::TWO_ADIC_ROOT_OF_UNITY; -} - -impl IsPrimeField for Felt { - type RepresentativeType = U256; - - fn representative(_a: &Self::BaseType) -> Self::RepresentativeType { - todo!() - } - - fn from_hex(_hex_string: &str) -> Result { - todo!() - } - - fn to_hex(_a: &Self::BaseType) -> String { - todo!() - } - - fn field_bit_size() -> usize { - 128 // TODO - } -} - -impl IsField for Felt { - type BaseType = Felt; - - fn add(a: &Self::BaseType, b: &Self::BaseType) -> Self::BaseType { - *a + *b - } - - fn mul(a: &Self::BaseType, b: &Self::BaseType) -> Self::BaseType { - *a * *b - } - - fn sub(a: &Self::BaseType, b: &Self::BaseType) -> Self::BaseType { - *a - *b - } - - fn neg(a: &Self::BaseType) -> Self::BaseType { - -*a - } - - fn inv(a: &Self::BaseType) -> Result { - Ok((*a).inv()) - } - - fn div(a: &Self::BaseType, b: &Self::BaseType) -> Self::BaseType { - *a / *b - } - - fn eq(a: &Self::BaseType, b: &Self::BaseType) -> bool { - *a == *b - } - - fn zero() -> Self::BaseType { - Self::BaseType::ZERO - } - - fn one() -> Self::BaseType { - Self::BaseType::ONE - } - - fn from_u64(x: u64) -> Self::BaseType { - Self::BaseType::from(x) - } - - fn from_base_type(x: Self::BaseType) -> Self::BaseType { - x - } -} - -#[cfg(feature = "alloc")] -impl AsBytes for FieldElement { - fn as_bytes(&self) -> Vec { - Felt::elements_as_bytes(&[*self.value()]).to_vec() - } -} - -#[cfg(feature = "alloc")] -impl From> for alloc::vec::Vec { - fn from(value: FieldElement) -> Self { - value.as_bytes() - } -} - -impl ByteConversion for Felt { - #[cfg(feature = "alloc")] - fn to_bytes_be(&self) -> Vec { - Felt::elements_as_bytes(&[*self]).to_vec() - } - - #[cfg(feature = "alloc")] - fn to_bytes_le(&self) -> Vec { - Felt::elements_as_bytes(&[*self]).to_vec() - } - - fn from_bytes_be(bytes: &[u8]) -> Result - where - Self: Sized, - { - unsafe { - let res = Felt::bytes_as_elements(bytes) - .map_err(|_| ByteConversionError::FromBEBytesError)?; - Ok(res[0]) - } - } - - fn from_bytes_le(bytes: &[u8]) -> Result - where - Self: Sized, - { - unsafe { - let res = Felt::bytes_as_elements(bytes) - .map_err(|_| ByteConversionError::FromBEBytesError)?; - Ok(res[0]) - } - } -} - -pub type QuadFelt = QuadExtension; - -impl ByteConversion for QuadFelt { - #[cfg(feature = "alloc")] - fn to_bytes_be(&self) -> Vec { - let [b0, b1] = self.to_base_elements(); - let mut bytes = b0.to_bytes_be(); - bytes.extend(&b1.to_bytes_be()); - bytes - } - - #[cfg(feature = "alloc")] - fn to_bytes_le(&self) -> Vec { - let [b0, b1] = self.to_base_elements(); - let mut bytes = b0.to_bytes_le(); - bytes.extend(&b1.to_bytes_be()); - bytes - } - - fn from_bytes_be(_bytes: &[u8]) -> Result - where - Self: Sized, - { - todo!() - } - - fn from_bytes_le(_bytes: &[u8]) -> Result - where - Self: Sized, - { - todo!() - } -} - -#[cfg(feature = "alloc")] -impl AsBytes for FieldElement { - fn as_bytes(&self) -> Vec { - let [b0, b1] = self.value().to_base_elements(); - let mut bytes = b0.to_bytes_be(); - bytes.extend(&b1.to_bytes_be()); - bytes - } -} - -#[cfg(feature = "alloc")] -impl From> for alloc::vec::Vec { - fn from(value: FieldElement) -> Self { - value.as_bytes() - } -} - -impl IsField for QuadFelt { - type BaseType = QuadFelt; - - fn add(a: &Self::BaseType, b: &Self::BaseType) -> Self::BaseType { - *a + *b - } - - fn mul(a: &Self::BaseType, b: &Self::BaseType) -> Self::BaseType { - *a * *b - } - - fn sub(a: &Self::BaseType, b: &Self::BaseType) -> Self::BaseType { - *a - *b - } - - fn neg(a: &Self::BaseType) -> Self::BaseType { - -*a - } - - fn inv(a: &Self::BaseType) -> Result { - Ok((*a).inv()) - } - - fn div(a: &Self::BaseType, b: &Self::BaseType) -> Self::BaseType { - *a / *b - } - - fn eq(a: &Self::BaseType, b: &Self::BaseType) -> bool { - *a == *b - } - - fn zero() -> Self::BaseType { - Self::BaseType::ZERO - } - - fn one() -> Self::BaseType { - Self::BaseType::ONE - } - - fn from_u64(x: u64) -> Self::BaseType { - Self::BaseType::from(x) - } - - fn from_base_type(x: Self::BaseType) -> Self::BaseType { - x - } -} - -impl IsSubFieldOf for Felt { - fn mul( - a: &Self::BaseType, - b: &::BaseType, - ) -> ::BaseType { - b.mul_base(*a) - } - - fn add( - a: &Self::BaseType, - b: &::BaseType, - ) -> ::BaseType { - let [b0, b1] = b.to_base_elements(); - QuadFelt::new(b0.add(*a), b1) - } - - fn div( - a: &Self::BaseType, - b: &::BaseType, - ) -> ::BaseType { - let b_inv = b.inv(); - >::mul(a, &b_inv) - } - - fn sub( - a: &Self::BaseType, - b: &::BaseType, - ) -> ::BaseType { - let [b0, b1] = b.to_base_elements(); - QuadFelt::new(a.add(-(b0)), -b1) - } - - fn embed(a: Self::BaseType) -> ::BaseType { - QuadFelt::new(a, Felt::ZERO) - } - - fn to_subfield_vec(b: ::BaseType) -> Vec { - b.to_base_elements().to_vec() - } -} diff --git a/crates/math/src/field/mod.rs b/crates/math/src/field/mod.rs deleted file mode 100644 index 5cb5f2506..000000000 --- a/crates/math/src/field/mod.rs +++ /dev/null @@ -1,11 +0,0 @@ -/// Implementation of FieldElement, a generic element of a field. -pub mod element; -pub mod errors; -/// Implementation of quadratic extensions of fields. -pub mod extensions; -/// Implementation of particular cases of fields. -pub mod fields; -/// Field for test purposes. -pub mod test_fields; -/// Common behaviour for field elements. -pub mod traits; diff --git a/crates/math/src/field/test_fields/mod.rs b/crates/math/src/field/test_fields/mod.rs deleted file mode 100644 index a0a369481..000000000 --- a/crates/math/src/field/test_fields/mod.rs +++ /dev/null @@ -1,2 +0,0 @@ -pub mod u32_test_field; -pub mod u64_test_field; diff --git a/crates/math/src/field/test_fields/u32_test_field.rs b/crates/math/src/field/test_fields/u32_test_field.rs deleted file mode 100644 index 9269bc4ff..000000000 --- a/crates/math/src/field/test_fields/u32_test_field.rs +++ /dev/null @@ -1,157 +0,0 @@ -use crate::{ - errors::CreationError, - field::errors::FieldError, - field::traits::{IsFFTField, IsField, IsPrimeField}, -}; - -use crate::traits::ByteConversion; - -#[derive(Debug, Clone, PartialEq, Eq)] - -pub struct U32Field; - -impl ByteConversion for u32 { - #[cfg(feature = "alloc")] - fn to_bytes_be(&self) -> alloc::vec::Vec { - unimplemented!() - } - - #[cfg(feature = "alloc")] - fn to_bytes_le(&self) -> alloc::vec::Vec { - unimplemented!() - } - - fn from_bytes_be(_bytes: &[u8]) -> Result - where - Self: Sized, - { - unimplemented!() - } - - fn from_bytes_le(_bytes: &[u8]) -> Result - where - Self: Sized, - { - unimplemented!() - } -} - -impl IsField for U32Field { - type BaseType = u32; - - fn add(a: &u32, b: &u32) -> u32 { - ((*a as u128 + *b as u128) % MODULUS as u128) as u32 - } - - fn sub(a: &u32, b: &u32) -> u32 { - (((*a as u128 + MODULUS as u128) - *b as u128) % MODULUS as u128) as u32 - } - - fn neg(a: &u32) -> u32 { - MODULUS - a - } - - fn mul(a: &u32, b: &u32) -> u32 { - ((*a as u128 * *b as u128) % MODULUS as u128) as u32 - } - - fn div(a: &u32, b: &u32) -> Result { - let b_inv = &Self::inv(b)?; - Ok(Self::mul(a, b_inv)) - } - - fn inv(a: &u32) -> Result { - if *a == 0 { - return Err(FieldError::InvZeroError); - } - Ok(Self::pow(a, MODULUS - 2)) - } - - fn eq(a: &u32, b: &u32) -> bool { - Self::from_base_type(*a) == Self::from_base_type(*b) - } - - fn zero() -> u32 { - 0 - } - - fn one() -> u32 { - 1 - } - - fn from_u64(x: u64) -> u32 { - (x % MODULUS as u64) as u32 - } - - fn from_base_type(x: u32) -> u32 { - x % MODULUS - } -} - -impl IsPrimeField for U32Field { - type RepresentativeType = u32; - - fn representative(a: &Self::BaseType) -> u32 { - *a - } - - /// Returns how many bits do you need to represent the biggest field element - /// It expects the MODULUS to be a Prime - fn field_bit_size() -> usize { - ((MODULUS - 1).ilog2() + 1) as usize - } - - /// Unimplemented for test fields - fn from_hex(hex_string: &str) -> Result { - let mut hex_string = hex_string; - // Remove 0x if it's on the string - let mut char_iterator = hex_string.chars(); - if hex_string.len() > 2 - && char_iterator.next().unwrap() == '0' - && char_iterator.next().unwrap() == 'x' - { - hex_string = &hex_string[2..]; - } - - u32::from_str_radix(hex_string, 16).map_err(|_| CreationError::InvalidHexString) - } - - #[cfg(feature = "std")] - fn to_hex(x: &u32) -> String { - format!("{x:X}") - } -} - -// 15 * 2^27 + 1; -pub type U32TestField = U32Field<2013265921>; - -// These params correspond to the 2013265921 modulus. -impl IsFFTField for U32TestField { - const TWO_ADICITY: u64 = 27; - const TWO_ADIC_PRIMITVE_ROOT_OF_UNITY: u32 = 440532289; -} - -#[cfg(test)] -mod tests_u32_test_field { - use crate::field::{test_fields::u32_test_field::U32TestField, traits::IsPrimeField}; - - #[test] - fn from_hex_for_b_is_11() { - assert_eq!(U32TestField::from_hex("B").unwrap(), 11); - } - - #[cfg(feature = "std")] - #[test] - fn to_hex_test() { - let num = U32TestField::from_hex("B").unwrap(); - assert_eq!(U32TestField::to_hex(&num), "B"); - } - - #[test] - fn bit_size_of_test_field_is_31() { - assert_eq!( - ::field_bit_size(), - 31 - ); - } -} diff --git a/crates/math/src/field/test_fields/u64_test_field.rs b/crates/math/src/field/test_fields/u64_test_field.rs deleted file mode 100644 index 032afd232..000000000 --- a/crates/math/src/field/test_fields/u64_test_field.rs +++ /dev/null @@ -1,155 +0,0 @@ -use crate::{ - errors::CreationError, - field::{ - element::FieldElement, - extensions::quadratic::QuadraticExtensionField, - traits::{IsFFTField, IsField, IsPrimeField}, - }, - field::{errors::FieldError, extensions::quadratic::HasQuadraticNonResidue}, -}; - -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct U64Field; - -impl IsField for U64Field { - type BaseType = u64; - - fn add(a: &u64, b: &u64) -> u64 { - ((*a as u128 + *b as u128) % MODULUS as u128) as u64 - } - - fn sub(a: &u64, b: &u64) -> u64 { - (((*a as u128 + MODULUS as u128) - *b as u128) % MODULUS as u128) as u64 - } - - fn neg(a: &u64) -> u64 { - MODULUS - a - } - - fn mul(a: &u64, b: &u64) -> u64 { - ((*a as u128 * *b as u128) % MODULUS as u128) as u64 - } - - fn div(a: &u64, b: &u64) -> Result { - let b_inv = &Self::inv(b)?; - Ok(Self::mul(a, b_inv)) - } - - fn inv(a: &u64) -> Result { - if *a == 0 { - return Err(FieldError::InvZeroError); - } - Ok(Self::pow(a, MODULUS - 2)) - } - - fn eq(a: &u64, b: &u64) -> bool { - Self::from_u64(*a) == Self::from_u64(*b) - } - - fn zero() -> u64 { - 0 - } - - fn one() -> u64 { - 1 - } - - fn from_u64(x: u64) -> u64 { - x % MODULUS - } - - fn from_base_type(x: u64) -> u64 { - Self::from_u64(x) - } -} - -impl IsPrimeField for U64Field { - type RepresentativeType = u64; - - fn representative(x: &u64) -> u64 { - *x - } - - /// Returns how many bits do you need to represent the biggest field element - /// It expects the MODULUS to be a Prime - fn field_bit_size() -> usize { - ((MODULUS - 1).ilog2() + 1) as usize - } - - fn from_hex(hex_string: &str) -> Result { - let mut hex_string = hex_string; - // Remove 0x if it's on the string - let mut char_iterator = hex_string.chars(); - if hex_string.len() > 2 - && char_iterator.next().unwrap() == '0' - && char_iterator.next().unwrap() == 'x' - { - hex_string = &hex_string[2..]; - } - - u64::from_str_radix(hex_string, 16).map_err(|_| CreationError::InvalidHexString) - } - - #[cfg(feature = "std")] - fn to_hex(x: &u64) -> String { - format!("{x:X}") - } -} - -pub type U64TestField = U64Field<18446744069414584321>; - -// These params correspond to the 18446744069414584321 modulus. -impl IsFFTField for U64TestField { - const TWO_ADICITY: u64 = 32; - const TWO_ADIC_PRIMITVE_ROOT_OF_UNITY: u64 = 1753635133440165772; -} - -#[derive(Clone, Debug)] -pub struct TestNonResidue; -impl HasQuadraticNonResidue for TestNonResidue { - fn residue() -> FieldElement { - FieldElement::from(7) - } -} - -pub type U64TestFieldExtension = QuadraticExtensionField; - -#[cfg(test)] -mod tests_u64_test_field { - use crate::field::{ - element::FieldElement, - test_fields::u64_test_field::{U64TestField, U64TestFieldExtension}, - traits::IsPrimeField, - }; - - #[test] - fn from_hex_for_b_is_11() { - assert_eq!(U64TestField::from_hex("B").unwrap(), 11); - } - - #[test] - fn bit_size_of_test_field_is_64() { - assert_eq!( - ::field_bit_size(), - 64 - ); - } - - #[cfg(feature = "alloc")] - #[test] - fn test_to_subfield_vec() { - let a = FieldElement::::from(&[ - FieldElement::from(1), - FieldElement::from(3), - ]); - let b = a.to_subfield_vec::(); - assert_eq!(b, alloc::vec![FieldElement::from(1), FieldElement::from(3)]); - } - - #[cfg(feature = "std")] - #[test] - fn to_hex_test() { - let num = U64TestField::from_hex("B").unwrap(); - assert_eq!(U64TestField::to_hex(&num), "B"); - } -} diff --git a/crates/math/src/field/traits.rs b/crates/math/src/field/traits.rs deleted file mode 100644 index 2bbb4586c..000000000 --- a/crates/math/src/field/traits.rs +++ /dev/null @@ -1,305 +0,0 @@ -use super::{element::FieldElement, errors::FieldError}; -use crate::traits::ByteConversion; -use crate::{errors::CreationError, unsigned_integer::traits::IsUnsignedInteger}; -use core::fmt::Debug; - -/// Represents different configurations that powers of roots of unity can be in. Some of these may -/// be necessary for FFT (as twiddle factors). -#[derive(Clone, Copy)] -pub enum RootsConfig { - Natural, // w^0, w^1, w^2... - NaturalInversed, // w^0, w^-1, w^-2... - BitReverse, // same as first but exponents are bit-reversed. - BitReverseInversed, // same as above but exponents are negated. -} - -/// Represents the subfield relation between two fields. -pub trait IsSubFieldOf: IsField { - fn mul(a: &Self::BaseType, b: &F::BaseType) -> F::BaseType; - fn add(a: &Self::BaseType, b: &F::BaseType) -> F::BaseType; - fn div(a: &Self::BaseType, b: &F::BaseType) -> Result; - fn sub(a: &Self::BaseType, b: &F::BaseType) -> F::BaseType; - fn embed(a: Self::BaseType) -> F::BaseType; - #[cfg(feature = "alloc")] - fn to_subfield_vec(b: F::BaseType) -> alloc::vec::Vec; -} - -impl IsSubFieldOf for F -where - F: IsField, -{ - #[inline(always)] - fn mul(a: &Self::BaseType, b: &F::BaseType) -> F::BaseType { - F::mul(a, b) - } - - #[inline(always)] - fn add(a: &Self::BaseType, b: &F::BaseType) -> F::BaseType { - F::add(a, b) - } - - #[inline(always)] - fn sub(a: &Self::BaseType, b: &F::BaseType) -> F::BaseType { - F::sub(a, b) - } - - #[inline(always)] - fn div(a: &Self::BaseType, b: &F::BaseType) -> Result { - F::div(a, b) - } - - #[inline(always)] - fn embed(a: Self::BaseType) -> F::BaseType { - a - } - - #[cfg(feature = "alloc")] - fn to_subfield_vec(b: F::BaseType) -> alloc::vec::Vec { - alloc::vec![b] - } -} - -/// Trait to define necessary parameters for FFT-friendly Fields. -/// Two-Adic fields are ones whose order is of the form $2^n k + 1$. -/// Here $n$ is usually called the *two-adicity* of the field. The -/// reason we care about it is that in an $n$-adic field there are $2^j$-roots -/// of unity for every `j` between 1 and n, which is needed to do Fast Fourier. -/// A two-adic primitive root of unity is a number w that satisfies w^(2^n) = 1 -/// and w^(j) != 1 for every j below 2^n. With this primitive root we can generate -/// any other root of unity we need to perform FFT. -pub trait IsFFTField: IsField { - const TWO_ADICITY: u64; - const TWO_ADIC_PRIMITVE_ROOT_OF_UNITY: Self::BaseType; - - /// Used for searching this field's implementation in other languages, e.g in MSL - /// for executing parallel operations with the Metal API. - fn field_name() -> &'static str { - "" - } - - /// Returns a primitive root of unity of order $2^{order}$. - /// This is an element w such that w^{2^order} = 1 modulo p and - /// w^k <> 1 modulo p for k not congruent to 2^order - fn get_primitive_root_of_unity(order: u64) -> Result, FieldError> { - let two_adic_primitive_root_of_unity = - FieldElement::new(Self::TWO_ADIC_PRIMITVE_ROOT_OF_UNITY); - if order == 0 { - return Ok(FieldElement::one()); - } - if order > Self::TWO_ADICITY { - return Err(FieldError::RootOfUnityError(order)); - } - let log_power = Self::TWO_ADICITY - order; - let root = (0..log_power).fold(two_adic_primitive_root_of_unity, |acc, _| acc.square()); - Ok(root) - } -} - -/// Trait to add field behaviour to a struct. -pub trait IsField: Debug + Clone { - /// The underlying base type for representing elements from the field. - // TODO: Relax Unpin for non cuda usage - type BaseType: Clone + Debug + Unpin + ByteConversion + Default; - - /// Returns the sum of `a` and `b`. - fn add(a: &Self::BaseType, b: &Self::BaseType) -> Self::BaseType; - - /// Returns the double of `a`. - fn double(a: &Self::BaseType) -> Self::BaseType { - Self::add(a, a) - } - - /// Returns the multiplication of `a` and `b`. - fn mul(a: &Self::BaseType, b: &Self::BaseType) -> Self::BaseType; - - /// Returns the multiplication of `a` and `a`. - fn square(a: &Self::BaseType) -> Self::BaseType { - Self::mul(a, a) - } - - /// Returns the power of `a` to the `T`, a^T - /// Uses the square and multiply algorithm, which takes O(log2(T)) steps - /// This is a non-constant time implementation! - fn pow(a: &Self::BaseType, mut exponent: T) -> Self::BaseType - where - T: IsUnsignedInteger, - { - let zero = T::from(0); - let one = T::from(1); - - if exponent == zero { - Self::one() - } else if exponent == one { - a.clone() - } else { - let mut result = a.clone(); - - while exponent & one == zero { - result = Self::square(&result); - exponent >>= 1; - } - - if exponent == zero { - result - } else { - let mut base = result.clone(); - exponent >>= 1; - - while exponent != zero { - base = Self::square(&base); - if exponent & one == one { - result = Self::mul(&result, &base); - } - exponent >>= 1; - } - - result - } - } - } - - /// Returns the subtraction of `a` and `b`. - fn sub(a: &Self::BaseType, b: &Self::BaseType) -> Self::BaseType; - - /// Returns the additive inverse of `a`. - fn neg(a: &Self::BaseType) -> Self::BaseType; - - /// Returns the multiplicative inverse of `a`. - fn inv(a: &Self::BaseType) -> Result; - - /// Returns the division of `a` and `b`. - fn div(a: &Self::BaseType, b: &Self::BaseType) -> Result; - - /// Returns a boolean indicating whether `a` and `b` are equal or not. - fn eq(a: &Self::BaseType, b: &Self::BaseType) -> bool; - - /// Returns the additive neutral element. - fn zero() -> Self::BaseType { - Self::BaseType::default() - } - - /// Returns the multiplicative neutral element. - fn one() -> Self::BaseType; - - /// Returns the element `x * 1` where 1 is the multiplicative neutral element. - fn from_u64(x: u64) -> Self::BaseType; - - /// Takes as input an element of BaseType and returns the internal representation - /// of that element in the field. - fn from_base_type(x: Self::BaseType) -> Self::BaseType; -} - -/// Provides the Legendre symbol for an element modulo p -/// The Legendre symbol is Zero if a is congruent to 0 modulo p -/// It is equal to One if a is a square modulo p (which means it has a square root) -/// It is equal to MinusOne if a is not a square modulo p -/// For example, p - 1 is not a square modulo p if p is congruent to 3 modulo 4 -/// This applies to Mersenne primes, for example -#[derive(PartialEq)] -pub enum LegendreSymbol { - MinusOne, - Zero, - One, -} - -pub trait IsPrimeField: IsField { - type RepresentativeType: IsUnsignedInteger; - - /// Returns the integer representative in - /// the range [0, p-1], where p the modulus - fn representative(a: &Self::BaseType) -> Self::RepresentativeType; - - /// Returns p - 1, which is -1 mod p - fn modulus_minus_one() -> Self::RepresentativeType { - Self::representative(&Self::neg(&Self::one())) - } - - /// Creates a BaseType from a Hex String - /// 0x is optional - /// Returns an `CreationError::InvalidHexString`if the value is not a hexstring - fn from_hex(hex_string: &str) -> Result; - - #[cfg(feature = "std")] - /// Creates a hexstring from a `FieldElement` without `0x`. - fn to_hex(a: &Self::BaseType) -> String; - - /// Returns the number of bits of the max element of the field, as per field documentation, not internal representation. - /// This is `log2(max FE)` rounded up - fn field_bit_size() -> usize; - - /// Computes the Legendre symbol using Euler's criterion - /// This is important to ensure that we find the square root of elements that are quadratic residues - fn legendre_symbol(a: &Self::BaseType) -> LegendreSymbol { - let symbol = Self::pow(a, Self::modulus_minus_one() >> 1); - - match symbol { - x if Self::eq(&x, &Self::zero()) => LegendreSymbol::Zero, - x if Self::eq(&x, &Self::one()) => LegendreSymbol::One, - _ => LegendreSymbol::MinusOne, - } - } - - /// Returns the two square roots of `self` if they exist and - /// `None` otherwise - fn sqrt(a: &Self::BaseType) -> Option<(Self::BaseType, Self::BaseType)> { - match Self::legendre_symbol(a) { - LegendreSymbol::Zero => return Some((Self::zero(), Self::zero())), - LegendreSymbol::MinusOne => return None, - LegendreSymbol::One => (), - }; - - let integer_one = Self::RepresentativeType::from(1_u16); - let mut s: usize = 0; - let mut q = Self::modulus_minus_one(); - - while q & integer_one != integer_one { - s += 1; - q >>= 1; - } - - let mut c = { - // Calculate a non residue: - let mut non_qr = Self::from_u64(2); - while Self::legendre_symbol(&non_qr) != LegendreSymbol::MinusOne { - non_qr = Self::add(&non_qr, &Self::one()); - } - - Self::pow(&non_qr, q) - }; - - let mut x = Self::pow(a, (q + integer_one) >> 1); - let mut t = Self::pow(a, q); - let mut m = s; - - let one = Self::one(); - while !Self::eq(&t, &one) { - let i = { - let mut i = 0; - let mut t = t.clone(); - let minus_one = Self::neg(&Self::one()); - while !Self::eq(&t, &minus_one) { - i += 1; - t = Self::mul(&t, &t); - } - i + 1 - }; - - let b = (0..(m - i - 1)).fold(c, |acc, _| Self::square(&acc)); - - c = Self::mul(&b, &b); - x = Self::mul(&x, &b); - t = Self::mul(&t, &c); - m = i; - } - - let neg_x = Self::neg(&x); - Some((x, neg_x)) - } -} - -/// This trait is necessary for sampling a random field element with a uniform distribution. -pub trait HasDefaultTranscript: IsField { - /// This function should truncates the sampled bits to the quantity required to represent the order of the base field - /// and returns a field element. - fn get_random_field_element_from_rng(rng: &mut impl rand::Rng) -> FieldElement; -} diff --git a/crates/math/src/gpu/cuda/field/element.rs b/crates/math/src/gpu/cuda/field/element.rs deleted file mode 100644 index d89aa5489..000000000 --- a/crates/math/src/gpu/cuda/field/element.rs +++ /dev/null @@ -1,42 +0,0 @@ -use crate::field::{element::FieldElement, traits::IsField}; -use cudarc::driver::safe::DeviceRepr; - -use core::ffi; - -#[derive(Clone)] -pub(crate) struct CUDAFieldElement { - value: F::BaseType, -} - -impl CUDAFieldElement { - /// Returns the underlying `value` - pub fn value(&self) -> &F::BaseType { - &self.value - } -} - -impl Default for CUDAFieldElement { - fn default() -> Self { - Self { value: F::zero() } - } -} - -unsafe impl DeviceRepr for CUDAFieldElement { - fn as_kernel_param(&self) -> *mut ffi::c_void { - [self].as_ptr() as *mut ffi::c_void - } -} - -impl From<&FieldElement> for CUDAFieldElement { - fn from(elem: &FieldElement) -> Self { - Self { - value: elem.value().clone(), - } - } -} - -impl From> for FieldElement { - fn from(elem: CUDAFieldElement) -> Self { - Self::from_raw(elem.value()) - } -} diff --git a/crates/math/src/gpu/cuda/field/mod.rs b/crates/math/src/gpu/cuda/field/mod.rs deleted file mode 100644 index a1b2323d0..000000000 --- a/crates/math/src/gpu/cuda/field/mod.rs +++ /dev/null @@ -1 +0,0 @@ -pub mod element; diff --git a/crates/math/src/gpu/cuda/mod.rs b/crates/math/src/gpu/cuda/mod.rs deleted file mode 100644 index 5b8f2df9f..000000000 --- a/crates/math/src/gpu/cuda/mod.rs +++ /dev/null @@ -1 +0,0 @@ -pub mod field; diff --git a/crates/math/src/gpu/cuda/shaders/fft/bitrev_permutation.cuh b/crates/math/src/gpu/cuda/shaders/fft/bitrev_permutation.cuh deleted file mode 100644 index 4c6626248..000000000 --- a/crates/math/src/gpu/cuda/shaders/fft/bitrev_permutation.cuh +++ /dev/null @@ -1,13 +0,0 @@ -#pragma once - -#include "../utils.h" - -template -inline __device__ void _bitrev_permutation(const Fp *input, Fp *result, const int len) -{ - unsigned thread_pos = blockDim.x * blockIdx.x + threadIdx.x; - if (thread_pos >= len) return; - // TODO: guard is not needed for inputs of len >=block_size * 2, if len is pow of two - - result[thread_pos] = input[reverse_index(thread_pos, len)]; -}; diff --git a/crates/math/src/gpu/cuda/shaders/fft/fft.cuh b/crates/math/src/gpu/cuda/shaders/fft/fft.cuh deleted file mode 100644 index 6768756e9..000000000 --- a/crates/math/src/gpu/cuda/shaders/fft/fft.cuh +++ /dev/null @@ -1,29 +0,0 @@ -#pragma once - -template -inline __device__ void _radix2_dit_butterfly(Fp *input, - const Fp *twiddles, - const int stage, - const int butterfly_count) -{ - int thread_pos = blockDim.x * blockIdx.x + threadIdx.x; - - if (thread_pos >= butterfly_count) return; - // TODO: guard is not needed for inputs of len >=block_size * 2, only if len is pow of two - - int half_group_size = butterfly_count >> stage; - int group = thread_pos / half_group_size; - - int pos_in_group = thread_pos & (half_group_size - 1); - int i = thread_pos * 2 - pos_in_group; // multiply quotient by 2 - - Fp w = twiddles[group]; - Fp a = input[i]; - Fp b = input[i + half_group_size]; - - Fp res_1 = a + w * b; - Fp res_2 = a - w * b; - - input[i] = res_1; // --\/-- - input[i + half_group_size] = res_2; // --/\-- -}; diff --git a/crates/math/src/gpu/cuda/shaders/fft/twiddles.cuh b/crates/math/src/gpu/cuda/shaders/fft/twiddles.cuh deleted file mode 100644 index 925bf71ac..000000000 --- a/crates/math/src/gpu/cuda/shaders/fft/twiddles.cuh +++ /dev/null @@ -1,29 +0,0 @@ -#pragma once - -#include "../utils.h" - -// NOTE: In order to calculate the inverse twiddles, call with _omega = _omega.inverse() -template -inline __device__ void _calc_twiddles(Fp *result, const Fp &_omega, const int count) -{ - unsigned thread_pos = blockDim.x * blockIdx.x + threadIdx.x; - if (thread_pos >= count) return; - // TODO: guard is not needed for count >=block_size * 2, if count is pow of two - - Fp omega = _omega; - result[thread_pos] = omega.pow(thread_pos); -}; - -// NOTE: In order to calculate the inverse twiddles, call with _omega = _omega.inverse() -template -inline __device__ void _calc_twiddles_bitrev(Fp *result, const Fp &_omega, const int count) -{ - unsigned thread_pos = blockDim.x * blockIdx.x + threadIdx.x; - if (thread_pos >= count) return; - // TODO: guard is not needed for count >=block_size * 2, if count is pow of two - - Fp omega = _omega; - result[thread_pos] = omega.pow(reverse_index(thread_pos, count)); -}; - - diff --git a/crates/math/src/gpu/cuda/shaders/field/fp_u256.cuh b/crates/math/src/gpu/cuda/shaders/field/fp_u256.cuh deleted file mode 100644 index e1933bbea..000000000 --- a/crates/math/src/gpu/cuda/shaders/field/fp_u256.cuh +++ /dev/null @@ -1,210 +0,0 @@ -// https://github.com/andrewmilson/ministark/blob/main/gpu-poly/src/metal/felt_u256.h.metal - -#ifndef felt_u256_h -#define felt_u256_h - -#include "../unsigned_integer/u256.cuh" - -template < - /* =N **/ unsigned long N_0, unsigned long N_1, unsigned long N_2, - unsigned long N_3, - /* =R_SQUARED **/ unsigned long R_SQUARED_0, unsigned long R_SQUARED_1, - unsigned long R_SQUARED_2, unsigned long R_SQUARED_3, - /* =N_PRIME **/ unsigned long N_PRIME_0, unsigned long N_PRIME_1, - unsigned long N_PRIME_2, unsigned long N_PRIME_3> -class Fp256 { -public: - Fp256() = default; - __device__ constexpr Fp256(unsigned long v) : inner(v) {} - __device__ constexpr Fp256(u256 v) : inner(v) {} - - __device__ constexpr explicit operator u256() const { return inner; } - - __device__ constexpr Fp256 operator+(const Fp256 rhs) const { - return Fp256(add(inner, rhs.inner)); - } - - __device__ constexpr Fp256 operator-(const Fp256 rhs) const { - return Fp256(sub(inner, rhs.inner)); - } - - __device__ Fp256 operator*(const Fp256 rhs) const { - return Fp256(mul(inner, rhs.inner)); - } - - // TODO: make method for all fields - __device__ Fp256 pow(unsigned exp) { - // TODO find a way to generate on compile time - Fp256 const ONE = mul(u256(1), R_SQUARED); - Fp256 res = ONE; - - while (exp > 0) { - if (exp & 1) { - res = res * *this; - } - exp >>= 1; - *this = *this * *this; - } - - return res; - } - - __device__ Fp256 inverse() { - // used addchain - // https://github.com/mmcloughlin/addchain - u256 _10 = mul(inner, inner); - u256 _11 = mul(_10, inner); - u256 _1100 = sqn<2>(_11); - u256 _1101 = mul(inner, _1100); - u256 _1111 = mul(_10, _1101); - u256 _11001 = mul(_1100, _1101); - u256 _110010 = mul(_11001, _11001); - u256 _110011 = mul(inner, _110010); - u256 _1000010 = mul(_1111, _110011); - u256 _1001110 = mul(_1100, _1000010); - u256 _10000001 = mul(_110011, _1001110); - u256 _11001111 = mul(_1001110, _10000001); - u256 i14 = mul(_11001111, _11001111); - u256 i15 = mul(_10000001, i14); - u256 i16 = mul(i14, i15); - u256 x10 = mul(_1000010, i16); - u256 i27 = sqn<10>(x10); - u256 i28 = mul(i16, i27); - u256 i38 = sqn<10>(i27); - u256 i39 = mul(i28, i38); - u256 i49 = sqn<10>(i38); - u256 i50 = mul(i39, i49); - u256 i60 = sqn<10>(i49); - u256 i61 = mul(i50, i60); - u256 i72 = mul(sqn<10>(i60), i61); - u256 x60 = mul(_1000010, i72); - u256 i76 = sqn<2>(mul(i72, x60)); - u256 x64 = mul(mul(i15, i76), i76); - u256 i208 = mul(sqn<64>(mul(sqn<63>(mul(i15, x64)), x64)), x64); - return Fp256(mul(sqn<60>(i208), x60)); - } - - __device__ Fp256 neg() { - // TODO: can improve - return Fp256(sub(0, inner)); - } - -private: - u256 inner; - - constexpr static const u256 N = u256(N_0, N_1, N_2, N_3); - constexpr static const u256 R_SQUARED = - u256(R_SQUARED_0, R_SQUARED_1, R_SQUARED_2, R_SQUARED_3); - constexpr static const u256 N_PRIME = - u256(N_PRIME_0, N_PRIME_1, N_PRIME_2, N_PRIME_3); - - // Equates to `(1 << 256) - N` - constexpr static const u256 R_SUB_N = - u256(0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF, - 0xFFFFFFFFFFFFFFFF) - - N + u256(1); - - template __device__ u256 sqn(u256 base) const { - u256 result = base; -#pragma unroll - for (unsigned i = 0; i < N_ACC; i++) { - result = mul(result, result); - } - return result; - } - - // Computes `lhs + rhs mod N` - // Returns value in range [0,N) - __device__ inline u256 add(const u256 lhs, const u256 rhs) const { - u256 addition = (lhs + rhs); - u256 res = addition; - // TODO: determine if an if statement here are more optimal - return res - u256(addition >= N) * N + u256(addition < lhs) * R_SUB_N; - } - - // Computes `lhs - rhs mod N` - // Assumes `rhs` value in range [0,N) - __device__ inline u256 sub(const u256 lhs, const u256 rhs) const { - // TODO: figure what goes on here with "constant" scope variables - return add(lhs, ((u256)N) - rhs); - } - - // Computes `lhs * rhs mod M` - // - // Essential that inputs are already in the range [0,N) and are in montgomery - // form. Multiplication performs single round of montgomery reduction. - // - // Reference: - // - https://en.wikipedia.org/wiki/Montgomery_modular_multiplication (REDC) - // - https://www.youtube.com/watch?v=2UmQDKcelBQ - __device__ u256 mul(const u256 lhs, const u256 rhs) const { - u256 lhs_low = lhs.low; - u256 lhs_high = lhs.high; - u256 rhs_low = rhs.low; - u256 rhs_high = rhs.high; - - u256 partial_t_high = lhs_high * rhs_high; - u256 partial_t_mid_a = lhs_high * rhs_low; - u256 partial_t_mid_a_low = partial_t_mid_a.low; - u256 partial_t_mid_a_high = partial_t_mid_a.high; - u256 partial_t_mid_b = rhs_high * lhs_low; - u256 partial_t_mid_b_low = partial_t_mid_b.low; - u256 partial_t_mid_b_high = partial_t_mid_b.high; - u256 partial_t_low = lhs_low * rhs_low; - - u256 tmp = partial_t_mid_a_low + partial_t_mid_b_low + partial_t_low.high; - u256 carry = tmp.high; - u256 t_low = u256(tmp.low, partial_t_low.low); - u256 t_high = - partial_t_high + partial_t_mid_a_high + partial_t_mid_b_high + carry; - - // Compute `m = T * N' mod R` - u256 m = t_low * N_PRIME; - - // Compute `t = (T + m * N) / R` - u256 n = N; - u256 n_low = n.low; - u256 n_high = n.high; - u256 m_low = m.low; - u256 m_high = m.high; - - u256 partial_mn_high = m_high * n_high; - u256 partial_mn_mid_a = m_high * n_low; - u256 partial_mn_mid_a_low = partial_mn_mid_a.low; - u256 partial_mn_mid_a_high = partial_mn_mid_a.high; - u256 partial_mn_mid_b = n_high * m_low; - u256 partial_mn_mid_b_low = partial_mn_mid_b.low; - u256 partial_mn_mid_b_high = partial_mn_mid_b.high; - u256 partial_mn_low = m_low * n_low; - - tmp = - partial_mn_mid_a_low + partial_mn_mid_b_low + u256(partial_mn_low.high); - carry = tmp.high; - u256 mn_low = u256(tmp.low, partial_mn_low.low); - u256 mn_high = - partial_mn_high + partial_mn_mid_a_high + partial_mn_mid_b_high + carry; - - u256 overflow = mn_low + t_low < mn_low; - u256 t_tmp = t_high + overflow; - u256 t = t_tmp + mn_high; - u256 overflows_r = t < t_tmp; - u256 overflows_modulus = t >= N; - - return t + overflows_r * R_SUB_N - overflows_modulus * N; - } -}; - -namespace p256 { -// StarkWare field for Cairo -// P = -// 3618502788666131213697322783095070105623107215331596699973092056135872020481 -using Fp = Fp256< - /* =N **/ /*u256(*/ 576460752303423505, 0, 0, 1 /*)*/, - /* =R_SQUARED **/ /*u256(*/ 576413109808302096, 18446744073700081664, - 5151653887, 18446741271209837569 /*)*/, - /* =N_PRIME **/ /*u256(*/ 576460752303423504, 18446744073709551615, - 18446744073709551615, 18446744073709551615 /*)*/ - >; -} // namespace p256 - -#endif diff --git a/crates/math/src/gpu/cuda/shaders/field/stark256.cu b/crates/math/src/gpu/cuda/shaders/field/stark256.cu deleted file mode 100644 index dfbae8388..000000000 --- a/crates/math/src/gpu/cuda/shaders/field/stark256.cu +++ /dev/null @@ -1,51 +0,0 @@ -#include "./fp_u256.cuh" -#include "../fft/fft.cuh" -#include "../fft/twiddles.cuh" -#include "../fft/bitrev_permutation.cuh" -#include "../utils.h" - -namespace p256 -{ - // StarkWare field for Cairo - // P = - // 3618502788666131213697322783095070105623107215331596699973092056135872020481 - using Fp = Fp256< - /* =N **/ /*u256(*/ 576460752303423505, 0, 0, 1 /*)*/, - /* =R_SQUARED **/ /*u256(*/ 576413109808302096, 18446744073700081664, - 5151653887, 18446741271209837569 /*)*/, - /* =N_PRIME **/ /*u256(*/ 576460752303423504, 18446744073709551615, - 18446744073709551615, 18446744073709551615 /*)*/ - >; -} // namespace p256 - -extern "C" -{ - __global__ void radix2_dit_butterfly( p256::Fp *input, - const p256::Fp *twiddles, - const int stage, - const int butterfly_count) - { - _radix2_dit_butterfly(input, twiddles, stage, butterfly_count); - } - // NOTE: In order to calculate the inverse twiddles, call with _omega = _omega.inverse() - __global__ void calc_twiddles(p256::Fp *result, const p256::Fp &_omega, const int count) - { - _calc_twiddles(result, _omega, count); - }; - - // NOTE: In order to calculate the inverse twiddles, call with _omega = _omega.inverse() - __global__ void calc_twiddles_bitrev(p256::Fp *result, - const p256::Fp &_omega, - const int count) - { - _calc_twiddles_bitrev(result, _omega, count); - }; - - __global__ void bitrev_permutation( - const p256::Fp *input, - p256::Fp *result, - const int len - ) { - _bitrev_permutation(input, result, len); - }; -} diff --git a/crates/math/src/gpu/cuda/shaders/unsigned_integer/u128.cuh b/crates/math/src/gpu/cuda/shaders/unsigned_integer/u128.cuh deleted file mode 100644 index 621bf66b4..000000000 --- a/crates/math/src/gpu/cuda/shaders/unsigned_integer/u128.cuh +++ /dev/null @@ -1,165 +0,0 @@ -// https://github.com/andrewmilson/ministark/blob/main/gpu-poly/src/metal/u128.h.metal - -#ifndef u128_h -#define u128_h - -typedef unsigned long long uint64_t; - -class u128 -{ -public: - u128() = default; - __device__ constexpr u128(int l) : low(l), high(0) {} - __device__ constexpr u128(uint64_t l) : low(l), high(0) {} - __device__ constexpr u128(bool b) : low(b), high(0) {} - __device__ constexpr u128(uint64_t h, uint64_t l) - : low(l), high(h) {} - - __device__ constexpr u128 operator+(const u128 rhs) const - { - return u128(high + rhs.high + ((low + rhs.low) < low), low + rhs.low); - } - - __device__ constexpr u128 operator+=(const u128 rhs) - { - *this = *this + rhs; - return *this; - } - - __device__ constexpr inline u128 operator-(const u128 rhs) const - { - return u128(high - rhs.high - ((low - rhs.low) > low), low - rhs.low); - } - - __device__ constexpr u128 operator-=(const u128 rhs) - { - *this = *this - rhs; - return *this; - } - - __device__ constexpr bool operator==(const u128 rhs) const - { - return high == rhs.high && low == rhs.low; - } - - __device__ constexpr bool operator!=(const u128 rhs) const - { - return !(*this == rhs); - } - - __device__ constexpr bool operator<(const u128 rhs) const - { - return ((high == rhs.high) && (low < rhs.low)) || (high < rhs.high); - } - - __device__ constexpr u128 operator&(const u128 rhs) const - { - return u128(high & rhs.high, low & rhs.low); - } - - __device__ constexpr u128 operator|(const u128 rhs) const - { - return u128(high | rhs.high, low | rhs.low); - } - - __device__ constexpr bool operator>(const u128 rhs) const - { - return ((high == rhs.high) && (low > rhs.low)) || (high > rhs.high); - } - - __device__ constexpr bool operator>=(const u128 rhs) const - { - return !(*this < rhs); - } - - __device__ constexpr bool operator<=(const u128 rhs) const - { - return !(*this > rhs); - } - - __device__ constexpr inline u128 operator>>(unsigned shift) const - { - uint64_t new_low = (shift == 0) * low - | (shift == 64) * high - | ((shift < 64) ^ (shift == 0)) * ((high << (64 - shift)) | (low >> shift)) - | ((shift > 64) & (shift < 128)) * (high >> (shift - 64)); - - uint64_t new_high = (shift == 0) * high - | ((shift < 64) ^ (shift == 0)) * (high >> shift); - - return u128(new_high, new_low); - - // Unoptimized form: - // if (shift >= 128) - // return u128(0); - // else if (shift == 64) - // return u128(0, high); - // else if (shift == 0) - // return *this; - // else if (shift < 64) - // return u128(high >> shift, (high << (64 - shift)) | (low >> shift)); - // else if ((128 > shift) && (shift > 64)) - // return u128(0, (high >> (shift - 64))); - // else - // return u128(0); - } - - __device__ constexpr inline u128 operator<<(unsigned shift) const - { - uint64_t new_low = (shift == 0) * low - | ((shift < 64) ^ (shift == 0)) * (low << shift); - - uint64_t new_high = (shift == 0) * high - | (shift == 64) * low - | ((shift < 64) ^ (shift == 0)) * (high << shift) | (low >> (64 - shift)) - | ((shift > 64) & (shift < 128)) * (low >> (shift - 64)); - - return u128(new_high, new_low); - - // Unoptimized form: - // if (shift >= 128) - // return u128(0); - // else if (shift == 64) - // return u128(low, 0); - // else if (shift == 0) - // return *this; - // else if (shift < 64) - // return u128((high << shift) | (low >> (64 - shift)), low << shift); - // else if ((128 > shift) && (shift > 64)) - // return u128((low >> (shift - 64)), 0); - // else - // return u128(0); - } - - __device__ constexpr u128 operator>>=(unsigned rhs) - { - *this = *this >> rhs; - return *this; - } - - __device__ u128 operator*(const bool rhs) const - { - return u128(high * rhs, low * rhs); - } - - __device__ u128 operator*(const u128 rhs) const - { - uint64_t t_low_high = low * rhs.high; - uint64_t t_high = __umul64hi(low, rhs.low); - uint64_t t_high_low = high * rhs.low; - uint64_t t_low = low * rhs.low; - return u128(t_low_high + t_high_low + t_high, t_low); - } - - __device__ u128 operator*=(const u128 rhs) - { - *this = *this * rhs; - return *this; - } - - // TODO: check if performance improves with a different limb size - uint64_t high; - uint64_t low; -}; - -#endif /* u128_h */ diff --git a/crates/math/src/gpu/cuda/shaders/unsigned_integer/u256.cuh b/crates/math/src/gpu/cuda/shaders/unsigned_integer/u256.cuh deleted file mode 100644 index 8093954eb..000000000 --- a/crates/math/src/gpu/cuda/shaders/unsigned_integer/u256.cuh +++ /dev/null @@ -1,171 +0,0 @@ -// https://github.com/andrewmilson/ministark/blob/6e96f6b6c83b7faf38a9e015bbedf2aa7b984092/gpu/src/metal/u256.h.metal - -#ifndef u256_h -#define u256_h - -#include "u128.cuh" - -typedef unsigned long long uint64_t; - -class u256 -{ -public: - u256() = default; - __device__ constexpr u256(int l) : low(l), high(0) {} - __device__ constexpr u256(uint64_t l) : low(u128(l)), high(0) {} - __device__ constexpr u256(u128 l) : low(l), high(0) {} - __device__ constexpr u256(bool b) : low(b), high(0) {} - __device__ constexpr u256(u128 h, u128 l) : low(l), high(h) {} - __device__ constexpr u256(uint64_t hh, uint64_t hl, - uint64_t lh, uint64_t ll) - : low(u128(lh, ll)), high(u128(hh, hl)) {} - - __device__ constexpr u256 operator+(const u256 rhs) const - { - return u256(high + rhs.high + ((low + rhs.low) < low), low + rhs.low); - } - - __device__ constexpr u256 operator+=(const u256 rhs) - { - *this = *this + rhs; - return *this; - } - - __device__ constexpr inline u256 operator-(const u256 rhs) const - { - return u256(high - rhs.high - ((low - rhs.low) > low), low - rhs.low); - } - - __device__ constexpr u256 operator-=(const u256 rhs) - { - *this = *this - rhs; - return *this; - } - - __device__ constexpr bool operator==(const u256 rhs) const - { - return high == rhs.high && low == rhs.low; - } - - __device__ constexpr bool operator!=(const u256 rhs) const - { - return !(*this == rhs); - } - - __device__ constexpr bool operator<(const u256 rhs) const - { - return ((high == rhs.high) && (low < rhs.low)) || (high < rhs.high); - } - - __device__ constexpr u256 operator&(const u256 rhs) const - { - return u256(high & rhs.high, low & rhs.low); - } - - __device__ constexpr bool operator>(const u256 rhs) const - { - return ((high == rhs.high) && (low > rhs.low)) || (high > rhs.high); - } - - __device__ constexpr bool operator>=(const u256 rhs) const - { - return !(*this < rhs); - } - - __device__ constexpr bool operator<=(const u256 rhs) const - { - return !(*this > rhs); - } - - __device__ constexpr inline u256 operator>>(unsigned shift) const - { - u128 new_low = low * (shift == 0) - | high * (shift == 128) - | (high << (128 - shift) | (low >> shift)) * ((shift < 128) ^ (shift == 0)) - | (high >> (shift - 128)) * ((shift < 256) & (shift > 128)); - - u128 new_high = high * (shift == 0) - | (high >> shift) * ((shift < 128) ^ (shift == 0)); - - return u256(new_high, new_low); - - // Unoptimized form: - // if (shift >= 256) - // return u256(0); - // else if (shift == 128) - // return u256(0, high); - // else if (shift == 0) - // return *this; - // else if (shift < 128) - // return u256(high >> shift, (high << (128 - shift)) | (low >> shift)); - // else if ((256 > shift) && (shift > 128)) - // return u256(0, (high >> (shift - 128))); - // else - // return u256(0); - } - - __device__ constexpr u256 operator>>=(unsigned rhs) - { - *this = *this >> rhs; - return *this; - } - - __device__ u256 operator*(const bool rhs) const - { - return u256(high * rhs, low * rhs); - } - - __device__ u256 operator*(const u256 rhs) const - { - // split values into 4 64-bit parts - u128 top[2] = {u128(low.high), u128(low.low)}; - u128 bottom[3] = {u128(rhs.high.low), u128(rhs.low.high), - u128(rhs.low.low)}; - - uint64_t tmp3_3 = high.high * rhs.low.low; - uint64_t tmp0_0 = low.low * rhs.high.high; - uint64_t tmp2_2 = high.low * rhs.low.high; - - u128 tmp2_3 = u128(high.low) * bottom[2]; - u128 tmp0_3 = top[1] * bottom[2]; - u128 tmp1_3 = top[0] * bottom[2]; - - u128 tmp0_2 = top[1] * bottom[1]; - u128 third64 = u128(tmp0_2.low) + u128(tmp0_3.high); - u128 tmp1_2 = top[0] * bottom[1]; - - u128 tmp0_1 = top[1] * bottom[0]; - u128 second64 = u128(tmp0_1.low) + u128(tmp0_2.high); - uint64_t first64 = tmp0_0 + tmp0_1.high; - - u128 tmp1_1 = top[0] * bottom[0]; - first64 += tmp1_1.low + tmp1_2.high; - - // second row - third64 += u128(tmp1_3.low); - second64 += u128(tmp1_2.low) + u128(tmp1_3.high); - - // third row - second64 += u128(tmp2_3.low); - first64 += tmp2_2 + tmp2_3.high; - - // fourth row - first64 += tmp3_3; - second64 += u128(third64.high); - first64 += second64.high; - - return u256(u128(first64, second64.low), u128(third64.low, tmp0_3.low)); - } - - __device__ u256 operator*=(const u256 rhs) - { - *this = *this * rhs; - return *this; - } - - // TODO: check if performance improves with a different limb size - u128 high; - u128 low; -}; - -#endif /* u256_h */ diff --git a/crates/math/src/gpu/cuda/shaders/utils.h b/crates/math/src/gpu/cuda/shaders/utils.h deleted file mode 100644 index 4099d8a5c..000000000 --- a/crates/math/src/gpu/cuda/shaders/utils.h +++ /dev/null @@ -1,17 +0,0 @@ -#ifndef UTILS_H -#define UTILS_H - -/// Reverses the `log2(size)` first bits of `i` -__device__ unsigned reverse_index(unsigned i, unsigned size) -{ - if (size == 1) - { // TODO: replace this statement with an alternative solution. - return i; - } - else - { - return __brev(i) >> (__clz(size) + 1); - } -} - -#endif // UTILS_H diff --git a/crates/math/src/gpu/mod.rs b/crates/math/src/gpu/mod.rs deleted file mode 100644 index 572db957b..000000000 --- a/crates/math/src/gpu/mod.rs +++ /dev/null @@ -1,2 +0,0 @@ -#[cfg(feature = "cuda")] -pub mod cuda; diff --git a/crates/math/src/helpers.rs b/crates/math/src/helpers.rs deleted file mode 100644 index 9a04e33b8..000000000 --- a/crates/math/src/helpers.rs +++ /dev/null @@ -1,27 +0,0 @@ -#[cfg(feature = "alloc")] -use crate::field::{element::FieldElement, traits::IsFFTField}; - -/// Computes the power of two that is equal or greater than n -pub fn next_power_of_two(n: u64) -> u64 { - if n <= 1 { - 1 - } else { - (u64::MAX >> (n - 1).leading_zeros()) + 1 - } -} - -/// Pads the trace table with zeros until the length of the columns of the trace -/// is equal to a power of 2 -/// This is required to ensure that we can use the radix-2 Cooley-Tukey FFT algorithm -#[cfg(feature = "alloc")] -pub fn resize_to_next_power_of_two( - trace_colums: &mut [alloc::vec::Vec>], -) { - trace_colums.iter_mut().for_each(|col| { - // TODO: Remove this unwrap. This may panic if the usize cant be - // casted into a u64. - let col_len = col.len().try_into().unwrap(); - let next_power_of_two_len = next_power_of_two(col_len); - col.resize(next_power_of_two_len as usize, FieldElement::::zero()) - }) -} diff --git a/crates/math/src/lib.rs b/crates/math/src/lib.rs deleted file mode 100644 index 1f5ae60d6..000000000 --- a/crates/math/src/lib.rs +++ /dev/null @@ -1,21 +0,0 @@ -#![cfg_attr(not(feature = "std"), no_std)] - -#[cfg(feature = "alloc")] -extern crate alloc; - -pub mod circle; -pub mod cyclic_group; -pub mod elliptic_curve; -pub mod errors; -pub mod field; -pub mod helpers; -pub mod traits; -pub mod unsigned_integer; - -pub mod gpu; - -// These modules don't work in no-std mode -pub mod fft; -pub mod msm; -#[cfg(feature = "alloc")] -pub mod polynomial; diff --git a/crates/math/src/msm/README.md b/crates/math/src/msm/README.md deleted file mode 100644 index 92f2915e5..000000000 --- a/crates/math/src/msm/README.md +++ /dev/null @@ -1,9 +0,0 @@ -# lambdaworks MultiScalar Multiplication (MSM) - -This contains implementations for the MultiScalar Multiplication (MSM): -- Naïve -- Pippenger - -[Multiscalar multiplication](https://blog.lambdaclass.com/multiscalar-multiplication-strategies-and-challenges/) is an important primitive that appears in some polynomial commitment schemes and proof systems. It is also at the core of [EIP-4844](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-4844.md). Given a set of scalars in a [finite field](../field/README.md) $a_0, a_1, ..., a_n$ and [elliptic curve points](../elliptic_curve/README.md) $P_0, P_1, ... , P_n$, the MSM computes -$$P = \sum_k a_k P_k$$ -where $a_k P_k$ is understood as applying the group operation with $P_k$ a number of $a_k$ times. For its application in a protocol, see [KZG](../../../crypto/src/commitments/README.md) \ No newline at end of file diff --git a/crates/math/src/msm/mod.rs b/crates/math/src/msm/mod.rs deleted file mode 100644 index a4980728d..000000000 --- a/crates/math/src/msm/mod.rs +++ /dev/null @@ -1,3 +0,0 @@ -pub mod naive; -#[cfg(feature = "alloc")] -pub mod pippenger; diff --git a/crates/math/src/msm/naive.rs b/crates/math/src/msm/naive.rs deleted file mode 100644 index fec773073..000000000 --- a/crates/math/src/msm/naive.rs +++ /dev/null @@ -1,115 +0,0 @@ -use core::fmt::Display; - -use crate::cyclic_group::IsGroup; -use crate::unsigned_integer::traits::IsUnsignedInteger; - -#[derive(Debug)] -pub enum MSMError { - LengthMismatch(usize, usize), -} - -impl Display for MSMError { - fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { - match self { - MSMError::LengthMismatch(cs, points) => write!(f, "`cs` and `points` must be of the same length to compute `msm`. Got: {cs} and {points}"), - } - } -} - -#[cfg(feature = "std")] -impl std::error::Error for MSMError {} - -/// This function computes the multiscalar multiplication (MSM). -/// -/// Assume a group G of order r is given. -/// Let `points = [g_1, ..., g_n]` be a tuple of group points in G and -/// let `cs = [k_1, ..., k_n]` be a tuple of scalars in the Galois field GF(r). -/// -/// Then, with additive notation, `msm(cs, points)` computes k_1 * g_1 + .... + k_n * g_n. -/// -/// If `points` and `cs` are empty, then `msm` returns the zero element of the group. -/// -/// Panics if `cs` and `points` have different lengths. -pub fn msm(cs: &[C], points: &[T]) -> Result -where - C: IsUnsignedInteger, - T: IsGroup, -{ - if cs.len() != points.len() { - return Err(MSMError::LengthMismatch(cs.len(), points.len())); - } - let res = cs - .iter() - .zip(points.iter()) - .map(|(&c, h)| h.operate_with_self(c)) - .reduce(|acc, x| acc.operate_with(&x)) - .unwrap_or_else(T::neutral_element); - - Ok(res) -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::elliptic_curve::short_weierstrass::curves::test_curve_1::TestCurve1; - use crate::elliptic_curve::short_weierstrass::point::ShortWeierstrassProjectivePoint; - use crate::elliptic_curve::traits::IsEllipticCurve; - use crate::field::fields::u64_prime_field::U64FieldElement; - - const ORDER_R: u64 = 5; - type FE = U64FieldElement; - - #[test] - fn msm_11_is_1_over_elliptic_curves() { - let c: [u64; 1] = [1]; - let hiding = [TestCurve1::generator()]; - assert_eq!(msm(&c, &hiding).unwrap(), TestCurve1::generator()); - } - - #[test] - fn msm_23_is_6_over_field_elements() { - let c: [u64; 1] = [3]; - let hiding = [FE::new(2)]; - assert_eq!(msm(&c, &hiding).unwrap(), FE::new(6)); - } - - #[test] - fn msm_23_is_6_over_elliptic_curves() { - let c: [u64; 1] = [3]; - let g = TestCurve1::generator(); - let hiding = [g.operate_with_self(2_u16)]; - assert_eq!(msm(&c, &hiding).unwrap(), g.operate_with_self(6_u16)); - } - - #[test] - fn msm_with_c_2_3_hiding_3_4_is_18_over_field_elements() { - let c: [u64; 2] = [2, 3]; - let hiding = [FE::new(3), FE::new(4)]; - assert_eq!(msm(&c, &hiding).unwrap(), FE::new(18)); - } - - #[test] - fn msm_with_c_2_3_hiding_3_4_is_18_over_elliptic_curves() { - let c: [u64; 2] = [2, 3]; - let g = TestCurve1::generator(); - let hiding = [g.operate_with_self(3_u16), g.operate_with_self(4_u16)]; - assert_eq!(msm(&c, &hiding).unwrap(), g.operate_with_self(18_u16)); - } - - #[test] - fn msm_with_empty_input_over_field_elements() { - let c: [u64; 0] = []; - let hiding: [FE; 0] = []; - assert_eq!(msm(&c, &hiding).unwrap(), FE::new(0)); - } - - #[test] - fn msm_with_empty_c_is_none_over_elliptic_curves() { - let c: [u64; 0] = []; - let hiding: [ShortWeierstrassProjectivePoint; 0] = []; - assert_eq!( - msm(&c, &hiding).unwrap(), - ShortWeierstrassProjectivePoint::neutral_element() - ); - } -} diff --git a/crates/math/src/msm/pippenger.rs b/crates/math/src/msm/pippenger.rs deleted file mode 100644 index bf1546384..000000000 --- a/crates/math/src/msm/pippenger.rs +++ /dev/null @@ -1,235 +0,0 @@ -use crate::{cyclic_group::IsGroup, unsigned_integer::element::UnsignedInteger}; - -use super::naive::MSMError; - -use alloc::vec; - -/// This function computes the multiscalar multiplication (MSM). -/// -/// Assume a group G of order r is given. -/// Let `points = [g_1, ..., g_n]` be a tuple of group points in G and -/// let `cs = [k_1, ..., k_n]` be a tuple of scalars in the Galois field GF(r). -/// -/// Then, with additive notation, `msm(cs, points)` computes k_1 * g_1 + .... + k_n * g_n. -/// -/// If `points` and `cs` are empty, then `msm` returns the zero element of the group. -/// -/// Panics if `cs` and `points` have different lengths. -pub fn msm( - cs: &[UnsignedInteger], - points: &[G], -) -> Result -where - G: IsGroup, -{ - if cs.len() != points.len() { - return Err(MSMError::LengthMismatch(cs.len(), points.len())); - } - - let window_size = optimum_window_size(cs.len()); - - Ok(msm_with(cs, points, window_size)) -} - -fn optimum_window_size(data_length: usize) -> usize { - const SCALE_FACTORS: (usize, usize) = (4, 5); - - // We approximate the optimum window size with: f(n) = k * log2(n), where k is a scaling factor - let len_isqrt = data_length.checked_ilog2().unwrap_or(0); - (len_isqrt as usize * SCALE_FACTORS.0) / SCALE_FACTORS.1 -} - -pub fn msm_with( - cs: &[UnsignedInteger], - points: &[G], - window_size: usize, -) -> G -where - G: IsGroup, -{ - // When input is small enough, windows of length 2 seem faster than 1. - const MIN_WINDOW_SIZE: usize = 2; - const MAX_WINDOW_SIZE: usize = 32; - - let window_size = window_size.clamp(MIN_WINDOW_SIZE, MAX_WINDOW_SIZE); - - // The number of windows of size `s` is ceil(lambda/s). - let num_windows = (64 * NUM_LIMBS - 1) / window_size + 1; - - // We define `buckets` outside of the loop so we only have to allocate once, and reuse it. - // - // This line forces a heap allocation which might be undesired. We can define this buckets - // variable in the Pippenger struct to only allocate once, but use a bit of extra memory. - // If we accept a const window_size, we could make it an array instaed of a vector - // avoiding the heap allocation. We should be aware if that might be too agressive for - // the stack and cause a potential stack overflow. - let n_buckets = (1 << window_size) - 1; - let mut buckets = vec![G::neutral_element(); n_buckets]; - - (0..num_windows) - .rev() - .map(|window_idx| { - // Put in the right bucket the corresponding ps[i] for the current window. - cs.iter().zip(points).for_each(|(k, p)| { - // We truncate the number to the least significative limb. - // This is ok because window_size < usize::BITS. - let window_unmasked = (k >> (window_idx * window_size)).limbs[NUM_LIMBS - 1]; - let m_ij = window_unmasked & n_buckets as u64; - if m_ij != 0 { - let idx = (m_ij - 1) as usize; - buckets[idx] = buckets[idx].operate_with(p); - } - }); - - // Do the reduction step for the buckets. - buckets - .iter_mut() - // This first part iterates buckets in descending order, generating an iterator with the sum of - // each bucket and all that came before as its items; i.e: (b_n, b_n + b_n-1, ..., b_n + ... + b_0) - .rev() - .scan(G::neutral_element(), |m, b| { - *m = m.operate_with(b); // Reduction step. - *b = G::neutral_element(); // Cleanup bucket slot to reuse in the next window. - Some(m.clone()) - }) - // This next part sums all elements of the iterator: (b_n) + (b_n + b_n-1) + ... - // This results in: (n + 1) * b_n + n * b_n-1 + ... + b_0 - .reduce(|g, m| g.operate_with(&m)) - .unwrap_or_else(G::neutral_element) - }) - // NOTE: this operation is non-associative and strictly sequential - .reduce(|t, g| t.operate_with_self(1_u64 << window_size).operate_with(&g)) - .unwrap_or_else(G::neutral_element) -} - -#[cfg(feature = "parallel")] -// It has the following differences with the sequential one: -// 1. It uses one vec per thread to store buckets. -// 2. It reduces all window results via a different method. -pub fn parallel_msm_with( - cs: &[UnsignedInteger], - points: &[G], - window_size: usize, -) -> G -where - G: IsGroup + Send + Sync, -{ - use rayon::prelude::*; - - assert!(window_size < usize::BITS as usize); // Program would go OOM anyways - - // The number of windows of size `s` is ceil(lambda/s). - let num_windows = (64 * NUM_LIMBS - 1) / window_size + 1; - let n_buckets = (1 << window_size) - 1; - - // TODO: limit the number of threads, and reuse vecs - (0..num_windows) - .into_par_iter() - .map(|window_idx| { - let mut buckets = vec![G::neutral_element(); n_buckets]; - // Put in the right bucket the corresponding ps[i] for the current window. - let shift = window_idx * window_size; - cs.iter().zip(points).for_each(|(k, p)| { - // We truncate the number to the least significative limb. - // This is ok because window_size < usize::BITS. - let window_unmasked = (k >> shift).limbs[NUM_LIMBS - 1]; - let m_ij = window_unmasked & n_buckets as u64; - if m_ij != 0 { - let idx = (m_ij - 1) as usize; - buckets[idx] = buckets[idx].operate_with(p); - } - }); - - let mut m = G::neutral_element(); - - // Do the reduction step for the buckets. - let window_item = buckets - // NOTE: changing this into a parallel iter drops performance, because of the - // need to use multiplication in the `map` step - .into_iter() - .rev() - .map(|b| { - m = m.operate_with(&b); // Reduction step. - m.clone() - }) - .reduce(|g, m| g.operate_with(&m)) - .unwrap_or_else(G::neutral_element); - - window_item.operate_with_self(UnsignedInteger::::from_u64(1) << shift) - }) - .reduce(G::neutral_element, |a, b| a.operate_with(&b)) -} - -#[cfg(test)] -mod tests { - use crate::cyclic_group::IsGroup; - use crate::msm::{naive, pippenger}; - use crate::{ - elliptic_curve::{ - short_weierstrass::curves::bls12_381::curve::BLS12381Curve, traits::IsEllipticCurve, - }, - unsigned_integer::element::UnsignedInteger, - }; - use alloc::vec::Vec; - use proptest::{collection, prelude::*, prop_assert_eq, prop_compose, proptest}; - - const _CASES: u32 = 20; - const _MAX_WSIZE: usize = 8; - const _MAX_LEN: usize = 30; - - prop_compose! { - fn unsigned_integer()(limbs: [u64; 6]) -> UnsignedInteger<6> { - UnsignedInteger::from_limbs(limbs) - } - } - - prop_compose! { - fn unsigned_integer_vec()(vec in collection::vec(unsigned_integer(), 0.._MAX_LEN)) -> Vec> { - vec - } - } - - prop_compose! { - fn point()(power: u128) -> ::PointRepresentation { - BLS12381Curve::generator().operate_with_self(power) - } - } - - prop_compose! { - fn points_vec()(vec in collection::vec(point(), 0.._MAX_LEN)) -> Vec<::PointRepresentation> { - vec - } - } - - proptest! { - #![proptest_config(ProptestConfig { - cases: _CASES, .. ProptestConfig::default() - })] - // Property-based test that ensures `pippenger::msm` gives same result as `naive::msm`. - #[test] - fn test_pippenger_matches_naive_msm(window_size in 1.._MAX_WSIZE, cs in unsigned_integer_vec(), points in points_vec()) { - let min_len = cs.len().min(points.len()); - let cs = cs[..min_len].to_vec(); - let points = points[..min_len].to_vec(); - - let pippenger = pippenger::msm_with(&cs, &points, window_size); - let naive = naive::msm(&cs, &points).unwrap(); - - prop_assert_eq!(naive, pippenger); - } - - // Property-based test that ensures `pippenger::msm_with` gives same result as `pippenger::parallel_msm_with`. - #[test] - #[cfg(feature = "parallel")] - fn test_parallel_pippenger_matches_sequential(window_size in 1.._MAX_WSIZE, cs in unsigned_integer_vec(), points in points_vec()) { - let min_len = cs.len().min(points.len()); - let cs = cs[..min_len].to_vec(); - let points = points[..min_len].to_vec(); - - let sequential = pippenger::msm_with(&cs, &points, window_size); - let parallel = pippenger::parallel_msm_with(&cs, &points, window_size); - - prop_assert_eq!(parallel, sequential); - } - } -} diff --git a/crates/math/src/polynomial/README.md b/crates/math/src/polynomial/README.md deleted file mode 100644 index ad8f22681..000000000 --- a/crates/math/src/polynomial/README.md +++ /dev/null @@ -1,131 +0,0 @@ -# lambdaworks Polynomials - -Contains all the relevant tools for polynomials. Supports: -- [Univariate polynomials](./mod.rs) -- [Dense Multivariate polynomials](../polynomial/dense_multilinear_poly.rs) and [Sparse Multilinear polynomials](../polynomial/sparse_multilinear_poly.rs) - -lambdaworks's polynomials work over [Finite Fields](../field/README.md). - -## Univariate polynomials - -Univariate polynomials are expressions of the form $p(x) = a_0 + a_1 x + a_2 x^2 + ... + a_n x^n$, where $x$ is the indeterminate and $a_0, a_1 , ... , a_n$ take values over a finite field. The power with the highest non-zero coefficient is called the degree of the polynomial. A univariate polynomial is represented by means of the following struct: -```rust -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct Polynomial { - pub coefficients: Vec, -} -``` -it contains the coefficients in increasing order (we start with the independent term, $a_0$, then $a_1$, and so on and so forth). To create a new polynomial, -```rust -let my_poly = Polynomial::new(&[FE::new(1), FE::new(2), FE::new(3)]) -``` -This creates the polynomial $p(x) = 1 + 2 x + 3 x^2$. If we provide additional zeros to the right, the `new` method will remove those unnecessary zeros. For example, -```rust -let my_poly = Polynomial::new(&[FE::new(1), FE::new(2), FE::new(3), FE::ZERO]) -``` -generates the same polynomial as before. We can also create a monomial, such as $5 x^4$ or $27 x^{120}$, which can be simpler sometimes (instead of providing a long list of zeros). To define a monomial, simply -```rust -let my_monomial = Polynomial::new_monomial(FE::new(27),6) -``` -generates the monomial $p(x) = 27 x^6$, which has a representation as polynomial $(0,0,0,0,0,0,27)$. - -Univariate polynomials have a [ring structure](https://en.wikipedia.org/wiki/Ring_(mathematics)): we can add, subtract, multiply and divide as we did with integers. For example, to add two polynomials, -```rust -let p_1 = Polynomial::new(&[FE::new(3), FE::new(4), FE::new(5)]) -let p_2 = Polynomial::new(&[FE::new(4), FE::new(6), FE::new(8)]) -let p_a = p_1 + p_2 -``` -Polynomial multiplication, -```rust -let p1 = Polynomial::new(&[FE::new(3), FE::new(3), FE::new(2)]); -let p2 = Polynomial::new(&[FE::new(4), FE::new(1)]); -assert_eq!( - p2 * p1, - Polynomial::new(&[FE::new(12), FE::new(15), FE::new(11), FE::new(2)]) - ); -``` -Division, -```rust -let p1 = Polynomial::new(&[FE::new(1), FE::new(3)]); -let p2 = Polynomial::new(&[FE::new(1), FE::new(3)]); -let p3 = p1.mul_with_ref(&p2); -assert_eq!(p3 / p2, p1); -``` -Note that, in the case of polynomial division, it may have a remainder. If you want to divide a polynomial $p(x)$ by $x - b$, you can use faster alternatives, such as `ruffini_division` or `ruffini_division_inplace`. - -Polynomials can also be evaluated at a point $x_0$ using `evaluate`. This provides the evaluation $p( x_0 ) = a_0 + a_1 x_0 + a_2 x_0^2 + ... + a_n x_0^n$. For example, -```rust -let p = Polynomial::new(&[FE::new(3), -FE::new(2), FE::new(4)]); -assert_eq!(p.evaluate(&FE::new(2)), FE::new(15)); -``` -evaluates the polynomial $p(x) = 3 - 2 x + 4 x^2$ at $2$ to yield $15$. If you need to evaluate at several points, you can use `evaluate_slice`. - -Alternatively, polynomials of degree $n$ can be defined by providing exactly $n + 1$ evaluations. For example, $p(1) = 1$ and $p(0) = 2$ defines a unique polynomial of degree $1$, $p(x) = 2 - x$. To obtain the coefficients of $p(x)$ we need to use the function `interpolate`, which takes to vectors, of equal length: the first contains the $x$ coordinates $(0,1)$ and the second, the $y$ components $(2,1)$ (note that we have to provide the evaluation points in the same order as their corresponding evaluations): -```rust -let p = Polynomial::interpolate(&[FE::new(0), FE::new(1)], &[FE::new(2), FE::new(1)]).unwrap(); -``` - -Many polynomial operations can go faster by using the [Fast Fourier Transform](../fft/polynomial.rs). - -## Multilinear polynomials - -Multilinear polynomials are useful to define multilinear extensions of functions, which then play an important role in proof systems involving the [sumcheck protocol](../../../provers/sumcheck/README.md). There are two ways to define multilinear polynomials: -- Dense -- Sparse - -Sparse is more convenient whenever the number of non-zero coefficients in the polynomial is small (compared to the length of the polynomial), avoiding the storage of unnecessary zeros. For dense multilinear polynomials we have the following structure, working over some field $F$: -```rust -pub struct DenseMultilinearPolynomial -where - ::BaseType: Send + Sync, -{ - evals: Vec>, - n_vars: usize, - len: usize, -} -``` -The polynomial is assumed to be given in evaluation form over the binary strings of length $\{0 , 1 \}^{n_{vars}}$. We can also interpret this as the coefficients of the polynomial with respect to the Lagrange basis polynomials over $\{0 , 1 \}^{n_{vars}}$. There are $2^{n_{vars}}$ Lagrange polynomials, given by the formula: -$L_k (x_0 , x_1 , ... , x_{n_{vars} - 1}) = \prod (x_j b_{kj} + (1 - x_j ) (1 - b_{kj} ))$ -where $b_{kj}$ are given by the binary decomposition of $k$, that is $k = \sum_j b_{kj} 2^j$. We can see that each such polynomial is equal to one over $\{b_{k0}, b_{k1} , ... b_{k (n_{vars} - 1)}}$ and zero for any other element in $\{0 , 1 \}^{n_{vars}}$. The polynomial is thus defined as -$p (x_0 , x_1, ... , x_{n_{vars} - 1} ) = \sum_k p(b_{k0}, b_{k1} , ... , b_{k (n_{vars} - 1)}) L_k (x_0 , x_1, ... , x_{n_{vars} - 1} )$ -Sometimes, we will use $L_k (j)$ to refer to the evaluation of $L_k$ at the binary decomposition of $j$, that is $j = \sum_k b_{k}2^k$. - -An advantage of Lagrange basis polynomials is that we can evaluate all $2^{n_{vars}}$ polynomials at a point $(r_0 , r_1 ... , r_{n_{vars} - 1})$ in $\mathcal{O}(2^{n_{vars}})$ operations (linear in the size of the number of polynomials). Refer to [Thaler's book](https://people.cs.georgetown.edu/jthaler/ProofsArgsAndZK.pdf) for more information. - -To create a new polynomial, provide a list of evaluations of $p$; the length of this list should be a power of 2. -```rust -pub fn new(mut evals: Vec>) -> Self { - while !evals.len().is_power_of_two() { - evals.push(FieldElement::zero()); - } - let len = evals.len(); - DenseMultilinearPolynomial { - n_vars: log_2(len), - evals, - len, - } -} -``` - -Dense multilinear polynomials allow you to access the fields `n_vars`, `len` and `evals` with the methods `pub fn num_vars(&self) -> usize`, `pub fn len(&self) -> usize` and `pub fn evals(&self) -> &Vec>`. - -If you want to evaluate outside $\{0 , 1 \}^{n_{vars}}$, you can use the functions `pub fn evaluate(&self, r: Vec>)` and `pub fn evaluate_with(evals: &[FieldElement], r: &[FieldElement])`, providing the point $r$ whose length must be $n_{vars}$. For evaluations over $\{0 , 1 \}^{n_{vars}}$, you can get the value directly from the list of evaluations defining the polynomial. For example, -```rust -// Example: Z = [1, 2, 1, 4] -let z = vec![FE::one(), FE::from(2u64), FE::one(), FE::from(4u64)]; -// r = [4, 3] -let r = vec![FE::from(4u64), FE::from(3u64)]; -let eval_with_lr = evaluate_with_lr(&z, &r); -let poly = DenseMultilinearPolynomial::new(z); -let eval = poly.evaluate(r).unwrap(); -assert_eq!(eval, FE::from(28u64)); -``` - -An important functionality is `pub fn to_univariate(&self) -> Polynomial>`, which converts a multilinear polynomial into a univariate polynomial, by summing over all variables over $\{0 , 1 \}^{n_{vars} - 1}$, leaving $x_{n_{vars} - 1}$ as the only variable, -$$f(x) = \sum_{(x_0 , x_1, ... , x_{n_{vars} - 2} ) \in \{0 , 1 \}^{n_{vars} - 1}} p(x_0 , x_1, ... , x_{n_{vars} - 2} , x)$$. For example, -```rust -let univar0 = prover.poly.to_univariate(); -``` -is used in the sumcheck protocol. - -Multilinear polynomials can be added and multiplied by scalars. \ No newline at end of file diff --git a/crates/math/src/polynomial/dense_multilinear_poly.rs b/crates/math/src/polynomial/dense_multilinear_poly.rs deleted file mode 100644 index 531eb1ff3..000000000 --- a/crates/math/src/polynomial/dense_multilinear_poly.rs +++ /dev/null @@ -1,401 +0,0 @@ -use crate::{ - field::{element::FieldElement, traits::IsField}, - polynomial::{error::MultilinearError, Polynomial}, -}; -use alloc::{vec, vec::Vec}; -use core::ops::{Add, Index, Mul}; -#[cfg(feature = "parallel")] -use rayon::iter::{IndexedParallelIterator, IntoParallelIterator, ParallelIterator}; - -/// Represents a multilinear polynomial as a vector of evaluations (FieldElements) in Lagrange basis. -#[derive(Debug, PartialEq, Clone)] -pub struct DenseMultilinearPolynomial -where - ::BaseType: Send + Sync, -{ - evals: Vec>, - n_vars: usize, - len: usize, -} - -impl DenseMultilinearPolynomial -where - ::BaseType: Send + Sync, -{ - /// Constructs a new multilinear polynomial from a collection of evaluations. - /// Pads non-power-of-2 evaluations with zeros. - pub fn new(mut evals: Vec>) -> Self { - while !evals.len().is_power_of_two() { - evals.push(FieldElement::zero()); - } - let len = evals.len(); - DenseMultilinearPolynomial { - n_vars: log_2(len), - evals, - len, - } - } - - /// Returns the number of variables. - pub fn num_vars(&self) -> usize { - self.n_vars - } - - /// Returns a reference to the evaluations vector. - pub fn evals(&self) -> &Vec> { - &self.evals - } - - /// Returns the total number of evaluations (2^num_vars). - #[allow(clippy::len_without_is_empty)] - pub fn len(&self) -> usize { - self.len - } - - /// Evaluates `self` at the point `r` (a vector of FieldElements) in O(n) time. - /// `r` must have a value for each variable. - pub fn evaluate(&self, r: Vec>) -> Result, MultilinearError> { - if r.len() != self.num_vars() { - return Err(MultilinearError::IncorrectNumberofEvaluationPoints( - r.len(), - self.num_vars(), - )); - } - let mut chis: Vec> = - vec![FieldElement::one(); (2usize).pow(r.len() as u32)]; - let mut size = 1; - for j in r { - size *= 2; - for i in (0..size).rev().step_by(2) { - let half_i = i / 2; - let temp = &chis[half_i] * &j; - chis[i] = temp; - chis[i - 1] = &chis[half_i] - &chis[i]; - } - } - #[cfg(feature = "parallel")] - let iter = (0..chis.len()).into_par_iter(); - #[cfg(not(feature = "parallel"))] - let iter = 0..chis.len(); - Ok(iter.map(|i| &self.evals[i] * &chis[i]).sum()) - } - - /// Evaluates a slice of evaluations with the given point `r`. - pub fn evaluate_with( - evals: &[FieldElement], - r: &[FieldElement], - ) -> Result, MultilinearError> { - let mut chis: Vec> = - vec![FieldElement::one(); (2usize).pow(r.len() as u32)]; - if chis.len() != evals.len() { - return Err(MultilinearError::ChisAndEvalsLengthMismatch( - chis.len(), - evals.len(), - )); - } - let mut size = 1; - for j in r { - size *= 2; - for i in (0..size).rev().step_by(2) { - let half_i = i / 2; - let temp = &chis[half_i] * j; - chis[i] = temp; - chis[i - 1] = &chis[half_i] - &chis[i]; - } - } - Ok((0..evals.len()).map(|i| &evals[i] * &chis[i]).sum()) - } - - /// Fixes the first variable to the given value `r` and returns a new DenseMultilinearPolynomial - /// with one fewer variable. - /// - /// Combines each pair of evaluations as: new_eval = a + r * (b - a) - /// This reduces the polynomial by one variable, allowing it to later be collapsed - /// into a univariate polynomial by summing over the remaining variables. - /// - /// Example (2 variables): evaluations are ordered as: - /// [f(0,0), f(0,1), f(1,0), f(1,1)] - /// Fixing the first variable `x = r` produces evaluations of a 1-variable polynomial: - /// [f(r,0), f(r,1)] - /// computed explicitly as: - /// f(r,0) = f(0,0) + r * ( f(1,0) - f(0,0)), - /// f(r,1) = f(0,1) + r * (f(1,1) - f(0,1)) - pub fn fix_first_variable(&self, r: &FieldElement) -> DenseMultilinearPolynomial { - let n = self.num_vars(); - assert!(n > 0, "Cannot fix variable in a 0-variable polynomial"); - let half = 1 << (n - 1); - let new_evals: Vec> = (0..half) - .map(|j| { - let a = &self.evals[j]; - let b = &self.evals[j + half]; - a + r * (b - a) - }) - .collect(); - DenseMultilinearPolynomial::from((n - 1, new_evals)) - } - - /// Returns the evaluations of the polynomial on the Boolean hypercube \(\{0,1\}^n\). - /// Since we are in Lagrange basis, this is just the elements stored in self.evals. - pub fn to_evaluations(&self) -> Vec> { - self.evals.clone() - } - - /// Collapses the last variable by fixing it to 0 and 1, - /// sums the evaluations, and returns a univariate polynomial (as a Polynomial) - /// of the form: sum0 + (sum1 - sum0) * x. - pub fn to_univariate(&self) -> Polynomial> { - let poly0 = self.fix_first_variable(&FieldElement::zero()); - let poly1 = self.fix_first_variable(&FieldElement::one()); - let sum0: FieldElement = poly0.to_evaluations().into_iter().sum(); - let sum1: FieldElement = poly1.to_evaluations().into_iter().sum(); - let diff = sum1 - &sum0; - Polynomial::new(&[sum0, diff]) - } - - /// Multiplies the polynomial by a scalar. - pub fn scalar_mul(&self, scalar: &FieldElement) -> Self { - let mut new_poly = self.clone(); - new_poly.evals.iter_mut().for_each(|eval| *eval *= scalar); - new_poly - } - - /// Extends this DenseMultilinearPolynomial by concatenating another polynomial of the same length. - pub fn extend(&mut self, other: &DenseMultilinearPolynomial) { - debug_assert_eq!(self.evals.len(), self.len); - debug_assert_eq!(other.evals.len(), self.len); - self.evals.extend(other.evals.iter().cloned()); - self.n_vars += 1; - self.len *= 2; - debug_assert_eq!(self.evals.len(), self.len); - } - - /// Merges a series of DenseMultilinearPolynomials into one polynomial. - /// Zero-pads the final merged polynomial to the next power-of-two length if necessary. - pub fn merge(polys: &[DenseMultilinearPolynomial]) -> DenseMultilinearPolynomial { - // TODO (performance): pre-allocate vector to avoid repeated resizing. - let mut z: Vec> = Vec::new(); - for poly in polys { - z.extend(poly.evals.iter().cloned()); - } - z.resize(z.len().next_power_of_two(), FieldElement::zero()); - DenseMultilinearPolynomial::new(z) - } - - /// Constructs a DenseMultilinearPolynomial from a slice of u64 values. - pub fn from_u64(evals: &[u64]) -> Self { - DenseMultilinearPolynomial::new(evals.iter().map(|&i| FieldElement::from(i)).collect()) - } -} - -impl Index for DenseMultilinearPolynomial -where - ::BaseType: Send + Sync, -{ - type Output = FieldElement; - - #[inline(always)] - fn index(&self, index: usize) -> &FieldElement { - &self.evals[index] - } -} - -/// Adds two DenseMultilinearPolynomials. -/// Assumes that both polynomials have the same number of variables. -impl Add for DenseMultilinearPolynomial -where - ::BaseType: Send + Sync, -{ - type Output = Result; - - fn add(self, other: Self) -> Self::Output { - if self.num_vars() != other.num_vars() { - return Err("Polynomials must have the same number of variables"); - } - #[cfg(feature = "parallel")] - let evals = self.evals.into_par_iter().zip(other.evals.into_par_iter()); - #[cfg(not(feature = "parallel"))] - let evals = self.evals.iter().zip(other.evals.iter()); - let sum: Vec> = evals.map(|(a, b)| a + b).collect(); - Ok(DenseMultilinearPolynomial::new(sum)) - } -} - -impl Mul> for DenseMultilinearPolynomial -where - ::BaseType: Send + Sync, -{ - type Output = DenseMultilinearPolynomial; - - fn mul(self, rhs: FieldElement) -> Self::Output { - Self::scalar_mul(&self, &rhs) - } -} - -impl Mul<&FieldElement> for DenseMultilinearPolynomial -where - ::BaseType: Send + Sync, -{ - type Output = DenseMultilinearPolynomial; - - fn mul(self, rhs: &FieldElement) -> Self::Output { - Self::scalar_mul(&self, rhs) - } -} - -/// Helper function to calculate log₂(n). -fn log_2(n: usize) -> usize { - if n == 0 { - return 0; - } - if n.is_power_of_two() { - (1usize.leading_zeros() - n.leading_zeros()) as usize - } else { - (0usize.leading_zeros() - n.leading_zeros()) as usize - } -} - -impl From<(usize, Vec>)> for DenseMultilinearPolynomial -where - ::BaseType: Send + Sync, -{ - fn from((num_vars, evaluations): (usize, Vec>)) -> Self { - assert_eq!( - evaluations.len(), - 1 << num_vars, - "The size of evaluations should be 2^num_vars." - ); - DenseMultilinearPolynomial { - n_vars: num_vars, - evals: evaluations, - len: 1 << num_vars, - } - } -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::field::fields::u64_prime_field::U64PrimeField; - const ORDER: u64 = 101; - type F = U64PrimeField; - type FE = FieldElement; - - pub fn evals(r: Vec) -> Vec { - let mut evals: Vec = vec![FE::one(); (2usize).pow(r.len() as u32)]; - let mut size = 1; - for j in r { - size *= 2; - for i in (0..size).rev().step_by(2) { - let scalar = evals[i / 2]; - evals[i] = scalar * j; - evals[i - 1] = scalar - evals[i]; - } - } - evals - } - - pub fn compute_factored_evals(r: Vec) -> (Vec, Vec) { - let size = r.len(); - let (left_num_vars, _right_num_vars) = (size / 2, size - size / 2); - let l = evals(r[..left_num_vars].to_vec()); - let r = evals(r[left_num_vars..size].to_vec()); - (l, r) - } - - fn evaluate_with_lr(z: &[FE], r: &[FE]) -> FE { - let (l, r) = compute_factored_evals(r.to_vec()); - let size = r.len(); - // Ensure size is even. - assert!(size % 2 == 0); - // n = 2^size - let n = (2usize).pow(size as u32); - // Compute m = sqrt(n) = 2^(l/2) - let m = (n as f64).sqrt() as usize; - // Compute vector-matrix product between L and Z (viewed as a matrix) - let lz = (0..m) - .map(|i| { - (0..m).fold(FE::zero(), |mut acc, j| { - acc += l[j] * z[j * m + i]; - acc - }) - }) - .collect::>(); - // Compute dot product between LZ and R - (0..lz.len()).map(|i| lz[i] * r[i]).sum() - } - - #[test] - fn evaluation() { - // Example: Z = [1, 2, 1, 4] - let z = vec![FE::one(), FE::from(2u64), FE::one(), FE::from(4u64)]; - // r = [4, 3] - let r = vec![FE::from(4u64), FE::from(3u64)]; - let eval_with_lr = evaluate_with_lr(&z, &r); - let poly = DenseMultilinearPolynomial::new(z); - let eval = poly.evaluate(r).unwrap(); - assert_eq!(eval, FE::from(28u64)); - assert_eq!(eval_with_lr, eval); - } - - #[test] - fn evaluate_with() { - let two = FE::from(2); - let z = vec![ - FE::zero(), - FE::zero(), - FE::zero(), - FE::one(), - FE::one(), - FE::one(), - FE::zero(), - two, - ]; - let x = vec![FE::one(), FE::one(), FE::one()]; - let y = DenseMultilinearPolynomial::::evaluate_with(z.as_slice(), x.as_slice()).unwrap(); - assert_eq!(y, two); - } - - #[test] - fn add() { - let a = DenseMultilinearPolynomial::new(vec![FE::from(3); 4]); - let b = DenseMultilinearPolynomial::new(vec![FE::from(7); 4]); - let c = a.add(b).unwrap(); - assert_eq!(*c.evals(), vec![FE::from(10); 4]); - } - - #[test] - fn mul() { - let a = DenseMultilinearPolynomial::new(vec![FE::from(3); 4]); - let b = a.mul(&FE::from(2)); - assert_eq!(*b.evals(), vec![FE::from(6); 4]); - } - - // Take a multilinear polynomial of length 2^2 and merge with a polynomial of 2^1. - // The resulting polynomial should be padded to length 2^3 = 8 and the last two evaluations should be FE::zero(). - #[test] - fn merge() { - let a = DenseMultilinearPolynomial::new(vec![FE::from(3); 4]); - let b = DenseMultilinearPolynomial::new(vec![FE::from(3); 2]); - let c = DenseMultilinearPolynomial::merge(&[a, b]); - assert_eq!(c.len(), 8); - assert_eq!(c[c.len() - 1], FE::zero()); - assert_eq!(c[c.len() - 2], FE::zero()); - } - - #[test] - fn extend() { - let mut a = DenseMultilinearPolynomial::new(vec![FE::from(3); 4]); - let b = DenseMultilinearPolynomial::new(vec![FE::from(3); 4]); - a.extend(&b); - assert_eq!(a.len(), 8); - assert_eq!(a.num_vars(), 3); - } - - #[test] - #[should_panic] - fn extend_unequal() { - let mut a = DenseMultilinearPolynomial::new(vec![FE::from(3); 4]); - let b = DenseMultilinearPolynomial::new(vec![FE::from(3); 2]); - a.extend(&b); - } -} diff --git a/crates/math/src/polynomial/error.rs b/crates/math/src/polynomial/error.rs deleted file mode 100644 index 69140c3e7..000000000 --- a/crates/math/src/polynomial/error.rs +++ /dev/null @@ -1,25 +0,0 @@ -use core::fmt::Display; - -#[derive(Debug)] -pub enum MultilinearError { - InvalidMergeLength, - IncorrectNumberofEvaluationPoints(usize, usize), - ChisAndEvalsLengthMismatch(usize, usize), -} - -impl Display for MultilinearError { - fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { - match self { - MultilinearError::InvalidMergeLength => write!(f, "Invalid Merge Length"), - MultilinearError::IncorrectNumberofEvaluationPoints(x, y) => { - write!(f, "points: {x}, vars: {y}") - } - MultilinearError::ChisAndEvalsLengthMismatch(x, y) => { - write!(f, "chis: {x}, evals: {y}") - } - } - } -} - -#[cfg(feature = "std")] -impl std::error::Error for MultilinearError {} diff --git a/crates/math/src/polynomial/mod.rs b/crates/math/src/polynomial/mod.rs deleted file mode 100644 index 6218df20e..000000000 --- a/crates/math/src/polynomial/mod.rs +++ /dev/null @@ -1,1313 +0,0 @@ -use super::field::element::FieldElement; -use crate::field::traits::{IsField, IsPrimeField, IsSubFieldOf}; -use alloc::string::{String, ToString}; -use alloc::{borrow::ToOwned, format, vec, vec::Vec}; -use core::{fmt::Display, ops, slice}; -pub mod dense_multilinear_poly; -mod error; -pub mod sparse_multilinear_poly; - -/// Represents the polynomial c_0 + c_1 * X + c_2 * X^2 + ... + c_n * X^n -/// as a vector of coefficients `[c_0, c_1, ... , c_n]` -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct Polynomial { - pub coefficients: Vec, -} - -impl Polynomial> { - /// Creates a new polynomial with the given coefficients - pub fn new(coefficients: &[FieldElement]) -> Self { - // Removes trailing zero coefficients at the end - let mut unpadded_coefficients = coefficients - .iter() - .rev() - .skip_while(|x| **x == FieldElement::zero()) - .cloned() - .collect::>>(); - unpadded_coefficients.reverse(); - Polynomial { - coefficients: unpadded_coefficients, - } - } - - /// Creates a new monomial term coefficient*x^degree - pub fn new_monomial(coefficient: FieldElement, degree: usize) -> Self { - let mut coefficients = vec![FieldElement::zero(); degree]; - coefficients.push(coefficient); - Self::new(&coefficients) - } - - /// Creates the null polynomial - pub fn zero() -> Self { - Self::new(&[]) - } - - /// Returns a polynomial that interpolates the points with x coordinates and y coordinates given by - /// `xs` and `ys`. - /// `xs` and `ys` must be the same length, and `xs` values should be unique. If not, panics. - /// In short, it finds P(x) such that P(xs[i]) = ys[i] - pub fn interpolate( - xs: &[FieldElement], - ys: &[FieldElement], - ) -> Result { - // TODO: try to use the type system to avoid this assert - if xs.len() != ys.len() { - return Err(InterpolateError::UnequalLengths(xs.len(), ys.len())); - } - if xs.is_empty() { - return Ok(Polynomial::new(&[])); - } - - let mut denominators = Vec::with_capacity(xs.len() * (xs.len() - 1) / 2); - let mut indexes = Vec::with_capacity(xs.len()); - - let mut idx = 0; - - for (i, xi) in xs.iter().enumerate().skip(1) { - indexes.push(idx); - for xj in xs.iter().take(i) { - if xi == xj { - return Err(InterpolateError::NonUniqueXs); - } - denominators.push(xi - xj); - idx += 1; - } - } - - FieldElement::inplace_batch_inverse(&mut denominators).unwrap(); - - let mut result = Polynomial::zero(); - - for (i, y) in ys.iter().enumerate() { - let mut y_term = Polynomial::new(slice::from_ref(y)); - for (j, x) in xs.iter().enumerate() { - if i == j { - continue; - } - let denominator = if i > j { - denominators[indexes[i - 1] + j].clone() - } else { - -&denominators[indexes[j - 1] + i] - }; - let denominator_poly = Polynomial::new(&[denominator]); - let numerator = Polynomial::new(&[-x, FieldElement::one()]); - y_term = y_term.mul_with_ref(&(numerator * denominator_poly)); - } - result = result + y_term; - } - Ok(result) - } - - /// Evaluates a polynomial P(t) at a point x, using Horner's algorithm - /// Returns y = P(x) - pub fn evaluate(&self, x: &FieldElement) -> FieldElement - where - E: IsField, - F: IsSubFieldOf, - { - self.coefficients - .iter() - .rev() - .fold(FieldElement::zero(), |acc, coeff| { - coeff + acc * x.to_owned() - }) - } - - /// Evaluates a polynomial P(t) at a slice of points x - /// Returns a vector y such that y[i] = P(input[i]) - pub fn evaluate_slice(&self, input: &[FieldElement]) -> Vec> { - input.iter().map(|x| self.evaluate(x)).collect() - } - - /// Returns the degree of a polynomial, which corresponds to the highest power of x^d - /// with non-zero coefficient - pub fn degree(&self) -> usize { - if self.coefficients.is_empty() { - 0 - } else { - self.coefficients.len() - 1 - } - } - - /// Returns the coefficient accompanying x^degree - pub fn leading_coefficient(&self) -> FieldElement { - if let Some(coefficient) = self.coefficients.last() { - coefficient.clone() - } else { - FieldElement::zero() - } - } - - /// Returns coefficients of the polynomial as an array - /// \[c_0, c_1, c_2, ..., c_n\] - /// that represents the polynomial - /// c_0 + c_1 * X + c_2 * X^2 + ... + c_n * X^n - pub fn coefficients(&self) -> &[FieldElement] { - &self.coefficients - } - - /// Returns the length of the vector of coefficients - pub fn coeff_len(&self) -> usize { - self.coefficients().len() - } - - /// Returns the derivative of the polynomial with respect to x. - pub fn differentiate(&self) -> Self { - let degree = self.degree(); - if degree == 0 { - return Polynomial::zero(); - } - let mut derivative = Vec::with_capacity(degree); - for (i, coeff) in self.coefficients().iter().enumerate().skip(1) { - derivative.push(FieldElement::::from(i as u64) * coeff); - } - Polynomial::new(&derivative) - } - - /// Computes quotient with `x - b` in place. - pub fn ruffini_division_inplace(&mut self, b: &FieldElement) { - let mut c = FieldElement::zero(); - for coeff in self.coefficients.iter_mut().rev() { - *coeff = &*coeff + b * &c; - core::mem::swap(coeff, &mut c); - } - self.coefficients.pop(); - } - - /// Computes the quotient of the division of P(x) with x - b using Ruffini's rule - pub fn ruffini_division(&self, b: &FieldElement) -> Polynomial> - where - L: IsField, - F: IsSubFieldOf, - { - if let Some(c) = self.coefficients.last() { - let mut c = c.clone().to_extension(); - let mut coefficients = Vec::with_capacity(self.degree()); - for coeff in self.coefficients.iter().rev().skip(1) { - coefficients.push(c.clone()); - c = coeff + c * b; - } - coefficients = coefficients.into_iter().rev().collect(); - Polynomial::new(&coefficients) - } else { - Polynomial::zero() - } - } - - /// Computes quotient and remainder of polynomial division. - /// - /// Output: (quotient, remainder) - pub fn long_division_with_remainder(self, dividend: &Self) -> (Self, Self) { - if dividend.degree() > self.degree() { - (Polynomial::zero(), self) - } else { - let mut n = self; - let mut q: Vec> = vec![FieldElement::zero(); n.degree() + 1]; - let denominator = dividend.leading_coefficient().inv().unwrap(); - while n != Polynomial::zero() && n.degree() >= dividend.degree() { - let new_coefficient = n.leading_coefficient() * &denominator; - q[n.degree() - dividend.degree()] = new_coefficient.clone(); - let d = dividend.mul_with_ref(&Polynomial::new_monomial( - new_coefficient, - n.degree() - dividend.degree(), - )); - n = n - d; - } - (Polynomial::new(&q), n) - } - } - - /// Extended Euclidean Algorithm for polynomials. - /// - /// This method computes the extended greatest common divisor (GCD) of two polynomials `self` and `y`. - /// It returns a tuple of three elements: `(a, b, g)` such that `a * self + b * y = g`, where `g` is the - /// greatest common divisor of `self` and `y`. - pub fn xgcd(&self, y: &Self) -> (Self, Self, Self) { - let one = Polynomial::new(&[FieldElement::one()]); - let zero = Polynomial::zero(); - let (mut old_r, mut r) = (self.clone(), y.clone()); - let (mut old_s, mut s) = (one.clone(), zero.clone()); - let (mut old_t, mut t) = (zero.clone(), one.clone()); - - while r != Polynomial::zero() { - let quotient = old_r.clone().div_with_ref(&r); - old_r = old_r - "ient * &r; - core::mem::swap(&mut old_r, &mut r); - old_s = old_s - "ient * &s; - core::mem::swap(&mut old_s, &mut s); - old_t = old_t - "ient * &t; - core::mem::swap(&mut old_t, &mut t); - } - - let lcinv = old_r.leading_coefficient().inv().unwrap(); - ( - old_s.scale_coeffs(&lcinv), - old_t.scale_coeffs(&lcinv), - old_r.scale_coeffs(&lcinv), - ) - } - - pub fn div_with_ref(self, dividend: &Self) -> Self { - let (quotient, _remainder) = self.long_division_with_remainder(dividend); - quotient - } - - pub fn mul_with_ref(&self, factor: &Self) -> Self { - let degree = self.degree() + factor.degree(); - let mut coefficients = vec![FieldElement::zero(); degree + 1]; - - if self.coefficients.is_empty() || factor.coefficients.is_empty() { - Polynomial::new(&[FieldElement::zero()]) - } else { - for i in 0..=factor.degree() { - if factor.coefficients[i] != FieldElement::zero() { - for j in 0..=self.degree() { - if self.coefficients[j] != FieldElement::zero() { - coefficients[i + j] += &factor.coefficients[i] * &self.coefficients[j]; - } - } - } - } - Polynomial::new(&coefficients) - } - } - - /// Scales the coefficients of a polynomial P by a factor - /// Returns P(factor * x) - pub fn scale>(&self, factor: &FieldElement) -> Self { - let scaled_coefficients = self - .coefficients - .iter() - .zip(core::iter::successors(Some(FieldElement::one()), |x| { - Some(x * factor) - })) - .map(|(coeff, power)| power * coeff) - .collect(); - Self { - coefficients: scaled_coefficients, - } - } - - /// Multiplies all coefficients by a factor - pub fn scale_coeffs(&self, factor: &FieldElement) -> Self { - let scaled_coefficients = self - .coefficients - .iter() - .map(|coeff| factor * coeff) - .collect(); - Self { - coefficients: scaled_coefficients, - } - } - - /// Returns a vector of polynomials [p₀, p₁, ..., p_{d-1}], where d is `number_of_parts`, such that `self` equals - /// p₀(Xᵈ) + Xp₁(Xᵈ) + ... + X^(d-1)p_{d-1}(Xᵈ). - /// - /// Example: if d = 2 and `self` is 3 X^3 + X^2 + 2X + 1, then `poly.break_in_parts(2)` - /// returns a vector with two polynomials `(p₀, p₁)`, where p₀ = X + 1 and p₁ = 3X + 2. - pub fn break_in_parts(&self, number_of_parts: usize) -> Vec { - let coef = self.coefficients(); - let mut parts: Vec = Vec::with_capacity(number_of_parts); - for i in 0..number_of_parts { - let coeffs: Vec<_> = coef - .iter() - .skip(i) - .step_by(number_of_parts) - .cloned() - .collect(); - parts.push(Polynomial::new(&coeffs)); - } - parts - } - - /// Embeds the coefficients of a polynomial into an extension field - /// For example, given a polynomial with coefficients in F_p, returns the same - /// polynomial with its coefficients as elements in F_{p^2} - pub fn to_extension(self) -> Polynomial> - where - F: IsSubFieldOf, - { - Polynomial { - coefficients: self - .coefficients - .into_iter() - .map(|x| x.to_extension::()) - .collect(), - } - } - - pub fn truncate(&self, k: usize) -> Self { - if k == 0 { - Self::zero() - } else { - Self::new(&self.coefficients[0..k.min(self.coefficients.len())]) - } - } - pub fn reverse(&self, d: usize) -> Self { - let mut coeffs = self.coefficients.clone(); - coeffs.resize(d + 1, FieldElement::zero()); - coeffs.reverse(); - Self::new(&coeffs) - } -} - -impl Polynomial> { - // Print the polynomial as a string ready to be used in SageMath, or just for pretty printing. - pub fn print_as_sage_poly(&self, var_name: Option) -> String { - let var_name = var_name.unwrap_or('x'); - if self.coefficients.is_empty() - || self.coefficients.len() == 1 && self.coefficients[0] == FieldElement::zero() - { - return String::new(); - } - - let mut string = String::new(); - let zero = FieldElement::::zero(); - - for (i, coeff) in self.coefficients.iter().rev().enumerate() { - if *coeff == zero { - continue; - } - - let coeff_str = coeff.representative().to_string(); - - if i == self.coefficients.len() - 1 { - string.push_str(&coeff_str); - } else if i == self.coefficients.len() - 2 { - string.push_str(&format!("{coeff_str}*{var_name} + ")); - } else { - string.push_str(&format!( - "{}*{}^{} + ", - coeff_str, - var_name, - self.coefficients.len() - 1 - i - )); - } - } - - string - } -} - -/// Pads a polynomial with zeros until the desired length -/// This function can be useful when evaluating polynomials with the FFT -pub fn pad_with_zero_coefficients_to_length( - pa: &mut Polynomial>, - n: usize, -) { - pa.coefficients.resize(n, FieldElement::zero()); -} - -/// Pads polynomial representations with minimum number of zeros to match lengths. -pub fn pad_with_zero_coefficients>( - pa: &Polynomial>, - pb: &Polynomial>, -) -> (Polynomial>, Polynomial>) { - let mut pa = pa.clone(); - let mut pb = pb.clone(); - - if pa.coefficients.len() > pb.coefficients.len() { - pad_with_zero_coefficients_to_length(&mut pb, pa.coefficients.len()); - } else { - pad_with_zero_coefficients_to_length(&mut pa, pb.coefficients.len()); - } - (pa, pb) -} - -/// Computes the composition of polynomials P1(t) and P2(t), that is P1(P2(t)) -/// It uses interpolation to determine the evaluation at points x_i and evaluates -/// P1(P2(x[i])). The interpolation theorem ensures that we can reconstruct the polynomial -/// uniquely by interpolation over a suitable number of points -/// This is an inefficient version, for something more efficient, use FFT for evaluation, -/// provided the field satisfies the necessary traits -pub fn compose( - poly_1: &Polynomial>, - poly_2: &Polynomial>, -) -> Polynomial> -where - F: IsField, -{ - let max_degree: u64 = (poly_1.degree() * poly_2.degree()) as u64; - - let mut interpolation_points = vec![]; - for i in 0_u64..max_degree + 1 { - interpolation_points.push(FieldElement::::from(i)); - } - - let values: Vec<_> = interpolation_points - .iter() - .map(|value| { - let intermediate_value = poly_2.evaluate(value); - poly_1.evaluate(&intermediate_value) - }) - .collect(); - - Polynomial::interpolate(interpolation_points.as_slice(), values.as_slice()) - .expect("xs and ys have equal length and xs are unique") -} - -// impl Add -impl ops::Add<&Polynomial>> for &Polynomial> -where - L: IsField, - F: IsSubFieldOf, -{ - type Output = Polynomial>; - - fn add(self, a_polynomial: &Polynomial>) -> Self::Output { - let (pa, pb) = pad_with_zero_coefficients(self, a_polynomial); - let iter_coeff_pa = pa.coefficients.iter(); - let iter_coeff_pb = pb.coefficients.iter(); - let new_coefficients = iter_coeff_pa.zip(iter_coeff_pb).map(|(x, y)| x + y); - let new_coefficients_vec = new_coefficients.collect::>>(); - Polynomial::new(&new_coefficients_vec) - } -} - -impl ops::Add>> for Polynomial> -where - L: IsField, - F: IsSubFieldOf, -{ - type Output = Polynomial>; - - fn add(self, a_polynomial: Polynomial>) -> Polynomial> { - &self + &a_polynomial - } -} - -impl ops::Add<&Polynomial>> for Polynomial> -where - L: IsField, - F: IsSubFieldOf, -{ - type Output = Polynomial>; - - fn add(self, a_polynomial: &Polynomial>) -> Polynomial> { - &self + a_polynomial - } -} - -impl ops::Add>> for &Polynomial> -where - L: IsField, - F: IsSubFieldOf, -{ - type Output = Polynomial>; - - fn add(self, a_polynomial: Polynomial>) -> Polynomial> { - self + &a_polynomial - } -} - -// impl neg, that is, additive inverse for polynomials P(t) + Q(t) = 0 -impl ops::Neg for &Polynomial> { - type Output = Polynomial>; - - fn neg(self) -> Polynomial> { - let neg = self - .coefficients - .iter() - .map(|x| -x) - .collect::>>(); - Polynomial::new(&neg) - } -} - -impl ops::Neg for Polynomial> { - type Output = Polynomial>; - - fn neg(self) -> Polynomial> { - -&self - } -} - -// impl Sub -impl ops::Sub<&Polynomial>> for &Polynomial> -where - L: IsField, - F: IsSubFieldOf, -{ - type Output = Polynomial>; - - fn sub(self, substrahend: &Polynomial>) -> Polynomial> { - self + (-substrahend) - } -} - -impl ops::Sub>> for Polynomial> -where - L: IsField, - F: IsSubFieldOf, -{ - type Output = Polynomial>; - - fn sub(self, substrahend: Polynomial>) -> Polynomial> { - &self - &substrahend - } -} - -impl ops::Sub<&Polynomial>> for Polynomial> -where - L: IsField, - F: IsSubFieldOf, -{ - type Output = Polynomial>; - - fn sub(self, substrahend: &Polynomial>) -> Polynomial> { - &self - substrahend - } -} - -impl ops::Sub>> for &Polynomial> -where - L: IsField, - F: IsSubFieldOf, -{ - type Output = Polynomial>; - - fn sub(self, substrahend: Polynomial>) -> Polynomial> { - self - &substrahend - } -} - -impl ops::Div>> for Polynomial> -where - F: IsField, -{ - type Output = Polynomial>; - - fn div(self, dividend: Polynomial>) -> Polynomial> { - self.div_with_ref(÷nd) - } -} - -impl ops::Mul<&Polynomial>> for &Polynomial> { - type Output = Polynomial>; - fn mul(self, factor: &Polynomial>) -> Polynomial> { - self.mul_with_ref(factor) - } -} - -impl ops::Mul>> for Polynomial> { - type Output = Polynomial>; - fn mul(self, factor: Polynomial>) -> Polynomial> { - &self * &factor - } -} - -impl ops::Mul>> for &Polynomial> { - type Output = Polynomial>; - fn mul(self, factor: Polynomial>) -> Polynomial> { - self * &factor - } -} - -impl ops::Mul<&Polynomial>> for Polynomial> { - type Output = Polynomial>; - fn mul(self, factor: &Polynomial>) -> Polynomial> { - &self * factor - } -} - -/* Operations between Polynomials and field elements */ -/* Multiplication field element at left */ -impl ops::Mul> for Polynomial> -where - L: IsField, - F: IsSubFieldOf, -{ - type Output = Polynomial>; - - fn mul(self, multiplicand: FieldElement) -> Polynomial> { - let new_coefficients = self - .coefficients - .iter() - .map(|value| &multiplicand * value) - .collect(); - Polynomial { - coefficients: new_coefficients, - } - } -} - -impl ops::Mul<&FieldElement> for &Polynomial> -where - L: IsField, - F: IsSubFieldOf, -{ - type Output = Polynomial>; - - fn mul(self, multiplicand: &FieldElement) -> Polynomial> { - self.clone() * multiplicand.clone() - } -} - -impl ops::Mul> for &Polynomial> -where - L: IsField, - F: IsSubFieldOf, -{ - type Output = Polynomial>; - - fn mul(self, multiplicand: FieldElement) -> Polynomial> { - self * &multiplicand - } -} - -impl ops::Mul<&FieldElement> for Polynomial> -where - L: IsField, - F: IsSubFieldOf, -{ - type Output = Polynomial>; - - fn mul(self, multiplicand: &FieldElement) -> Polynomial> { - &self * multiplicand - } -} - -/* Multiplication field element at right */ -impl ops::Mul<&Polynomial>> for &FieldElement -where - L: IsField, - F: IsSubFieldOf, -{ - type Output = Polynomial>; - - fn mul(self, multiplicand: &Polynomial>) -> Polynomial> { - multiplicand * self - } -} - -impl ops::Mul>> for &FieldElement -where - L: IsField, - F: IsSubFieldOf, -{ - type Output = Polynomial>; - - fn mul(self, multiplicand: Polynomial>) -> Polynomial> { - &multiplicand * self - } -} - -impl ops::Mul<&Polynomial>> for FieldElement -where - L: IsField, - F: IsSubFieldOf, -{ - type Output = Polynomial>; - - fn mul(self, multiplicand: &Polynomial>) -> Polynomial> { - multiplicand * self - } -} - -impl ops::Mul>> for FieldElement -where - L: IsField, - F: IsSubFieldOf, -{ - type Output = Polynomial>; - - fn mul(self, multiplicand: Polynomial>) -> Polynomial> { - &multiplicand * &self - } -} - -/* Addition field element at left */ -impl ops::Add<&FieldElement> for &Polynomial> -where - L: IsField, - F: IsSubFieldOf, -{ - type Output = Polynomial>; - - fn add(self, other: &FieldElement) -> Polynomial> { - Polynomial::new_monomial(other.clone(), 0) + self - } -} - -impl ops::Add> for Polynomial> -where - L: IsField, - F: IsSubFieldOf, -{ - type Output = Polynomial>; - - fn add(self, other: FieldElement) -> Polynomial> { - &self + &other - } -} - -impl ops::Add> for &Polynomial> -where - L: IsField, - F: IsSubFieldOf, -{ - type Output = Polynomial>; - - fn add(self, other: FieldElement) -> Polynomial> { - self + &other - } -} - -impl ops::Add<&FieldElement> for Polynomial> -where - L: IsField, - F: IsSubFieldOf, -{ - type Output = Polynomial>; - - fn add(self, other: &FieldElement) -> Polynomial> { - &self + other - } -} - -/* Addition field element at right */ -impl ops::Add<&Polynomial>> for &FieldElement -where - L: IsField, - F: IsSubFieldOf, -{ - type Output = Polynomial>; - - fn add(self, other: &Polynomial>) -> Polynomial> { - Polynomial::new_monomial(self.clone(), 0) + other - } -} - -impl ops::Add>> for FieldElement -where - L: IsField, - F: IsSubFieldOf, -{ - type Output = Polynomial>; - - fn add(self, other: Polynomial>) -> Polynomial> { - &self + &other - } -} - -impl ops::Add>> for &FieldElement -where - L: IsField, - F: IsSubFieldOf, -{ - type Output = Polynomial>; - - fn add(self, other: Polynomial>) -> Polynomial> { - self + &other - } -} - -impl ops::Add<&Polynomial>> for FieldElement -where - L: IsField, - F: IsSubFieldOf, -{ - type Output = Polynomial>; - - fn add(self, other: &Polynomial>) -> Polynomial> { - &self + other - } -} - -/* Substraction field element at left */ -impl ops::Sub<&FieldElement> for &Polynomial> -where - L: IsField, - F: IsSubFieldOf, -{ - type Output = Polynomial>; - - fn sub(self, other: &FieldElement) -> Polynomial> { - -Polynomial::new_monomial(other.clone(), 0) + self - } -} - -impl ops::Sub> for Polynomial> -where - L: IsField, - F: IsSubFieldOf, -{ - type Output = Polynomial>; - - fn sub(self, other: FieldElement) -> Polynomial> { - &self - &other - } -} - -impl ops::Sub> for &Polynomial> -where - L: IsField, - F: IsSubFieldOf, -{ - type Output = Polynomial>; - - fn sub(self, other: FieldElement) -> Polynomial> { - self - &other - } -} - -impl ops::Sub<&FieldElement> for Polynomial> -where - L: IsField, - F: IsSubFieldOf, -{ - type Output = Polynomial>; - - fn sub(self, other: &FieldElement) -> Polynomial> { - &self - other - } -} - -/* Substraction field element at right */ -impl ops::Sub<&Polynomial>> for &FieldElement -where - L: IsField, - F: IsSubFieldOf, -{ - type Output = Polynomial>; - - fn sub(self, other: &Polynomial>) -> Polynomial> { - Polynomial::new_monomial(self.clone(), 0) - other - } -} - -impl ops::Sub>> for FieldElement -where - L: IsField, - F: IsSubFieldOf, -{ - type Output = Polynomial>; - - fn sub(self, other: Polynomial>) -> Polynomial> { - &self - &other - } -} - -impl ops::Sub>> for &FieldElement -where - L: IsField, - F: IsSubFieldOf, -{ - type Output = Polynomial>; - - fn sub(self, other: Polynomial>) -> Polynomial> { - self - &other - } -} - -impl ops::Sub<&Polynomial>> for FieldElement -where - L: IsField, - F: IsSubFieldOf, -{ - type Output = Polynomial>; - - fn sub(self, other: &Polynomial>) -> Polynomial> { - &self - other - } -} - -#[derive(Debug)] -pub enum InterpolateError { - UnequalLengths(usize, usize), - NonUniqueXs, -} - -impl Display for InterpolateError { - fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { - match self { - InterpolateError::UnequalLengths(x, y) => { - write!(f, "xs and ys must be the same length. Got: {x} != {y}") - } - InterpolateError::NonUniqueXs => write!(f, "xs values should be unique."), - } - } -} - -#[cfg(feature = "std")] -impl std::error::Error for InterpolateError {} - -#[cfg(test)] -mod tests { - use crate::field::fields::u64_prime_field::U64PrimeField; - - // Some of these tests work when the finite field has order greater than 2. - use super::*; - const ORDER: u64 = 23; - type F = U64PrimeField; - type FE = FieldElement; - - fn polynomial_a() -> Polynomial { - Polynomial::new(&[FE::new(1), FE::new(2), FE::new(3)]) - } - - fn polynomial_minus_a() -> Polynomial { - Polynomial::new(&[FE::new(ORDER - 1), FE::new(ORDER - 2), FE::new(ORDER - 3)]) - } - - fn polynomial_b() -> Polynomial { - Polynomial::new(&[FE::new(3), FE::new(4), FE::new(5)]) - } - - fn polynomial_a_plus_b() -> Polynomial { - Polynomial::new(&[FE::new(4), FE::new(6), FE::new(8)]) - } - - fn polynomial_b_minus_a() -> Polynomial { - Polynomial::new(&[FE::new(2), FE::new(2), FE::new(2)]) - } - - #[test] - fn adding_a_and_b_equals_a_plus_b() { - assert_eq!(polynomial_a() + polynomial_b(), polynomial_a_plus_b()); - } - - #[test] - fn adding_a_and_a_plus_b_does_not_equal_b() { - assert_ne!(polynomial_a() + polynomial_a_plus_b(), polynomial_b()); - } - - #[test] - fn add_5_to_0_is_5() { - let p1 = Polynomial::new(&[FE::new(5)]); - let p2 = Polynomial::new(&[FE::new(0)]); - assert_eq!(p1 + p2, Polynomial::new(&[FE::new(5)])); - } - - #[test] - fn add_0_to_5_is_5() { - let p1 = Polynomial::new(&[FE::new(5)]); - let p2 = Polynomial::new(&[FE::new(0)]); - assert_eq!(p2 + p1, Polynomial::new(&[FE::new(5)])); - } - - #[test] - fn negating_0_returns_0() { - let p1 = Polynomial::new(&[FE::new(0)]); - assert_eq!(-p1, Polynomial::new(&[FE::new(0)])); - } - - #[test] - fn negating_a_is_equal_to_minus_a() { - assert_eq!(-polynomial_a(), polynomial_minus_a()); - } - - #[test] - fn negating_a_is_not_equal_to_a() { - assert_ne!(-polynomial_a(), polynomial_a()); - } - - #[test] - fn substracting_5_5_gives_0() { - let p1 = Polynomial::new(&[FE::new(5)]); - let p2 = Polynomial::new(&[FE::new(5)]); - let p3 = Polynomial::new(&[FE::new(0)]); - assert_eq!(p1 - p2, p3); - } - - #[test] - fn substracting_b_and_a_equals_b_minus_a() { - assert_eq!(polynomial_b() - polynomial_a(), polynomial_b_minus_a()); - } - - #[test] - fn constructor_removes_zeros_at_the_end_of_polynomial() { - let p1 = Polynomial::new(&[FE::new(3), FE::new(4), FE::new(0)]); - assert_eq!(p1.coefficients, &[FE::new(3), FE::new(4)]); - } - - #[test] - fn pad_with_zero_coefficients_returns_polynomials_with_zeros_until_matching_size() { - let p1 = Polynomial::new(&[FE::new(3), FE::new(4)]); - let p2 = Polynomial::new(&[FE::new(3)]); - - assert_eq!(p2.coefficients, &[FE::new(3)]); - let (pp1, pp2) = pad_with_zero_coefficients(&p1, &p2); - assert_eq!(pp1, p1); - assert_eq!(pp2.coefficients, &[FE::new(3), FE::new(0)]); - } - - #[test] - fn multiply_5_and_0_is_0() { - let p1 = Polynomial::new(&[FE::new(5)]); - let p2 = Polynomial::new(&[FE::new(0)]); - assert_eq!(p1 * p2, Polynomial::new(&[FE::new(0)])); - } - - #[test] - fn multiply_0_and_x_is_0() { - let p1 = Polynomial::new(&[FE::new(0)]); - let p2 = Polynomial::new(&[FE::new(0), FE::new(1)]); - assert_eq!(p1 * p2, Polynomial::new(&[FE::new(0)])); - } - - #[test] - fn multiply_2_by_3_is_6() { - let p1 = Polynomial::new(&[FE::new(2)]); - let p2 = Polynomial::new(&[FE::new(3)]); - assert_eq!(p1 * p2, Polynomial::new(&[FE::new(6)])); - } - - #[test] - fn multiply_2xx_3x_3_times_x_4() { - let p1 = Polynomial::new(&[FE::new(3), FE::new(3), FE::new(2)]); - let p2 = Polynomial::new(&[FE::new(4), FE::new(1)]); - assert_eq!( - p1 * p2, - Polynomial::new(&[FE::new(12), FE::new(15), FE::new(11), FE::new(2)]) - ); - } - - #[test] - fn multiply_x_4_times_2xx_3x_3() { - let p1 = Polynomial::new(&[FE::new(3), FE::new(3), FE::new(2)]); - let p2 = Polynomial::new(&[FE::new(4), FE::new(1)]); - assert_eq!( - p2 * p1, - Polynomial::new(&[FE::new(12), FE::new(15), FE::new(11), FE::new(2)]) - ); - } - - #[test] - fn division_works() { - let p1 = Polynomial::new(&[FE::new(1), FE::new(3)]); - let p2 = Polynomial::new(&[FE::new(1), FE::new(3)]); - let p3 = p1.mul_with_ref(&p2); - assert_eq!(p3 / p2, p1); - } - - #[test] - fn division_by_zero_degree_polynomial_works() { - let four = FE::new(4); - let two = FE::new(2); - let p1 = Polynomial::new(&[four, four]); - let p2 = Polynomial::new(&[two]); - assert_eq!(Polynomial::new(&[two, two]), p1 / p2); - } - - #[test] - fn evaluate_constant_polynomial_returns_constant() { - let three = FE::new(3); - let p = Polynomial::new(&[three]); - assert_eq!(p.evaluate(&FE::new(10)), three); - } - - #[test] - fn evaluate_slice() { - let three = FE::new(3); - let p = Polynomial::new(&[three]); - let ret = p.evaluate_slice(&[FE::new(10), FE::new(15)]); - assert_eq!(ret, [three, three]); - } - - #[test] - fn create_degree_0_new_monomial() { - assert_eq!( - Polynomial::new_monomial(FE::new(3), 0), - Polynomial::new(&[FE::new(3)]) - ); - } - - #[test] - fn zero_poly_evals_0_in_3() { - assert_eq!( - Polynomial::new_monomial(FE::new(0), 0).evaluate(&FE::new(3)), - FE::new(0) - ); - } - - #[test] - fn evaluate_degree_1_new_monomial() { - let two = FE::new(2); - let four = FE::new(4); - let p = Polynomial::new_monomial(two, 1); - assert_eq!(p.evaluate(&two), four); - } - - #[test] - fn evaluate_degree_2_monomyal() { - let two = FE::new(2); - let eight = FE::new(8); - let p = Polynomial::new_monomial(two, 2); - assert_eq!(p.evaluate(&two), eight); - } - - #[test] - fn evaluate_3_term_polynomial() { - let p = Polynomial::new(&[FE::new(3), -FE::new(2), FE::new(4)]); - assert_eq!(p.evaluate(&FE::new(2)), FE::new(15)); - } - - #[test] - fn simple_interpolating_polynomial_by_hand_works() { - let denominator = Polynomial::new(&[FE::new(1) * (FE::new(2) - FE::new(4)).inv().unwrap()]); - let numerator = Polynomial::new(&[-FE::new(4), FE::new(1)]); - let interpolating = numerator * denominator; - assert_eq!( - (FE::new(2) - FE::new(4)) * (FE::new(1) * (FE::new(2) - FE::new(4)).inv().unwrap()), - FE::new(1) - ); - assert_eq!(interpolating.evaluate(&FE::new(2)), FE::new(1)); - assert_eq!(interpolating.evaluate(&FE::new(4)), FE::new(0)); - } - - #[test] - fn interpolate_x_2_y_3() { - let p = Polynomial::interpolate(&[FE::new(2)], &[FE::new(3)]).unwrap(); - assert_eq!(FE::new(3), p.evaluate(&FE::new(2))); - } - - #[test] - fn interpolate_x_0_2_y_3_4() { - let p = - Polynomial::interpolate(&[FE::new(0), FE::new(2)], &[FE::new(3), FE::new(4)]).unwrap(); - assert_eq!(FE::new(3), p.evaluate(&FE::new(0))); - assert_eq!(FE::new(4), p.evaluate(&FE::new(2))); - } - - #[test] - fn interpolate_x_2_5_7_y_10_19_43() { - let p = Polynomial::interpolate( - &[FE::new(2), FE::new(5), FE::new(7)], - &[FE::new(10), FE::new(19), FE::new(43)], - ) - .unwrap(); - - assert_eq!(FE::new(10), p.evaluate(&FE::new(2))); - assert_eq!(FE::new(19), p.evaluate(&FE::new(5))); - assert_eq!(FE::new(43), p.evaluate(&FE::new(7))); - } - - #[test] - fn interpolate_x_0_0_y_1_1() { - let p = - Polynomial::interpolate(&[FE::new(0), FE::new(1)], &[FE::new(0), FE::new(1)]).unwrap(); - - assert_eq!(FE::new(0), p.evaluate(&FE::new(0))); - assert_eq!(FE::new(1), p.evaluate(&FE::new(1))); - } - - #[test] - fn interpolate_x_0_y_0() { - let p = Polynomial::interpolate(&[FE::new(0)], &[FE::new(0)]).unwrap(); - assert_eq!(FE::new(0), p.evaluate(&FE::new(0))); - } - - #[test] - fn composition_works() { - let p = Polynomial::new(&[FE::new(0), FE::new(2)]); - let q = Polynomial::new(&[FE::new(0), FE::new(0), FE::new(1)]); - assert_eq!( - compose(&p, &q), - Polynomial::new(&[FE::new(0), FE::new(0), FE::new(2)]) - ); - } - - #[test] - fn break_in_parts() { - // p = 3 X^3 + X^2 + 2X + 1 - let p = Polynomial::new(&[FE::new(1), FE::new(2), FE::new(1), FE::new(3)]); - let p0_expected = Polynomial::new(&[FE::new(1), FE::new(1)]); - let p1_expected = Polynomial::new(&[FE::new(2), FE::new(3)]); - let parts = p.break_in_parts(2); - assert_eq!(parts.len(), 2); - let p0 = &parts[0]; - let p1 = &parts[1]; - assert_eq!(p0, &p0_expected); - assert_eq!(p1, &p1_expected); - } - - use proptest::prelude::*; - proptest! { - #[test] - fn ruffini_inplace_equals_division(p in any::>(), b in any::()) { - let p: Vec<_> = p.into_iter().map(FE::from).collect(); - let mut p = Polynomial::new(&p); - let b = FE::from(b); - - let p_ref = p.clone(); - let m = Polynomial::new_monomial(FE::one(), 1) - b; - - p.ruffini_division_inplace(&b); - prop_assert_eq!(p, p_ref / m); - } - } - - proptest! { - #[test] - fn ruffini_inplace_equals_ruffini(p in any::>(), b in any::()) { - let p: Vec<_> = p.into_iter().map(FE::from).collect(); - let mut p = Polynomial::new(&p); - let b = FE::from(b); - let q = p.ruffini_division(&b); - p.ruffini_division_inplace(&b); - prop_assert_eq!(q, p); - } - } - #[test] - fn test_xgcd() { - // Case 1: Simple polynomials - let p1 = Polynomial::new(&[FE::new(1), FE::new(0), FE::new(1)]); // x^2 + 1 - let p2 = Polynomial::new(&[FE::new(1), FE::new(1)]); // x + 1 - let (a, b, g) = p1.xgcd(&p2); - // Check that a * p1 + b * p2 = g - let lhs = a.mul_with_ref(&p1) + b.mul_with_ref(&p2); - assert_eq!(a, Polynomial::new(&[FE::new(12)])); - assert_eq!(b, Polynomial::new(&[FE::new(12), FE::new(11)])); - assert_eq!(lhs, g); - assert_eq!(g, Polynomial::new(&[FE::new(1)])); - - // x^2-1 : - let p3 = Polynomial::new(&[FE::new(ORDER - 1), FE::new(0), FE::new(1)]); - // x^3-x = x(x^2-1) - let p4 = Polynomial::new(&[FE::new(0), FE::new(ORDER - 1), FE::new(0), FE::new(1)]); - let (a, b, g) = p3.xgcd(&p4); - - let lhs = a.mul_with_ref(&p3) + b.mul_with_ref(&p4); - assert_eq!(a, Polynomial::new(&[FE::new(1)])); - assert_eq!(b, Polynomial::zero()); - assert_eq!(lhs, g); - assert_eq!(g, p3); - } - - #[test] - fn test_differentiate() { - // 3x^2 + 2x + 42 - let px = Polynomial::new(&[FE::new(42), FE::new(2), FE::new(3)]); - // 6x + 2 - let dpdx = px.differentiate(); - assert_eq!(dpdx, Polynomial::new(&[FE::new(2), FE::new(6)])); - - // 128 - let px = Polynomial::new(&[FE::new(128)]); - // 0 - let dpdx = px.differentiate(); - assert_eq!(dpdx, Polynomial::new(&[FE::new(0)])); - } - - #[test] - fn test_reverse() { - let p = Polynomial::new(&[FE::new(3), FE::new(2), FE::new(1)]); - assert_eq!( - p.reverse(3), - Polynomial::new(&[FE::new(0), FE::new(1), FE::new(2), FE::new(3)]) - ); - } - - #[test] - fn test_truncate() { - let p = Polynomial::new(&[FE::new(3), FE::new(2), FE::new(1)]); - assert_eq!(p.truncate(2), Polynomial::new(&[FE::new(3), FE::new(2)])); - } - - #[test] - fn test_print_as_sage_poly() { - let p = Polynomial::new(&[FE::new(1), FE::new(2), FE::new(3)]); - assert_eq!(p.print_as_sage_poly(None), "3*x^2 + 2*x + 1"); - } -} diff --git a/crates/math/src/polynomial/sparse_multilinear_poly.rs b/crates/math/src/polynomial/sparse_multilinear_poly.rs deleted file mode 100644 index 1767b502c..000000000 --- a/crates/math/src/polynomial/sparse_multilinear_poly.rs +++ /dev/null @@ -1,156 +0,0 @@ -#[cfg(feature = "parallel")] -use rayon::iter::{IntoParallelIterator, ParallelIterator}; - -use crate::field::{element::FieldElement, traits::IsField}; -use crate::polynomial::error::MultilinearError; -use alloc::vec::Vec; - -pub struct SparseMultilinearPolynomial -where - ::BaseType: Send + Sync, -{ - num_vars: usize, - evals: Vec<(usize, FieldElement)>, -} - -impl SparseMultilinearPolynomial -where - ::BaseType: Send + Sync, -{ - /// Creates a new sparse multilinear polynomial - pub fn new(num_vars: usize, evals: Vec<(usize, FieldElement)>) -> Self { - SparseMultilinearPolynomial { num_vars, evals } - } - - /// Returns the number of variables of the sparse multilinear polynomial - pub fn num_vars(&self) -> usize { - self.num_vars - } - - /// Computes the eq extension polynomial of the polynomial. - /// return 1 when a == r, otherwise return 0. - /// Chi is the Lagrange basis polynomial evaluated at r - /// a indicates the binary decomposition of the index of the Lagrange basis polynomial - fn compute_chi(a: &[bool], r: &[FieldElement]) -> Result, MultilinearError> { - assert_eq!(a.len(), r.len()); - if a.len() != r.len() { - return Err(MultilinearError::ChisAndEvalsLengthMismatch( - a.len(), - r.len(), - )); - } - let mut chi_i = FieldElement::one(); - for j in 0..r.len() { - if a[j] { - chi_i *= &r[j]; - } else { - chi_i *= FieldElement::::one() - &r[j]; - } - } - Ok(chi_i) - } - - // Takes O(n log n) - /// Evaluates the multilinear polynomial at a point r - pub fn evaluate(&self, r: &[FieldElement]) -> Result, MultilinearError> { - if r.len() != self.num_vars() { - return Err(MultilinearError::IncorrectNumberofEvaluationPoints( - r.len(), - self.num_vars(), - )); - } - - #[cfg(feature = "parallel")] - let iter = (0..self.evals.len()).into_par_iter(); - - #[cfg(not(feature = "parallel"))] - let iter = 0..self.evals.len(); - - Ok(iter - .map(|i| { - let bits = get_bits(self.evals[i].0, r.len()); - let mut chi_i = FieldElement::::one(); - for j in 0..r.len() { - if bits[j] { - chi_i *= &r[j]; - } else { - chi_i *= FieldElement::::one() - &r[j]; - } - } - chi_i * &self.evals[i].1 - }) - .sum()) - } - - // Takes O(n log n) - /// Evaluates the multilinear polynomial at a point r - pub fn evaluate_with( - num_vars: usize, - evals: &[(usize, FieldElement)], - r: &[FieldElement], - ) -> Result, MultilinearError> { - assert_eq!(num_vars, r.len()); - if r.len() != num_vars { - return Err(MultilinearError::IncorrectNumberofEvaluationPoints( - r.len(), - num_vars, - )); - } - - #[cfg(feature = "parallel")] - let iter = (0..evals.len()).into_par_iter(); - - #[cfg(not(feature = "parallel"))] - let iter = 0..evals.len(); - Ok(iter - .map(|i| { - let bits = get_bits(evals[i].0, r.len()); - SparseMultilinearPolynomial::compute_chi(&bits, r).unwrap() * &evals[i].1 - }) - .sum()) - } -} - -/// Returns the bit decomposition (Vec) of the `index` of an evaluation within the sparse multilinear polynomial. -fn get_bits(n: usize, num_bits: usize) -> Vec { - (0..num_bits) - .map(|shift_amount| (n & (1 << (num_bits - shift_amount - 1))) > 0) - .collect::>() -} - -#[cfg(test)] -mod test { - - #[test] - fn evaluate() { - use crate::field::fields::u64_prime_field::U64PrimeField; - use alloc::vec; - - use super::*; - - const ORDER: u64 = 101; - type F = U64PrimeField; - type FE = FieldElement; - - // Let the polynomial have 3 variables, p(x_1, x_2, x_3) = (x_1 + x_2) * x_3 - // Evaluations of the polynomial at boolean cube are [0, 0, 0, 1, 0, 1, 0, 2]. - - let two = FE::from(2); - let z = vec![(3, FE::one()), (5, FE::one()), (7, two)]; - let m_poly = SparseMultilinearPolynomial::::new(3, z.clone()); - - let x = vec![FE::one(), FE::one(), FE::one()]; - assert_eq!(m_poly.evaluate(x.as_slice()).unwrap(), two); - assert_eq!( - SparseMultilinearPolynomial::evaluate_with(3, &z, x.as_slice()).unwrap(), - two - ); - - let x = vec![FE::one(), FE::zero(), FE::one()]; - assert_eq!(m_poly.evaluate(x.as_slice()).unwrap(), FE::one()); - assert_eq!( - SparseMultilinearPolynomial::evaluate_with(3, &z, x.as_slice()).unwrap(), - FE::one() - ); - } -} diff --git a/crates/math/src/traits.rs b/crates/math/src/traits.rs deleted file mode 100644 index 83d394de0..000000000 --- a/crates/math/src/traits.rs +++ /dev/null @@ -1,61 +0,0 @@ -use crate::{ - errors::DeserializationError, - field::{element::FieldElement, traits::IsField}, -}; - -use crate::errors::ByteConversionError; -/// A trait for converting an element to and from its byte representation and -/// for getting an element from its byte representation in big-endian or -/// little-endian order. -pub trait ByteConversion { - /// Returns the byte representation of the element in big-endian order.} - #[cfg(feature = "alloc")] - fn to_bytes_be(&self) -> alloc::vec::Vec; - - /// Returns the byte representation of the element in little-endian order. - #[cfg(feature = "alloc")] - fn to_bytes_le(&self) -> alloc::vec::Vec; - - /// Returns the element from its byte representation in big-endian order. - fn from_bytes_be(bytes: &[u8]) -> Result - where - Self: Sized; - - /// Returns the element from its byte representation in little-endian order. - fn from_bytes_le(bytes: &[u8]) -> Result - where - Self: Sized; -} - -/// Serialize function without args -/// Used for serialization when formatting options are not relevant -#[cfg(feature = "alloc")] -pub trait AsBytes { - /// Default serialize without args - fn as_bytes(&self) -> alloc::vec::Vec; -} - -#[cfg(feature = "alloc")] -impl AsBytes for u32 { - fn as_bytes(&self) -> alloc::vec::Vec { - self.to_le_bytes().to_vec() - } -} - -#[cfg(feature = "alloc")] -impl AsBytes for u64 { - fn as_bytes(&self) -> alloc::vec::Vec { - self.to_le_bytes().to_vec() - } -} - -/// Deserialize function without args -pub trait Deserializable { - fn deserialize(bytes: &[u8]) -> Result - where - Self: Sized; -} - -pub trait IsRandomFieldElementGenerator { - fn generate(&self) -> FieldElement; -} diff --git a/crates/math/src/unsigned_integer/element.rs b/crates/math/src/unsigned_integer/element.rs deleted file mode 100644 index 5f25a67da..000000000 --- a/crates/math/src/unsigned_integer/element.rs +++ /dev/null @@ -1,3115 +0,0 @@ -use core::cmp::Ordering; -use core::convert::From; -use core::ops::{ - Add, BitAnd, BitAndAssign, BitOr, BitOrAssign, BitXor, BitXorAssign, Mul, Shl, Shr, ShrAssign, - Sub, -}; - -#[cfg(feature = "proptest")] -use proptest::{ - arbitrary::Arbitrary, - prelude::any, - strategy::{SBoxedStrategy, Strategy}, -}; - -use crate::errors::ByteConversionError; -use crate::errors::CreationError; -#[cfg(feature = "alloc")] -use crate::traits::AsBytes; -use crate::traits::ByteConversion; -use crate::unsigned_integer::traits::IsUnsignedInteger; - -use core::fmt::{self, Debug, Display}; - -pub type U384 = UnsignedInteger<6>; -pub type U256 = UnsignedInteger<4>; -pub type U128 = UnsignedInteger<2>; -pub type U64 = UnsignedInteger<1>; - -/// A big unsigned integer in base 2^{64} represented -/// as fixed-size array `limbs` of `u64` components. -/// The most significant bit is in the left-most position. -/// That is, the array `[a_n, ..., a_0]` represents the -/// integer 2^{64 * n} * a_n + ... + 2^{64} * a_1 + a_0. -#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] -pub struct UnsignedInteger { - pub limbs: [u64; NUM_LIMBS], -} - -impl Default for UnsignedInteger { - fn default() -> Self { - Self { - limbs: [0; NUM_LIMBS], - } - } -} - -// NOTE: manually implementing `PartialOrd` may seem unorthodox, but the -// derived implementation had terrible performance. -#[allow(clippy::non_canonical_partial_ord_impl)] -impl PartialOrd for UnsignedInteger { - fn partial_cmp(&self, other: &Self) -> Option { - let mut i = 0; - while i < NUM_LIMBS { - if self.limbs[i] != other.limbs[i] { - return Some(self.limbs[i].cmp(&other.limbs[i])); - } - i += 1; - } - Some(Ordering::Equal) - } -} - -// NOTE: because we implemented `PartialOrd`, clippy asks us to implement -// this manually too. -impl Ord for UnsignedInteger { - fn cmp(&self, other: &Self) -> Ordering { - let mut i = 0; - while i < NUM_LIMBS { - if self.limbs[i] != other.limbs[i] { - return self.limbs[i].cmp(&other.limbs[i]); - } - i += 1; - } - Ordering::Equal - } -} - -/// impl from u128 for UnSignedInteger -impl From for UnsignedInteger { - fn from(value: u128) -> Self { - let mut limbs = [0u64; NUM_LIMBS]; - limbs[NUM_LIMBS - 1] = value as u64; - limbs[NUM_LIMBS - 2] = (value >> 64) as u64; - UnsignedInteger { limbs } - } -} - -/// impl from u64 for UnSignedInteger -impl From for UnsignedInteger { - fn from(value: u64) -> Self { - Self::from_u64(value) - } -} - -/// impl from u16 for UnSignedInteger -impl From for UnsignedInteger { - fn from(value: u16) -> Self { - let mut limbs = [0u64; NUM_LIMBS]; - limbs[NUM_LIMBS - 1] = value as u64; - UnsignedInteger { limbs } - } -} - -impl From<&str> for UnsignedInteger { - fn from(hex_str: &str) -> Self { - Self::from_hex_unchecked(hex_str) - } -} - -impl Display for UnsignedInteger { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let mut limbs_iterator = self.limbs.iter().skip_while(|limb| **limb == 0).peekable(); - - if limbs_iterator.peek().is_none() { - write!(f, "0x0")?; - } else { - write!(f, "0x")?; - if let Some(most_significant_limb) = limbs_iterator.next() { - write!(f, "{most_significant_limb:x}")?; - } - - for limb in limbs_iterator { - write!(f, "{limb:016x}")?; - } - } - - Ok(()) - } -} - -// impl Add for both references and variables - -impl Add<&UnsignedInteger> for &UnsignedInteger { - type Output = UnsignedInteger; - - fn add(self, other: &UnsignedInteger) -> UnsignedInteger { - let (result, overflow) = UnsignedInteger::add(self, other); - debug_assert!(!overflow, "UnsignedInteger addition overflow."); - result - } -} - -impl Add> for UnsignedInteger { - type Output = UnsignedInteger; - - fn add(self, other: UnsignedInteger) -> UnsignedInteger { - &self + &other - } -} - -impl Add<&UnsignedInteger> for UnsignedInteger { - type Output = UnsignedInteger; - - fn add(self, other: &Self) -> Self { - &self + other - } -} - -impl Add> for &UnsignedInteger { - type Output = UnsignedInteger; - - fn add(self, other: UnsignedInteger) -> UnsignedInteger { - self + &other - } -} - -// impl Sub - -impl Sub<&UnsignedInteger> for &UnsignedInteger { - type Output = UnsignedInteger; - - fn sub(self, other: &UnsignedInteger) -> UnsignedInteger { - let (result, overflow) = UnsignedInteger::sub(self, other); - debug_assert!(!overflow, "UnsignedInteger subtraction overflow."); - result - } -} - -impl Sub> for UnsignedInteger { - type Output = UnsignedInteger; - - fn sub(self, other: UnsignedInteger) -> UnsignedInteger { - &self - &other - } -} - -impl Sub<&UnsignedInteger> for UnsignedInteger { - type Output = UnsignedInteger; - - fn sub(self, other: &Self) -> Self { - &self - other - } -} - -impl Sub> for &UnsignedInteger { - type Output = UnsignedInteger; - #[inline(always)] - fn sub(self, other: UnsignedInteger) -> UnsignedInteger { - self - &other - } -} - -/// Multi-precision multiplication. -/// Algorithm 14.12 of "Handbook of Applied Cryptography" (https://cacr.uwaterloo.ca/hac/) -impl Mul<&UnsignedInteger> for &UnsignedInteger { - type Output = UnsignedInteger; - - #[inline(always)] - fn mul(self, other: &UnsignedInteger) -> UnsignedInteger { - let (mut n, mut t) = (0, 0); - for i in (0..NUM_LIMBS).rev() { - if self.limbs[i] != 0u64 { - n = NUM_LIMBS - 1 - i; - } - if other.limbs[i] != 0u64 { - t = NUM_LIMBS - 1 - i; - } - } - debug_assert!( - n + t < NUM_LIMBS, - "UnsignedInteger multiplication overflow." - ); - - // 1. - let mut limbs = [0u64; NUM_LIMBS]; - // 2. - let mut carry = 0u128; - for i in 0..=t { - // 2.2 - for j in 0..=n { - let uv = (limbs[NUM_LIMBS - 1 - (i + j)] as u128) - + (self.limbs[NUM_LIMBS - 1 - j] as u128) - * (other.limbs[NUM_LIMBS - 1 - i] as u128) - + carry; - carry = uv >> 64; - limbs[NUM_LIMBS - 1 - (i + j)] = uv as u64; - } - if i + n + 1 < NUM_LIMBS { - // 2.3 - limbs[NUM_LIMBS - 1 - (i + n + 1)] = carry as u64; - carry = 0; - } - } - assert_eq!(carry, 0, "UnsignedInteger multiplication overflow."); - // 3. - Self::Output { limbs } - } -} - -impl Mul> for UnsignedInteger { - type Output = UnsignedInteger; - #[inline(always)] - fn mul(self, other: UnsignedInteger) -> UnsignedInteger { - &self * &other - } -} - -impl Mul<&UnsignedInteger> for UnsignedInteger { - type Output = UnsignedInteger; - #[inline(always)] - fn mul(self, other: &Self) -> Self { - &self * other - } -} - -impl Mul> for &UnsignedInteger { - type Output = UnsignedInteger; - #[inline(always)] - fn mul(self, other: UnsignedInteger) -> UnsignedInteger { - self * &other - } -} - -impl Shl for &UnsignedInteger { - type Output = UnsignedInteger; - #[inline(always)] - fn shl(self, times: usize) -> UnsignedInteger { - self.const_shl(times) - } -} - -impl Shl for UnsignedInteger { - type Output = UnsignedInteger; - #[inline(always)] - fn shl(self, times: usize) -> UnsignedInteger { - &self << times - } -} - -// impl Shr - -impl Shr for &UnsignedInteger { - type Output = UnsignedInteger; - #[inline(always)] - fn shr(self, times: usize) -> UnsignedInteger { - self.const_shr(times) - } -} - -impl Shr for UnsignedInteger { - type Output = UnsignedInteger; - #[inline(always)] - fn shr(self, times: usize) -> UnsignedInteger { - &self >> times - } -} - -impl ShrAssign for UnsignedInteger { - fn shr_assign(&mut self, times: usize) { - debug_assert!( - times < 64 * NUM_LIMBS, - "UnsignedInteger shift left overflows." - ); - - let (a, b) = (times / 64, times % 64); - - if b == 0 { - self.limbs.copy_within(..NUM_LIMBS - a, a); - } else { - for i in (a + 1..NUM_LIMBS).rev() { - self.limbs[i] = (self.limbs[i - a] >> b) | (self.limbs[i - a - 1] << (64 - b)); - } - self.limbs[a] = self.limbs[0] >> b; - } - - for limb in self.limbs.iter_mut().take(a) { - *limb = 0; - } - } -} - -/// Impl BitAnd -impl BitAnd for UnsignedInteger { - type Output = Self; - - #[inline(always)] - fn bitand(self, rhs: Self) -> Self::Output { - let mut result = self; - result &= rhs; - result - } -} - -impl BitAndAssign for UnsignedInteger { - fn bitand_assign(&mut self, rhs: Self) { - for (a_i, b_i) in self.limbs.iter_mut().zip(rhs.limbs.iter()) { - *a_i &= b_i; - } - } -} - -/// Impl BitOr -impl BitOr for UnsignedInteger { - type Output = Self; - - #[inline(always)] - fn bitor(self, rhs: Self) -> Self::Output { - let mut result = self; - result |= rhs; - result - } -} - -impl BitOrAssign for UnsignedInteger { - #[inline(always)] - fn bitor_assign(&mut self, rhs: Self) { - for (a_i, b_i) in self.limbs.iter_mut().zip(rhs.limbs.iter()) { - *a_i |= b_i; - } - } -} - -/// Impl BitXor -impl BitXor for UnsignedInteger { - type Output = Self; - - #[inline(always)] - fn bitxor(self, rhs: Self) -> Self::Output { - let mut result = self; - result ^= rhs; - result - } -} - -impl BitXorAssign for UnsignedInteger { - #[inline(always)] - fn bitxor_assign(&mut self, rhs: Self) { - for (a_i, b_i) in self.limbs.iter_mut().zip(rhs.limbs.iter()) { - *a_i ^= b_i; - } - } -} - -impl UnsignedInteger { - pub const fn from_limbs(limbs: [u64; NUM_LIMBS]) -> Self { - Self { limbs } - } - - #[inline(always)] - pub const fn from_u64(value: u64) -> Self { - let mut limbs = [0u64; NUM_LIMBS]; - limbs[NUM_LIMBS - 1] = value; - UnsignedInteger { limbs } - } - - #[inline(always)] - pub const fn from_u128(value: u128) -> Self { - let mut limbs = [0u64; NUM_LIMBS]; - limbs[NUM_LIMBS - 1] = value as u64; - limbs[NUM_LIMBS - 2] = (value >> 64) as u64; - UnsignedInteger { limbs } - } - - #[inline(always)] - const fn is_hex_string(string: &str) -> bool { - let len: usize = string.len(); - let bytes = string.as_bytes(); - let mut i = 0; - - while i < len { - match bytes[i] { - b'0'..=b'9' => (), - b'a'..=b'f' => (), - b'A'..=b'F' => (), - _ => return false, - } - i += 1; - } - - true - } - - /// Creates an `UnsignedInteger` from a hexstring. It can contain `0x` or not. - /// Returns an `CreationError::InvalidHexString`if the value is not a hexstring. - /// Returns a `CreationError::EmptyString` if the input string is empty. - /// Returns a `CreationError::HexStringIsTooBig` if the the input hex string is bigger - /// than the maximum amount of characters for this element. - pub fn from_hex(value: &str) -> Result { - let mut string = value; - let mut char_iterator = value.chars(); - if string.len() > 2 - && char_iterator.next().unwrap() == '0' - && char_iterator.next().unwrap() == 'x' - { - string = &string[2..]; - } - if string.is_empty() { - return Err(CreationError::EmptyString); - } - if !Self::is_hex_string(string) { - return Err(CreationError::InvalidHexString); - } - - // Limbs are of 64 bits - 8 bytes - // We have 16 nibbles per bytes - let max_amount_of_hex_chars = NUM_LIMBS * 16; - if string.len() > max_amount_of_hex_chars { - return Err(CreationError::HexStringIsTooBig); - } - - Ok(Self::from_hex_unchecked(string)) - } - - /// Creates an `UnsignedInteger` from a hexstring - /// # Panics - /// Panics if value is not a hexstring. It can contain `0x` or not. - pub const fn from_hex_unchecked(value: &str) -> Self { - let mut result = [0u64; NUM_LIMBS]; - let mut limb = 0; - let mut limb_index = NUM_LIMBS - 1; - let mut shift = 0; - - let value_bytes = value.as_bytes(); - - // Remove "0x" if it's at the beginning of the string - let mut i = 0; - if value_bytes.len() > 2 && value_bytes[0] == b'0' && value_bytes[1] == b'x' { - i = 2; - } - - let mut j = value_bytes.len(); - while j > i { - j -= 1; - limb |= match value_bytes[j] { - c @ b'0'..=b'9' => (c as u64 - b'0' as u64) << shift, - c @ b'a'..=b'f' => (c as u64 - b'a' as u64 + 10) << shift, - c @ b'A'..=b'F' => (c as u64 - b'A' as u64 + 10) << shift, - _ => panic!("Malformed hex expression."), - }; - shift += 4; - if shift == 64 && limb_index > 0 { - result[limb_index] = limb; - limb = 0; - limb_index -= 1; - shift = 0; - } - } - - result[limb_index] = limb; - UnsignedInteger { limbs: result } - } - - /// Creates a hexstring from a `FieldElement` without `0x`. - #[cfg(feature = "std")] - pub fn to_hex(&self) -> String { - let mut hex_string = String::new(); - for &limb in self.limbs.iter() { - hex_string.push_str(&format!("{limb:016X}")); - } - hex_string.trim_start_matches('0').to_string() - } - - pub const fn const_ne(a: &UnsignedInteger, b: &UnsignedInteger) -> bool { - let mut i = 0; - while i < NUM_LIMBS { - if a.limbs[i] != b.limbs[i] { - return true; - } - i += 1; - } - false - } - - pub const fn const_le(a: &UnsignedInteger, b: &UnsignedInteger) -> bool { - let mut i = 0; - while i < NUM_LIMBS { - if a.limbs[i] < b.limbs[i] { - return true; - } else if a.limbs[i] > b.limbs[i] { - return false; - } - i += 1; - } - true - } - - pub const fn const_shl(self, times: usize) -> Self { - debug_assert!( - times < 64 * NUM_LIMBS, - "UnsignedInteger shift left overflows." - ); - let mut limbs = [0u64; NUM_LIMBS]; - let (a, b) = (times / 64, times % 64); - - if b == 0 { - let mut i = 0; - while i < NUM_LIMBS - a { - limbs[i] = self.limbs[a + i]; - i += 1; - } - Self { limbs } - } else { - limbs[NUM_LIMBS - 1 - a] = self.limbs[NUM_LIMBS - 1] << b; - let mut i = a + 1; - while i < NUM_LIMBS { - limbs[NUM_LIMBS - 1 - i] = (self.limbs[NUM_LIMBS - 1 - i + a] << b) - | (self.limbs[NUM_LIMBS - i + a] >> (64 - b)); - i += 1; - } - Self { limbs } - } - } - - pub const fn const_shr(self, times: usize) -> UnsignedInteger { - debug_assert!( - times < 64 * NUM_LIMBS, - "UnsignedInteger shift right overflows." - ); - - let mut limbs = [0u64; NUM_LIMBS]; - let (a, b) = (times / 64, times % 64); - - if b == 0 { - let mut i = 0; - while i < NUM_LIMBS - a { - limbs[a + i] = self.limbs[i]; - i += 1; - } - Self { limbs } - } else { - limbs[a] = self.limbs[0] >> b; - let mut i = a + 1; - while i < NUM_LIMBS { - limbs[i] = (self.limbs[i - a - 1] << (64 - b)) | (self.limbs[i - a] >> b); - i += 1; - } - Self { limbs } - } - } - - pub const fn add( - a: &UnsignedInteger, - b: &UnsignedInteger, - ) -> (UnsignedInteger, bool) { - let mut limbs = [0u64; NUM_LIMBS]; - let mut carry = 0u64; - let mut i = NUM_LIMBS; - while i > 0 { - let (x, cb) = a.limbs[i - 1].overflowing_add(b.limbs[i - 1]); - let (x, cc) = x.overflowing_add(carry); - limbs[i - 1] = x; - carry = (cb | cc) as u64; - i -= 1; - } - (UnsignedInteger { limbs }, carry > 0) - } - - pub fn double(a: &UnsignedInteger) -> (UnsignedInteger, bool) { - let mut cloned = *a; - let overflow = cloned.double_in_place(); - (cloned, overflow) - } - - pub fn double_in_place(&mut self) -> bool { - let mut msb_of_previous_limb = 0; - for i in (0..NUM_LIMBS).rev() { - let limb_ref = &mut self.limbs[i]; - let msb_of_current_limb = *limb_ref >> 63; - *limb_ref <<= 1; - *limb_ref |= msb_of_previous_limb; - msb_of_previous_limb = msb_of_current_limb; - } - msb_of_previous_limb != 0 - } - - /// Multi-precision subtraction. - /// Adapted from Algorithm 14.9 of "Handbook of Applied Cryptography" (https://cacr.uwaterloo.ca/hac/) - /// Returns the results and a flag that is set if the substraction underflowed - #[inline(always)] - pub const fn sub( - a: &UnsignedInteger, - b: &UnsignedInteger, - ) -> (UnsignedInteger, bool) { - let mut limbs = [0u64; NUM_LIMBS]; - // 1. - let mut carry = false; - // 2. - let mut i: usize = NUM_LIMBS; - while i > 0 { - i -= 1; - let (x, cb) = a.limbs[i].overflowing_sub(b.limbs[i]); - let (x, cc) = x.overflowing_sub(carry as u64); - // Casting i128 to u64 drops the most significant bits of i128, - // which effectively computes residue modulo 2^{64} - // 2.1 - limbs[i] = x; - // 2.2 - carry = cb | cc; - } - // 3. - (Self { limbs }, carry) - } - - /// Multi-precision multiplication. - /// Adapted from Algorithm 14.12 of "Handbook of Applied Cryptography" (https://cacr.uwaterloo.ca/hac/) - pub const fn mul( - a: &UnsignedInteger, - b: &UnsignedInteger, - ) -> (UnsignedInteger, UnsignedInteger) { - // 1. - let mut hi = [0u64; NUM_LIMBS]; - let mut lo = [0u64; NUM_LIMBS]; - // Const functions don't support for loops so we use whiles - // this is equivalent to: - // for i in (0..NUM_LIMBS).rev() - // 2. - let mut i = NUM_LIMBS; - while i > 0 { - i -= 1; - // 2.1 - let mut carry = 0u128; - let mut j = NUM_LIMBS; - // 2.2 - while j > 0 { - j -= 1; - let mut k = i + j; - if k >= NUM_LIMBS - 1 { - k -= NUM_LIMBS - 1; - let uv = (lo[k] as u128) + (a.limbs[j] as u128) * (b.limbs[i] as u128) + carry; - carry = uv >> 64; - // Casting u128 to u64 takes modulo 2^{64} - lo[k] = uv as u64; - } else { - let uv = - (hi[k + 1] as u128) + (a.limbs[j] as u128) * (b.limbs[i] as u128) + carry; - carry = uv >> 64; - // Casting u128 to u64 takes modulo 2^{64} - hi[k + 1] = uv as u64; - } - } - // 2.3 - hi[i] = carry as u64; - } - // 3. - (Self { limbs: hi }, Self { limbs: lo }) - } - - #[inline(always)] - pub fn square( - a: &UnsignedInteger, - ) -> (UnsignedInteger, UnsignedInteger) { - // NOTE: we use explicit `while` loops in this function because profiling pointed - // at iterators of the form `(..).rev()` as the main performance bottleneck. - - let mut hi = Self { - limbs: [0u64; NUM_LIMBS], - }; - let mut lo = Self { - limbs: [0u64; NUM_LIMBS], - }; - - // Compute products between a[i] and a[j] when i != j. - // The variable `index` below is the index of `lo` or - // `hi` to update - let mut i = NUM_LIMBS; - while i > 1 { - i -= 1; - let mut c: u128 = 0; - let mut j = i; - while j > 0 { - j -= 1; - let k = i + j; - if k >= NUM_LIMBS - 1 { - let index = k + 1 - NUM_LIMBS; - let cs = lo.limbs[index] as u128 + a.limbs[i] as u128 * a.limbs[j] as u128 + c; - c = cs >> 64; - lo.limbs[index] = cs as u64; - } else { - let index = k + 1; - let cs = hi.limbs[index] as u128 + a.limbs[i] as u128 * a.limbs[j] as u128 + c; - c = cs >> 64; - hi.limbs[index] = cs as u64; - } - } - hi.limbs[i] = c as u64; - } - - // All these terms should appear twice each, - // so we have to multiply what we got so far by two. - let carry = lo.limbs[0] >> 63; - lo = lo << 1; - hi = hi << 1; - hi.limbs[NUM_LIMBS - 1] |= carry; - - // Add the only remaning terms, which are the squares a[i] * a[i]. - // The variable `index` below is the index of `lo` or - // `hi` to update - let mut c = 0; - let mut i = NUM_LIMBS; - while i > 0 { - i -= 1; - if NUM_LIMBS - 1 <= i * 2 { - let index = 2 * i + 1 - NUM_LIMBS; - let cs = lo.limbs[index] as u128 + a.limbs[i] as u128 * a.limbs[i] as u128 + c; - c = cs >> 64; - lo.limbs[index] = cs as u64; - } else { - let index = 2 * i + 1; - let cs = hi.limbs[index] as u128 + a.limbs[i] as u128 * a.limbs[i] as u128 + c; - c = cs >> 64; - hi.limbs[index] = cs as u64; - } - if NUM_LIMBS - 1 < i * 2 { - let index = 2 * i - NUM_LIMBS; - let cs = lo.limbs[index] as u128 + c; - c = cs >> 64; - lo.limbs[index] = cs as u64; - } else { - let index = 2 * i; - let cs = hi.limbs[index] as u128 + c; - c = cs >> 64; - hi.limbs[index] = cs as u64; - } - } - debug_assert_eq!(c, 0); - (hi, lo) - } - - #[inline(always)] - /// Returns the number of bits needed to represent the number (0 for zero). - /// If nonzero, this is equivalent to one plus the floored log2 of the number. - pub const fn bits(&self) -> u32 { - let mut i = NUM_LIMBS; - while i > 0 { - if self.limbs[i - 1] != 0 { - return i as u32 * u64::BITS - self.limbs[i - 1].leading_zeros(); - } - i -= 1; - } - 0 - } - - /// Returns the truthy value if `self != 0` and the falsy value otherwise. - #[inline] - const fn ct_is_nonzero(ct: u64) -> u64 { - Self::ct_from_lsb((ct | ct.wrapping_neg()) >> (u64::BITS - 1)) - } - - /// Returns the truthy value if `value == 1`, and the falsy value if `value == 0`. - /// Panics for other values. - const fn ct_from_lsb(value: u64) -> u64 { - debug_assert!(value == 0 || value == 1); - value.wrapping_neg() - } - - /// Return `b` if `c` is truthy, otherwise return `a`. - #[inline] - const fn ct_select_limb(a: u64, b: u64, ct: u64) -> u64 { - a ^ (ct & (a ^ b)) - } - - /// Return `b` if `c` is truthy, otherwise return `a`. - #[inline] - const fn ct_select(a: &Self, b: &Self, c: u64) -> Self { - let mut limbs = [0_u64; NUM_LIMBS]; - - let mut i = 0; - while i < NUM_LIMBS { - limbs[i] = Self::ct_select_limb(a.limbs[i], b.limbs[i], c); - i += 1; - } - - Self { limbs } - } - - /// Computes `self - (rhs + borrow)`, returning the result along with the new borrow. - #[inline(always)] - const fn sbb_limbs(lhs: u64, rhs: u64, borrow: u64) -> (u64, u64) { - let a = lhs as u128; - let b = rhs as u128; - let borrow = (borrow >> (u64::BITS - 1)) as u128; - let ret = a.wrapping_sub(b + borrow); - (ret as u64, (ret >> u64::BITS) as u64) - } - - #[inline(always)] - /// Computes `a - (b + borrow)`, returning the result along with the new borrow. - pub fn sbb(&self, rhs: &Self, mut borrow: u64) -> (Self, u64) { - let mut limbs = [0; NUM_LIMBS]; - - for i in (0..NUM_LIMBS).rev() { - let (w, b) = Self::sbb_limbs(self.limbs[i], rhs.limbs[i], borrow); - limbs[i] = w; - borrow = b; - } - - (Self { limbs }, borrow) - } - - #[inline(always)] - /// Returns the number of bits needed to represent the number as little endian - pub const fn bits_le(&self) -> usize { - let mut i = 0; - while i < NUM_LIMBS { - if self.limbs[i] != 0 { - return u64::BITS as usize * (NUM_LIMBS - i) - - self.limbs[i].leading_zeros() as usize; - } - i += 1; - } - 0 - } - - /// Computes self / rhs, returns the quotient, remainder. - pub fn div_rem(&self, rhs: &Self) -> (Self, Self) { - debug_assert!( - *rhs != UnsignedInteger::from_u64(0), - "Attempted to divide by zero" - ); - let mb = rhs.bits_le(); - let mut bd = (NUM_LIMBS * u64::BITS as usize) - mb; - let mut rem = *self; - let mut quo = Self::from_u64(0); - let mut c = rhs.shl(bd); - - loop { - let (mut r, borrow) = rem.sbb(&c, 0); - debug_assert!(borrow == 0 || borrow == u64::MAX); - rem = Self::ct_select(&r, &rem, borrow); - r = quo.bitor(Self::from_u64(1)); - quo = Self::ct_select(&r, &quo, borrow); - if bd == 0 { - break; - } - bd -= 1; - c = c.shr(1); - quo = quo.shl(1); - } - - let is_some = Self::ct_is_nonzero(mb as u64); - quo = Self::ct_select(&Self::from_u64(0), &quo, is_some); - (quo, rem) - } - - /// Convert from a decimal string. - pub fn from_dec_str(value: &str) -> Result { - if value.is_empty() { - return Err(CreationError::InvalidDecString); - } - let mut res = Self::from_u64(0); - for b in value.bytes().map(|b| b.wrapping_sub(b'0')) { - if b > 9 { - return Err(CreationError::InvalidDecString); - } - let (high, low) = Self::mul(&res, &Self::from(10_u64)); - if high > Self::from_u64(0) { - return Err(CreationError::InvalidDecString); - } - res = low + Self::from(b as u64); - } - Ok(res) - } - - #[cfg(feature = "proptest")] - pub fn nonzero_uint() -> impl Strategy> { - any_uint::().prop_filter("is_zero", |&x| x != UnsignedInteger::from_u64(0)) - } -} - -impl IsUnsignedInteger for UnsignedInteger {} - -impl ByteConversion for UnsignedInteger { - #[cfg(feature = "alloc")] - fn to_bytes_be(&self) -> alloc::vec::Vec { - self.limbs - .iter() - .flat_map(|limb| limb.to_be_bytes()) - .collect() - } - - #[cfg(feature = "alloc")] - fn to_bytes_le(&self) -> alloc::vec::Vec { - self.limbs - .iter() - .rev() - .flat_map(|limb| limb.to_le_bytes()) - .collect() - } - - fn from_bytes_be(bytes: &[u8]) -> Result { - // We cut off extra bytes, this is useful when you use this function to generate the element from randomness - // In the future with the right algorithm this shouldn't be needed - - let needed_bytes = bytes - .get(0..NUM_LIMBS * 8) - .ok_or(ByteConversionError::FromBEBytesError)?; - - let mut limbs: [u64; NUM_LIMBS] = [0; NUM_LIMBS]; - - needed_bytes - .chunks_exact(8) - .enumerate() - .try_for_each(|(i, chunk)| { - let limb = u64::from_be_bytes( - chunk - .try_into() - .map_err(|_| ByteConversionError::FromBEBytesError)?, - ); - limbs[i] = limb; - Ok::<_, ByteConversionError>(()) - })?; - - Ok(Self { limbs }) - } - - fn from_bytes_le(bytes: &[u8]) -> Result { - let needed_bytes = bytes - .get(0..NUM_LIMBS * 8) - .ok_or(ByteConversionError::FromBEBytesError)?; - - let mut limbs: [u64; NUM_LIMBS] = [0; NUM_LIMBS]; - - needed_bytes - .chunks_exact(8) - .rev() - .enumerate() - .try_for_each(|(i, chunk)| { - let limb = u64::from_le_bytes( - chunk - .try_into() - .map_err(|_| ByteConversionError::FromLEBytesError)?, - ); - limbs[i] = limb; - Ok::<_, ByteConversionError>(()) - })?; - - Ok(Self { limbs }) - } -} - -impl From> for u16 { - fn from(value: UnsignedInteger) -> Self { - value.limbs[NUM_LIMBS - 1] as u16 - } -} - -#[cfg(feature = "alloc")] -impl AsBytes for UnsignedInteger { - fn as_bytes(&self) -> alloc::vec::Vec { - self.limbs - .into_iter() - .fold(alloc::vec::Vec::new(), |mut acc, limb| { - acc.extend_from_slice(&limb.as_bytes()); - acc - }) - } -} - -#[cfg(feature = "alloc")] -impl From> for alloc::vec::Vec { - fn from(val: UnsignedInteger) -> Self { - val.as_bytes() - } -} - -#[cfg(feature = "proptest")] -fn any_uint() -> impl Strategy> { - any::<[u64; NUM_LIMBS]>().prop_map(UnsignedInteger::from_limbs) -} - -#[cfg(feature = "proptest")] -impl Arbitrary for UnsignedInteger { - type Parameters = (); - - fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy { - any_uint::().sboxed() - } - - type Strategy = SBoxedStrategy; -} - -#[cfg(test)] -mod tests_u384 { - use crate::traits::ByteConversion; - use crate::unsigned_integer::element::{UnsignedInteger, U256, U384}; - #[cfg(feature = "proptest")] - use proptest::prelude::*; - #[cfg(feature = "proptest")] - use std::ops::Shr; - - #[cfg(feature = "proptest")] - const N_LIMBS: usize = 8; - #[cfg(feature = "proptest")] - type Uint = UnsignedInteger; - - #[cfg(feature = "proptest")] - proptest! { - #[test] - fn bitand(a in any::(), b in any::()) { - let result = a & b; - - for i in 0..N_LIMBS { - assert_eq!(result.limbs[i], a.limbs[i] & b.limbs[i]); - } - } - - #[test] - fn bitand_assign(a in any::(), b in any::()) { - let mut result = a; - result &= b; - - for i in 0..N_LIMBS { - assert_eq!(result.limbs[i], a.limbs[i] & b.limbs[i]); - } - } - - #[test] - fn bitor(a in any::(), b in any::()) { - let result = a | b; - - for i in 0..N_LIMBS { - assert_eq!(result.limbs[i], a.limbs[i] | b.limbs[i]); - } - } - - #[test] - fn bitor_assign(a in any::(), b in any::()) { - let mut result = a; - result |= b; - - for i in 0..N_LIMBS { - assert_eq!(result.limbs[i], a.limbs[i] | b.limbs[i]); - } - } - - #[test] - fn bitxor(a in any::(), b in any::()) { - let result = a ^ b; - - for i in 0..N_LIMBS { - assert_eq!(result.limbs[i], a.limbs[i] ^ b.limbs[i]); - } - } - - #[test] - fn bitxor_assign(a in any::(), b in any::()) { - let mut result = a; - result ^= b; - - for i in 0..N_LIMBS { - assert_eq!(result.limbs[i], a.limbs[i] ^ b.limbs[i]); - } - } - - #[test] - fn div_rem(a in any::(), b in any::()) { - let a = a.shr(256); - let b = b.shr(256); - assert_eq!((a * b).div_rem(&b), (a, Uint::from_u64(0))); - } - } - - #[test] - fn construct_new_integer_from_limbs() { - let a: U384 = UnsignedInteger { - limbs: [0, 1, 2, 3, 4, 5], - }; - assert_eq!(U384::from_limbs([0, 1, 2, 3, 4, 5]), a); - } - - #[test] - fn construct_new_integer_from_u64_1() { - let a = U384::from_u64(1_u64); - assert_eq!(a.limbs, [0, 0, 0, 0, 0, 1]); - } - - #[test] - fn construct_new_integer_from_u54_2() { - let a = U384::from_u64(u64::MAX); - assert_eq!(a.limbs, [0, 0, 0, 0, 0, u64::MAX]); - } - - #[test] - fn construct_new_integer_from_u128_1() { - let a = U384::from_u128(u128::MAX); - assert_eq!(a.limbs, [0, 0, 0, 0, u64::MAX, u64::MAX]); - } - - #[test] - fn construct_new_integer_from_u128_2() { - let a = U384::from_u128(276371540478856090688472252609570374439); - assert_eq!( - a.limbs, - [0, 0, 0, 0, 14982131230017065096, 14596400355126379303] - ); - } - - #[test] - fn construct_new_integer_from_hex_1() { - let a = U384::from_hex_unchecked("1"); - assert_eq!(a.limbs, [0, 0, 0, 0, 0, 1]); - } - - #[test] - fn construct_new_integer_from_zero_x_1() { - let a = U384::from_hex_unchecked("0x1"); - assert_eq!(a.limbs, [0, 0, 0, 0, 0, 1]); - } - - #[test] - fn construct_new_integer_from_hex_2() { - let a = U384::from_hex_unchecked("f"); - assert_eq!(a.limbs, [0, 0, 0, 0, 0, 15]); - } - - #[test] - fn construct_new_integer_from_hex_3() { - let a = U384::from_hex_unchecked("10000000000000000"); - assert_eq!(a.limbs, [0, 0, 0, 0, 1, 0]); - } - - #[test] - fn construct_new_integer_from_hex_4() { - let a = U384::from_hex_unchecked("a0000000000000000"); - assert_eq!(a.limbs, [0, 0, 0, 0, 10, 0]); - } - - #[test] - fn construct_new_integer_from_hex_5() { - let a = U384::from_hex_unchecked("ffffffffffffffffff"); - assert_eq!(a.limbs, [0, 0, 0, 0, 255, u64::MAX]); - } - - #[test] - fn construct_new_integer_from_hex_6() { - let a = U384::from_hex_unchecked("eb235f6144d9e91f4b14"); - assert_eq!(a.limbs, [0, 0, 0, 0, 60195, 6872850209053821716]); - } - - #[test] - fn construct_new_integer_from_hex_7() { - let a = U384::from_hex_unchecked("2b20aaa5cf482b239e2897a787faf4660cc95597854beb2"); - assert_eq!( - a.limbs, - [ - 0, - 0, - 0, - 194229460750598834, - 4171047363999149894, - 6975114134393503410 - ] - ); - } - - #[test] - fn construct_new_integer_from_hex_checked_7() { - let a = U384::from_hex("2b20aaa5cf482b239e2897a787faf4660cc95597854beb2").unwrap(); - assert_eq!( - a.limbs, - [ - 0, - 0, - 0, - 194229460750598834, - 4171047363999149894, - 6975114134393503410 - ] - ); - } - - #[test] - fn construct_new_integer_from_hex_checked_7_with_zero_x() { - let a = U384::from_hex("0x2b20aaa5cf482b239e2897a787faf4660cc95597854beb2").unwrap(); - assert_eq!( - a.limbs, - [ - 0, - 0, - 0, - 194229460750598834, - 4171047363999149894, - 6975114134393503410 - ] - ); - } - - #[test] - fn construct_new_integer_from_non_hex_errs() { - assert!(U384::from_hex("0xTEST").is_err()); - } - - #[test] - fn construct_new_integer_from_empty_string_errs() { - assert!(U384::from_hex("").is_err()); - } - - #[test] - fn construct_new_integer_from_hex_checked_8() { - let a = U384::from_hex("140f5177b90b4f96b61bb8ccb4f298ad2b20aaa5cf482b239e2897a787faf4660cc95597854beb235f6144d9e91f4b14").unwrap(); - assert_eq!( - a.limbs, - [ - 1445463580056702870, - 13122285128622708909, - 3107671372009581347, - 11396525602857743462, - 921361708038744867, - 6872850209053821716 - ] - ); - } - - #[test] - fn construct_new_integer_from_hex_8() { - let a = U384::from_hex_unchecked("140f5177b90b4f96b61bb8ccb4f298ad2b20aaa5cf482b239e2897a787faf4660cc95597854beb235f6144d9e91f4b14"); - assert_eq!( - a.limbs, - [ - 1445463580056702870, - 13122285128622708909, - 3107671372009581347, - 11396525602857743462, - 921361708038744867, - 6872850209053821716 - ] - ); - } - - #[test] - fn from_hex_with_overflowing_hexstring_should_error() { - let u256_from_big_string = U256::from_hex(&"f".repeat(65)); - assert!(u256_from_big_string.is_err()); - assert!( - u256_from_big_string - == Err(crate::unsigned_integer::element::CreationError::HexStringIsTooBig) - ); - } - - #[test] - fn from_hex_with_non_overflowing_hexstring_should_work() { - assert_eq!(U256::from_hex(&"0".repeat(64)).unwrap().limbs, [0, 0, 0, 0]) - } - - #[test] - fn construct_new_integer_from_dec_1() { - let a = U384::from_dec_str("1").unwrap(); - assert_eq!(a.limbs, [0, 0, 0, 0, 0, 1]); - } - - #[test] - fn construct_new_integer_from_dec_2() { - let a = U384::from_dec_str("15").unwrap(); - assert_eq!(a.limbs, [0, 0, 0, 0, 0, 15]); - } - - #[test] - fn construct_new_integer_from_dec_3() { - let a = U384::from_dec_str("18446744073709551616").unwrap(); - assert_eq!(a.limbs, [0, 0, 0, 0, 1, 0]); - } - - #[test] - fn construct_new_integer_from_dec_4() { - let a = U384::from_dec_str("184467440737095516160").unwrap(); - assert_eq!(a.limbs, [0, 0, 0, 0, 10, 0]); - } - - #[test] - fn construct_new_integer_from_dec_5() { - let a = U384::from_dec_str("4722366482869645213695").unwrap(); - assert_eq!(a.limbs, [0, 0, 0, 0, 255, u64::MAX]); - } - - #[test] - fn construct_new_integer_from_dec_6() { - let a = U384::from_dec_str("1110408632367155513346836").unwrap(); - assert_eq!(a.limbs, [0, 0, 0, 0, 60195, 6872850209053821716]); - } - - #[test] - fn construct_new_integer_from_dec_7() { - let a = - U384::from_dec_str("66092860629991288370279803883558073888453977263446474418").unwrap(); - assert_eq!( - a.limbs, - [ - 0, - 0, - 0, - 194229460750598834, - 4171047363999149894, - 6975114134393503410 - ] - ); - } - - #[test] - fn construct_new_integer_from_dec_8() { - let a = U384::from_dec_str("3087491467896943881295768554872271030441880044814691421073017731442549147034464936390742057449079000462340371991316").unwrap(); - assert_eq!( - a.limbs, - [ - 1445463580056702870, - 13122285128622708909, - 3107671372009581347, - 11396525602857743462, - 921361708038744867, - 6872850209053821716 - ] - ); - } - - #[test] - fn construct_new_integer_from_dec_empty() { - assert!(U384::from_dec_str("").is_err()); - } - - #[test] - fn construct_new_integer_from_dec_invalid() { - assert!(U384::from_dec_str("0xff").is_err()); - } - - #[test] - fn equality_works_1() { - let a = U384::from_hex_unchecked("1"); - let b = U384 { - limbs: [0, 0, 0, 0, 0, 1], - }; - assert_eq!(a, b); - } - #[test] - fn equality_works_2() { - let a = U384::from_hex_unchecked("f"); - let b = U384 { - limbs: [0, 0, 0, 0, 0, 15], - }; - assert_eq!(a, b); - } - - #[test] - fn equality_works_3() { - let a = U384::from_hex_unchecked("10000000000000000"); - let b = U384 { - limbs: [0, 0, 0, 0, 1, 0], - }; - assert_eq!(a, b); - } - - #[test] - fn equality_works_4() { - let a = U384::from_hex_unchecked("a0000000000000000"); - let b = U384 { - limbs: [0, 0, 0, 0, 10, 0], - }; - assert_eq!(a, b); - } - - #[test] - fn equality_works_5() { - let a = U384::from_hex_unchecked("ffffffffffffffffff"); - let b = U384 { - limbs: [0, 0, 0, 0, u8::MAX as u64, u64::MAX], - }; - assert_eq!(a, b); - } - - #[test] - fn equality_works_6() { - let a = U384::from_hex_unchecked("eb235f6144d9e91f4b14"); - let b = U384 { - limbs: [0, 0, 0, 0, 60195, 6872850209053821716], - }; - assert_eq!(a, b); - } - - #[test] - fn equality_works_7() { - let a = U384::from_hex_unchecked("2b20aaa5cf482b239e2897a787faf4660cc95597854beb2"); - let b = U384 { - limbs: [ - 0, - 0, - 0, - 194229460750598834, - 4171047363999149894, - 6975114134393503410, - ], - }; - assert_eq!(a, b); - } - - #[test] - fn equality_works_8() { - let a = U384::from_hex_unchecked("140f5177b90b4f96b61bb8ccb4f298ad2b20aaa5cf482b239e2897a787faf4660cc95597854beb235f6144d9e91f4b14"); - let b = U384 { - limbs: [ - 1445463580056702870, - 13122285128622708909, - 3107671372009581347, - 11396525602857743462, - 921361708038744867, - 6872850209053821716, - ], - }; - assert_eq!(a, b); - } - - #[test] - fn equality_works_9() { - let a = U384::from_hex_unchecked("fffffff"); - let b = U384::from_hex_unchecked("fefffff"); - assert_ne!(a, b); - } - - #[test] - fn equality_works_10() { - let a = U384::from_hex_unchecked("ffff000000000000"); - let b = U384::from_hex_unchecked("ffff000000100000"); - assert_ne!(a, b); - } - - #[test] - fn const_ne_works_1() { - let a = U384::from_hex_unchecked("ffff000000000000"); - let b = U384::from_hex_unchecked("ffff000000100000"); - assert!(U384::const_ne(&a, &b)); - } - - #[test] - fn const_ne_works_2() { - let a = U384::from_hex_unchecked("140f5177b90b4f96b61bb8ccb4f298ad2b20aaa5cf482b239e2897a787faf4660cc95597854beb235f6144d9e91f4b14"); - let b = U384 { - limbs: [ - 1445463580056702870, - 13122285128622708909, - 3107671372009581347, - 11396525602857743462, - 921361708038744867, - 6872850209053821716, - ], - }; - assert!(!U384::const_ne(&a, &b)); - } - - #[test] - fn double_two_384_bit_integers() { - let a = U384::from_u64(2); - let b = U384::from_u64(5); - let c = U384::from_u64(7); - assert_eq!(U384::double(&a).0, a + a); - assert_eq!(U384::double(&b).0, b + b); - assert_eq!(U384::double(&c).0, c + c); - } - - #[test] - fn add_two_384_bit_integers_1() { - let a = U384::from_u64(2); - let b = U384::from_u64(5); - let c = U384::from_u64(7); - assert_eq!(a + b, c); - } - - #[test] - fn add_two_384_bit_integers_2() { - let a = U384::from_u64(334); - let b = U384::from_u64(666); - let c = U384::from_u64(1000); - assert_eq!(a + b, c); - } - - #[test] - fn add_two_384_bit_integers_3() { - let a = U384::from_hex_unchecked("ffffffffffffffff"); - let b = U384::from_hex_unchecked("1"); - let c = U384::from_hex_unchecked("10000000000000000"); - assert_eq!(a + b, c); - } - - #[test] - fn add_two_384_bit_integers_4() { - let a = U384::from_hex_unchecked("b58e1e0b66"); - let b = U384::from_hex_unchecked("55469d9619"); - let c = U384::from_hex_unchecked("10ad4bba17f"); - assert_eq!(a + b, c); - } - - #[test] - fn add_two_384_bit_integers_5() { - let a = U384::from_hex_unchecked("e8dff25cb6160f7705221da6f"); - let b = U384::from_hex_unchecked("ab879169b5f80dc8a7969f0b0"); - let c = U384::from_hex_unchecked("1946783c66c0e1d3facb8bcb1f"); - assert_eq!(a + b, c); - } - - #[test] - fn add_two_384_bit_integers_6() { - let a = U384::from_hex_unchecked("9adf291af3a64d59e14e7b440c850508014c551ed5"); - let b = U384::from_hex_unchecked("e7948474bce907f0feaf7e5d741a8cd2f6d1fb9448"); - let c = U384::from_hex_unchecked("18273ad8fb08f554adffdf9a1809f91daf81e50b31d"); - assert_eq!(a + b, c); - } - - #[test] - fn add_two_384_bit_integers_7() { - let a = U384::from_hex_unchecked( - "f866aef803c92bf02e85c7fad0eccb4881c59825e499fa22f98e1a8fefed4cd9a03647cd3cc84", - ); - let b = U384::from_hex_unchecked( - "9b4000dccf01a010e196154a1b998408f949d734389626ba97cb3331ee87e01dd5badc58f41b2", - ); - let c = U384::from_hex_unchecked( - "193a6afd4d2cacc01101bdd44ec864f517b0f6f5a1d3020dd91594dc1de752cf775f1242630e36", - ); - assert_eq!(a + b, c); - } - - #[test] - fn add_two_384_bit_integers_8() { - let a = U384::from_hex_unchecked("07df9c74fa9d5aafa74a87dbbf93215659d8a3e1706d4b06de9512284802580eb36ae12ea59f90db5b1799d0970a42e"); - let b = U384::from_hex_unchecked("d515e54973f0643a6a9957579c1f84020a6a91d5d5f27b75401c7538d2c9ea9cafff44a2c606877d46c49a3433cc85e"); - let c = U384::from_hex_unchecked("dcf581be6e8dbeea11e3df335bb2a558644335b7465fc67c1eb187611acc42ab636a25d16ba61858a1dc3404cad6c8c"); - assert_eq!(a + b, c); - } - - #[test] - fn add_two_384_bit_integers_9() { - let a = U384::from_hex_unchecked("92977527a0f8ba00d18c1b2f1900d965d4a70e5f5f54468ffb2d4d41519385f24b078a0e7d0281d5ad0c36724dc4233"); - let b = U384::from_hex_unchecked("46facf9953a9494822bf18836ffd7e55c48b30aa81e17fa1ace0b473015307e4622b8bd6fa68ef654796a183abde842"); - let c = U384::from_hex_unchecked("d99244c0f4a20348f44b33b288fe57bb99323f09e135c631a80e01b452e68dd6ad3315e5776b713af4a2d7f5f9a2a75"); - assert_eq!(a + b, c); - } - - #[test] - fn add_two_384_bit_integers_10() { - let a = U384::from_hex_unchecked("07df9c74fa9d5aafa74a87dbbf93215659d8a3e1706d4b06de9512284802580eb36ae12ea59f90db5b1799d0970a42e"); - let b = U384::from_hex_unchecked("d515e54973f0643a6a9957579c1f84020a6a91d5d5f27b75401c7538d2c9ea9cafff44a2c606877d46c49a3433cc85e"); - let c_expected = U384::from_hex_unchecked("dcf581be6e8dbeea11e3df335bb2a558644335b7465fc67c1eb187611acc42ab636a25d16ba61858a1dc3404cad6c8c"); - let (c, overflow) = U384::add(&a, &b); - assert_eq!(c, c_expected); - assert!(!overflow); - } - - #[test] - fn add_two_384_bit_integers_11() { - let a = U384::from_hex_unchecked("92977527a0f8ba00d18c1b2f1900d965d4a70e5f5f54468ffb2d4d41519385f24b078a0e7d0281d5ad0c36724dc4233"); - let b = U384::from_hex_unchecked("46facf9953a9494822bf18836ffd7e55c48b30aa81e17fa1ace0b473015307e4622b8bd6fa68ef654796a183abde842"); - let c_expected = U384::from_hex_unchecked("d99244c0f4a20348f44b33b288fe57bb99323f09e135c631a80e01b452e68dd6ad3315e5776b713af4a2d7f5f9a2a75"); - let (c, overflow) = U384::add(&a, &b); - assert_eq!(c, c_expected); - assert!(!overflow); - } - - #[test] - fn add_two_384_bit_integers_12_with_overflow() { - let a = U384::from_hex_unchecked("b07bc844363dd56467d9ebdd5929e9bb34a8e2577db77df6cf8f2ac45bd3d0bc2fc3078d265fe761af51d6aec5b59428"); - let b = U384::from_hex_unchecked("cbbc474761bb7995ff54e25fa5d30295604fe3545d0cde405e72d8c0acebb119e9158131679b6c34483a3dafb49deeea"); - let c_expected = U384::from_hex_unchecked("7c380f8b97f94efa672ece3cfefcec5094f8c5abdac45c372e02038508bf81d618d888be8dfb5395f78c145e7a538312"); - let (c, overflow) = U384::add(&a, &b); - assert_eq!(c, c_expected); - assert!(overflow); - } - - #[test] - fn double_384_bit_integer_12_with_overflow() { - let a = U384::from_hex_unchecked("b07bc844363dd56467d9ebdd5929e9bb34a8e2577db77df6cf8f2ac45bd3d0bc2fc3078d265fe761af51d6aec5b59428"); - assert_eq!(U384::double(&a), U384::add(&a, &a)); - } - - #[test] - fn sub_two_384_bit_integers_1() { - let a = U384::from_u64(2); - let b = U384::from_u64(5); - let c = U384::from_u64(7); - assert_eq!(c - a, b); - } - - #[test] - fn sub_two_384_bit_integers_2() { - let a = U384::from_u64(334); - let b = U384::from_u64(666); - let c = U384::from_u64(1000); - assert_eq!(c - a, b); - } - - #[test] - fn sub_two_384_bit_integers_3() { - let a = U384::from_hex_unchecked("ffffffffffffffff"); - let b = U384::from_hex_unchecked("1"); - let c = U384::from_hex_unchecked("10000000000000000"); - assert_eq!(c - a, b); - } - - #[test] - fn sub_two_384_bit_integers_4() { - let a = U384::from_hex_unchecked("b58e1e0b66"); - let b = U384::from_hex_unchecked("55469d9619"); - let c = U384::from_hex_unchecked("10ad4bba17f"); - assert_eq!(c - a, b); - } - - #[test] - fn sub_two_384_bit_integers_5() { - let a = U384::from_hex_unchecked("e8dff25cb6160f7705221da6f"); - let b = U384::from_hex_unchecked("ab879169b5f80dc8a7969f0b0"); - let c = U384::from_hex_unchecked("1946783c66c0e1d3facb8bcb1f"); - assert_eq!(c - a, b); - } - - #[test] - fn sub_two_384_bit_integers_6() { - let a = U384::from_hex_unchecked("9adf291af3a64d59e14e7b440c850508014c551ed5"); - let b = U384::from_hex_unchecked("e7948474bce907f0feaf7e5d741a8cd2f6d1fb9448"); - let c = U384::from_hex_unchecked("18273ad8fb08f554adffdf9a1809f91daf81e50b31d"); - assert_eq!(c - a, b); - } - - #[test] - fn sub_two_384_bit_integers_7() { - let a = U384::from_hex_unchecked( - "f866aef803c92bf02e85c7fad0eccb4881c59825e499fa22f98e1a8fefed4cd9a03647cd3cc84", - ); - let b = U384::from_hex_unchecked( - "9b4000dccf01a010e196154a1b998408f949d734389626ba97cb3331ee87e01dd5badc58f41b2", - ); - let c = U384::from_hex_unchecked( - "193a6afd4d2cacc01101bdd44ec864f517b0f6f5a1d3020dd91594dc1de752cf775f1242630e36", - ); - assert_eq!(c - a, b); - } - - #[test] - fn sub_two_384_bit_integers_8() { - let a = U384::from_hex_unchecked("07df9c74fa9d5aafa74a87dbbf93215659d8a3e1706d4b06de9512284802580eb36ae12ea59f90db5b1799d0970a42e"); - let b = U384::from_hex_unchecked("d515e54973f0643a6a9957579c1f84020a6a91d5d5f27b75401c7538d2c9ea9cafff44a2c606877d46c49a3433cc85e"); - let c = U384::from_hex_unchecked("dcf581be6e8dbeea11e3df335bb2a558644335b7465fc67c1eb187611acc42ab636a25d16ba61858a1dc3404cad6c8c"); - assert_eq!(c - a, b); - } - - #[test] - fn sub_two_384_bit_integers_9() { - let a = U384::from_hex_unchecked("92977527a0f8ba00d18c1b2f1900d965d4a70e5f5f54468ffb2d4d41519385f24b078a0e7d0281d5ad0c36724dc4233"); - let b = U384::from_hex_unchecked("46facf9953a9494822bf18836ffd7e55c48b30aa81e17fa1ace0b473015307e4622b8bd6fa68ef654796a183abde842"); - let c = U384::from_hex_unchecked("d99244c0f4a20348f44b33b288fe57bb99323f09e135c631a80e01b452e68dd6ad3315e5776b713af4a2d7f5f9a2a75"); - assert_eq!(c - a, b); - } - - #[test] - fn sub_two_384_bit_integers_11_without_overflow() { - let a = U384::from_u64(334); - let b_expected = U384::from_u64(666); - let c = U384::from_u64(1000); - let (b, underflow) = U384::sub(&c, &a); - assert!(!underflow); - assert_eq!(b_expected, b); - } - - #[test] - fn sub_two_384_bit_integers_11_with_overflow() { - let a = U384::from_u64(334); - let b_expected = U384::from_hex_unchecked("fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd66"); - let c = U384::from_u64(1000); - let (b, underflow) = U384::sub(&a, &c); - assert!(underflow); - assert_eq!(b_expected, b); - } - - #[test] - fn partial_order_works() { - assert!(U384::from_u64(10) <= U384::from_u64(10)); - assert!(U384::from_u64(1) < U384::from_u64(2)); - assert!(U384::from_u64(2) >= U384::from_u64(1)); - - assert!(U384::from_u64(10) >= U384::from_u64(10)); - assert!(U384::from_u64(2) > U384::from_u64(1)); - assert!(U384::from_u64(1) <= U384::from_u64(2)); - - let a = U384::from_hex_unchecked("92977527a0f8ba00d18c1b2f1900d965d4a70e5f5f54468ffb2d4d41519385f24b078a0e7d0281d5ad0c36724dc4233"); - let c = U384::from_hex_unchecked("d99244c0f4a20348f44b33b288fe57bb99323f09e135c631a80e01b452e68dd6ad3315e5776b713af4a2d7f5f9a2a75"); - - assert!(&a <= &a); - assert!(&a >= &a); - assert!(&a >= &a); - assert!(&a <= &a); - assert!(&a < &(&a + U384::from_u64(1))); - assert!(&a <= &(&a + U384::from_u64(1))); - assert!(&a + U384::from_u64(1) > a); - assert!((&a + U384::from_u64(1) >= a)); - assert!(&a <= &c); - assert!(&a < &c); - assert!(&a < &c); - assert!(&a <= &c); - assert!(&c > &a); - assert!(&c >= &a); - assert!(&c >= &a); - assert!(&c > &a); - assert!(a < c); - } - - #[test] - fn mul_two_384_bit_integers_works_1() { - let a = U384::from_u64(3); - let b = U384::from_u64(8); - let c = U384::from_u64(3 * 8); - assert_eq!(a * b, c); - } - - #[test] - fn mul_two_384_bit_integers_works_2() { - let a = U384::from_hex_unchecked("6131d99f840b3b0"); - let b = U384::from_hex_unchecked("6f5c466db398f43"); - let c = U384::from_hex_unchecked("2a47a603a77f871dfbb937af7e5710"); - assert_eq!(a * b, c); - } - - #[test] - fn mul_two_384_bit_integers_works_3() { - let a = U384::from_hex_unchecked("84a6add5db9e095b2e0f6b40eff8ee"); - let b = U384::from_hex_unchecked("2347db918f725461bec2d5c57"); - let c = U384::from_hex_unchecked("124805c476c9462adc0df6c88495d4253f5c38033afc18d78d920e2"); - assert_eq!(a * b, c); - } - - #[test] - fn mul_two_384_bit_integers_works_4() { - let a = U384::from_hex_unchecked("04050753dd7c0b06c404633016f87040"); - let b = U384::from_hex_unchecked("dc3830be041b3b4476445fcad3dac0f6f3a53e4ba12da"); - let c = U384::from_hex_unchecked( - "375342999dab7f52f4010c4abc2e18b55218015931a55d6053ac39e86e2a47d6b1cb95f41680", - ); - assert_eq!(a * b, c); - } - - #[test] - fn mul_two_384_bit_integers_works_5() { - let a = U384::from_hex_unchecked("7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8"); - let b = U384::from_hex_unchecked("2"); - let c_expected = U384::from_hex_unchecked( - "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0", - ); - assert_eq!(a * b, c_expected); - } - - #[test] - #[should_panic] - fn mul_two_384_bit_integers_works_6() { - let a = U384::from_hex_unchecked("800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"); - let b = U384::from_hex_unchecked("2"); - let _c = a * b; - } - - #[test] - fn mul_two_384_bit_integers_works_7_hi_lo() { - let a = U384::from_hex_unchecked("04050753dd7c0b06c404633016f87040"); - let b = U384::from_hex_unchecked("dc3830be041b3b4476445fcad3dac0f6f3a53e4ba12da"); - let hi_expected = U384::from_hex_unchecked("0"); - let lo_expected = U384::from_hex_unchecked( - "375342999dab7f52f4010c4abc2e18b55218015931a55d6053ac39e86e2a47d6b1cb95f41680", - ); - let (hi, lo) = U384::mul(&a, &b); - assert_eq!(hi, hi_expected); - assert_eq!(lo, lo_expected); - } - - #[test] - fn mul_two_384_bit_integers_works_8_hi_lo() { - let a = U384::from_hex_unchecked("5e2d939b602a50911232731d04fe6f40c05f97da0602307099fb991f9b414e2d52bef130349ec18db1a0215ea6caf76"); - let b = U384::from_hex_unchecked("3f3ad1611ab58212f92a2484e9560935b9ac4615fe61cfed1a4861e193a74d20c94f9f88d8b2cc089543c3f699969d9"); - let hi_expected = U384::from_hex_unchecked( - "1742daad9c7861dd3499e7ece65467e337937b27e20d641b225bfe00323d33ed62715654eadc092b057a5f19f2ad6c", - ); - let lo_expected = U384::from_hex_unchecked("9969c0417b9304d9c16b046c860447d3533999e16710d2e90a44959a168816c015ffb44b987e8cbb82bd46b08d9e2106"); - let (hi, lo) = U384::mul(&a, &b); - assert_eq!(hi, hi_expected); - assert_eq!(lo, lo_expected); - } - - #[test] - fn mul_two_384_bit_integers_works_9_hi_lo() { - let a = U384::from_hex_unchecked("800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"); - let b = U384::from_hex_unchecked("2"); - let hi_expected = U384::from_hex_unchecked("1"); - let lo_expected = U384::from_hex_unchecked("0"); - let (hi, lo) = U384::mul(&a, &b); - assert_eq!(hi, hi_expected); - assert_eq!(lo, lo_expected); - } - - #[test] - fn shift_left_on_384_bit_integer_works_1() { - let a = U384::from_hex_unchecked("1"); - let b = U384::from_hex_unchecked("10"); - assert_eq!(a << 4, b); - } - - #[test] - fn shift_left_on_384_bit_integer_works_2() { - let a = U384::from_u64(1); - let b = U384::from_u128(1_u128 << 64); - assert_eq!(a << 64, b); - } - - #[test] - fn shift_left_on_384_bit_integer_works_3() { - let a = U384::from_hex_unchecked("10"); - let b = U384::from_hex_unchecked("1000"); - assert_eq!(&a << 8, b); - } - - #[test] - fn shift_left_on_384_bit_integer_works_4() { - let a = U384::from_hex_unchecked("e45542992b6844553f3cb1c5ac33e7fa5"); - let b = U384::from_hex_unchecked("391550a64ada11154fcf2c716b0cf9fe940"); - assert_eq!(a << 6, b); - } - - #[test] - fn shift_left_on_384_bit_integer_works_5() { - let a = U384::from_hex_unchecked( - "03303f4d6c2d1caf0c24a6b0239b679a8390aa99bead76bc0093b1bc1a8101f5ce", - ); - let b = U384::from_hex_unchecked("6607e9ad85a395e18494d604736cf35072155337d5aed7801276378350203eb9c0000000000000000000000000000000"); - assert_eq!(&a << 125, b); - } - - #[test] - fn shift_left_on_384_bit_integer_works_6() { - let a = U384::from_hex_unchecked("762e8968bc392ed786ab132f0b5b0cacd385dd51de3a"); - let b = U384::from_hex_unchecked( - "762e8968bc392ed786ab132f0b5b0cacd385dd51de3a00000000000000000000000000000000", - ); - assert_eq!(&a << (64 * 2), b); - } - - #[test] - fn shift_left_on_384_bit_integer_works_7() { - let a = U384::from_hex_unchecked("90823e0bd707f"); - let b = U384::from_hex_unchecked( - "90823e0bd707f000000000000000000000000000000000000000000000000", - ); - assert_eq!(&a << (64 * 3), b); - } - - #[test] - fn shift_right_on_384_bit_integer_works_1() { - let a = U384::from_hex_unchecked("1"); - let b = U384::from_hex_unchecked("10"); - assert_eq!(b >> 4, a); - } - - #[test] - fn shift_right_on_384_bit_integer_works_2() { - let a = U384::from_hex_unchecked("10"); - let b = U384::from_hex_unchecked("1000"); - assert_eq!(&b >> 8, a); - } - - #[test] - fn shift_right_on_384_bit_integer_works_3() { - let a = U384::from_hex_unchecked("e45542992b6844553f3cb1c5ac33e7fa5"); - let b = U384::from_hex_unchecked("391550a64ada11154fcf2c716b0cf9fe940"); - assert_eq!(b >> 6, a); - } - - #[test] - fn shift_right_on_384_bit_integer_works_4() { - let a = U384::from_hex_unchecked( - "03303f4d6c2d1caf0c24a6b0239b679a8390aa99bead76bc0093b1bc1a8101f5ce", - ); - let b = U384::from_hex_unchecked("6607e9ad85a395e18494d604736cf35072155337d5aed7801276378350203eb9c0000000000000000000000000000000"); - assert_eq!(&b >> 125, a); - } - - #[test] - fn shift_right_on_384_bit_integer_works_5() { - let a = U384::from_hex_unchecked("ba6ab46f9a9a2f20e4061b67ce4d8c3da98091cf990d7b14ef47ffe27370abbdeb6a3ce9f9cbf5df1b2430114c8558eb"); - let b = - U384::from_hex_unchecked("174d568df35345e41c80c36cf9c9b187b5301239f321af629de8fffc4e6"); - assert_eq!(a >> 151, b); - } - - #[test] - fn shift_right_on_384_bit_integer_works_6() { - let a = U384::from_hex_unchecked( - "076c075d2f65e39b9ecdde8bf6f8c94241962ce0f557b7739673200c777152eb7e772ad35", - ); - let b = U384::from_hex_unchecked("ed80eba5ecbc7373d9bbd17edf19284832c59c1eaaf6ee7"); - assert_eq!(&a >> 99, b); - } - - #[test] - fn shift_right_on_384_bit_integer_works_7() { - let a = U384::from_hex_unchecked("6a9ce35d8940a5ebd29604ce9a182ade76f03f7e9965760b84a8cfd1d3dd2e612669fe000e58b2af688fd90"); - let b = U384::from_hex_unchecked("6a9ce35d8940a5ebd29604ce9a182ade76f03f7"); - assert_eq!(&a >> (64 * 3), b); - } - - #[test] - fn shift_right_on_384_bit_integer_works_8() { - let a = U384::from_hex_unchecked("5322c128ec84081b6c376c108ebd7fd36bbd44f71ee5e6ad6bcb3dd1c5265bd7db75c90b2665a0826d17600f0e9"); - let b = - U384::from_hex_unchecked("5322c128ec84081b6c376c108ebd7fd36bbd44f71ee5e6ad6bcb3dd1c52"); - assert_eq!(&a >> (64 * 2), b); - } - - #[test] - #[cfg(feature = "alloc")] - fn to_be_bytes_works() { - let number = U384::from_u64(1); - let expected_bytes = [ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, - ]; - - assert_eq!(number.to_bytes_be(), expected_bytes); - } - - #[test] - #[cfg(feature = "alloc")] - fn to_le_bytes_works() { - let number = U384::from_u64(1); - let expected_bytes = [ - 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - ]; - - assert_eq!(number.to_bytes_le(), expected_bytes); - } - - #[test] - fn from_bytes_be_works() { - let bytes = [ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, - ]; - let expected_number = U384::from_u64(1); - - assert_eq!(U384::from_bytes_be(&bytes).unwrap(), expected_number); - } - - #[test] - fn from_bytes_le_works() { - let bytes = [ - 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - ]; - let expected_number = U384::from_u64(1); - - assert_eq!(U384::from_bytes_le(&bytes).unwrap(), expected_number); - } - - #[test] - fn from_bytes_be_works_with_extra_data() { - let bytes = [ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - ]; - let expected_number = U384::from_u64(0); - - assert_eq!(U384::from_bytes_be(&bytes).unwrap(), expected_number); - } - - #[test] - #[should_panic] - fn from_bytes_be_errs_with_less_data() { - let bytes = [0, 0, 0, 0, 0]; - U384::from_bytes_be(&bytes).unwrap(); - } - - #[test] - #[should_panic] - fn from_bytes_le_errs_with_less_data() { - let bytes = [0, 0, 0, 0, 0]; - U384::from_bytes_le(&bytes).unwrap(); - } - - #[test] - fn from_bytes_le_works_with_extra_data() { - let bytes = [ - 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, - ]; - let expected_number = U384::from_u64(1); - - assert_eq!(U384::from_bytes_le(&bytes).unwrap(), expected_number); - } - - #[test] - fn test_square_0() { - let a = U384::from_hex_unchecked("362e35606447fb568704026c25da7a304bc7bd0aea36a61d77d4151395078cfa332b9d4928a60721eece725bbc81e158"); - let (hi, lo) = U384::square(&a); - assert_eq!(lo, U384::from_hex_unchecked("11724caeb10c4bce5319097d74aed2246e2942b56b7365b5b2f8ceb3bb847db4828862043299d798577996e210bce40")); - assert_eq!(hi, U384::from_hex_unchecked("b7786dbe41375b7ff64dbdc65152ef7d3fdbf499485e26486201cdbfb71b5673c77eb355a1274d08cbfbc1a4cdfdfad")); - } - - #[test] - fn test_square_1() { - let a = U384::from_limbs([0, 0, 0, 0, 0, u64::MAX]); - let (hi, lo) = U384::square(&a); - assert_eq!( - lo, - U384::from_hex_unchecked("fffffffffffffffe0000000000000001") - ); - assert_eq!(hi, U384::from_hex_unchecked("0")); - } - - #[test] - fn test_square_2() { - let a = U384::from_limbs([0, 0, 0, 0, u64::MAX, 0]); - let (hi, lo) = U384::square(&a); - assert_eq!( - lo, - U384::from_hex_unchecked( - "fffffffffffffffe000000000000000100000000000000000000000000000000" - ) - ); - assert_eq!(hi, U384::from_hex_unchecked("0")); - } - - #[test] - fn test_square_3() { - let a = U384::from_limbs([0, 0, 0, u64::MAX, 0, 0]); - let (hi, lo) = U384::square(&a); - assert_eq!(lo, U384::from_hex_unchecked("fffffffffffffffe00000000000000010000000000000000000000000000000000000000000000000000000000000000")); - assert_eq!(hi, U384::from_hex_unchecked("0")); - } - - #[test] - fn test_square_4() { - let a = U384::from_limbs([0, 0, u64::MAX, 0, 0, 0]); - let (hi, lo) = U384::square(&a); - assert_eq!(lo, U384::from_hex_unchecked("0")); - assert_eq!( - hi, - U384::from_hex_unchecked("fffffffffffffffe0000000000000001") - ); - } - - #[test] - fn test_square_5() { - let a = U384::from_limbs([0, 0, u64::MAX, u64::MAX, u64::MAX, u64::MAX]); - let (hi, lo) = U384::square(&a); - assert_eq!(lo, U384::from_hex_unchecked("fffffffffffffffffffffffffffffffe0000000000000000000000000000000000000000000000000000000000000001")); - assert_eq!( - hi, - U384::from_hex_unchecked("ffffffffffffffffffffffffffffffff") - ); - } - - #[test] - fn test_square_6() { - let a = U384::from_limbs([0, u64::MAX, u64::MAX, u64::MAX, u64::MAX, u64::MAX]); - let (hi, lo) = U384::square(&a); - assert_eq!(lo, U384::from_hex_unchecked("fffffffffffffffe00000000000000000000000000000000000000000000000000000000000000000000000000000001")); - assert_eq!( - hi, - U384::from_hex_unchecked( - "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" - ) - ); - } - - #[test] - fn test_square_7() { - let a = U384::from_limbs([u64::MAX, u64::MAX, u64::MAX, u64::MAX, u64::MAX, u64::MAX]); - let (hi, lo) = U384::square(&a); - assert_eq!(lo, U384::from_hex_unchecked("1")); - assert_eq!(hi, U384::from_hex_unchecked("fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe")); - } -} - -#[cfg(test)] -mod tests_u256 { - use crate::unsigned_integer::element::ByteConversion; - use crate::unsigned_integer::element::{UnsignedInteger, U256}; - #[cfg(feature = "proptest")] - use proptest::prelude::*; - #[cfg(feature = "proptest")] - use std::ops::Shr; - - #[cfg(feature = "proptest")] - const N_LIMBS: usize = 4; - #[cfg(feature = "proptest")] - type Uint = UnsignedInteger; - - #[cfg(feature = "proptest")] - proptest! { - #[test] - fn bitand(a in any::(), b in any::()) { - let result = a & b; - - for i in 0..N_LIMBS { - assert_eq!(result.limbs[i], a.limbs[i] & b.limbs[i]); - } - } - - #[test] - fn bitand_assign(a in any::(), b in any::()) { - let mut result = a; - result &= b; - - for i in 0..N_LIMBS { - assert_eq!(result.limbs[i], a.limbs[i] & b.limbs[i]); - } - } - - #[test] - fn bitor(a in any::(), b in any::()) { - let result = a | b; - - for i in 0..N_LIMBS { - assert_eq!(result.limbs[i], a.limbs[i] | b.limbs[i]); - } - } - - #[test] - fn bitor_assign(a in any::(), b in any::()) { - let mut result = a; - result |= b; - - for i in 0..N_LIMBS { - assert_eq!(result.limbs[i], a.limbs[i] | b.limbs[i]); - } - } - - #[test] - fn bitxor(a in any::(), b in any::()) { - let result = a ^ b; - - for i in 0..N_LIMBS { - assert_eq!(result.limbs[i], a.limbs[i] ^ b.limbs[i]); - } - } - - #[test] - fn bitxor_assign(a in any::(), b in any::()) { - let mut result = a; - result ^= b; - - for i in 0..N_LIMBS { - assert_eq!(result.limbs[i], a.limbs[i] ^ b.limbs[i]); - } - } - - #[test] - fn div_rem(a in any::(), b in any::()) { - let a = a.shr(128); - let b = b.shr(128); - assert_eq!((a * b).div_rem(&b), (a, Uint::from_u64(0))); - } - } - - #[test] - fn construct_new_integer_from_limbs() { - let a: U256 = UnsignedInteger { - limbs: [0, 1, 2, 3], - }; - assert_eq!(U256::from_limbs([0, 1, 2, 3]), a); - } - - #[test] - fn construct_new_integer_from_u64_1() { - let a = U256::from_u64(1_u64); - assert_eq!(a.limbs, [0, 0, 0, 1]); - } - - #[test] - fn construct_new_integer_from_u64_2() { - let a = U256::from_u64(u64::MAX); - assert_eq!(a.limbs, [0, 0, 0, u64::MAX]); - } - - #[test] - fn construct_new_integer_from_u128_1() { - let a = U256::from_u128(u128::MAX); - assert_eq!(a.limbs, [0, 0, u64::MAX, u64::MAX]); - } - - #[test] - fn construct_new_integer_from_u128_4() { - let a = U256::from_u128(276371540478856090688472252609570374439); - assert_eq!(a.limbs, [0, 0, 14982131230017065096, 14596400355126379303]); - } - - #[test] - fn construct_new_integer_from_hex_1() { - let a = U256::from_hex_unchecked("1"); - assert_eq!(a.limbs, [0, 0, 0, 1]); - } - - #[test] - fn construct_new_integer_from_hex_2() { - let a = U256::from_hex_unchecked("f"); - assert_eq!(a.limbs, [0, 0, 0, 15]); - } - - #[test] - fn construct_new_integer_from_hex_3() { - let a = U256::from_hex_unchecked("10000000000000000"); - assert_eq!(a.limbs, [0, 0, 1, 0]); - } - - #[test] - fn construct_new_integer_from_hex_4() { - let a = U256::from_hex_unchecked("a0000000000000000"); - assert_eq!(a.limbs, [0, 0, 10, 0]); - } - - #[test] - fn construct_new_integer_from_hex_5() { - let a = U256::from_hex_unchecked("ffffffffffffffffff"); - assert_eq!(a.limbs, [0, 0, 255, u64::MAX]); - } - - #[test] - fn construct_new_integer_from_hex_6() { - let a = U256::from_hex_unchecked("eb235f6144d9e91f4b14"); - assert_eq!(a.limbs, [0, 0, 60195, 6872850209053821716]); - } - - #[test] - fn construct_new_integer_from_hex_7() { - let a = U256::from_hex_unchecked("2b20aaa5cf482b239e2897a787faf4660cc95597854beb2"); - assert_eq!( - a.limbs, - [ - 0, - 194229460750598834, - 4171047363999149894, - 6975114134393503410 - ] - ); - } - - #[test] - fn construct_new_integer_from_hex_8() { - let a = U256::from_hex_unchecked( - "2B20AAA5CF482B239E2897A787FAF4660CC95597854BEB235F6144D9E91F4B14", - ); - assert_eq!( - a.limbs, - [ - 3107671372009581347, - 11396525602857743462, - 921361708038744867, - 6872850209053821716 - ] - ); - } - - #[test] - fn construct_new_integer_from_dec_1() { - let a = U256::from_dec_str("1").unwrap(); - assert_eq!(a.limbs, [0, 0, 0, 1]); - } - - #[test] - fn construct_integer_from_invalid_hex_returns_error() { - use crate::unsigned_integer::element::CreationError; - assert_eq!(U256::from_hex("0xaO"), Err(CreationError::InvalidHexString)); - assert_eq!(U256::from_hex("0xOa"), Err(CreationError::InvalidHexString)); - assert_eq!(U256::from_hex("0xm"), Err(CreationError::InvalidHexString)); - } - - #[test] - fn construct_new_integer_from_dec_2() { - let a = U256::from_dec_str("15").unwrap(); - assert_eq!(a.limbs, [0, 0, 0, 15]); - } - - #[test] - fn construct_new_integer_from_dec_3() { - let a = U256::from_dec_str("18446744073709551616").unwrap(); - assert_eq!(a.limbs, [0, 0, 1, 0]); - } - - #[test] - fn construct_new_integer_from_dec_4() { - let a = U256::from_dec_str("184467440737095516160").unwrap(); - assert_eq!(a.limbs, [0, 0, 10, 0]); - } - - #[test] - fn construct_new_integer_from_dec_5() { - let a = U256::from_dec_str("4722366482869645213695").unwrap(); - assert_eq!(a.limbs, [0, 0, 255, u64::MAX]); - } - - #[test] - fn construct_new_integer_from_dec_6() { - let a = U256::from_dec_str("1110408632367155513346836").unwrap(); - assert_eq!(a.limbs, [0, 0, 60195, 6872850209053821716]); - } - - #[test] - fn construct_new_integer_from_dec_7() { - let a = - U256::from_dec_str("66092860629991288370279803883558073888453977263446474418").unwrap(); - assert_eq!( - a.limbs, - [ - 0, - 194229460750598834, - 4171047363999149894, - 6975114134393503410 - ] - ); - } - - #[test] - fn construct_new_integer_from_dec_8() { - let a = U256::from_dec_str( - "19507169362252850253634654373914901165934018806002526957372506333098895428372", - ) - .unwrap(); - assert_eq!( - a.limbs, - [ - 3107671372009581347, - 11396525602857743462, - 921361708038744867, - 6872850209053821716 - ] - ); - } - - #[test] - fn construct_new_integer_from_dec_empty() { - assert!(U256::from_dec_str("").is_err()); - } - - #[test] - fn construct_new_integer_from_dec_invalid() { - assert!(U256::from_dec_str("0xff").is_err()); - } - - #[test] - fn equality_works_1() { - let a = U256::from_hex_unchecked("1"); - let b = U256 { - limbs: [0, 0, 0, 1], - }; - assert_eq!(a, b); - } - #[test] - fn equality_works_2() { - let a = U256::from_hex_unchecked("f"); - let b = U256 { - limbs: [0, 0, 0, 15], - }; - assert_eq!(a, b); - } - - #[test] - fn equality_works_3() { - let a = U256::from_hex_unchecked("10000000000000000"); - let b = U256 { - limbs: [0, 0, 1, 0], - }; - assert_eq!(a, b); - } - - #[test] - fn equality_works_4() { - let a = U256::from_hex_unchecked("a0000000000000000"); - let b = U256 { - limbs: [0, 0, 10, 0], - }; - assert_eq!(a, b); - } - - #[test] - fn equality_works_5() { - let a = U256::from_hex_unchecked("ffffffffffffffffff"); - let b = U256 { - limbs: [0, 0, u8::MAX as u64, u64::MAX], - }; - assert_eq!(a, b); - } - - #[test] - fn equality_works_6() { - let a = U256::from_hex_unchecked("eb235f6144d9e91f4b14"); - let b = U256 { - limbs: [0, 0, 60195, 6872850209053821716], - }; - assert_eq!(a, b); - } - - #[test] - fn equality_works_7() { - let a = U256::from_hex_unchecked("2b20aaa5cf482b239e2897a787faf4660cc95597854beb2"); - let b = U256 { - limbs: [ - 0, - 194229460750598834, - 4171047363999149894, - 6975114134393503410, - ], - }; - assert_eq!(a, b); - } - - #[test] - fn equality_works_8() { - let a = U256::from_hex_unchecked( - "2B20AAA5CF482B239E2897A787FAF4660CC95597854BEB235F6144D9E91F4B14", - ); - let b = U256 { - limbs: [ - 3107671372009581347, - 11396525602857743462, - 921361708038744867, - 6872850209053821716, - ], - }; - assert_eq!(a, b); - } - - #[test] - fn equality_works_9() { - let a = U256::from_hex_unchecked("fffffff"); - let b = U256::from_hex_unchecked("fefffff"); - assert_ne!(a, b); - } - - #[test] - fn equality_works_10() { - let a = U256::from_hex_unchecked("ffff000000000000"); - let b = U256::from_hex_unchecked("ffff000000100000"); - assert_ne!(a, b); - } - - #[test] - fn double_256_bit_integer_1() { - let a = U256::from_u64(2); - let b = U256::from_u64(5); - let c = U256::from_u64(7); - assert_eq!(U256::double(&a).0, a + a); - assert_eq!(U256::double(&b).0, b + b); - assert_eq!(U256::double(&c).0, c + c); - } - - #[test] - fn add_two_256_bit_integers_1() { - let a = U256::from_u64(2); - let b = U256::from_u64(5); - let c = U256::from_u64(7); - assert_eq!(a + b, c); - } - - #[test] - fn add_two_256_bit_integers_2() { - let a = U256::from_u64(334); - let b = U256::from_u64(666); - let c = U256::from_u64(1000); - assert_eq!(a + b, c); - } - - #[test] - fn add_two_256_bit_integers_3() { - let a = U256::from_hex_unchecked("ffffffffffffffff"); - let b = U256::from_hex_unchecked("1"); - let c = U256::from_hex_unchecked("10000000000000000"); - assert_eq!(a + b, c); - } - - #[test] - fn add_two_256_bit_integers_4() { - let a = U256::from_hex_unchecked("b58e1e0b66"); - let b = U256::from_hex_unchecked("55469d9619"); - let c = U256::from_hex_unchecked("10ad4bba17f"); - assert_eq!(a + b, c); - } - - #[test] - fn add_two_256_bit_integers_5() { - let a = U256::from_hex_unchecked("e8dff25cb6160f7705221da6f"); - let b = U256::from_hex_unchecked("ab879169b5f80dc8a7969f0b0"); - let c = U256::from_hex_unchecked("1946783c66c0e1d3facb8bcb1f"); - assert_eq!(a + b, c); - } - - #[test] - fn add_two_256_bit_integers_6() { - let a = U256::from_hex_unchecked("9adf291af3a64d59e14e7b440c850508014c551ed5"); - let b = U256::from_hex_unchecked("e7948474bce907f0feaf7e5d741a8cd2f6d1fb9448"); - let c = U256::from_hex_unchecked("18273ad8fb08f554adffdf9a1809f91daf81e50b31d"); - assert_eq!(a + b, c); - } - - #[test] - fn add_two_256_bit_integers_7() { - let a = U256::from_hex_unchecked( - "10d3bc05496380cfe27bf5d97ddb99ac95eb5ecfbd3907eadf877a4c2dfa05f6", - ); - let b = U256::from_hex_unchecked( - "0866aef803c92bf02e85c7fad0eccb4881c59825e499fa22f98e1a8fefed4cd9", - ); - let c = U256::from_hex_unchecked( - "193a6afd4d2cacc01101bdd44ec864f517b0f6f5a1d3020dd91594dc1de752cf", - ); - assert_eq!(a + b, c); - } - - #[test] - fn add_two_256_bit_integers_8() { - let a = U256::from_hex_unchecked( - "07df9c74fa9d5aafa74a87dbbf93215659d8a3e1706d4b06de9512284802580f", - ); - let b = U256::from_hex_unchecked( - "d515e54973f0643a6a9957579c1f84020a6a91d5d5f27b75401c7538d2c9ea9c", - ); - let c = U256::from_hex_unchecked( - "dcf581be6e8dbeea11e3df335bb2a558644335b7465fc67c1eb187611acc42ab", - ); - assert_eq!(a + b, c); - } - - #[test] - fn add_two_256_bit_integers_9() { - let a = U256::from_hex_unchecked( - "92977527a0f8ba00d18c1b2f1900d965d4a70e5f5f54468ffb2d4d41519385f2", - ); - let b = U256::from_hex_unchecked( - "46facf9953a9494822bf18836ffd7e55c48b30aa81e17fa1ace0b473015307e4", - ); - let c = U256::from_hex_unchecked( - "d99244c0f4a20348f44b33b288fe57bb99323f09e135c631a80e01b452e68dd6", - ); - assert_eq!(a + b, c); - } - - #[test] - fn add_two_256_bit_integers_10() { - let a = U256::from_hex_unchecked( - "07df9c74fa9d5aafa74a87dbbf93215659d8a3e1706d4b06de9512284802580f", - ); - let b = U256::from_hex_unchecked( - "d515e54973f0643a6a9957579c1f84020a6a91d5d5f27b75401c7538d2c9ea9c", - ); - let c_expected = U256::from_hex_unchecked( - "dcf581be6e8dbeea11e3df335bb2a558644335b7465fc67c1eb187611acc42ab", - ); - let (c, overflow) = U256::add(&a, &b); - assert_eq!(c, c_expected); - assert!(!overflow); - } - - #[test] - fn add_two_256_bit_integers_11() { - let a = U256::from_hex_unchecked( - "92977527a0f8ba00d18c1b2f1900d965d4a70e5f5f54468ffb2d4d41519385f2", - ); - let b = U256::from_hex_unchecked( - "46facf9953a9494822bf18836ffd7e55c48b30aa81e17fa1ace0b473015307e4", - ); - let c_expected = U256::from_hex_unchecked( - "d99244c0f4a20348f44b33b288fe57bb99323f09e135c631a80e01b452e68dd6", - ); - let (c, overflow) = U256::add(&a, &b); - assert_eq!(c, c_expected); - assert!(!overflow); - } - - #[test] - fn add_two_256_bit_integers_12_with_overflow() { - let a = U256::from_hex_unchecked( - "b07bc844363dd56467d9ebdd5929e9bb34a8e2577db77df6cf8f2ac45bd3d0bc", - ); - let b = U256::from_hex_unchecked( - "cbbc474761bb7995ff54e25fa5d30295604fe3545d0cde405e72d8c0acebb119", - ); - let c_expected = U256::from_hex_unchecked( - "7c380f8b97f94efa672ece3cfefcec5094f8c5abdac45c372e02038508bf81d5", - ); - let (c, overflow) = U256::add(&a, &b); - assert_eq!(c, c_expected); - assert!(overflow); - } - - #[test] - fn double_256_bit_integer_12_with_overflow() { - let a = U256::from_hex_unchecked( - "b07bc844363dd56467d9ebdd5929e9bb34a8e2577db77df6cf8f2ac45bd3d0bc", - ); - let b = U256::from_hex_unchecked( - "cbbc474761bb7995ff54e25fa5d30295604fe3545d0cde405e72d8c0acebb119", - ); - assert_eq!(U256::double(&a), U256::add(&a, &a)); - assert_eq!(U256::double(&b), U256::add(&b, &b)); - } - - #[test] - fn sub_two_256_bit_integers_1() { - let a = U256::from_u64(2); - let b = U256::from_u64(5); - let c = U256::from_u64(7); - assert_eq!(c - a, b); - } - - #[test] - fn sub_two_256_bit_integers_2() { - let a = U256::from_u64(334); - let b = U256::from_u64(666); - let c = U256::from_u64(1000); - assert_eq!(c - a, b); - } - - #[test] - fn sub_two_256_bit_integers_3() { - let a = U256::from_hex_unchecked("ffffffffffffffff"); - let b = U256::from_hex_unchecked("1"); - let c = U256::from_hex_unchecked("10000000000000000"); - assert_eq!(c - a, b); - } - - #[test] - fn sub_two_256_bit_integers_4() { - let a = U256::from_hex_unchecked("b58e1e0b66"); - let b = U256::from_hex_unchecked("55469d9619"); - let c = U256::from_hex_unchecked("10ad4bba17f"); - assert_eq!(c - a, b); - } - - #[test] - fn sub_two_256_bit_integers_5() { - let a = U256::from_hex_unchecked("e8dff25cb6160f7705221da6f"); - let b = U256::from_hex_unchecked("ab879169b5f80dc8a7969f0b0"); - let c = U256::from_hex_unchecked("1946783c66c0e1d3facb8bcb1f"); - assert_eq!(c - a, b); - } - - #[test] - fn sub_two_256_bit_integers_6() { - let a = U256::from_hex_unchecked("9adf291af3a64d59e14e7b440c850508014c551ed5"); - let b = U256::from_hex_unchecked("e7948474bce907f0feaf7e5d741a8cd2f6d1fb9448"); - let c = U256::from_hex_unchecked("18273ad8fb08f554adffdf9a1809f91daf81e50b31d"); - assert_eq!(c - a, b); - } - - #[test] - fn sub_two_256_bit_integers_7() { - let a = U256::from_hex_unchecked( - "9b4000dccf01a010e196154a1b998408f949d734389626ba97cb3331ee87e01d", - ); - let b = U256::from_hex_unchecked( - "5d26ae1b34c78bdf4cefb2b0b553473f887bc0f1ac03d36861c2e75e01656cbc", - ); - let c = U256::from_hex_unchecked( - "f866aef803c92bf02e85c7fad0eccb4881c59825e499fa22f98e1a8fefed4cd9", - ); - assert_eq!(c - a, b); - } - - #[test] - fn sub_two_256_bit_integers_8() { - let a = U256::from_hex_unchecked( - "07df9c74fa9d5aafa74a87dbbf93215659d8a3e1706d4b06de9512284802580e", - ); - let b = U256::from_hex_unchecked( - "d515e54973f0643a6a9957579c1f84020a6a91d5d5f27b75401c7538d2c9ea9d", - ); - let c = U256::from_hex_unchecked( - "dcf581be6e8dbeea11e3df335bb2a558644335b7465fc67c1eb187611acc42ab", - ); - assert_eq!(c - a, b); - } - - #[test] - fn sub_two_256_bit_integers_9() { - let a = U256::from_hex_unchecked( - "92977527a0f8ba00d18c1b2f1900d965d4a70e5f5f54468ffb2d4d41519385f2", - ); - let b = U256::from_hex_unchecked( - "46facf9953a9494822bf18836ffd7e55c48b30aa81e17fa1ace0b473015307e4", - ); - let c = U256::from_hex_unchecked( - "d99244c0f4a20348f44b33b288fe57bb99323f09e135c631a80e01b452e68dd6", - ); - assert_eq!(c - a, b); - } - - #[test] - fn sub_two_256_bit_integers_11_without_overflow() { - let a = U256::from_u64(334); - let b_expected = U256::from_u64(666); - let c = U256::from_u64(1000); - let (b, overflow) = U256::sub(&c, &a); - assert!(!overflow); - assert_eq!(b_expected, b); - } - - #[test] - fn sub_two_256_bit_integers_11_with_overflow() { - let a = U256::from_u64(334); - let b_expected = U256::from_hex_unchecked( - "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd66", - ); - let c = U256::from_u64(1000); - let (b, overflow) = U256::sub(&a, &c); - assert!(overflow); - assert_eq!(b_expected, b); - } - - #[test] - fn const_le_works() { - let a = U256::from_u64(334); - let b = U256::from_u128(333); - assert!(U256::const_le(&b, &a)); - assert!(U256::const_le(&a, &a)); - assert!(!U256::const_le(&a, &b)); - } - - #[test] - fn partial_order_works() { - assert!(U256::from_u64(10) <= U256::from_u64(10)); - assert!(U256::from_u64(1) < U256::from_u64(2)); - assert!(U256::from_u64(2) >= U256::from_u64(1)); - - assert!(U256::from_u64(10) >= U256::from_u64(10)); - assert!(U256::from_u64(2) > U256::from_u64(1)); - assert!(U256::from_u64(1) <= U256::from_u64(2)); - - let a = U256::from_hex_unchecked( - "5d4a70e5f5f54468ffb2d4d41519385f24b078a0e7d0281d5ad0c36724dc4233", - ); - let c = U256::from_hex_unchecked( - "b99323f09e135c631a80e01b452e68dd6ad3315e5776b713af4a2d7f5f9a2a75", - ); - - assert!(&a <= &a); - assert!(&a >= &a); - assert!(&a >= &a); - assert!(&a <= &a); - assert!(&a < &(&a + U256::from_u64(1))); - assert!(&a <= &(&a + U256::from_u64(1))); - assert!(&a + U256::from_u64(1) > a); - assert!((&a + U256::from_u64(1) >= a)); - assert!(&a <= &c); - assert!(&a < &c); - assert!(&a < &c); - assert!(&a <= &c); - assert!(&c > &a); - assert!(&c >= &a); - assert!(&c >= &a); - assert!(&c > &a); - assert!(a < c); - } - - #[test] - fn mul_two_256_bit_integers_works_1() { - let a = U256::from_u64(3); - let b = U256::from_u64(8); - let c = U256::from_u64(3 * 8); - assert_eq!(a * b, c); - } - - #[test] - fn mul_two_256_bit_integers_works_2() { - let a = U256::from_hex_unchecked("6131d99f840b3b0"); - let b = U256::from_hex_unchecked("6f5c466db398f43"); - let c = U256::from_hex_unchecked("2a47a603a77f871dfbb937af7e5710"); - assert_eq!(a * b, c); - } - - #[test] - fn mul_two_256_bit_integers_works_3() { - let a = U256::from_hex_unchecked("84a6add5db9e095b2e0f6b40eff8ee"); - let b = U256::from_hex_unchecked("2347db918f725461bec2d5c57"); - let c = U256::from_hex_unchecked("124805c476c9462adc0df6c88495d4253f5c38033afc18d78d920e2"); - assert_eq!(a * b, c); - } - - #[test] - fn mul_two_256_bit_integers_works_4() { - let a = U256::from_hex_unchecked("15bf61fcf53a3f0ae1e8e555d"); - let b = U256::from_hex_unchecked("cbbc474761bb7995ff54e25fa5d5d0cde405e9f"); - let c_expected = U256::from_hex_unchecked( - "114ec14db0c80d30b7dcb9c45948ef04cc149e612cb544f447b146553aff2ac3", - ); - assert_eq!(a * b, c_expected); - } - - #[test] - fn mul_two_256_bit_integers_works_5_hi_lo() { - let a = U256::from_hex_unchecked( - "8e2d939b602a50911232731d04fe6f40c05f97da0602307099fb991f9b414e2d", - ); - let b = U256::from_hex_unchecked( - "7f3ad1611ab58212f92a2484e9560935b9ac4615fe61cfed1a4861e193a74d20", - ); - let hi_expected = U256::from_hex_unchecked( - "46A946D6A984FE6507DE6B8D1354256D7A7BAE4283404733BDC876A264BCE5EE", - ); - let lo_expected = U256::from_hex_unchecked( - "43F24263F10930EBE3EA0307466C19B13B9C7DBA6B3F7604B7F32FB0E3084EA0", - ); - let (hi, lo) = U256::mul(&a, &b); - assert_eq!(hi, hi_expected); - assert_eq!(lo, lo_expected); - } - - #[test] - fn shift_left_on_256_bit_integer_works_1() { - let a = U256::from_hex_unchecked("1"); - let b = U256::from_hex_unchecked("10"); - assert_eq!(a << 4, b); - } - - #[test] - fn shift_left_on_256_bit_integer_works_2() { - let a = U256::from_u64(1); - let b = U256::from_u128(1_u128 << 64); - assert_eq!(a << 64, b); - } - - #[test] - fn shift_left_on_256_bit_integer_works_3() { - let a = U256::from_hex_unchecked("10"); - let b = U256::from_hex_unchecked("1000"); - assert_eq!(&a << 8, b); - } - - #[test] - fn shift_left_on_256_bit_integer_works_4() { - let a = U256::from_hex_unchecked("e45542992b6844553f3cb1c5ac33e7fa5"); - let b = U256::from_hex_unchecked("391550a64ada11154fcf2c716b0cf9fe940"); - assert_eq!(a << 6, b); - } - - #[test] - fn shift_left_on_256_bit_integer_works_5() { - let a = U256::from_hex_unchecked("a8390aa99bead76bc0093b1bc1a8101f5ce"); - let b = U256::from_hex_unchecked( - "72155337d5aed7801276378350203eb9c0000000000000000000000000000000", - ); - assert_eq!(&a << 125, b); - } - - #[test] - fn shift_left_on_256_bit_integer_works_6() { - let a = U256::from_hex_unchecked("2ed786ab132f0b5b0cacd385dd51de3a"); - let b = U256::from_hex_unchecked( - "2ed786ab132f0b5b0cacd385dd51de3a00000000000000000000000000000000", - ); - assert_eq!(&a << (64 * 2), b); - } - - #[test] - fn shift_left_on_256_bit_integer_works_7() { - let a = U256::from_hex_unchecked("90823e0bd707f"); - let b = U256::from_hex_unchecked( - "90823e0bd707f000000000000000000000000000000000000000000000000", - ); - assert_eq!(&a << (64 * 3), b); - } - - #[test] - fn shift_right_on_256_bit_integer_works_1() { - let a = U256::from_hex_unchecked("1"); - let b = U256::from_hex_unchecked("10"); - assert_eq!(b >> 4, a); - } - - #[test] - fn shift_right_on_256_bit_integer_works_2() { - let a = U256::from_hex_unchecked("10"); - let b = U256::from_hex_unchecked("1000"); - assert_eq!(&b >> 8, a); - } - - #[test] - fn shift_right_on_256_bit_integer_works_3() { - let a = U256::from_hex_unchecked("e45542992b6844553f3cb1c5ac33e7fa5"); - let b = U256::from_hex_unchecked("391550a64ada11154fcf2c716b0cf9fe940"); - assert_eq!(b >> 6, a); - } - - #[test] - fn shift_right_on_256_bit_integer_works_4() { - let a = U256::from_hex_unchecked("390aa99bead76bc0093b1bc1a8101f5ce"); - let b = U256::from_hex_unchecked( - "72155337d5aed7801276378350203eb9c0000000000000000000000000000000", - ); - assert_eq!(&b >> 125, a); - } - - #[test] - fn shift_right_on_256_bit_integer_works_5() { - let a = U256::from_hex_unchecked( - "ba6ab46f9a9a2f20e4061b67ce4d8c3da98091cf990d7b14ef47ffe27370abbd", - ); - let b = U256::from_hex_unchecked("174d568df35345e41c80c36cf9c"); - assert_eq!(a >> 151, b); - } - - #[test] - fn shift_right_on_256_bit_integer_works_6() { - let a = U256::from_hex_unchecked( - "076c075d2f65e39b9ecdde8bf6f8c94241962ce0f557b7739673200c777152eb", - ); - let b = U256::from_hex_unchecked("ed80eba5ecbc7373d9bbd17edf19284832c59c"); - assert_eq!(&a >> 99, b); - } - - #[test] - fn shift_right_on_256_bit_integer_works_7() { - let a = U256::from_hex_unchecked( - "6a9ce35d8940a5ebd29604ce9a182ade76f03f7e9965760b84a8cfd1d3dd2e61", - ); - let b = U256::from_hex_unchecked("6a9ce35d8940a5eb"); - assert_eq!(&a >> (64 * 3), b); - } - - #[test] - fn shift_right_on_256_bit_integer_works_8() { - let a = U256::from_hex_unchecked( - "5322c128ec84081b6c376c108ebd7fd36bbd44f71ee5e6ad6bcb3dd1c5265bd7", - ); - let b = U256::from_hex_unchecked("5322c128ec84081b6c376c108ebd7fd3"); - assert_eq!(&a >> (64 * 2), b); - } - - #[test] - #[cfg(feature = "alloc")] - fn to_be_bytes_works() { - let number = U256::from_u64(1); - let expected_bytes = [ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 1, - ]; - - assert_eq!(number.to_bytes_be(), expected_bytes); - } - - #[test] - #[cfg(feature = "alloc")] - fn to_le_bytes_works() { - let number = U256::from_u64(1); - let expected_bytes = [ - 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, - ]; - - assert_eq!(number.to_bytes_le(), expected_bytes); - } - - #[test] - fn from_bytes_be_works() { - let bytes = [ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 1, - ]; - let expected_number = U256::from_u64(1); - assert_eq!(U256::from_bytes_be(&bytes).unwrap(), expected_number); - } - - #[test] - fn from_bytes_le_works() { - let bytes = [ - 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, - ]; - let expected_number = U256::from_u64(1); - assert_eq!(U256::from_bytes_le(&bytes).unwrap(), expected_number); - } - - #[test] - fn shr_inplace_works_1() { - let mut n = UnsignedInteger::<3>::from(4u64); - n >>= 1; - - assert_eq!(n, UnsignedInteger::<3>::from(2u64)); - } - - #[test] - fn shr_inplace_on_256_bit_integer_works_1() { - let a = U256::from_hex_unchecked("e45542992b6844553f3cb1c5ac33e7fa5"); - let mut b = U256::from_hex_unchecked("391550a64ada11154fcf2c716b0cf9fe940"); - b >>= 6; - assert_eq!(a, b); - } - - #[test] - fn shr_inplace_on_254_bit_integer_works_2() { - let a = U256::from_hex_unchecked("390aa99bead76bc0093b1bc1a8101f5ce"); - let mut b = U256::from_hex_unchecked( - "72155337d5aed7801276378350203eb9c0000000000000000000000000000000", - ); - b >>= 125; - assert_eq!(a, b); - } - - #[test] - fn shr_inplace_on_256_bit_integer_works_3() { - let a = U256::from_hex_unchecked("2ed786ab132f0b5b0cacd385dd51de3a"); - let mut b = U256::from_hex_unchecked( - "2ed786ab132f0b5b0cacd385dd51de3a00000000000000000000000000000000", - ); - b >>= 64 * 2; - assert_eq!(a, b); - } - - #[test] - fn shr_inplace_on_256_bit_integer_works_4() { - let a = U256::from_hex_unchecked("90823e0bd707f"); - let mut b = U256::from_hex_unchecked( - "90823e0bd707f000000000000000000000000000000000000000000000000", - ); - b >>= 64 * 3; - assert_eq!(a, b); - } - - #[test] - fn shr_inplace_on_256_bit_integer_works_5() { - let a = U256::from_hex_unchecked("24208f"); - let mut b = U256::from_hex_unchecked( - "90823e0bd707f000000000000000000000000000000000000000000000000", - ); - b >>= 222; - assert_eq!(a, b); - } - - #[test] - fn multiplying_and_dividing_for_number_is_number_with_remainder_0() { - let a = U256::from_u128(12678920202929299999999999282828); - let b = U256::from_u128(9000000000000); - assert_eq!((a * b).div_rem(&b), (a, U256::from_u64(0))); - } - - #[test] - fn unsigned_int_8_div_rem_3_is_2_2() { - let a: UnsignedInteger<4> = U256::from_u64(8); - let b = U256::from_u64(3); - assert_eq!(a.div_rem(&b), (U256::from_u64(2), U256::from_u64(2))); - } - - #[test] - fn unsigned_int_500721_div_rem_5_is_100144_1() { - let a = U256::from_u64(500721); - let b = U256::from_u64(5); - assert_eq!(a.div_rem(&b), (U256::from_u64(100144), U256::from_u64(1))); - } - - #[test] - fn div_rem_works_with_big_numbers() { - let a = U256::from_u128(4758402376589578934275873583589345); - let b = U256::from_u128(43950384634609); - assert_eq!( - a.div_rem(&b), - ( - U256::from_u128(108267593472721187331), - U256::from_u128(12368508650766) - ) - ); - } - - #[cfg(feature = "std")] - #[test] - fn to_hex_test() { - let a = U256::from_hex_unchecked("390aa99bead76bc0093b1bc1a8101f5ce"); - assert_eq!(U256::to_hex(&a), "390AA99BEAD76BC0093B1BC1A8101F5CE") - } -} diff --git a/crates/math/src/unsigned_integer/mod.rs b/crates/math/src/unsigned_integer/mod.rs deleted file mode 100644 index 5cb6907f8..000000000 --- a/crates/math/src/unsigned_integer/mod.rs +++ /dev/null @@ -1,6 +0,0 @@ -// By removing refs as clippy wants -// Implementations with all the combination of reference and not references become recursive -#[allow(clippy::op_ref)] -pub mod element; -pub mod montgomery; -pub mod traits; diff --git a/crates/math/src/unsigned_integer/montgomery.rs b/crates/math/src/unsigned_integer/montgomery.rs deleted file mode 100644 index e9202f004..000000000 --- a/crates/math/src/unsigned_integer/montgomery.rs +++ /dev/null @@ -1,269 +0,0 @@ -use super::element::UnsignedInteger; - -pub struct MontgomeryAlgorithms; -impl MontgomeryAlgorithms { - /// Compute CIOS multiplication of `a` * `b` - /// `q` is the modulus - /// `mu` is the inverse of -q modulo 2^{64} - /// Notice CIOS stands for Coarsely Integrated Operand Scanning - /// For more information see section 2.3.2 of Tolga Acar's thesis - /// https://www.microsoft.com/en-us/research/wp-content/uploads/1998/06/97Acar.pdf - #[inline(always)] - pub const fn cios( - a: &UnsignedInteger, - b: &UnsignedInteger, - q: &UnsignedInteger, - mu: &u64, - ) -> UnsignedInteger { - let mut t = [0_u64; NUM_LIMBS]; - let mut t_extra = [0_u64; 2]; - let mut i: usize = NUM_LIMBS; - while i > 0 { - i -= 1; - // C := 0 - let mut c: u128 = 0; - - // for j=N-1 to 0 - // (C,t[j]) := t[j] + a[j]*b[i] + C - let mut cs: u128; - let mut j: usize = NUM_LIMBS; - while j > 0 { - j -= 1; - cs = t[j] as u128 + (a.limbs[j] as u128) * (b.limbs[i] as u128) + c; - c = cs >> 64; - t[j] = cs as u64; - } - - // (t_extra[0],t_extra[1]) := t_extra[1] + C - cs = (t_extra[1] as u128) + c; - t_extra[0] = (cs >> 64) as u64; - t_extra[1] = cs as u64; - - let mut c: u128; - - // m := t[N-1]*q'[N-1] mod D - let m = ((t[NUM_LIMBS - 1] as u128 * *mu as u128) << 64) >> 64; - - // (C,_) := t[N-1] + m*q[N-1] - c = (t[NUM_LIMBS - 1] as u128 + m * (q.limbs[NUM_LIMBS - 1] as u128)) >> 64; - - // for j=N-1 to 1 - // (C,t[j+1]) := t[j] + m*q[j] + C - let mut j: usize = NUM_LIMBS - 1; - while j > 0 { - j -= 1; - cs = t[j] as u128 + m * (q.limbs[j] as u128) + c; - c = cs >> 64; - t[j + 1] = ((cs << 64) >> 64) as u64; - } - - // (C,t[0]) := t_extra[1] + C - cs = (t_extra[1] as u128) + c; - c = cs >> 64; - t[0] = ((cs << 64) >> 64) as u64; - - // t_extra[1] := t_extra[0] + C - t_extra[1] = t_extra[0] + c as u64; - } - let mut result = UnsignedInteger { limbs: t }; - - let overflow = t_extra[1] > 0; - - if overflow || UnsignedInteger::const_le(q, &result) { - (result, _) = UnsignedInteger::sub(&result, q); - } - result - } - - /// Compute CIOS multiplication of `a` * `b` - /// This is the Algorithm 2 described in the paper - /// "EdMSM: Multi-Scalar-Multiplication for SNARKs and Faster Montgomery multiplication" - /// https://eprint.iacr.org/2022/1400.pdf. - /// It is only suited for moduli with `q[0]` smaller than `2^63 - 1`. - /// `q` is the modulus - /// `mu` is the inverse of -q modulo 2^{64} - #[inline(always)] - pub fn cios_optimized_for_moduli_with_one_spare_bit( - a: &UnsignedInteger, - b: &UnsignedInteger, - q: &UnsignedInteger, - mu: &u64, - ) -> UnsignedInteger { - let mut t = [0_u64; NUM_LIMBS]; - let mut t_extra; - let mut i: usize = NUM_LIMBS; - while i > 0 { - i -= 1; - // C := 0 - let mut c: u128 = 0; - - // for j=N-1 to 0 - // (C,t[j]) := t[j] + a[j]*b[i] + C - let mut cs: u128; - let mut j: usize = NUM_LIMBS; - while j > 0 { - j -= 1; - cs = t[j] as u128 + (a.limbs[j] as u128) * (b.limbs[i] as u128) + c; - c = cs >> 64; - t[j] = cs as u64; - } - - t_extra = c as u64; - - let mut c: u128; - - // m := t[N-1]*q'[N-1] mod D - let m = ((t[NUM_LIMBS - 1] as u128 * *mu as u128) << 64) >> 64; - - // (C,_) := t[0] + m*q[0] - c = (t[NUM_LIMBS - 1] as u128 + m * (q.limbs[NUM_LIMBS - 1] as u128)) >> 64; - - // for j=N-1 to 1 - // (C,t[j+1]) := t[j] + m*q[j] + C - let mut j: usize = NUM_LIMBS - 1; - while j > 0 { - j -= 1; - cs = t[j] as u128 + m * (q.limbs[j] as u128) + c; - c = cs >> 64; - t[j + 1] = ((cs << 64) >> 64) as u64; - } - - // (C,t[0]) := t_extra + C - cs = (t_extra as u128) + c; - t[0] = ((cs << 64) >> 64) as u64; - } - let mut result = UnsignedInteger { limbs: t }; - - if UnsignedInteger::const_le(q, &result) { - (result, _) = UnsignedInteger::sub(&result, q); - } - result - } - - // Separated Operand Scanning Method (2.3.1) - #[inline(always)] - pub fn sos_square( - a: &UnsignedInteger, - q: &UnsignedInteger, - mu: &u64, - ) -> UnsignedInteger { - // NOTE: we use explicit `while` loops in this function because profiling pointed - // at iterators of the form `(..).rev()` as the main performance bottleneck. - - // Step 1: Compute `(hi, lo) = a * a` - let (mut hi, mut lo) = UnsignedInteger::square(a); - - // Step 2: Add terms to `(hi, lo)` until multiple it - // is a multiple of both `2^{NUM_LIMBS * 64}` and - // `q`. - let mut c: u128 = 0; - let mut i = NUM_LIMBS; - let mut overflow = false; - while i > 0 { - i -= 1; - c = 0; - let m = (lo.limbs[i] as u128 * *mu as u128) as u64; - let mut j = NUM_LIMBS; - while j > 0 { - j -= 1; - if i + j >= NUM_LIMBS - 1 { - let index = i + j - (NUM_LIMBS - 1); - let cs = lo.limbs[index] as u128 + m as u128 * (q.limbs[j] as u128) + c; - c = cs >> 64; - lo.limbs[index] = cs as u64; - } else { - let index = i + j + 1; - let cs = hi.limbs[index] as u128 + m as u128 * (q.limbs[j] as u128) + c; - c = cs >> 64; - hi.limbs[index] = cs as u64; - } - } - - // Carry propagation to `hi` - let mut t = 0; - while c > 0 && i >= t { - let cs = hi.limbs[i - t] as u128 + c; - c = cs >> 64; - hi.limbs[i - t] = cs as u64; - t += 1; - } - overflow |= c > 0; - } - - // Step 3: At this point `overflow * 2^{2 * NUM_LIMBS * 64} + (hi, lo)` is a multiple - // of `2^{NUM_LIMBS * 64}` and the result is obtained by dividing it by `2^{NUM_LIMBS * 64}`. - // In other words, `lo` is zero and the result is - // `overflow * 2^{NUM_LIMBS * 64} + hi`. - // That number is always strictly smaller than `2 * q`. To normalize it we substract - // `q` whenever it is larger than `q`. - // The easy case is when `overflow` is zero. We just use the `sub` function. - // If `overflow` is 1, then `hi` is smaller than `q`. The function `sub(hi, q)` wraps - // around `2^{NUM_LIMBS * 64}`. This is the result we need. - overflow |= c > 0; - if overflow || UnsignedInteger::const_le(q, &hi) { - (hi, _) = UnsignedInteger::sub(&hi, q); - } - hi - } -} - -#[cfg(test)] -mod tests { - use crate::unsigned_integer::{element::U384, montgomery::MontgomeryAlgorithms}; - - use proptest::prelude::*; - - proptest! { - #[test] - fn cios_vs_cios_optimized(a in any::<[u64; 6]>(), b in any::<[u64; 6]>()) { - let x = U384::from_limbs(a); - let y = U384::from_limbs(b); - let m = U384::from_hex_unchecked("cdb061954fdd36e5176f50dbdcfd349570a29ce1"); // this is prime - let mu: u64 = 16085280245840369887; // negative of the inverse of `m` modulo 2^{64} - assert_eq!( - MontgomeryAlgorithms::cios(&x, &y, &m, &mu), - MontgomeryAlgorithms::cios_optimized_for_moduli_with_one_spare_bit(&x, &y, &m, &mu) - ); - } - - #[test] - fn cios_vs_sos_square(a in any::<[u64; 6]>()) { - let x = U384::from_limbs(a); - let m = U384::from_hex_unchecked("cdb061954fdd36e5176f50dbdcfd349570a29ce1"); // this is prime - let mu: u64 = 16085280245840369887; // negative of the inverse of `m` modulo 2^{64} - assert_eq!( - MontgomeryAlgorithms::cios(&x, &x, &m, &mu), - MontgomeryAlgorithms::sos_square(&x, &m, &mu) - ); - } - } - #[test] - fn montgomery_multiplication_works_0() { - let x = U384::from_u64(11_u64); - let y = U384::from_u64(10_u64); - let m = U384::from_u64(23_u64); // - let mu: u64 = 3208129404123400281; // negative of the inverse of `m` modulo 2^{64}. - let c = U384::from_u64(13_u64); // x * y * (r^{-1}) % m, where r = 2^{64 * 6} and r^{-1} mod m = 2. - assert_eq!(MontgomeryAlgorithms::cios(&x, &y, &m, &mu), c); - } - - #[test] - fn montgomery_multiplication_works_1() { - let x = U384::from_hex_unchecked("05ed176deb0e80b4deb7718cdaa075165f149c"); - let y = U384::from_hex_unchecked("5f103b0bd4397d4df560eb559f38353f80eeb6"); - let m = U384::from_hex_unchecked("cdb061954fdd36e5176f50dbdcfd349570a29ce1"); // this is prime - let mu: u64 = 16085280245840369887; // negative of the inverse of `m` modulo 2^{64} - let c = U384::from_hex_unchecked("8d65cdee621682815d59f465d2641eea8a1274dc"); // x * y * (r^{-1}) % m, where r = 2^{64 * 6} - assert_eq!(MontgomeryAlgorithms::cios(&x, &y, &m, &mu), c); - } - - #[test] - fn montgomery_multiplication_works_2() { - let x = U384::from_hex_unchecked("8d65cdee621682815d59f465d2641eea8a1274dc"); - let m = U384::from_hex_unchecked("cdb061954fdd36e5176f50dbdcfd349570a29ce1"); // this is prime - let r_mod_m = U384::from_hex_unchecked("58dfb0e1b3dd5e674bdcde4f42eb5533b8759d33"); - let mu: u64 = 16085280245840369887; // negative of the inverse of `m` modulo 2^{64} - let c = U384::from_hex_unchecked("8d65cdee621682815d59f465d2641eea8a1274dc"); - assert_eq!(MontgomeryAlgorithms::cios(&x, &r_mod_m, &m, &mu), c); - } -} diff --git a/crates/math/src/unsigned_integer/traits.rs b/crates/math/src/unsigned_integer/traits.rs deleted file mode 100644 index ccda44f9f..000000000 --- a/crates/math/src/unsigned_integer/traits.rs +++ /dev/null @@ -1,23 +0,0 @@ -use core::{ - fmt::Display, - ops::{Add, BitAnd, Shr, ShrAssign}, -}; - -pub trait IsUnsignedInteger: - Shr - + ShrAssign - + BitAnd - + Eq - + Ord - + From - + Copy - + Display - + Add -{ -} - -impl IsUnsignedInteger for u128 {} -impl IsUnsignedInteger for u64 {} -impl IsUnsignedInteger for u32 {} -impl IsUnsignedInteger for u16 {} -impl IsUnsignedInteger for usize {} diff --git a/crates/provers/README.md b/crates/provers/README.md deleted file mode 100644 index c726174db..000000000 --- a/crates/provers/README.md +++ /dev/null @@ -1,25 +0,0 @@ -# lambdaworks Provers - -Provers allow one party, the prover, to show to other parties, the verifiers, that a given computer program has been executed correctly by means of a cryptographic proof. This proof ideally satisfies the following two properties: it is fast to verify and its size is small (smaller than the size of the witness). All provers have a `prove` function, which takes some description of the program and other input and outputs a proof. There is also a `verify` function which takes the proof and other input and accepts or rejects the proof. - -This folder contains the different provers currently supported by lambdaworks: -- [Groth 16](./groth16/) -- [Plonk](./plonk/) -- [STARKs](./stark/) -- [Cairo](https://github.com/lambdaclass/lambdaworks/tree/a591186e6c4dd53301b03b4ddd69369abe99f960/provers/cairo) - This is only for learning purposes and no longer supported. The [docs](../docs/src/starks/) still contain information that could be useful to understand and learn how Cairo works. - -The reference papers for each of the provers is given below: -- [Groth 16](https://eprint.iacr.org/2016/260) -- [Plonk](https://eprint.iacr.org/2019/953) -- [STARKs](https://eprint.iacr.org/2018/046.pdf) - -A brief description of the Plonk and STARKs provers can be found [here](https://github.com/lambdaclass/lambdaworks/tree/main/docs/src) - -Using one prover or another depends on usecase and other desired properties. We recommend reading and understanding how each prover works, so as to choose the most adequate. -- Groth 16: Shortest proof length. Security depends on pairing-friendly elliptic curves. Needs a new trusted setup for every program you want to prove. -- Plonk (using KZG as commitment scheme): Short proof length. Security depends on pairing-friendly elliptic curves. Universal trusted setup. -- STARKs: longer proof length. Security depends on collision-resistant hash functions. Conjectured to be post-quantum secure. Transparent (no trusted setup). - -## Using provers - -- [Plonk prover](./plonk/README.md) diff --git a/crates/provers/gkr/Cargo.toml b/crates/provers/gkr/Cargo.toml deleted file mode 100644 index 06abe0a2f..000000000 --- a/crates/provers/gkr/Cargo.toml +++ /dev/null @@ -1,20 +0,0 @@ -[package] -name = "lambdaworks-gkr-prover" -version = "0.1.0" -edition = "2021" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] -lambdaworks-math = { workspace = true } -lambdaworks-crypto = { workspace = true } -lambdaworks-sumcheck = { workspace = true } -thiserror = "1.0" -blake2 = "0.10" -sha3 = "0.10" -digest = "0.10" - - -[lib] -name = "lambdaworks_gkr_prover" -path = "src/lib.rs" diff --git a/crates/provers/gkr/README.md b/crates/provers/gkr/README.md deleted file mode 100644 index da3e0ed79..000000000 --- a/crates/provers/gkr/README.md +++ /dev/null @@ -1,173 +0,0 @@ -# GKR Protocol - -An implementation of the Goldwasser-Kalai-Rothblum (GKR) Non-Interactive Protocol for proving correct evaluation of arithmetic circuits. - -To help with the understanding of this implementation, we recommend reading our [blog post](https://blog.lambdaclass.com/gkr-protocol-a-step-by-step-example/). - -**Warning:** This GKR implementation is for educational purposes and should not be used in production. It uses the Fiat-Shamir transform, which is vulnerable to practical attacks in this context (see ["How to Prove False Statements"](https://eprint.iacr.org/2025/118.pdf)). - -## Overview - -The GKR Protocol allows a Prover to convince a Verifier that he correctly evaluated an arithmetic circuit on a given input without the Verifier having to perform the entire computation. - -It is a fundamental building block for many interactive proof systems and argument systems, providing a way to verify computations in time roughly proportional to the circuit's depth rather than its size. - -The protocol works by reducing claims about each layer of the circuit to claims about the next layer, using the Sumcheck Protocol as a subroutine. This process continues until reaching the input layer, where the verifier can directly check the final claim. The key insight is that the wiring of the circuit can be expressed as multilinear polynomials, allowing the use of sumcheck for efficient verification. - -### Key Features - -- **Layered Circuit Support**: Works with circuits organized in layers where each gate takes inputs from the previous layer. -- **Power-of-Two Constraint**: Each layer must have a power-of-two number of gates for protocol compatibility. -- **Addition and Multiplication Gates**: Supports both addition and multiplication operations. -- **Complete Verification**: Includes input verification to ensure end-to-end correctness. - -## Circuit Structure - -Circuits are organized in layers, with each layer containing gates that operate on outputs from the previous layer: - -``` -Output: o o <- circuit.layers[0] - / \ / \ - o o o o <- circuit.layers[1] - ... - o o o o <- circuit.layers[layer.len() - 1] - / \ / \ / \ / \ -Input: o o o o o o o o -``` - -Each layer must have a power-of-two number of gates. - -## API - -### Main Functions - -- `gkr_prove(circuit, input)` - Generate a GKR proof for a circuit evaluation. -- `gkr_verify(proof, circuit, input)` - Verify a GKR proof. - -### Circuit Construction - -- `Circuit::new(layers, num_inputs)` - Create a new circuit with specified layers, gates, and number of inputs. -- `CircuitLayer::new(gates)` - Create a layer with the given gates. -- `Gate::new(gate_type, inputs_idx)` - Create a gate with type (Add/Mul) and certain input indices. - -## Example - -Here's a simple example of how to use the GKR Protocol: - -```rust -use lambdaworks_math::field::fields::u64_prime_field::U64PrimeField; -use lambdaworks_math::field::element::FieldElement; -use lambdaworks_gkr::{gkr_prove, gkr_verify, circuit_from_lambda}; - -// Define the field (We use modulus 23 as in the example of our blog post). -const MODULUS23: u64 = 23; -type F23 = U64PrimeField; -type F23E = FieldElement; - -// Create the circuit of our blog post. -// This creates a 2-layer circuit plus the input layer. -let circuit = lambda_post_circuit.unwrap(); - -// Define input values (from the post example). -let input = [F23E::from(3), F23E::from(1)]; - -// Generate proof. -let proof_result = gkr_prove(&circuit, &input); -assert!(proof_result.is_ok()); -let proof = proof_result.unwrap(); - -// Verify proof. -let verification_result = gkr_verify(&proof, &circuit, &input); -assert!(verification_result.is_ok() && verification_result.unwrap()); -println!("GKR verification successful!"); - -// You can also check the actual output. -let evaluation = circuit.evaluate(&input); -println!("Circuit output: {:?}", evaluation.layers[0]); -``` - -### Creating Custom Circuits - -You can create custom circuits by defining your own layers, gates, and number of inputs.: - -```rust -use lambdaworks_gkr::circuit::{Circuit, CircuitLayer, Gate, GateType}; - -// Create a simple 2-layer circuit -let custom_circuit = Circuit::new( - vec![ - // Output layer: 1 gate - CircuitLayer::new(vec![ - Gate::new(GateType::Add, [0, 1]), // Add the two results from layer 1 - ]), - // Layer 1: 2 gates - CircuitLayer::new(vec![ - Gate::new(GateType::Mul, [0, 1]), // Multiply first two inputs - Gate::new(GateType::Mul, [2, 3]), // Multiply last two inputs - ]), - ], - 4, // 4 inputs (power of 2) -).unwrap(); - -// Test with inputs [2, 3, 4, 5] -let input = [F23E::from(2), F23E::from(3), F23E::from(4), F23E::from(5)]; - -let proof = gkr_prove(&custom_circuit, &input).unwrap(); -let is_valid = gkr_verify(&proof, &custom_circuit, &input).unwrap(); - -assert!(is_valid); -``` - -## Protocol Details - -The GKR protocol works through the following steps: - -1. **Circuit Evaluation**: The prover evaluates the circuit on the given input to obtain the values at each layer. -2. **Layer-by-Layer Reduction**: For each layer $i$, the prover uses the Sumcheck Protocol to reduce a claim about the current layer to a claim about the next layer $i+1$. - - - **Wiring Polynomial Construction**: The circuit's wiring is encoded as multilinear polynomials $\widetilde{\text{add}_i}$ and $\widetilde{\text{mul}_i}$ that describe which gates are addition/multiplication gates. - - - **Sumcheck Application**: The sumcheck is applied to the polynomial: - $$\tilde f_{r_i}(b,c) = \widetilde{\text{add}_i(}r_i, b, c) \cdot (\tilde W_{i+1}(b) + \tilde W_{i+1}(c)) + \widetilde{\text{mul}_i}(r_i, b, c) \cdot (\tilde W_{i+1}(b) \cdot \tilde W_{i+1}(c))$$ - - where $\tilde W_{i+1}$ is the multilinear extension of layer $i+1$ values. - - - **Line Function**: A line function transforms the two claims of $\tilde W_{i+1}(b)$ and $\tilde W_{i+1}(c)$ into a single claim. -3. **Input Verification**: The verifier checks the final claim, evaluating the input multilinear polynomial extension at the final evaluation point. - -Each *layer proof* consists of: -- The claimed sum (that the sumcheck proves). -- All the univariate polynomials used in the sumcheck. They are built by fixing the first variable and summing over the remaining variables. -- The polynomial $q = \tilde W_{i+1} \circ \ell$ where $\ell$ is the line function. - -The protocol achieves $O(d \log S)$ verifier time and $O(S)$ prover time, where $d$ is the circuit depth and $S$ is the circuit size (i.e., the number of gates). - - -## Fiat-Shamir transform - -This implementation uses the **Fiat-Shamir** to transform the interactive GKR protocol into a non-interactive proof system. Instead of requiring back-and-forth communication between prover and verifier, the prover generates all challenges deterministically by applying a hash function to the transcript. - -### How Fiat-Shamir is Applied - -The transformation works by replacing the verifier's random challenges with outputs from a cryptographic hash function applied on the transcript: - -1. **Transcript Initialization**: A transcript is created and seeded with: - - Circuit structure (via `circuit_to_bytes(circuit)`) - - Input values - - Output values - -2. **Challenge Generation**: At each step where the interactive protocol would require a random challenge, the implementation: - - Adds the current proof data to the transcript. - - Samples a "random" field element from the transcript using `transcript.sample_field_element()`. - -3. **Key Challenge Points**: - - **Initial random values** `r_0` for the output layer. - - **Sumcheck challenges** ($s_j$) for each round of each layer's sumcheck protocol. - - **Line function parameter** `r_last` for connecting layers. - - -## References - -- [Goldwasser, Kalai, and Rothblum. "Delegating computation: interactive proofs for muggles"](https://dl.acm.org/doi/10.1145/1374376.1374396) -- [Proofs, Arguments, and Zero-Knowledge. Chapter 4](https://people.cs.georgetown.edu/jthaler/ProofsArgsAndZK.pdf) -- [Lambdaclass Blog Post: GKR protocol: a step-by-step example](https://blog.lambdaclass.com/gkr-protocol-a-step-by-step-example/) diff --git a/crates/provers/gkr/src/circuit.rs b/crates/provers/gkr/src/circuit.rs deleted file mode 100644 index e7923f6a1..000000000 --- a/crates/provers/gkr/src/circuit.rs +++ /dev/null @@ -1,290 +0,0 @@ -use lambdaworks_math::field::element::FieldElement; -use lambdaworks_math::field::traits::IsField; -use lambdaworks_math::polynomial::dense_multilinear_poly::DenseMultilinearPolynomial; - -/// A type of a gate in the Circuit. -#[derive(Copy, Clone, PartialEq, Eq)] -pub enum GateType { - /// An addition gate. - Add, - /// A multiplication gate. - Mul, -} - -/// A gate in the Circuit. -#[derive(Clone, Copy)] -pub struct Gate { - /// A type of the gate. - pub gate_type: GateType, - - /// Two inputs, indexes into the previous layer gates outputs. - pub inputs_idx: [usize; 2], -} - -impl Gate { - pub fn new(gate_type: GateType, inputs_idx: [usize; 2]) -> Self { - Self { - gate_type, - inputs_idx, - } - } -} - -/// A layer of gates in the circuit. -#[derive(Clone)] -pub struct CircuitLayer { - pub gates: Vec, - pub num_of_vars: usize, // log2 of number of gates in this layer -} - -impl CircuitLayer { - pub fn new(gates: Vec) -> Self { - let num_of_vars = if gates.is_empty() { - 0 - } else { - gates.len().next_power_of_two().trailing_zeros() as usize - }; - Self { gates, num_of_vars } - } - - pub fn len(&self) -> usize { - self.gates.len() - } - - pub fn is_empty(&self) -> bool { - self.gates.is_empty() - } -} - -#[derive(Debug, Clone)] -pub enum CircuitError { - InputsNotPowerOfTwo, - LayerNotPowerOfTwo(usize), - GateInputsError(usize), - EmptyCircuitError, -} - -/// The circuit in layered form. -/// -/// ## Circuit Structure -/// -/// Circuits are organized in layers, with each layer containing gates that operate on outputs from the previous layer: -/// -/// ```text -/// Output: o o <- circuit.layers[0] -/// / \ / \ -/// o o o o <- circuit.layers[1] -/// ... -/// o o o o <- circuit.layers[layer.len() - 1] -/// / \ / \ / \ / \ -/// Input: o o o o o o o o -/// ``` -/// -/// - The top nodes are the circuit outputs (layers[0]). -/// - The bottom nodes are the circuit inputs, they don't belong to the vector `layers`. -/// - Each layer contains gates; edges represent wiring between layers. -/// - The circuit is evaluated from inputs upward, but layers are stored from output to input. -#[derive(Clone)] -pub struct Circuit { - /// First layer is the output layer. It doesn't include the input layer. - layers: Vec, - - /// Number of inputs - num_inputs: usize, - input_num_vars: usize, // log2 of number of inputs -} - -/// An evaluation of a `Circuit` on some input. -/// Stores the outputs, every circuit layer intermediate evaluations and the inputs -pub struct CircuitEvaluation { - /// Evaluations on per-layer. First layer is the output and last layer is the input. - pub layers: Vec>>, -} - -impl Circuit { - pub fn new(layers: Vec, num_inputs: usize) -> Result { - if layers.is_empty() { - return Err(CircuitError::EmptyCircuitError); - } - - if !num_inputs.is_power_of_two() { - return Err(CircuitError::InputsNotPowerOfTwo); - } - - let input_num_vars = num_inputs.trailing_zeros() as usize; - - // Validate that each layer has power-of-two gates - for (i, layer) in layers.iter().enumerate() { - if !layer.len().is_power_of_two() { - return Err(CircuitError::LayerNotPowerOfTwo(i)); - } - } - - // Validate that gate inputs in each layer don't exceed the next layer number of gates - for (i, layer_pair) in layers.windows(2).enumerate() { - let current_layer = &layer_pair[0]; - let next_layer = &layer_pair[1]; - let next_layer_gates = next_layer.len(); - - if current_layer.gates.iter().any(|gate| { - let [a, b] = gate.inputs_idx; - a >= next_layer_gates || b >= next_layer_gates - }) { - return Err(CircuitError::GateInputsError(i)); - } - } - - // Validate that the last layer gate inputs don't exceed the number of inputs - if let Some(last_layer) = layers.last() { - if last_layer.gates.iter().any(|gate| { - let [a, b] = gate.inputs_idx; - a >= num_inputs || b >= num_inputs - }) { - return Err(CircuitError::GateInputsError(layers.len() - 1)); - } - } - - Ok(Self { - layers, - num_inputs, - input_num_vars, - }) - } - - pub fn num_vars_at(&self, layer: usize) -> Option { - if let Some(layer) = self.layers.get(layer) { - Some(layer.num_of_vars) - } else if layer == self.layers.len() { - Some(self.input_num_vars) - } else { - None - } - } - - /// Evaluate a `Circuit` on a given input. - pub fn evaluate(&self, input: &[FieldElement]) -> CircuitEvaluation - where - F: IsField, - { - let mut layers = Vec::with_capacity(self.layers.len() + 1); - let mut current_input = input.to_vec(); - - layers.push(current_input.clone()); - - for layer in self.layers.iter().rev() { - let temp_layer: Vec<_> = layer - .gates - .iter() - .map(|gate| match gate.gate_type { - GateType::Add => { - ¤t_input[gate.inputs_idx[0]] + ¤t_input[gate.inputs_idx[1]] - } - GateType::Mul => { - ¤t_input[gate.inputs_idx[0]] * ¤t_input[gate.inputs_idx[1]] - } - }) - .collect(); - - layers.push(temp_layer.clone()); - current_input = temp_layer; - } - - // Reverse the order so that the first layer is the output layer. - layers.reverse(); - CircuitEvaluation { layers } - } - - /// The add_i(a, b, c) polynomial value at layer i. - pub fn add_i(&self, i: usize, a: usize, b: usize, c: usize) -> bool { - let gate = &self.layers[i].gates[a]; - gate.gate_type == GateType::Add && gate.inputs_idx[0] == b && gate.inputs_idx[1] == c - } - - /// The mul_i(a, b, c) polynomial value at layer i. - pub fn mul_i(&self, i: usize, a: usize, b: usize, c: usize) -> bool { - let gate = &self.layers[i].gates[a]; - gate.gate_type == GateType::Mul && gate.inputs_idx[0] == b && gate.inputs_idx[1] == c - } - - pub fn layers(&self) -> &[CircuitLayer] { - &self.layers - } - - pub fn num_outputs(&self) -> usize { - self.layers[0].gates.len() - } - - pub fn num_inputs(&self) -> usize { - self.num_inputs - } - - /// The multilinear polynomial extension of the function `add_i(a, b, c)`, where `a` is fixed at `r_i`. - pub fn add_i_ext( - &self, - r_i: &[FieldElement], - i: usize, - ) -> DenseMultilinearPolynomial - where - F::BaseType: Send + Sync + Copy, - { - let num_vars_current = self.layers[i].num_of_vars; - let num_vars_next = if let Some(layer) = self.layers.get(i + 1) { - layer.num_of_vars - } else { - self.input_num_vars - }; - let total_vars = num_vars_current + 2 * num_vars_next; - let mut add_i_evals = vec![FieldElement::zero(); 1 << total_vars]; - - // For each Add gate, we set the corresponding index `a || b || c` in the evaluation vector to one. - for (a, gate) in self.layers[i].gates.iter().enumerate() { - if gate.gate_type == GateType::Add { - let b = gate.inputs_idx[0]; - let c = gate.inputs_idx[1]; - let idx = (a << (2 * num_vars_next)) | (b << num_vars_next) | c; - add_i_evals[idx] = FieldElement::one(); - } - } - - let mut add_i_poly = DenseMultilinearPolynomial::new(add_i_evals); - for val in r_i.iter() { - add_i_poly = add_i_poly.fix_first_variable(val); - } - add_i_poly - } - - /// The multilinear polynomial extension of the function `mul_i(a, b, c)`, where `a` is fixed at `r_i`. - pub fn mul_i_ext( - &self, - r_i: &[FieldElement], - i: usize, - ) -> DenseMultilinearPolynomial - where - F::BaseType: Send + Sync + Copy, - { - let num_vars_current = self.layers[i].num_of_vars; - let num_vars_next = if let Some(layer) = self.layers.get(i + 1) { - layer.num_of_vars - } else { - self.input_num_vars - }; - let total_vars = num_vars_current + 2 * num_vars_next; - let mut mul_i_evals = vec![FieldElement::zero(); 1 << total_vars]; - - // For each Mul gate, we set the corresponding index `a || b || c` in the evaluation vector to one. - for (a, gate) in self.layers[i].gates.iter().enumerate() { - if gate.gate_type == GateType::Mul { - let b = gate.inputs_idx[0]; - let c = gate.inputs_idx[1]; - let idx = (a << (2 * num_vars_next)) | (b << num_vars_next) | c; - mul_i_evals[idx] = FieldElement::one(); - } - } - - let mut mul_i_poly = DenseMultilinearPolynomial::new(mul_i_evals); - for val in r_i.iter() { - mul_i_poly = mul_i_poly.fix_first_variable(val); - } - mul_i_poly - } -} diff --git a/crates/provers/gkr/src/lib.rs b/crates/provers/gkr/src/lib.rs deleted file mode 100644 index 3d10ccbce..000000000 --- a/crates/provers/gkr/src/lib.rs +++ /dev/null @@ -1,314 +0,0 @@ -pub mod circuit; -pub mod prover; -pub mod sumcheck; -pub mod verifier; -use crate::circuit::Circuit; -use crate::prover::{GKRProof, Prover, ProverError}; -use crate::verifier::Verifier; -use crate::verifier::VerifierError; -use lambdaworks_math::field::element::FieldElement; -use lambdaworks_math::field::traits::{HasDefaultTranscript, IsField}; -use lambdaworks_math::traits::ByteConversion; - -/// Serialize the circuit, so that it can be appended to the transcript. -/// This function is used at the beginning of the protocol by the prover and the verifier. -pub fn circuit_to_bytes(circuit: &Circuit) -> Vec { - let mut bytes = Vec::new(); - // Append the number of layers and the number of inputs. - bytes.extend_from_slice(&(circuit.layers().len() as u32).to_le_bytes()); - bytes.extend_from_slice(&(circuit.num_inputs() as u32).to_le_bytes()); - // For each layer append the number of gates, the type and the input indeces of each gate. - for layer in circuit.layers() { - bytes.extend_from_slice(&(layer.len() as u32).to_le_bytes()); - for gate in &layer.gates { - let gate_type = match gate.gate_type { - crate::circuit::GateType::Add => 0u8, - crate::circuit::GateType::Mul => 1u8, - }; - bytes.push(gate_type); - bytes.extend_from_slice(&(gate.inputs_idx[0] as u32).to_le_bytes()); - bytes.extend_from_slice(&(gate.inputs_idx[1] as u32).to_le_bytes()); - } - } - bytes -} - -/// Evaluate the line polynomial that goes from `b` to `c`, at point `t`: -/// `l(t) = b + t * (c - b)`. -/// `l` satisfies: l(0) = b and l(1) = c. -/// This function is used in the protocol by the prover and the verifier in each layer. -pub fn line( - b: &[FieldElement], - c: &[FieldElement], - t: &FieldElement, -) -> Vec> -where - F: IsField, -{ - b.iter() - .zip(c.iter()) - .map(|(b_val, c_val)| b_val + t * (c_val - b_val)) - .collect() -} - -pub fn gkr_prove( - circuit: &Circuit, - input: &[FieldElement], -) -> Result, ProverError> -where - F: IsField + HasDefaultTranscript, - FieldElement: ByteConversion, - ::BaseType: Send + Sync + Copy, -{ - Prover::generate_proof(circuit, input) -} - -pub fn gkr_verify(proof: &GKRProof, circuit: &Circuit) -> Result -where - F: IsField + HasDefaultTranscript, - FieldElement: ByteConversion, - ::BaseType: Send + Sync + Copy, -{ - Verifier::verify(proof, circuit) -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::circuit::{Circuit, CircuitError, CircuitLayer, Gate, GateType}; - use lambdaworks_math::{field::fields::u64_prime_field::U64PrimeField, polynomial::Polynomial}; - - const MODULUS389: u64 = 389; - type F389 = U64PrimeField; - type F389E = FieldElement; - - const MODULUS23: u64 = 23; - type F23 = U64PrimeField; - type F23E = FieldElement; - - /// Create the circuit from Thaler's book (Figure 4.12) - fn thaler_book_circuit() -> Result { - Circuit::new( - vec![ - CircuitLayer::new(vec![ - Gate::new(GateType::Mul, [0, 1]), - Gate::new(GateType::Mul, [2, 3]), - ]), - CircuitLayer::new(vec![ - Gate::new(GateType::Mul, [0, 0]), - Gate::new(GateType::Mul, [1, 1]), - Gate::new(GateType::Mul, [1, 2]), - Gate::new(GateType::Mul, [3, 3]), - ]), - ], - 4, - ) - } - - /// Create the circuit from our blog post on the GKR protocol. - /// https://blog.lambdaclass.com/gkr-protocol-a-step-by-step-example/ - pub fn lambda_post_circuit() -> Result { - use crate::circuit::{Circuit, CircuitLayer, Gate, GateType}; - Circuit::new( - vec![ - CircuitLayer::new(vec![ - Gate::new(GateType::Mul, [0, 1]), - Gate::new(GateType::Add, [2, 3]), - ]), - CircuitLayer::new(vec![ - Gate::new(GateType::Mul, [0, 1]), - Gate::new(GateType::Add, [0, 0]), - Gate::new(GateType::Add, [0, 1]), - Gate::new(GateType::Mul, [0, 1]), - ]), - ], - 2, - ) - } - /// Create a circuit with four layers (without counting the input layer). - /// To picture this circuit, imagine a tree structure where each layer has twice the number of gates as the layer above. - pub fn four_layer_circuit() -> Result { - use crate::circuit::{CircuitLayer, Gate, GateType}; - use GateType::{Add, Mul}; - - let l0 = CircuitLayer::new(vec![Gate::new(Mul, [0, 1])]); - - let l1 = CircuitLayer::new(vec![Gate::new(Add, [0, 1]), Gate::new(Mul, [2, 3])]); - - let l2 = CircuitLayer::new(vec![ - Gate::new(Mul, [0, 1]), - Gate::new(Add, [2, 3]), - Gate::new(Mul, [4, 5]), - Gate::new(Add, [6, 7]), - ]); - - let l3 = CircuitLayer::new(vec![ - Gate::new(Add, [0, 1]), - Gate::new(Mul, [2, 3]), - Gate::new(Add, [4, 5]), - Gate::new(Mul, [6, 7]), - Gate::new(Add, [8, 9]), - Gate::new(Mul, [10, 11]), - Gate::new(Add, [12, 13]), - Gate::new(Mul, [14, 15]), - ]); - - Circuit::new(vec![l0, l1, l2, l3], 16) - } - - #[test] - fn test_lambda_circuit_evaluation() { - let circuit = lambda_post_circuit().unwrap(); - let input = [F23E::from(3), F23E::from(1)]; - let evaluation = circuit.evaluate(&input); - assert_eq!(evaluation.layers.len(), 3); - assert_eq!(evaluation.layers[0], [F23E::from(18), F23E::from(7)]); - assert_eq!( - evaluation.layers[1], - [F23E::from(3), F23E::from(6), F23E::from(4), F23E::from(3)] - ); - assert_eq!(evaluation.layers[2], input.to_vec()); - } - - #[test] - fn test_thaler_book_circuit_evaluation() { - let circuit = thaler_book_circuit().unwrap(); - let input = [ - F389E::from(3), - F389E::from(2), - F389E::from(3), - F389E::from(1), - ]; - - let evaluation = circuit.evaluate(&input); - - assert_eq!(evaluation.layers.len(), 3); - assert_eq!(evaluation.layers[0], [F389E::from(36), F389E::from(6)]); - assert_eq!( - evaluation.layers[1], - [ - F389E::from(9), - F389E::from(4), - F389E::from(6), - F389E::from(1) - ] - ); - assert_eq!(evaluation.layers[2], input.to_vec()); - } - - #[test] - fn test_four_layer_circuit_evaluation() { - let circuit = four_layer_circuit().unwrap(); - let input: Vec = (0u64..16u64).map(F23E::from).collect(); - - let evaluation = circuit.evaluate(&input); - - assert_eq!(evaluation.layers.len(), 5); - - assert_eq!(evaluation.layers[0], vec![F23E::from(17)]); - assert_eq!(evaluation.layers[1], vec![F23E::from(11), F23E::from(12)]); - assert_eq!( - evaluation.layers[2], - vec![F23E::from(6), F23E::from(5), F23E::from(7), F23E::from(5)] - ); - assert_eq!( - evaluation.layers[3], - vec![ - F23E::from(1), - F23E::from(6), - F23E::from(9), - F23E::from(19), - F23E::from(17), - F23E::from(18), - F23E::from(2), - F23E::from(3) - ] - ); - assert_eq!(evaluation.layers[4], input); - } - - #[test] - fn test_gkr_complete_verification() { - let circuit = thaler_book_circuit().unwrap(); - let input = [ - F389E::from(3), - F389E::from(2), - F389E::from(3), - F389E::from(1), - ]; - - let proof = gkr_prove(&circuit, &input).unwrap(); - let result = gkr_verify(&proof, &circuit); - - assert!(result.is_ok()); - assert!(result.unwrap()); - } - - #[test] - fn test_gkr_complete_verification_lambda() { - let circuit = lambda_post_circuit().unwrap(); - let input = [F23E::from(3), F23E::from(1)]; - - let proof = gkr_prove(&circuit, &input).unwrap(); - let result = gkr_verify(&proof, &circuit); - - assert!(result.is_ok()); - assert!(result.unwrap()); - } - - #[test] - fn test_gkr_complete_verification_four_layer() { - let circuit = four_layer_circuit().unwrap(); - let input: Vec = (0u64..16u64).map(F23E::from).collect(); - - let proof = gkr_prove(&circuit, &input).unwrap(); - let result = gkr_verify(&proof, &circuit); - - assert!(result.is_ok()); - assert!(result.unwrap()); - } - - #[test] - fn test_gkr_protocol_lambda_invalid_outputs() { - let circuit = lambda_post_circuit().unwrap(); - let input = [F23E::from(3), F23E::from(1)]; - - let proof_result = gkr_prove(&circuit, &input); - let mut proof = proof_result.unwrap(); - - // Corrupt the output values - proof.output_values = vec![F23E::from(1), F23E::from(2)]; - - let verification_result = gkr_verify(&proof, &circuit); - - assert!( - matches!(verification_result, Ok(false)), - "The protocol should reject a fake output" - ) - } - - #[test] - fn test_invalid_proof_rejection() { - let circuit = thaler_book_circuit().unwrap(); - let input = [ - F389E::from(3), - F389E::from(2), - F389E::from(3), - F389E::from(1), - ]; - - // Generate a valid proof - let mut proof = gkr_prove(&circuit, &input).expect("Proof generation failed"); - - // Corrupt the proof by modifying a round polynomial g_0. - proof.layer_proofs[0].sumcheck_proof.round_polynomials[0] = - Polynomial::new(&[F389E::from(1), F389E::from(2), F389E::from(3)]); - - let verification_result = gkr_verify(&proof, &circuit); - - assert!( - matches!(verification_result, Ok(false)), - "The protocol should reject an invalid proof" - ) - } -} diff --git a/crates/provers/gkr/src/prover.rs b/crates/provers/gkr/src/prover.rs deleted file mode 100644 index 32e8a9edd..000000000 --- a/crates/provers/gkr/src/prover.rs +++ /dev/null @@ -1,233 +0,0 @@ -use crate::sumcheck::GKRSumcheckProof; -use crate::{circuit::Circuit, sumcheck::gkr_sumcheck_prove}; - -use lambdaworks_crypto::fiat_shamir::{ - default_transcript::DefaultTranscript, is_transcript::IsTranscript, -}; -use lambdaworks_math::polynomial::Polynomial; -use lambdaworks_math::{ - field::{ - element::FieldElement, - traits::{HasDefaultTranscript, IsField}, - }, - polynomial::dense_multilinear_poly::DenseMultilinearPolynomial, - traits::ByteConversion, -}; - -type GKRPolynomialTerms = ( - Vec>, - Vec>, -); -#[derive(Debug)] -pub enum ProverError { - MultilinearPolynomialEvaluationError, - SumcheckError, - CircuitError, - InterpolationError, -} - -/// Each layer proof contains the sumcheck round polynomials g_j, the sumcheck challenges and the polynomial q. -/// Recall that the polynomial q is the composition of the circuit evaluation polynomial extension W_{i+1} and the line function l. -#[derive(Debug, Clone)] -pub struct LayerProof { - pub sumcheck_proof: GKRSumcheckProof, - pub poly_q: Polynomial>, -} - -/// A GKR proof. -#[derive(Debug, Clone)] -pub struct GKRProof { - pub input_values: Vec>, - pub output_values: Vec>, - pub layer_proofs: Vec>, -} - -pub struct Prover; - -impl Prover { - /// Constructs the polynomial `f(b, c)` that the GKR sumcheck will use for a specific layer and fixed values `r_i`. - /// - /// The polynomial `f` is defined as: - /// `f(b, c) = add_i(r_i, b, c) * (W_{i+1}(b) + W_{i+1}(c)) + mul_i(r_i, b, c) * (W_{i+1}(b) * W_{i+1}(c))` - /// - /// where: - /// - `add_i` and `mul_i` are the multilinear polynomial extensions for the addition and multiplication gates of layer `i`. - /// - `r_i` contains the random challenges for layer `i`. - /// - `W_{i+1}` is the multilinear polynomial extension representing the circuit evaluations of layer `i+1`. - /// - `b` and `c` are the inputs to the gates, with `k_{i+1}` variables each. - /// - /// See our blog post: - /// - /// This function is only known to the prover. - /// - /// It returns the components of `f` grouped into two terms, ready to be used by the sumcheck prover, which expects - /// a product of multilinear polynomials. - /// Both terms are the product of two multilinear polynomials. - /// The first term is `add_i(r_i, b, c) * (W_{i+1}(b) + W_{i+1}(c))`. - /// And the second term is `mul_i(r_i, b, c) * (W_{i+1}(b) * W_{i+1}(c))`. - fn build_gkr_polynomial( - circuit: &Circuit, - r_i: &[FieldElement], - w_next_evals: &[FieldElement], - layer_idx: usize, - ) -> Result, ProverError> - where - ::BaseType: Send + Sync + Copy, - { - // Get the multilinear extensions of the wiring predicates `add(a, b, c)` and - // `mul(a, b, c)` with the variable `a` fixed at `r_i`. - let add_i_ext = circuit.add_i_ext::(r_i, layer_idx); - let mul_i_ext = circuit.mul_i_ext::(r_i, layer_idx); - - let num_vars_next = circuit - .num_vars_at(layer_idx + 1) - .ok_or(ProverError::CircuitError)?; - - let mut w_sum_evals: Vec> = Vec::new(); - let mut w_mul_evals: Vec> = Vec::new(); - let next_layer_size = 1 << num_vars_next; - - for c_idx in 0..next_layer_size { - for b_idx in 0..next_layer_size { - let w_b = &w_next_evals[b_idx]; - let w_c = &w_next_evals[c_idx]; - w_sum_evals.push(w_b + w_c); - w_mul_evals.push(w_b * w_c); - } - } - // w_sum_ext(b, c) = W_{i+1}(b) + W_{i+1}(c) - let w_sum_ext = DenseMultilinearPolynomial::new(w_sum_evals); - // w_mul_ext(b, c) = W_{i+1}(b) * W_{i+1}(c) - let w_mul_ext = DenseMultilinearPolynomial::new(w_mul_evals); - - // Corresponds to add_i(r_i,b,c) * (W_{i+1}(b) + W_{i+1}(c)) - let term1 = vec![add_i_ext, w_sum_ext]; - // Corresponds to mul_i(r_i,b,c) * W_{i+1}(b) * W_{i+1}(c) - let term2 = vec![mul_i_ext, w_mul_ext]; - - Ok((term1, term2)) - } - - /// Given `b` and `c` (challenges for the input of the gates), - /// and given the evaluations of W_{i+1} (the gates evaluations), - /// it returns the composition polynomial `q = W_{i+1} o l`, where `l` is the line function from `b` to `c`. - /// It builds the polynomial `q` by interpolating three points (0, 1 and 2). - /// There is no need to interpolate more points because `q` has degree 2 (since `l` is linear and - /// `W` is a 2-variable multlinear polynomial). - fn build_polynomial_q( - b: &[FieldElement], - c: &[FieldElement], - w_next_evals: Vec>, - ) -> Result>, ProverError> - where - F: IsField, - ::BaseType: Send + Sync + Copy, - { - let mut domain_points_x = Vec::new(); - let mut evaluation_values_y = Vec::new(); - let w_next_poly = DenseMultilinearPolynomial::new(w_next_evals); - for i in 0..3 { - let eval_point_x = FieldElement::from(i as u64); - let line_at_eval_point = crate::line(b, c, &eval_point_x); - let q_at_eval_point = w_next_poly - .evaluate(line_at_eval_point) - .map_err(|_| ProverError::MultilinearPolynomialEvaluationError)?; - - domain_points_x.push(eval_point_x); - evaluation_values_y.push(q_at_eval_point); - } - let poly_q = Polynomial::interpolate(&domain_points_x, &evaluation_values_y) - .map_err(|_| ProverError::InterpolationError)?; - Ok(poly_q) - } - - /// Generate a GKR proof. - /// - /// The GKR protocol reduces a claim about layer `i` to a claim about layer `i+1`, from the top layer (outputs) to the bottom layer (inputs). - /// This reduction is proven using a sumcheck protocol on the polynomial `f` that is - /// constructed by the prover using the function `build_gkr_polynomial`. - pub fn generate_proof( - circuit: &Circuit, - input: &[FieldElement], - ) -> Result, ProverError> - where - F: IsField + HasDefaultTranscript, - FieldElement: ByteConversion, - ::BaseType: Send + Sync + Copy, - { - if input.len() != circuit.num_inputs() { - return Err(ProverError::CircuitError); - } - - // The prover evaluates the circuit. - let evaluation = circuit.evaluate(input); - - // Fiat-Shamir heuristic: - // Both parties need to to append to the transcript the circuit, the inputs and the outputs. - // See https://eprint.iacr.org/2025/118.pdf, Sections 2.1 and 2.2. - let mut transcript = DefaultTranscript::::default(); - // Append the circuit data to the transcript. - transcript.append_bytes(&crate::circuit_to_bytes(circuit)); - // Append public inputs x. - for x in input { - transcript.append_bytes(&x.to_bytes_be()); - } - // Append public outputs y (first layer of evaluation). - for y in &evaluation.layers[0] { - transcript.append_bytes(&y.to_bytes_be()); - } - - let k_0 = circuit.num_vars_at(0).ok_or(ProverError::CircuitError)?; - let mut r_i: Vec> = (0..k_0) - .map(|_| transcript.sample_field_element()) - .collect(); - - let mut layer_proofs = Vec::new(); - - // For each layer, the prover makes a layer proof applying the sumcheck protocol to the function `f`. - for layer_idx in 0..circuit.layers().len() { - // Evaluations of W_{i+1} multilinear polynomial extension. - let w_next_evals = &evaluation.layers[layer_idx + 1]; - - // Build the GKR polynomial terms - let (term_1, term_2) = - Prover::build_gkr_polynomial(circuit, &r_i, w_next_evals, layer_idx)?; - - let sumcheck_proof = gkr_sumcheck_prove(term_1, term_2, &mut transcript)?; - - let sumcheck_challenges = &sumcheck_proof.challenges; - - // After applying the sumcheck, the prover needs to sample the challenges for the next layer and - // build the polynomial q = W o l. - let num_vars_next = circuit - .num_vars_at(layer_idx + 1) - .ok_or(ProverError::CircuitError)?; - - // r* in our blog post - let r_new = transcript.sample_field_element(); - - // Construct the next round's random point using line function - // l(x) = b + x * (c - b) - let (b, c) = sumcheck_challenges.split_at(num_vars_next); - // r_i = l(r_new) - r_i = crate::line(b, c, &r_new); - - let poly_q = Prover::build_polynomial_q(b, c, w_next_evals.clone())?; - - let layer_proof = LayerProof { - sumcheck_proof, - poly_q, - }; - layer_proofs.push(layer_proof); - } - - let output_values = evaluation.layers[0].clone(); - let proof = GKRProof { - input_values: input.to_vec(), - output_values, - layer_proofs, - }; - - Ok(proof) - } -} diff --git a/crates/provers/gkr/src/sumcheck.rs b/crates/provers/gkr/src/sumcheck.rs deleted file mode 100644 index adea977a7..000000000 --- a/crates/provers/gkr/src/sumcheck.rs +++ /dev/null @@ -1,178 +0,0 @@ -use lambdaworks_crypto::fiat_shamir::is_transcript::IsTranscript; -use lambdaworks_math::{ - field::{ - element::FieldElement, - traits::{HasDefaultTranscript, IsField}, - }, - polynomial::{dense_multilinear_poly::DenseMultilinearPolynomial, Polynomial}, - traits::ByteConversion, -}; -use lambdaworks_sumcheck::{Channel, Prover}; - -use crate::prover::ProverError; - -/// GKR-specific sumcheck proof, which contains each round polynomial `g_j` and the challenges used. -#[derive(Debug, Clone)] -pub struct GKRSumcheckProof { - pub round_polynomials: Vec>>, - pub challenges: Vec>, -} - -/// GKR-specific sumcheck prover. -/// This function will recieves two terms. Each term contains two multilinear polynomials. -/// This separation of terms is necessary because the classic/original sumcheck only accepts a product of multilinear polynomials. -/// In this way, we apply the sumcheck to two terms, each consisting of two factors. -pub fn gkr_sumcheck_prove( - term_1: Vec>, - term_2: Vec>, - transcript: &mut T, -) -> Result, ProverError> -where - F: IsField + HasDefaultTranscript, - ::BaseType: Send + Sync + Copy, - FieldElement: ByteConversion, - T: IsTranscript + Channel, -{ - // Create two separate sumcheck provers for each term. - let mut prover_term_1 = Prover::new(term_1).map_err(|_| ProverError::SumcheckError)?; - let mut prover_term_2 = Prover::new(term_2).map_err(|_| ProverError::SumcheckError)?; - - // Both terms have the same number of variables. - let num_vars = prover_term_1.num_vars(); - - let claimed_sum_term_1 = prover_term_1 - .compute_initial_sum() - .map_err(|_| ProverError::SumcheckError)?; - - let claimed_sum_term_2 = prover_term_2 - .compute_initial_sum() - .map_err(|_| ProverError::SumcheckError)?; - let claimed_sum = claimed_sum_term_1 + claimed_sum_term_2; - - transcript.append_bytes(b"initial_sum"); - transcript.append_felt(&FieldElement::from(num_vars as u64)); - transcript.append_felt(&FieldElement::from(1u64)); - transcript.append_felt(&claimed_sum); - - let mut proof_polys = Vec::with_capacity(num_vars); - let mut challenges = Vec::new(); - let mut current_challenge: Option> = None; - - // Execute sumcheck rounds - for j in 0..num_vars { - let g_j_term_1 = prover_term_1 - .round(current_challenge.as_ref()) - .map_err(|_| ProverError::SumcheckError)?; - - let g_j_term_2 = prover_term_2 - .round(current_challenge.as_ref()) - .map_err(|_| ProverError::SumcheckError)?; - - let g_j = g_j_term_1 + g_j_term_2; - - // Add polynomial to transcript - let round_label = format!("round_{j}_poly"); - transcript.append_bytes(round_label.as_bytes()); - - let coeffs = g_j.coefficients(); - transcript.append_bytes(&(coeffs.len() as u64).to_be_bytes()); - if coeffs.is_empty() { - transcript.append_felt(&FieldElement::zero()); - } else { - for coeff in coeffs { - transcript.append_felt(coeff); - } - } - - proof_polys.push(g_j); - - // Get challenge for next round (if not the last round) - let r_j = transcript.sample_field_element(); - - challenges.push(r_j.clone()); - current_challenge = Some(r_j); - } - - let sumcheck_proof = GKRSumcheckProof { - round_polynomials: proof_polys, - challenges, - }; - - Ok(sumcheck_proof) -} - -/// GKR-specific sumcheck Verifier. -pub fn gkr_sumcheck_verify( - claimed_sum: FieldElement, - sumcheck_proof: &GKRSumcheckProof, - transcript: &mut T, -) -> Result<(bool, Vec>), crate::verifier::VerifierError> -where - F: IsField + HasDefaultTranscript, - ::BaseType: Send + Sync + Copy, - FieldElement: ByteConversion, - T: IsTranscript + Channel, -{ - let proof_polys = &sumcheck_proof.round_polynomials; - if proof_polys.is_empty() { - return Err(crate::verifier::VerifierError::InvalidProof); - } - - let num_vars = proof_polys.len(); - - transcript.append_bytes(b"initial_sum"); - transcript.append_felt(&FieldElement::from(num_vars as u64)); - transcript.append_felt(&FieldElement::from(1u64)); // single factor - transcript.append_felt(&claimed_sum); - - let mut challenges = Vec::new(); - - // Verify each round polynomial. - for (j, g_j) in proof_polys.iter().enumerate() { - // Add polynomial info to transcript - let round_label = format!("round_{j}_poly"); - transcript.append_bytes(round_label.as_bytes()); - let coeffs = g_j.coefficients(); - transcript.append_bytes(&(coeffs.len() as u64).to_be_bytes()); - if coeffs.is_empty() { - transcript.append_felt(&FieldElement::zero()); - } else { - for coeff in coeffs { - transcript.append_felt(coeff); - } - } - - // Check that the degree of `g_j` does not exceed the theoretical bound. - // The polynomial `g_j` should be cuadratic since the polynomial `f(b,c)` to which the sumcheck is applied - // is the sum of of two products, where each one is the product of two multilinear polynomials. - if g_j.degree() > 2 { - return Err(crate::verifier::VerifierError::InvalidDegree); - } - - // Verify `g_j(0) + g_j(1) = m_{j-1}`, where: - // `m_{j-1} = g_{j-1} (s_{j-1})`, the previous claimed sum. - let g_j_0 = g_j.evaluate::(&FieldElement::zero()); - let g_j_1 = g_j.evaluate::(&FieldElement::one()); - let sum_evals = &g_j_0 + &g_j_1; - - let expected_sum = if j == 0 { - claimed_sum.clone() - } else { - // Should be the evaluation of previous polynomial at previous challenge - let prev_poly = &proof_polys[j - 1]; - let prev_challenge = &challenges[j - 1]; - prev_poly.evaluate::(prev_challenge) - }; - - if sum_evals != expected_sum { - println!("Sumcheck verification failed at round {j}"); - return Ok((false, challenges)); - } - - let r_j = transcript.sample_field_element(); - - challenges.push(r_j.clone()); - } - - Ok((true, challenges)) -} diff --git a/crates/provers/gkr/src/verifier.rs b/crates/provers/gkr/src/verifier.rs deleted file mode 100644 index 417eefcb2..000000000 --- a/crates/provers/gkr/src/verifier.rs +++ /dev/null @@ -1,146 +0,0 @@ -use crate::{circuit::Circuit, sumcheck::gkr_sumcheck_verify, GKRProof}; -use lambdaworks_math::{ - field::{ - element::FieldElement, - traits::{HasDefaultTranscript, IsField}, - }, - polynomial::dense_multilinear_poly::DenseMultilinearPolynomial, - traits::ByteConversion, -}; - -use lambdaworks_crypto::fiat_shamir::{ - default_transcript::DefaultTranscript, is_transcript::IsTranscript, -}; -#[derive(Debug)] -pub enum VerifierError { - InvalidProof, - InvalidDegree, - MultilinearPolynomialEvaluationError, - SumcheckError, - CircuitError, -} - -pub struct Verifier; - -impl Verifier { - pub fn verify(proof: &GKRProof, circuit: &Circuit) -> Result - where - F: IsField + HasDefaultTranscript, - FieldElement: ByteConversion, - ::BaseType: Send + Sync + Copy, - { - // The proof should haver one layer-proof for each circuit layer. - if proof.layer_proofs.len() != circuit.layers().len() { - println!("The proof has an invalid number of proof layers"); - return Ok(false); - } - // Fiat-Shamir heuristic: - // Both parties need to to append to the transcript the circuit, the inputs and the outputs. - // See https://eprint.iacr.org/2025/118.pdf, Sections 2.1 and 2.2 - let mut transcript = DefaultTranscript::::default(); - // Append the circuit data to the transcript. - transcript.append_bytes(&crate::circuit_to_bytes(circuit)); - // Append public inputs x. - for x in &proof.input_values { - transcript.append_bytes(&x.to_bytes_be()); - } - // Append public outputs y (first layer of evaluation). - for y in &proof.output_values { - transcript.append_bytes(&y.to_bytes_be()); - } - - let k_0 = circuit.num_vars_at(0).ok_or(VerifierError::CircuitError)?; - let mut r_i: Vec> = (0..k_0) - .map(|_| transcript.sample_field_element()) - .collect(); - - // Calculate the initial claimed sum `m_0 = W_0(r_0)`. - let output_poly_ext = DenseMultilinearPolynomial::new(proof.output_values.clone()); - let mut claimed_sum = output_poly_ext - .evaluate(r_i.clone()) - .map_err(|_e| VerifierError::MultilinearPolynomialEvaluationError)?; - - // For each layer, verify the sumcheck proof and calculate the next layer's challenges and claimed sum. - for (layer_idx, layer_proof) in proof.layer_proofs.iter().enumerate() { - // The number of sumcheck round polynomials `g_j` should be `2 * k_{i+1}`, - // where `k_{i+1} = next_num_vars` is the number of variables in the next layer. - let next_num_vars = circuit - .num_vars_at(layer_idx + 1) - .ok_or(VerifierError::InvalidProof)?; - if layer_proof.sumcheck_proof.round_polynomials.len() != 2 * next_num_vars { - println!("The proof has an invalid number of sumcheck round polynomials at layer {layer_idx}"); - return Ok(false); - } - - // Sumcheck verification. - let (sumcheck_verified, sumcheck_challenges) = gkr_sumcheck_verify( - claimed_sum.clone(), - &layer_proof.sumcheck_proof, - &mut transcript, - )?; - - if !sumcheck_verified { - println!("Sumcheck verification failed at layer {layer_idx}"); - return Ok(false); - } - - // Final sumcheck verification. - // The verifier must check the last claim using the polynommial q sent by the prover. - let last_poly = layer_proof - .sumcheck_proof - .round_polynomials - .last() - .ok_or(VerifierError::InvalidProof)?; - let last_challenge = sumcheck_challenges - .last() - .ok_or(VerifierError::SumcheckError)?; - let expected_final_eval = last_poly.evaluate::(last_challenge); - - let q_at_0: FieldElement = layer_proof.poly_q.evaluate(&FieldElement::zero()); - let q_at_1: FieldElement = layer_proof.poly_q.evaluate(&FieldElement::one()); - - let add_eval = circuit - .add_i_ext(&r_i, layer_idx) - .evaluate(sumcheck_challenges.clone()) - .map_err(|_| VerifierError::MultilinearPolynomialEvaluationError)?; - let mul_eval = circuit - .mul_i_ext(&r_i, layer_idx) - .evaluate(sumcheck_challenges.clone()) - .map_err(|_| VerifierError::MultilinearPolynomialEvaluationError)?; - - let final_eval = add_eval * (&q_at_0 + &q_at_1) + mul_eval * q_at_0 * q_at_1; - - // Final claim verification. - if final_eval != expected_final_eval { - println!("Final sumcheck verification failed at layer {layer_idx}."); - return Ok(false); - } - - // Sample challenges for the next round using line function. - let num_vars_next = circuit - .num_vars_at(layer_idx + 1) - .ok_or(VerifierError::CircuitError)?; - - // r* in our blog post - let r_new = transcript.sample_field_element(); - - // Construct the next round's random point using line function that goes from `b` to `c`. - let (b, c) = sumcheck_challenges.split_at(num_vars_next); - r_i = crate::line(b, c, &r_new); - // Set the next layer's claimed sum. - claimed_sum = layer_proof.poly_q.evaluate(&r_new); - } - - // At the last layer the verifier checks the last claim using the input multilinear polynomial extension W. - let input_poly_ext = DenseMultilinearPolynomial::new(proof.input_values.clone()); - if claimed_sum - != input_poly_ext - .evaluate(r_i) - .map_err(|_| VerifierError::MultilinearPolynomialEvaluationError)? - { - return Ok(false); - } - - Ok(true) - } -} diff --git a/crates/provers/groth16/Cargo.toml b/crates/provers/groth16/Cargo.toml deleted file mode 100644 index 557dc4fe1..000000000 --- a/crates/provers/groth16/Cargo.toml +++ /dev/null @@ -1,16 +0,0 @@ -[package] -name = "lambdaworks-groth16" -version.workspace = true -edition.workspace = true -license.workspace = true -repository.workspace = true - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] -lambdaworks-math.workspace = true -lambdaworks-crypto.workspace = true -rand_chacha = "0.3.1" -serde = "1.0" -serde_json = "1.0" -rand = "0.8.5" diff --git a/crates/provers/groth16/README.md b/crates/provers/groth16/README.md deleted file mode 100644 index 5e48d6b3f..000000000 --- a/crates/provers/groth16/README.md +++ /dev/null @@ -1,156 +0,0 @@ -# Lambdaworks Groth16 Prover - -An under-optimized implementation of [Groth16](https://eprint.iacr.org/2016/260) protocol. To use it with Circom, check the [examples](./circom-adapter/src/README.md). - -## Introduction - -Over the last decade, SNARKs (succinct, non-interactive arguments of knowledge) and STARKs (scalable, transparent arguments of knowledge) have been gaining attention due to their applications in verifiable private computation and scalability of blockchains. - -Groth introduced this [proof system](https://eprint.iacr.org/2016/260.pdf) in 2016 and saw an early application in ZCash. The protocol relies on pairing-friendly elliptic curves, such as BN254, BLS12-381, and BLS12-377 (more later). Its proof size is among the smallest (consisting of only three elliptic curve elements) and fastest to verify. The main drawback is that it needs a trusted setup per program. In other words, we need to regenerate all the parameters whenever we want to prove a new program (or change the original one). - -## Arithmetization - -To prove the execution of a given program, we have to transform it to a SNARK (succinct, non-interactive argument of knowledge) friendly form. One of such forms is arithmetic circuit satisfiability, where one can prove knowledge of a valid circuit assignment. This first step, known as arithmetization, is the program's transformation into an arithmetic circuit or equivalent form. - -## R1CS - -Arithmetic circuits can be expressed equivalently as (quadratic) rank one constraint systems (R1CS), which are systems of equations of the form: -$$(Az)\times (Bz) = Cz$$ -where $A, B, C$ are matrices of size $m + 1$ rows by $n + 1$ columns, $z$ is a (column) vector of size $n + 1$ and $\times$ indicates the componentwise product of the resulting vectors. - -We can alternatively view this compact form as -$\left( \sum_k a_{0k} z_k \right) \left( \sum_k b_{0k} z_k \right) - \left( \sum_k c_{0k} z_k \right) = 0$ -$\left( \sum_k a_{1k} z_k \right) \left( \sum_k b_{1k} z_k \right) - \left( \sum_k c_{1k} z_k \right) = 0$ -$\left( \sum_k a_{2k} z_k \right) \left( \sum_k b_{2k} z_k \right) - \left( \sum_k c_{2k} z_k \right) = 0$ -$\vdots$ -$\left( \sum_k a_{mk} z_k \right) \left( \sum_k b_{mk} z_k \right) - \left( \sum_k c_{mk} z_k \right) = 0$ - -We could express these equations more compactly by using polynomials and prove the solution of the R1CS system more concisely. To this end, we will introduce quadratic arithmetic programs, [QAP](https://vitalik.ca/general/2016/12/10/qap.html). - -## Quadratic Arithmetic Program - -We can interpret each column of the $A$ matrix as evaluations of some polynomial over some suitable domain. This is a common practice in many SNARKs, where we try to encode a vector as a polynomial; see, for example, our [post about STARKs](https://blog.lambdaclass.com/diving-deep-fri/). We sample $D_0 = \{ x_0 , x_1 , ... , x_n \}$ over the finite field and define the polynomial $A_i (x)$ as the polynomial of at most degree $n$ such that $A_i ( x_k ) = a_{ki}$. - -For performance reasons, it is convenient to select as interpolation domain $D_0$ the n-th roots of unity since we can use the Fast Fourier Transform to interpolate. Similarly, we can interpret the columns of $B$ and $C$ as polynomials $B_k (x)$ and $C_k (x)$. Taking advantage of these polynomials, we can express the R1CS system in polynomial form, -$P (x) = \left( \sum_k A_{k} (x) z_k \right) \left( \sum_k B_{k} (x) z_k \right) - \left( \sum_k C_{k} (x) z_k \right)$ - -We can see that if we have a valid solution for the R1CS, the polynomial $P (x)$ evaluates to $0$ over $D_0$ (since we require the polynomial to interpolate the values of the columns of the matrices). Therefore, we can express the condition as -$P (x) = 0$ for $x \in D_0$ -We now introduce the vanishing polynomial over the set $D_0$, $Z_D (x) = \prod_k (x - x_k )$ -So, if the polynomial $P (x)$ evaluates to $0$ over $D_0$, it is divisible by $Z_D (x)$. This can be written as there is some polynomial $h (x)$ such that -$P (x) = h(x) Z_D (x)$ -The degree of the polynomial $h(x)$ is the degree of $P$ minus the degree of $Z_D$. An honest prover should be able to find the resulting quotient and use it to show that he correctly executed the program. - -## Transforming QAP into a zero-knowledge proof - -We need to make some transformation to the above problem if we want to turn it into a zero-knowledge proof. For a more detailed description of this process, see [here](https://www.rareskills.io/post/groth16). We must ensure that the prover cannot cheat and that the verifier cannot learn anything about the private input or witness. One key ingredient is a polynomial commitment scheme (PCS): we can make the prover commit to a given polynomial so that he cannot change it later. One such commitment scheme is the [KZG commitment](https://dankradfeist.de/ethereum/2020/06/16/kate-polynomial-commitments.html), where we use [pairing-friendly elliptic curves](https://static1.squarespace.com/static/5fdbb09f31d71c1227082339/t/5ff394720493bd28278889c6/1609798774687/PairingsForBeginners.pdf) to bind the prover to a polynomial. The scheme's security relies on the hardness of the discrete logarithm problem over the curve. Pairings can be considered an operation that allows a one-time multiplication between points in an elliptic curve. In our case, we will work over type3 III pairings, $\dagger : G_1 \times G_2 \rightarrow G_t$, which have the following nice property (bilinearity): -$(a g_1 ) \dagger (b g_2 ) = (ab) (g_1 \dagger g_2)$ -To commit to a polynomial using KZG, we need to sample a random scalar $\tau$ (which is considered toxic waste and should be forgotten, or we could forge proofs) and generate the following sequence of points in the elliptic curve, whose generator is $g_1$, -$P_0 = g_1$, -$P_1 = \tau g_1$ -$P_2 = \tau^2 g_1$ -$\vdots$ -$P_n = \tau^n g_1$ -Then, given a polynomial $p(x) = a_0 + a_1 x + a_2 x^2 + ... + a_n x^n$ we compute the commitment as -$\mathrm{cm} (p) = a_0 P_0 + a_1 P_1 + ... + a_n P_n$ -which is the same as $\mathrm{cm} (p) = p(\tau) g_1$, that is, hiding the evaluation of $p(x)$ inside the elliptic curve. Because the discrete log problem is hard, we cannot use our knowledge of $g_1$ and $\mathrm{cm} (p)$ to obtain $p(\tau)$. - -To check that the polynomial $p(x)$ evaluates to $v$ at $z$ we can use the fact that -$p(x) - v = (x - z)q(x)$ -where $q(x)$ is the quotient polynomial of the division of $p(x)$ by $x - z$. The prover can produce proof of such evaluation by committing to $q(x)$ using the same trick. Still, the verifier will need some additional information (included in the verifying key), $g_2$ (the generator of the group $G_2$), and $\tau g_2$ (remember, nobody must know $\tau$). Then, using pairings, the verifier can check the evaluation using the points in the elliptic curves, -$(\mathrm{cm} (p) - vg_1 \dagger g_2) = a = p(\tau) (g_1 \dagger g_2)$ -$\mathrm{cm} (q) \dagger (\tau g_2 - z g_2) = b = q(\tau) ( \tau - z)(g_1 \dagger g_2)$ -If $a$ and $b$ are the same, and since $\tau$ is a random point with high probability, we assume that $p(z) = v$ (This depends on the Schwartz-Zippel lemma). - -Remember that we want to prove that the verifier knows some $w$ and a polynomial $h(x)$ of degree $m - 1$ such that if $z= (1, x, w)$, the following condition holds -$\left( \sum_k A_{k} (x) z_k \right) \left( \sum_k B_{k} (x) z_k \right) = \left( \sum_k C_{k} (x) z_k \right) + h(x)Z_D (x)$ - -If we force the prover first to commit to the polynomials $A_k (x)$ and $B_k (x)$ and then produce the quotient polynomial, we have to make sure that he cannot forge $C_k (x)$ to fulfill the previous condition. To do so, we are going to introduce random shifts ($\alpha$ and $\beta$) to the evaluations: -$\mathrm{cm} (\sum A_i z_i ) = \sum (A_i (\tau) z_i) g_1 + \alpha g_1$ -$\mathrm{cm} (\sum B_i z_i) = \sum (B_i (\tau) z_i) g_2 + \beta g_2$ -The $B_i (x)$ are committed to using group $G_2$ so that we can compute the product on the left-hand side through a pairing, -$(\mathrm{cm} (\sum A_i z_i )) \dagger ( \mathrm{cm} (\sum B_i z_i )) = (\sum A_i (\tau) z_i )(\sum B_i (\tau) z_i ) (g_1 \dagger g_2)$ - -Because we introduce these shifts, we need to modify the $C_k$ term accordingly, -$\begin{equation}\left( \alpha + \sum_k A_{k} (x) z_k \right) \left( \beta + \sum_k B_{k} (x) z_k \right) = \\ \alpha \beta + \left( \sum_k (C_{k} (x) + \beta A_k (x) + \alpha B_k (x)) z_k \right) + h(x)Z_D (x) \end{equation}$ -Since the prover cannot know $\alpha$ and $\beta$, we need to provide them hidden as part of the trusted setup, as $\alpha g_1$ and $\beta g_2$, so that we can compute -$(\alpha g_1) \dagger (\beta g_2) = \alpha \beta (g_1 \dagger g_2)$ -so that we can compare this result to the pairing between the shifted $A_i$ and $B_i$. - -Also, since the prover does not have $\alpha$ and $\beta$, he needs to be supplied with all the elements of the form $C_{k} (x) + \beta A_k (x) + \alpha B_k (x)$. However, when we want to calculate the product between these terms and $z$, we must recall that $z$ contains both the public input and the witness. The verifier cannot learn anything about the witness (therefore, the evaluations involving the witness should be provided by the prover). We introduce two additional variables, $\gamma$, and $\delta$, to split the variable $z$ between public input and witness. The first $k$ terms correspond to the public input, and these are encoded as -$K_i^v = \gamma^{- 1} (C_{i} (\tau) + \beta A_i (\tau) + \alpha B_i (\tau)) g_1$ -for $i = 0, 1, 2 ... , k$. For the witness, we have -$K_i^p = \delta^{- 1} (C_{i} (\tau) + \beta A_i (\tau) + \alpha B_i (\tau)) g_1$ -With these new parameters, we get -$\begin{equation}\left( \alpha + \sum_j A_{j} (x) z_j \right) \left( \beta + \sum_j B_{j} (x) z_j \right) = \\ \alpha \beta + \gamma \left( \sum_i^k \gamma^{- 1} (C_{i} (x) + \beta A_i (x) + \alpha B_i (x)) x_i \right) + \\ -\delta \left( \sum_{j = k + 1}^n \delta^{- 1} (C_{i} (x) + \beta A_i (x) + \alpha B_i (x)) x_i \right) + h(x)Z_D (x) \end{equation}$ -We can combine the last two terms into one (since they contain all the information that the verifier must not learn) -$D = \left( \sum_{j = k + 1}^n \delta^{- 1} (C_{i} (x) + \beta A_i (x) + \alpha B_i (x)) x_i \right) + h(x)Z_D (x)\delta^{- 1}$ - -Since we want to compute the product $h(x) Z_D(x)$ with the help of one pairing, we can compute the following group elements, -$Z_0 = \delta^{ - 1} Z_D (\tau)$ -$Z_1 = \delta^{ - 1} \tau Z_D (\tau)$ -$Z_2 = \delta^{ - 1} \tau^2 Z_D (\tau)$ -$\vdots$ -$Z_{m - 1} = \delta^{ - 1} \tau^{ m - 1 } Z_D (\tau)$ - -With these changes, the right-hand side of the QAP is the sum of 3 terms: -A constant (related to the random shifts). -A term involving the public input. -A term that contains the secret terms (known only to the prover). - -## Setup - -Groth16 requires sampling five random field elements to generate the proving and verifying key, $t, \alpha, \beta, \gamma, \delta$. These are toxic waste and should be discarded and wholly forgotten once the keys have been generated. - -We will use a pairing-friendly elliptic curve (with type III pairing), with subgroups $G_1$ and $G_2$ of prime order $r$. We will call the generators $g_1$ and $g_2$, respectively. To make notation easier, we will write -$[x]_1 = x g_1$ -$[x]_2 = x g_2$ -to denote points in $G_1$ and $G_2$, where $x g$ means the scalar product of $x$ and the generator of the group (i.e., applying x times the elliptic curve group operation to the generator). We will follow the notation given by [DIZK](https://eprint.iacr.org/2018/691.pdf). First, we compute the following vectors, -$K_i^v (t) = \gamma^{-1} \left( \beta A_i(t) + \alpha B_i (t) + C_i (t)\right)$ -for $i = 0, 1, 2 , ... k$, -$K_i^p (t) = \delta^{-1} \left( \beta A_i(t) + \alpha B_i (t) + C_i (t)\right)$ -for $i = k+1, 1, 2 , ... n$ and -$Z_k (t) = t^k Z_D (t) \delta^{-1}$ -for $k = 0, 1, 2, ... m - 1$. -The proving key consists of the following elements: -1. $[\alpha]_1$ -2. $[\beta]_1$ -3. $[\beta]_2$ -4. $[\delta]_1$ -5. $[\delta]_2$ -6. $[A_0 (t) ]_1, [A_1 (t) ]_1 , ... , [A_n (t) ]_1$ -7. $[B_0 (t) ]_1, [B_1 (t) ]_1 , ... , [B_n (t) ]_1$ -8. $[B_0 (t) ]_2, [B_1 (t) ]_2 , ... , [B_n (t) ]_2$ -9. $[K_{ k + 1 }^p (t)] , [ K_{ k + 2 }^p (t)] , ... , [K_n^p (t)]$ -10. $[Z_0 (t)] , [Z_1 (t)] , ... , [ Z_{ m - 1 } (t)]$ - -The verifying key is much shorter and will contain in addition the value of one pairing because that value is constant: -1. $[\alpha]_1 \dagger [\beta]_2$ -2. $[\gamma]_2$ -3. $[\delta]_2$ -4. $[K_0^v (t)]_1 , [K_1^v (t)]_1 , ... , [K_k^v (t)]_1$ - -## Proof generation - -The prover receives the proving key and knows the polynomials representing the program and the public input, and he wants to prove that he has a witness satisfying that program. First, the prover needs to calculate the quotient polynomial $h(x)$ or, more precisely, its coefficients. The prover has to calculate -$$h(x) = \frac{\sum A_k(x) z_k \sum B_k (x) z_k - \sum C_k (x) z_k}{Z_D (X) }$$ - -The best way to evaluate this quotient is by choosing a domain $D_{ev}$, of size at least the degree of the quotient polynomial plus one and not containing elements from $D_0$ (the interpolation domain) and evaluating numerator and denominator at all the elements of $D_{ev}$. Since we have at least as many evaluations of the polynomial $h (x)$ as its degree plus one, we can reconstruct $h(x)$ via interpolation. In practice, the fastest way to do this is by using the Fast Fourier Transform for evaluation and interpolation. The prover now possesses a vector of coefficients $h_0 , h_1 , h_2 , ... , h_m$. - -To ensure that the proof is zero-knowledge, the prover sample two random scalars, $r$ and $s$. - -The prover can compute the three elements of the proof, $\pi = ([\pi_1 ]_1 , [\pi_2 ]_2 , [\pi_3 ]_1)$ by doing the following calculations, -$[\pi_1 ]_1 = [\alpha]_1 + \sum z_k [A_k (t) ]_1 + r [\delta]_1$ -$[\pi_2 ]_2 = [\beta]_2 + \sum z_k [B_k (t) ]_2 + s[\delta]_2$ -$[\pi_2 ]_1 = [\beta]_1 + \sum z_k [B_k (t) ]_1 + s[\delta]_1$ -$[h(t)z(t)]_1 = \sum h_i [Z_i (t)]_1$ -$[\pi_3 ]_1 = \sum w_i [K_i^p ]_1 + [h(t)z(t)]_1 + s[\pi_1 ]_1 + r [\pi_2 ]_1 - rs [\delta]_1$ - -## Verification - -The verifier has the verifying key, the public input and parses the proof as $[\pi_1 ]_1, [\pi_2 ]_2, [\pi_3 ]_1$ and computes the following: -$[\pi_1 ]_1 \dagger [\pi_2 ]_2 = P_1$ -$[\pi_3 ]_1 \dagger [\delta]_2 + [\alpha]_1 \dagger [\beta]_2 + \left(\sum x_i [K_i^v ]_1 \right) \dagger [\gamma]_2 = P_2$ - -The proof is valid if $P_1$ and $P_2$ coincide. This is equivalent to checking the modified QAP. diff --git a/crates/provers/groth16/arkworks-adapter/Cargo.toml b/crates/provers/groth16/arkworks-adapter/Cargo.toml deleted file mode 100644 index dd5082a98..000000000 --- a/crates/provers/groth16/arkworks-adapter/Cargo.toml +++ /dev/null @@ -1,19 +0,0 @@ -[package] -name = "arkworks_adapter" -version.workspace = true -edition.workspace = true -license.workspace = true -repository.workspace = true - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] -lambdaworks-math.workspace = true -lambdaworks-groth16.workspace = true -ark-r1cs-std = { version = "^0.3.1" } -ark-bls12-381 = { version = "0.4.0" } -ark-ff = { version = "^0.4.2" } -ark-relations = { version = "^0.4.0" } -ark-serialize = { version = "0.4.2" } -num-bigint = { version = "0.4", default-features = false } -rand = "0.8.5" diff --git a/crates/provers/groth16/arkworks-adapter/README.md b/crates/provers/groth16/arkworks-adapter/README.md deleted file mode 100644 index 442122c30..000000000 --- a/crates/provers/groth16/arkworks-adapter/README.md +++ /dev/null @@ -1,84 +0,0 @@ -# Arkworks Adapter for Lambdaworks Groth16 Backend - -This crate enables circuits written in Arkworks to be proven / verified via Lambdaworks Groth16 backend. - -Crate exposes [to_lambda](./src/lib.rs#to_lambda) function for ConstraintSystemRef type of Arkworks, which is expected to carry all constraints, witnesses, and public variable assignments: - -```rust -pub fn to_lambda(cs: &ConstraintSystemRef) -> (QuadraticArithmeticProgram, Vec) -``` - -It returns a Lambdaworks-compatible QAP struct alongside variable assignments. Please note that public variable assignments are bundled with witnesses, and this vector of field elements is called **witness** alltogether. - -```rust -let (qap, w) = to_lambda(&cs); -``` - -After this point, typical steps of Groth16 can be performed using Lamdaworks: setup, prove, verify - -```rust -let (pk, vk) = setup(&qap); - -let proof = Prover::prove(&w, &qap, &pk); - -let public_inputs = &w[..qap.num_of_public_inputs]; -let accept = verify(&vk, &proof, public_inputs); - -assert!(accept); -``` - -## Full Example - -A linear exponentiation example on BLS12-381 can be found here. -Please check [integration_tests.rs](./src/integration_tests.rs) for more examples. - -```rust - -use crate::to_lambda; -use ark_bls12_381::Fr; -use ark_relations::{lc, r1cs::ConstraintSystem, r1cs::Variable}; -use lambdaworks_groth16::{setup, verify, Prover}; -use rand::Rng; - -// ... -// ... - -let mut rng = rand::thread_rng(); -let x = rng.gen::(); -let exp = rng.gen::(); - -// Define the circuit using Arkworks - -let cs = ConstraintSystem::::new_ref(); - -let x = Fr::from(x); -let mut _x = cs.new_witness_variable(|| Ok(x)).unwrap(); - -let mut acc = Fr::from(x); -let mut _acc = cs.new_witness_variable(|| Ok(x)).unwrap(); - -for _ in 0..exp - 1 { - acc *= x; - let _new_acc = cs.new_witness_variable(|| Ok(acc)).unwrap(); - cs.enforce_constraint(lc!() + _acc, lc!() + _x, lc!() + _new_acc) - .unwrap(); - _acc = _new_acc; -} - -let _out = cs.new_input_variable(|| Ok(acc)).unwrap(); -cs.enforce_constraint(lc!() + _out, lc!() + Variable::One, lc!() + _acc) - .unwrap(); - -// Make Lambdaworks-compatible -let (qap, w) = to_lambda(&cs); - -// Use Lambdaworks Groth16 backend - -let (pk, vk) = setup(&qap); - -let proof = Prover::prove(&w, &qap, &pk); - -let public_inputs = &w[..qap.num_of_public_inputs]; -let accept = verify(&vk, &proof, public_inputs); -assert!(accept); -``` diff --git a/crates/provers/groth16/arkworks-adapter/src/integration_tests.rs b/crates/provers/groth16/arkworks-adapter/src/integration_tests.rs deleted file mode 100644 index d03f5e79d..000000000 --- a/crates/provers/groth16/arkworks-adapter/src/integration_tests.rs +++ /dev/null @@ -1,195 +0,0 @@ -use crate::arkworks_cs_to_lambda_cs; -use ark_bls12_381::Fr; -use ark_relations::{lc, r1cs::ConstraintSystem, r1cs::Variable}; -use lambdaworks_groth16::{setup, verify, Prover, QuadraticArithmeticProgram}; -use rand::Rng; - -#[test] -fn pinocchio_paper_example() { - /* - pub inp a, b, c, d - pub out result - sig e - - c * d = e - (a + b) + e = result - */ - let cs = ConstraintSystem::::new_ref(); - - let _a = Fr::from(1555); - let _b = Fr::from(25555); - let _c = Fr::from(35555); - let _d = Fr::from(45555); - - let a = cs.new_input_variable(|| Ok(_a)).unwrap(); - let b = cs.new_input_variable(|| Ok(_b)).unwrap(); - let c = cs.new_input_variable(|| Ok(_c)).unwrap(); - let d = cs.new_input_variable(|| Ok(_d)).unwrap(); - - let e = cs.new_witness_variable(|| Ok(_c * _d)).unwrap(); - cs.enforce_constraint(lc!() + c, lc!() + d, lc!() + e) - .unwrap(); - - let result = cs.new_input_variable(|| Ok(_c * _d * (_a + _b))).unwrap(); - - cs.enforce_constraint(lc!() + a + b, lc!() + e, lc!() + result) - .unwrap(); - - let lambda_cs = arkworks_cs_to_lambda_cs(&cs); - - let qap = QuadraticArithmeticProgram::from_r1cs(lambda_cs.constraints); - - let (pk, vk) = setup(&qap); - - let accept = verify( - &vk, - &Prover::prove(&lambda_cs.witness, &qap, &pk), - &lambda_cs.witness[..qap.num_of_public_inputs], - ); - assert!(accept); -} - -#[test] -fn vitalik_example() { - /* - pub out ~out - sig x, sym_1, y, sym_2 - - x * x = sym_1; - sym_1 * x = y; - (y + x) * 1 = sym_2 - (sym_2 + 5) * 1 = ~out - */ - let cs = ConstraintSystem::::new_ref(); - - //["0x1", "0x3", "0x23", "0x9", "0x1b", "0x1e"] - let _x = Fr::from(3); - let _sym_1 = Fr::from(9); - let _y = Fr::from(27); - let _sym_2 = Fr::from(30); - - let _out = Fr::from(35); - - let x = cs.new_witness_variable(|| Ok(_x)).unwrap(); - let sym_1 = cs.new_witness_variable(|| Ok(_sym_1)).unwrap(); - let y = cs.new_witness_variable(|| Ok(_y)).unwrap(); - let sym_2 = cs.new_witness_variable(|| Ok(_sym_2)).unwrap(); - - let out = cs.new_input_variable(|| Ok(_out)).unwrap(); - - cs.enforce_constraint(lc!() + x, lc!() + x, lc!() + sym_1) - .unwrap(); - cs.enforce_constraint(lc!() + sym_1, lc!() + x, lc!() + y) - .unwrap(); - cs.enforce_constraint(lc!() + y + x, lc!() + Variable::One, lc!() + sym_2) - .unwrap(); - cs.enforce_constraint( - lc!() + sym_2 + (Fr::from(5), Variable::One), - lc!() + Variable::One, - lc!() + out, - ) - .unwrap(); - - let lambda_cs = arkworks_cs_to_lambda_cs(&cs); - - let qap = QuadraticArithmeticProgram::from_r1cs(lambda_cs.constraints); - - let (pk, vk) = setup(&qap); - - let accept = verify( - &vk, - &Prover::prove(&lambda_cs.witness, &qap, &pk), - &lambda_cs.witness[..qap.num_of_public_inputs], - ); - assert!(accept); -} - -#[test] -fn failing_vitalik() { - // Same circuit as vitalik_example, but with an incorrect witness assignment. - let cs = ConstraintSystem::::new_ref(); - - //["0x1", "0x3", "0x23", "0x9", "0x1b", "0x1e"] - let _x = Fr::from(3); - let _sym_1 = Fr::from(10); // should be have been 9 - let _y = Fr::from(27); - let _sym_2 = Fr::from(30); - - let _out = Fr::from(35); - - let x = cs.new_witness_variable(|| Ok(_x)).unwrap(); - let sym_1 = cs.new_witness_variable(|| Ok(_sym_1)).unwrap(); - let y = cs.new_witness_variable(|| Ok(_y)).unwrap(); - let sym_2 = cs.new_witness_variable(|| Ok(_sym_2)).unwrap(); - - let out = cs.new_input_variable(|| Ok(_out)).unwrap(); - - cs.enforce_constraint(lc!() + x, lc!() + x, lc!() + sym_1) - .unwrap(); - cs.enforce_constraint(lc!() + sym_1, lc!() + x, lc!() + y) - .unwrap(); - cs.enforce_constraint(lc!() + y + x, lc!() + Variable::One, lc!() + sym_2) - .unwrap(); - cs.enforce_constraint( - lc!() + sym_2 + (Fr::from(5), Variable::One), - lc!() + Variable::One, - lc!() + out, - ) - .unwrap(); - - let lambda_cs = arkworks_cs_to_lambda_cs(&cs); - - let qap = QuadraticArithmeticProgram::from_r1cs(lambda_cs.constraints); - - let (pk, vk) = setup(&qap); - - let accept = verify( - &vk, - &Prover::prove(&lambda_cs.witness, &qap, &pk), - &lambda_cs.witness[..qap.num_of_public_inputs], - ); - assert!(!accept); -} - -#[test] -fn exponentiation_example() { - /* - Generates a "linear exponentiation" circuit with a random base and a random exponent. - Only the output ~out is public input. - */ - let cs = ConstraintSystem::::new_ref(); - - let mut rng = rand::thread_rng(); - let x = rng.gen::(); - let exp = rng.gen::(); // Bigger data types take too much time for a test - - let x = Fr::from(x); - let mut _x = cs.new_witness_variable(|| Ok(x)).unwrap(); - - let mut acc = Fr::from(x); - let mut _acc = cs.new_witness_variable(|| Ok(x)).unwrap(); - - for _ in 0..exp - 1 { - acc *= x; - let _new_acc = cs.new_witness_variable(|| Ok(acc)).unwrap(); - cs.enforce_constraint(lc!() + _acc, lc!() + _x, lc!() + _new_acc) - .unwrap(); - _acc = _new_acc; - } - - let _out = cs.new_input_variable(|| Ok(acc)).unwrap(); - cs.enforce_constraint(lc!() + _out, lc!() + Variable::One, lc!() + _acc) - .unwrap(); - - let lambda_cs = arkworks_cs_to_lambda_cs(&cs); - - let qap = QuadraticArithmeticProgram::from_r1cs(lambda_cs.constraints); - - let (pk, vk) = setup(&qap); - - let proof = Prover::prove(&lambda_cs.witness, &qap, &pk); - - let public_inputs = &lambda_cs.witness[..qap.num_of_public_inputs]; - let accept = verify(&vk, &proof, public_inputs); - assert!(accept); -} diff --git a/crates/provers/groth16/arkworks-adapter/src/lib.rs b/crates/provers/groth16/arkworks-adapter/src/lib.rs deleted file mode 100644 index f0403ef20..000000000 --- a/crates/provers/groth16/arkworks-adapter/src/lib.rs +++ /dev/null @@ -1,107 +0,0 @@ -#[cfg(test)] -mod integration_tests; - -use ark_ff::PrimeField; -use ark_relations::r1cs::{ConstraintSystemRef, Field}; -use lambdaworks_groth16::{common::*, r1cs::R1CS, ConstraintSystem}; -use lambdaworks_math::traits::ByteConversion; - -use std::ops::Deref; - -/// Accepts an Arkworks circuit as a ConstraintSystem reference, and creates a -/// Lambdaworks ConstraintSystem, which can then be used with the Lambdaworks -/// Groth16 backend for setup, proving, and verification. -pub fn arkworks_cs_to_lambda_cs( - cs: &ConstraintSystemRef, -) -> ConstraintSystem { - ConstraintSystem { - constraints: r1cs_from_arkworks_cs(cs), - witness: extract_witness_from_arkworks_cs(cs), - } -} - -#[inline] -fn r1cs_from_arkworks_cs(cs: &ConstraintSystemRef) -> R1CS { - cs.inline_all_lcs(); - - let r1cs_matrices = cs.to_matrices().unwrap(); - let num_pub_vars = cs.num_instance_variables(); - let total_variables = cs.num_witness_variables() + num_pub_vars; - - R1CS::from_matrices( - ark_to_lambda_matrix(&r1cs_matrices.a, total_variables), - ark_to_lambda_matrix(&r1cs_matrices.b, total_variables), - ark_to_lambda_matrix(&r1cs_matrices.c, total_variables), - num_pub_vars, - ) -} - -#[inline] -fn ark_to_lambda_matrix( - m: &[Vec<(F, usize)>], - total_variables: usize, -) -> Vec> { - sparse_matrix_to_dense(&arkworks_matrix_fps_to_fr_elements(m), total_variables) -} - -#[inline] -fn arkworks_matrix_fps_to_fr_elements( - m: &[Vec<(F, usize)>], -) -> Vec> { - m.iter() - .map(|x| { - x.iter() - .map(|(x, y)| (ark_fr_to_fr_element(x), *y)) - .collect() - }) - .collect() -} - -#[inline] -fn extract_witness_from_arkworks_cs(cs: &ConstraintSystemRef) -> Vec { - let binding = cs.borrow().unwrap(); - let borrowed_cs_ref = binding.deref(); - - // Place public variables first, then witness assignments. - // That's how Lambdaworks Groth16 expects the witness vector. - let mut witness = vec![]; - witness.extend( - borrowed_cs_ref - .instance_assignment - .iter() - .map(ark_fr_to_fr_element), - ); - witness.extend( - borrowed_cs_ref - .witness_assignment - .iter() - .map(ark_fr_to_fr_element), - ); - witness -} - -#[inline] -fn ark_fr_to_fr_element(ark_fq: &F) -> FrElement { - let mut buff = Vec::::new(); - ark_fq.serialize_compressed(&mut buff).unwrap(); - FrElement::from_bytes_le(&buff).unwrap() -} - -#[inline] -fn sparse_matrix_to_dense( - m: &[Vec<(FrElement, usize)>], - total_variables: usize, -) -> Vec> { - m.iter() - .map(|row| sparse_row_to_dense(row, total_variables)) - .collect() -} - -#[inline] -fn sparse_row_to_dense(row: &[(FrElement, usize)], total_variables: usize) -> Vec { - let mut dense_row = vec![FrElement::from(0); total_variables]; - row.iter().for_each(|e| { - dense_row[e.1] = e.0.clone(); - }); - dense_row -} diff --git a/crates/provers/groth16/circom-adapter/Cargo.toml b/crates/provers/groth16/circom-adapter/Cargo.toml deleted file mode 100644 index 2921a8276..000000000 --- a/crates/provers/groth16/circom-adapter/Cargo.toml +++ /dev/null @@ -1,15 +0,0 @@ -[package] -name = "lambdaworks-circom-adapter" -version.workspace = true -edition.workspace = true -license.workspace = true -repository.workspace = true - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] -lambdaworks-math.workspace = true -lambdaworks-groth16.workspace = true - -serde = { version = "1.0", features = ["derive"] } -serde_json = "1" diff --git a/crates/provers/groth16/circom-adapter/README.md b/crates/provers/groth16/circom-adapter/README.md deleted file mode 100644 index 3ad2dfab7..000000000 --- a/crates/provers/groth16/circom-adapter/README.md +++ /dev/null @@ -1,76 +0,0 @@ -# Circom - Lambdaworks Groth16 Adapter - -This package allows one to perform trusted setup, prove, and verify constraints generated by [SnarkJS](https://github.com/iden3/snarkjs) from a [Circom](https://github.com/iden3/circom) circuit over [BLS12-381](https://github.com/lambdaclass/lambdaworks/tree/main/crates/math/src/elliptic_curve/short_weierstrass/curves/bls12_381). - -## Setup - -1. Install [Circom](https://github.com/iden3/circom) and [SnarkJS](https://github.com/iden3/snarkjs). - -2. Compile your circuit with `circom`, e.g. for a circuit named `test.circom` you would do: - -```bash -circom test.circom --r1cs --wasm -p bls12381 -``` - -> [!IMPORTANT] -> Note that `-p bls12381` is important as that is the field supported by Lambdaworks Circom Adapter. - -3. Compiling a circuit like above will create a `test_js` directory, and a `test.r1cs` file. Now, we will create a witness for an input that is saved within `input.json`: - -```bash -node test_js/generate_witness.js test_js/test.wasm input.json witness.wtns -``` - -This will generate a **witness.wtns** file. - -4. For the Circom Adapter we need to export the witness and R1CS files as JSON: - -```bash -snarkjs wtns export json witness.wtns witness.wtns.json -snarkjs r1cs export json test.r1cs test.r1cs.json -``` - -To do these steps all at once, you can copy-paste the following snippet to your terminal in the same directory as your circuit, using your own circuit name instead of `test` here: - -```bash -circom test.circom --r1cs --wasm -p bls12381; - -node test_js/generate_witness.js test_js/test.wasm input.json witness.wtns; - -snarkjs wtns export json witness.wtns; -snarkjs r1cs export json test.r1cs test.r1cs.json; -``` - -## Usage - -This crate exposes a `circom_to_lambda` function along with readers for R1CS and witness files. `circom_to_lambda` accepts a Witness and R1CS. - -```rust -let circom_r1cs = read_circom_r1cs("test.r1cs.json").expect("could not read r1cs"); -let circom_wtns = read_circom_witness("witness.json").expect("could not read witness"); - -let (qap, wtns, pubs) = circom_to_lambda(circom_r1cs, circom_wtns); -``` - -This function returns a Lambdaworks-compatible QAP, the witness assignments and public signals. Then one should perform setup, prove, and verify. Here's the complete procedure: - -```rust -fn poseidon_parse_prove_verify() { - let (qap, wtns, pubs) = circom_to_lambda( - &fs::read_to_string("test.r1cs.json").expect("Error reading file"), - &fs::read_to_string("witness.json").expect("Error reading file"), - ); - - let (pk, vk) = setup(&qap); - let proof = Prover::prove(&wtns, &qap, &pk); - let accept = verify(&vk, &proof, &pubs); - assert!(accept); -} -``` - -## Examples - -There are a few examples within the [tests](./tests/) folder: - -- [`poseidon_test.rs`](./tests/poseidon_test.rs): Poseidon hash of $100$ is proven and verified. -- [`vitalik_test.rs`](./tests/vitalik_test.rs): Here we demonstrate the example from [Vitalik's Medium post](https://medium.com/@VitalikButerin/quadratic-arithmetic-programs-from-zero-to-hero-f6d558cea649) where $x^3 + x + 5 = 35$ is zk-proven. diff --git a/crates/provers/groth16/circom-adapter/src/lib.rs b/crates/provers/groth16/circom-adapter/src/lib.rs deleted file mode 100644 index fb4a2821b..000000000 --- a/crates/provers/groth16/circom-adapter/src/lib.rs +++ /dev/null @@ -1,50 +0,0 @@ -use lambdaworks_groth16::{common::FrElement, QuadraticArithmeticProgram}; - -mod readers; -pub use readers::*; - -/// Given a [`CircomR1CS`] and [`CircomWitness`] it returns a [QAP](QuadraticArithmeticProgram), -/// witness, and public signals; all compatible with Lambdaworks. -pub fn circom_to_lambda( - circom_r1cs: CircomR1CS, - witness: CircomWitness, -) -> (QuadraticArithmeticProgram, Vec, Vec) { - let num_of_outputs = circom_r1cs.num_outputs; - let num_of_pub_inputs = circom_r1cs.num_pub_inputs; - - // we could get a slice using the QAP but the QAP does not keep track of the number of private inputs; - // so instead we get the public signals here - let public_inputs = witness[..num_of_pub_inputs + num_of_outputs + 1].to_vec(); - - // get L,R,O matrices from R1CS - let [l, r, o] = build_lro_from_circom_r1cs(circom_r1cs); - let qap = QuadraticArithmeticProgram::from_variable_matrices(public_inputs.len(), &l, &r, &o); - - (qap, witness, public_inputs) -} - -/// Takes as input circom.r1cs.json file and outputs LRO matrices -#[inline] -fn build_lro_from_circom_r1cs(circom_r1cs: CircomR1CS) -> [Vec>; 3] { - let mut l = vec![vec![FrElement::zero(); circom_r1cs.num_constraints]; circom_r1cs.num_vars]; - let mut r = l.clone(); // same initial value as above - let mut o = l.clone(); // same initial value as above - - // assign each constraint from the R1CS hash-maps to LRO matrices - for (constraint_idx, constraint) in circom_r1cs.constraints.into_iter().enumerate() { - // destructuring here to avoid clones - let [lc, rc, oc] = constraint; - - for (var_idx, val) in lc { - l[var_idx][constraint_idx] = val; - } - for (var_idx, val) in rc { - r[var_idx][constraint_idx] = val; - } - for (var_idx, val) in oc { - o[var_idx][constraint_idx] = val; - } - } - - [l, r, o] -} diff --git a/crates/provers/groth16/circom-adapter/src/readers.rs b/crates/provers/groth16/circom-adapter/src/readers.rs deleted file mode 100644 index 0ed3fa4c6..000000000 --- a/crates/provers/groth16/circom-adapter/src/readers.rs +++ /dev/null @@ -1,113 +0,0 @@ -use lambdaworks_groth16::common::FrElement; -use lambdaworks_math::{errors::CreationError, unsigned_integer::element::UnsignedInteger}; -use serde::Deserialize; -use std::{collections::HashMap, fs::File, io::BufReader, path::Path}; - -#[derive(Debug)] -pub enum CircomReaderError { - IOError(std::io::Error), - SerdeError(serde_json::Error), - ParseError(CreationError), -} - -/// A witness for a Circom circuit, alias for a vector of field elements. -pub type CircomWitness = Vec; - -/// A rank-1 constraint system (R1CS) for a Circom circuit, as read from -/// a JSON export of that R1CS. -/// -/// Further information: -/// - -/// - -#[derive(Debug, Clone, Deserialize)] -pub struct CircomR1CS { - /// Number of bytes per field element. - pub n8: usize, - /// Order of the field used in this R1CS. - pub prime: String, - /// Number of variables in total. - #[serde(rename = "nVars")] - pub num_vars: usize, - /// Number of outputs (public). - #[serde(rename = "nOutputs")] - pub num_outputs: usize, - /// Number of public inputs, does not include the constant term! - #[serde(rename = "nPubInputs")] - pub num_pub_inputs: usize, - /// Number of private inputs. - #[serde(rename = "nPrvInputs")] - pub num_priv_inputs: usize, - /// Number of labels. - #[serde(rename = "nLabels")] - pub num_labels: usize, - /// Number of constraints. - #[serde(rename = "nConstraints")] - pub num_constraints: usize, - /// Constraints, where each `HashMap` contains mapping from witness - /// index to coefficient for that linear combination. - #[serde(deserialize_with = "constraint_deserializer")] - pub constraints: Vec<[HashMap; 3]>, -} - -fn constraint_deserializer<'de, D>( - deserializer: D, -) -> Result; 3]>, D::Error> -where - D: serde::Deserializer<'de>, -{ - let raw_constraints: Vec<[HashMap; 3]> = Deserialize::deserialize(deserializer)?; - - Ok(raw_constraints - .into_iter() - .map(|[a, b, c]| { - [ - a.into_iter() - .map(|(k, v)| (k, circom_str_to_lambda_field_element(v))) - .collect(), - b.into_iter() - .map(|(k, v)| (k, circom_str_to_lambda_field_element(v))) - .collect(), - c.into_iter() - .map(|(k, v)| (k, circom_str_to_lambda_field_element(v))) - .collect(), - ] - }) - .collect()) -} - -/// Reads and parses an R1CS file at the given path. -#[inline] -pub fn read_circom_r1cs(path: impl AsRef) -> Result { - let file = File::open(path).map_err(CircomReaderError::IOError)?; - serde_json::from_reader(BufReader::new(file)).map_err(CircomReaderError::SerdeError) -} - -/// Reads and parses a witness file at the given path. -#[inline] -pub fn read_circom_witness(path: impl AsRef) -> Result { - let file = File::open(path).map_err(CircomReaderError::IOError)?; - let wtns: Vec = - serde_json::from_reader(BufReader::new(file)).map_err(CircomReaderError::SerdeError)?; - - Ok(wtns - .into_iter() - .map(circom_str_to_lambda_field_element) - .collect()) -} - -/// Converts a string Circom field element to Lambda field element. -/// -/// The Circom field element can be in either decimal or hexadecimal format; -/// otherwise, this function will panic. -#[inline] -fn circom_str_to_lambda_field_element(value: impl AsRef) -> FrElement { - let value = value.as_ref(); - - if let Ok(big_uint) = UnsignedInteger::<4>::from_dec_str(value) { - FrElement::from(&big_uint) - } else if let Ok(big_uint) = UnsignedInteger::<4>::from_hex(value) { - FrElement::from(&big_uint) - } else { - panic!("Could not parse field element from string: {value}"); - } -} diff --git a/crates/provers/groth16/circom-adapter/tests/poseidon/test.r1cs.json b/crates/provers/groth16/circom-adapter/tests/poseidon/test.r1cs.json deleted file mode 100644 index 693a6d2e1..000000000 --- a/crates/provers/groth16/circom-adapter/tests/poseidon/test.r1cs.json +++ /dev/null @@ -1,4512 +0,0 @@ -{ - "n8": 32, - "prime": "52435875175126190479447740508185965837690552500527637822603658699938581184513", - "nVars": 215, - "nOutputs": 1, - "nPubInputs": 0, - "nPrvInputs": 1, - "nLabels": 583, - "nConstraints": 213, - "useCustomGates": false, - "constraints": [ - [ - { - "0": "47002224662166672867131413033472899870931743636313811084027392038215058404480", - "2": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - }, - { - "0": "5433650512959517612316327474713065966758808864213826738576266661723522780033", - "2": "1" - }, - { - "73": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - } - ], - [ - { - "73": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - }, - { - "73": "1" - }, - { - "74": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - } - ], - [ - { - "74": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - }, - { - "0": "5433650512959517612316327474713065966758808864213826738576266661723522780033", - "2": "1" - }, - { - "0": "17301668213014761646596552653015591039760461391617952799153519234591864034463", - "3": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - } - ], - [ - { - "0": "18121914352619992825529298674955760274687060138678729211531310305112441479676", - "3": "32708508311735022941325600146712381710542921827904536994669574389708558585369" - }, - { - "0": "34313960822506197653918441833230205563003492361848908611072348394826139704837", - "3": "19727366863391167538122140361473584127147630672623100827934084310230022599144" - }, - { - "75": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - } - ], - [ - { - "75": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - }, - { - "75": "1" - }, - { - "76": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - } - ], - [ - { - "76": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - }, - { - "0": "34313960822506197653918441833230205563003492361848908611072348394826139704837", - "3": "19727366863391167538122140361473584127147630672623100827934084310230022599144" - }, - { - "0": "19776162473771124504636460540005465934623588388516917789964545078054814040751", - "4": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - } - ], - [ - { - "0": "39641237770587972878319552685981320289244633020922775863749204380887651483675", - "3": "44087700254192067928964146508732085830934444379186570650215212783609639205945" - }, - { - "0": "12794637404538217601128187822204645548445919479604861958854454319050929700838", - "3": "8348174920934122550483593999453880006756108121341067172388445916328941978568" - }, - { - "77": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - } - ], - [ - { - "77": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - }, - { - "77": "1" - }, - { - "78": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - } - ], - [ - { - "78": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - }, - { - "0": "12794637404538217601128187822204645548445919479604861958854454319050929700838", - "3": "8348174920934122550483593999453880006756108121341067172388445916328941978568" - }, - { - "0": "11316768622745143833150712249439306411966930091146927416794626237074092972974", - "5": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - } - ], - [ - { - "4": "49525108357280539459569165668684164497620522385376616561300824389215851676972", - "5": "32708508311735022941325600146712381710542921827904536994669574389708558585369" - }, - { - "4": "2910766817845651019878574839501801340070030115151021261302834310722729507541", - "5": "19727366863391167538122140361473584127147630672623100827934084310230022599144" - }, - { - "79": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - } - ], - [ - { - "79": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - }, - { - "79": "1" - }, - { - "80": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - } - ], - [ - { - "80": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - }, - { - "4": "2910766817845651019878574839501801340070030115151021261302834310722729507541", - "5": "19727366863391167538122140361473584127147630672623100827934084310230022599144" - }, - { - "0": "16445483823163065295596987123801926703922762982696126295152762759513382273866", - "6": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - } - ], - [ - { - "4": "46659190381000641016999143094135733593911872198348198329939611371656852828168", - "5": "44087700254192067928964146508732085830934444379186570650215212783609639205945" - }, - { - "4": "5776684794125549462448597414050232243778680302179439492664047328281728356345", - "5": "8348174920934122550483593999453880006756108121341067172388445916328941978568" - }, - { - "81": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - } - ], - [ - { - "81": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - }, - { - "81": "1" - }, - { - "82": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - } - ], - [ - { - "82": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - }, - { - "4": "5776684794125549462448597414050232243778680302179439492664047328281728356345", - "5": "8348174920934122550483593999453880006756108121341067172388445916328941978568" - }, - { - "0": "4514474156763384993737907502541146539969481658202448264361041161669007485071", - "7": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - } - ], - [ - { - "6": "49525108357280539459569165668684164497620522385376616561300824389215851676972", - "7": "32708508311735022941325600146712381710542921827904536994669574389708558585369" - }, - { - "6": "2910766817845651019878574839501801340070030115151021261302834310722729507541", - "7": "19727366863391167538122140361473584127147630672623100827934084310230022599144" - }, - { - "83": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - } - ], - [ - { - "83": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - }, - { - "83": "1" - }, - { - "84": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - } - ], - [ - { - "84": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - }, - { - "6": "2910766817845651019878574839501801340070030115151021261302834310722729507541", - "7": "19727366863391167538122140361473584127147630672623100827934084310230022599144" - }, - { - "0": "15962945726901666037200703277090943625692830090147479512076829448913210994056", - "8": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - } - ], - [ - { - "6": "46659190381000641016999143094135733593911872198348198329939611371656852828168", - "7": "44087700254192067928964146508732085830934444379186570650215212783609639205945" - }, - { - "6": "5776684794125549462448597414050232243778680302179439492664047328281728356345", - "7": "8348174920934122550483593999453880006756108121341067172388445916328941978568" - }, - { - "85": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - } - ], - [ - { - "85": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - }, - { - "85": "1" - }, - { - "86": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - } - ], - [ - { - "86": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - }, - { - "6": "5776684794125549462448597414050232243778680302179439492664047328281728356345", - "7": "8348174920934122550483593999453880006756108121341067172388445916328941978568" - }, - { - "0": "887718591790650017281197227986729839639624303500401204697133087008458682956", - "9": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - } - ], - [ - { - "72": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - }, - { - "72": "1" - }, - { - "87": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - } - ], - [ - { - "87": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - }, - { - "87": "1" - }, - { - "88": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - } - ], - [ - { - "88": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - }, - { - "72": "1" - }, - { - "0": "12275810531539430738053742281045622394839331044128025660443842052759513336728", - "10": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - } - ], - [ - { - "8": "5192654897427357008432485609548618383439366938048738267512357806175323156466", - "9": "44191900748914651603154436884985405802624310922043415177054320835582811318799", - "17": "16682593524536877201531712829085670433517609722011452244873718259799228348094", - "18": "43176292574239827709965913132349833968585313120748753931461671119571506050120", - "19": "22454277736302330539981016565441139619576134744538169566477933175530787940739", - "20": "30123729579747296778679082467374526570498598882963001935435199420320161942256", - "21": "46568232823494654032688015031064077399377245709898471396748713760514442211391", - "22": "25337516245609128609822543248097808410082403769697789578791662343706028102192", - "23": "35601550524008744029282673352847160429101650733862130894197735477082149245871", - "24": "35137801412648608602082879801594064027148126627191773745053156849660147041891", - "25": "40398626151690458309263530700753346533191711706310534404013923450891362313020", - "26": "49254854458955331381907192669226021831064520795257317826598691575112032093544", - "27": "34654188339918270879861929787619795508384021635481876666056120963255152633433", - "28": "494391027058814481580993061254426732497840799120447570371498432602819883101", - "29": "44946201903288700296658382468835869814262726797502836774418588029043176260447", - "30": "48528295336382606069489647204400489577673347693131220253265802156768701700347", - "31": "31360798183006332565597117389323951060666556605711330882110548035101473192155", - "32": "14199984020335272086112293703616407891113291629004800921837716240396770592817", - "33": "22499065136731618754150770882412842216746313195882824776123236056140771071854", - "34": "52217880981612453390038597745102175942273063793798860889994274578587614827152", - "35": "11537655746854260565095799304447329691170321378827272307394507431534802009593", - "36": "4976638099565858473647778905610272835781322907363722016885461315219100861322", - "37": "4108392090303695195493660099979157995992377166454976378640241409951861734299", - "38": "15653641529872622376681823131135518307232703072159041211329697102310448663115", - "39": "14800899020251847241232019228031646644613998640250076359556728567450119111044", - "40": "44463855465583845034168653476990587477897453789614831858855295629714808931485", - "41": "10017085478120126963175632015936019718784225748796618639521456603694580582439", - "42": "5964435497664887447234757603860015773037854172250406422904388839641102056810", - "43": "5437229115590008548073876339184123793065964842700306165967098364197229809109", - "44": "31761090498660381152590868148703047507119657770368107945038318332745129332080", - "45": "52164753007942648654126677065408014405462714011956435929334483881356324845985", - "46": "8491974724559101596776574308942970972383695354753462027230993760828778482293", - "47": "46151185706456268294809352088665486118335041496311606337323937958920678053636", - "48": "26163285151599097458896485665061693812858393921677914525792777641618531124982", - "49": "20680354245047335378058215136376195768406919946856627631309921694439969779746", - "50": "17670284351817699338614554067914420616735028118279118466474981361397453000395", - "51": "48726527588140482709743231003634452131141550449721745648864847320882060170540", - "52": "1855745755075703151059428086310382931309105627860435429284103108472625282784", - "53": "27754302890119318476717015603950964862285802702681699302181928196592129086473", - "54": "49105981729716423446324504208277584360266771134924957425516715062055227989458", - "55": "2431220470210283703128932870876730811953054921751866995952505982901254332105", - "56": "17241810903661829527159007203551354915356058054897363299778832770398291954875", - "57": "5506609965393505601379869526086751832292859950316561376217538900402655037296", - "58": "35055291447473077149257142781429556079981892410110587627461981785180960067866", - "59": "27099544121785566071163804026757738834751009441787367088902224089246104816217", - "60": "21168488962979702344414435570748821232449040113325022888730742258043507028405", - "61": "47670895780436930537707698149600870156430827387591564803827756947402825569930", - "62": "2796943212009229214673244125547912349110865240615935726835249132294761701941", - "63": "7918403660682730431927324352637792984964501341438814631906703645998903792014", - "64": "32683555564922264902143485015196964126056753474964765939073261049223026516501", - "65": "13416403507941528912250512177702720074184965506688177765950834406754453210568", - "66": "38787375715048970217010351389657525454533757158251583363414573878811191758526", - "67": "46406132564233381384920233542963075657760887680299224559198572377706144536577", - "68": "30201327076396983193347672435573825141534190996210192641635524573209002434330", - "69": "38470913765980546510790377478989823683444782719827572704647142504929737797128", - "70": "8710830633971736968251063972026569291314211937138214596925867734113744983382", - "71": "51635308733343782767757203821132158717542520956470118440081593984226660505683", - "72": "35111823997307573390460048516833474775712674889514381017874831449584379582888" - }, - { - "8": "47243220277698833471015254898637347454251185562478899555091300893763258028047", - "9": "8243974426211538876293303623200560035066241578484222645549337864355769865714", - "17": "35753281650589313277916027679100295404172942778516185577729940440139352836419", - "18": "9259582600886362769481827375836131869105239379778883891141987580367075134393", - "19": "29981597438823859939466723942744826218114417755989468256125725524407793243774", - "20": "22312145595378893700768658040811439267191953617564635887168459279618419242257", - "21": "5867642351631536446759725477121888438313306790629166425854944939424138973122", - "22": "27098358929517061869625197260088157427608148730829848243811996356232553082321", - "23": "16834324651117446450165067155338805408588901766665506928405923222856431938642", - "24": "17298073762477581877364860706591901810542425873335864077550501850278434142622", - "25": "12037249023435732170184209807432619304498840794217103418589735249047218871493", - "26": "3181020716170859097540547838959944006626031705270319996004967124826549090969", - "27": "17781686835207919599585810720566170329306530865045761156547537736683428551080", - "28": "51941484148067375997866747446931539105192711701407190252232160267335761301412", - "29": "7489673271837490182789358039350096023427825703024801048185070670895404924066", - "30": "3907579838743584409958093303785476260017204807396417569337856543169879484166", - "31": "21075076992119857913850623118862014777023995894816306940493110664837107992358", - "32": "38235891154790918393335446804569557946577260871522836900765942459541810591696", - "33": "29936810038394571725296969625773123620944239304644813046480422643797810112659", - "34": "217994193513737089409142763083789895417488706728776932609384121350966357361", - "35": "40898219428271929914351941203738636146520231121700365515209151268403779174920", - "36": "47459237075560332005799961602575693001909229593163915805718197384719480323191", - "37": "48327483084822495283954080408206807841698175334072661443963417289986719450214", - "38": "36782233645253568102765917377050447530457849428368596611273961597628132521398", - "39": "37634976154874343238215721280154319193076553860277561463046930132488462073469", - "40": "7972019709542345445279087031195378359793098710912805963748363070223772253028", - "41": "42418789697006063516272108492249946118906326751731019183082202096244000602074", - "42": "46471439677461303032212982904325950064652698328277231399699269860297479127703", - "43": "46998646059536181931373864169001842044624587657827331656636560335741351375404", - "44": "20674784676465809326856872359482918330570894730159529877565340367193451852433", - "45": "271122167183541825321063442777951432227838488571201893269174818582256338528", - "46": "43943900450567088882671166199242994865306857145774175795372664939109802702220", - "47": "6284689468669922184638388419520479719355511004216031485279720741017903130877", - "48": "26272590023527093020551254843124272024832158578849723296810881058320050059531", - "49": "31755520930078855101389525371809770069283632553671010191293737005498611404767", - "50": "34765590823308491140833186440271545220955524382248519356128677338541128184118", - "51": "3709347586985707769704509504551513706549002050805892173738811379056521013973", - "52": "50580129420050487328388312421875582906381446872667202393319555591465955901729", - "53": "24681572285006872002730724904235000975404749797845938520421730503346452098040", - "54": "3329893445409767033123236299908381477423781365602680397086943637883353195055", - "55": "50004654704915906776318807637309235025737497578775770826651152717037326852408", - "56": "35194064271464360952288733304634610922334494445630274522824825929540289229638", - "57": "46929265209732684878067870982099214005397692550211076446386119799535926147217", - "58": "17380583727653113330190597726756409757708660090417050195141676914757621116647", - "59": "25336331053340624408283936481428227002939543058740270733701434610692476368296", - "60": "31267386212146488135033304937437144605241512387202614933872916441895074156108", - "61": "4764979394689259941740042358585095681259725112936073018775901752535755614583", - "62": "49638931963116961264774496382638053488579687259911702095768409567643819482572", - "63": "44517471514443460047520416155548172852726051159088823190696955053939677392499", - "64": "19752319610203925577304255492989001711633799025562871883530397650715554668012", - "65": "39019471667184661567197228330483245763505586993839460056652824293184127973945", - "66": "13648499460077220262437389118528440383156795342276054459189084821127389425987", - "67": "6029742610892809094527506965222890179929664820228413263405086322232436647936", - "68": "22234548098729207286100068072612140696156361504317445180968134126729578750183", - "69": "13964961409145643968657363029196142154245769780700065117956516195008843387385", - "70": "43725044541154453511196676536159396546376340563389423225677790965824836201131", - "71": "800566441782407711690536687053807120148031544057519382522064715711920678830", - "72": "17324051177818617088987691991352491061977877611013256804728827250354201601625" - }, - { - "89": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - } - ], - [ - { - "89": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - }, - { - "89": "1" - }, - { - "90": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - } - ], - [ - { - "90": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - }, - { - "8": "47243220277698833471015254898637347454251185562478899555091300893763258028047", - "9": "8243974426211538876293303623200560035066241578484222645549337864355769865714", - "17": "35753281650589313277916027679100295404172942778516185577729940440139352836419", - "18": "9259582600886362769481827375836131869105239379778883891141987580367075134393", - "19": "29981597438823859939466723942744826218114417755989468256125725524407793243774", - "20": "22312145595378893700768658040811439267191953617564635887168459279618419242257", - "21": "5867642351631536446759725477121888438313306790629166425854944939424138973122", - "22": "27098358929517061869625197260088157427608148730829848243811996356232553082321", - "23": "16834324651117446450165067155338805408588901766665506928405923222856431938642", - "24": "17298073762477581877364860706591901810542425873335864077550501850278434142622", - "25": "12037249023435732170184209807432619304498840794217103418589735249047218871493", - "26": "3181020716170859097540547838959944006626031705270319996004967124826549090969", - "27": "17781686835207919599585810720566170329306530865045761156547537736683428551080", - "28": "51941484148067375997866747446931539105192711701407190252232160267335761301412", - "29": "7489673271837490182789358039350096023427825703024801048185070670895404924066", - "30": "3907579838743584409958093303785476260017204807396417569337856543169879484166", - "31": "21075076992119857913850623118862014777023995894816306940493110664837107992358", - "32": "38235891154790918393335446804569557946577260871522836900765942459541810591696", - "33": "29936810038394571725296969625773123620944239304644813046480422643797810112659", - "34": "217994193513737089409142763083789895417488706728776932609384121350966357361", - "35": "40898219428271929914351941203738636146520231121700365515209151268403779174920", - "36": "47459237075560332005799961602575693001909229593163915805718197384719480323191", - "37": "48327483084822495283954080408206807841698175334072661443963417289986719450214", - "38": "36782233645253568102765917377050447530457849428368596611273961597628132521398", - "39": "37634976154874343238215721280154319193076553860277561463046930132488462073469", - "40": "7972019709542345445279087031195378359793098710912805963748363070223772253028", - "41": "42418789697006063516272108492249946118906326751731019183082202096244000602074", - "42": "46471439677461303032212982904325950064652698328277231399699269860297479127703", - "43": "46998646059536181931373864169001842044624587657827331656636560335741351375404", - "44": "20674784676465809326856872359482918330570894730159529877565340367193451852433", - "45": "271122167183541825321063442777951432227838488571201893269174818582256338528", - "46": "43943900450567088882671166199242994865306857145774175795372664939109802702220", - "47": "6284689468669922184638388419520479719355511004216031485279720741017903130877", - "48": "26272590023527093020551254843124272024832158578849723296810881058320050059531", - "49": "31755520930078855101389525371809770069283632553671010191293737005498611404767", - "50": "34765590823308491140833186440271545220955524382248519356128677338541128184118", - "51": "3709347586985707769704509504551513706549002050805892173738811379056521013973", - "52": "50580129420050487328388312421875582906381446872667202393319555591465955901729", - "53": "24681572285006872002730724904235000975404749797845938520421730503346452098040", - "54": "3329893445409767033123236299908381477423781365602680397086943637883353195055", - "55": "50004654704915906776318807637309235025737497578775770826651152717037326852408", - "56": "35194064271464360952288733304634610922334494445630274522824825929540289229638", - "57": "46929265209732684878067870982099214005397692550211076446386119799535926147217", - "58": "17380583727653113330190597726756409757708660090417050195141676914757621116647", - "59": "25336331053340624408283936481428227002939543058740270733701434610692476368296", - "60": "31267386212146488135033304937437144605241512387202614933872916441895074156108", - "61": "4764979394689259941740042358585095681259725112936073018775901752535755614583", - "62": "49638931963116961264774496382638053488579687259911702095768409567643819482572", - "63": "44517471514443460047520416155548172852726051159088823190696955053939677392499", - "64": "19752319610203925577304255492989001711633799025562871883530397650715554668012", - "65": "39019471667184661567197228330483245763505586993839460056652824293184127973945", - "66": "13648499460077220262437389118528440383156795342276054459189084821127389425987", - "67": "6029742610892809094527506965222890179929664820228413263405086322232436647936", - "68": "22234548098729207286100068072612140696156361504317445180968134126729578750183", - "69": "13964961409145643968657363029196142154245769780700065117956516195008843387385", - "70": "43725044541154453511196676536159396546376340563389423225677790965824836201131", - "71": "800566441782407711690536687053807120148031544057519382522064715711920678830", - "72": "17324051177818617088987691991352491061977877611013256804728827250354201601625" - }, - { - "0": "21143091624051898429049779202777783090616631078773674742631481136382890245166", - "11": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - } - ], - [ - { - "10": "49525108357280539459569165668684164497620522385376616561300824389215851676972", - "11": "32708508311735022941325600146712381710542921827904536994669574389708558585369" - }, - { - "10": "2910766817845651019878574839501801340070030115151021261302834310722729507541", - "11": "19727366863391167538122140361473584127147630672623100827934084310230022599144" - }, - { - "91": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - } - ], - [ - { - "91": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - }, - { - "91": "1" - }, - { - "92": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - } - ], - [ - { - "92": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - }, - { - "10": "2910766817845651019878574839501801340070030115151021261302834310722729507541", - "11": "19727366863391167538122140361473584127147630672623100827934084310230022599144" - }, - { - "0": "10171452642243387955781526332645193123346266850900972162093561209595357586423", - "12": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - } - ], - [ - { - "10": "46659190381000641016999143094135733593911872198348198329939611371656852828168", - "11": "44087700254192067928964146508732085830934444379186570650215212783609639205945" - }, - { - "10": "5776684794125549462448597414050232243778680302179439492664047328281728356345", - "11": "8348174920934122550483593999453880006756108121341067172388445916328941978568" - }, - { - "93": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - } - ], - [ - { - "93": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - }, - { - "93": "1" - }, - { - "94": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - } - ], - [ - { - "94": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - }, - { - "10": "5776684794125549462448597414050232243778680302179439492664047328281728356345", - "11": "8348174920934122550483593999453880006756108121341067172388445916328941978568" - }, - { - "0": "3855895713134056811998624598511873826580243324673003091901619068671473231435", - "13": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - } - ], - [ - { - "12": "49525108357280539459569165668684164497620522385376616561300824389215851676972", - "13": "32708508311735022941325600146712381710542921827904536994669574389708558585369" - }, - { - "12": "2910766817845651019878574839501801340070030115151021261302834310722729507541", - "13": "19727366863391167538122140361473584127147630672623100827934084310230022599144" - }, - { - "95": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - } - ], - [ - { - "95": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - }, - { - "95": "1" - }, - { - "96": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - } - ], - [ - { - "96": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - }, - { - "12": "2910766817845651019878574839501801340070030115151021261302834310722729507541", - "13": "19727366863391167538122140361473584127147630672623100827934084310230022599144" - }, - { - "0": "13528768565745931233460851026527843568952293200662922221190432010518079703355", - "14": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - } - ], - [ - { - "12": "46659190381000641016999143094135733593911872198348198329939611371656852828168", - "13": "44087700254192067928964146508732085830934444379186570650215212783609639205945" - }, - { - "12": "5776684794125549462448597414050232243778680302179439492664047328281728356345", - "13": "8348174920934122550483593999453880006756108121341067172388445916328941978568" - }, - { - "97": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - } - ], - [ - { - "97": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - }, - { - "97": "1" - }, - { - "98": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - } - ], - [ - { - "98": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - }, - { - "12": "5776684794125549462448597414050232243778680302179439492664047328281728356345", - "13": "8348174920934122550483593999453880006756108121341067172388445916328941978568" - }, - { - "0": "6443318563434187156482979037576690662213493094314678699384407936675395356263", - "15": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - } - ], - [ - { - "14": "49525108357280539459569165668684164497620522385376616561300824389215851676972", - "15": "32708508311735022941325600146712381710542921827904536994669574389708558585369" - }, - { - "14": "2910766817845651019878574839501801340070030115151021261302834310722729507541", - "15": "19727366863391167538122140361473584127147630672623100827934084310230022599144" - }, - { - "99": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - } - ], - [ - { - "99": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - }, - { - "99": "1" - }, - { - "100": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - } - ], - [ - { - "100": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - }, - { - "14": "2910766817845651019878574839501801340070030115151021261302834310722729507541", - "15": "19727366863391167538122140361473584127147630672623100827934084310230022599144" - }, - { - "16": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - } - ], - [ - { - "14": "46659190381000641016999143094135733593911872198348198329939611371656852828168", - "15": "44087700254192067928964146508732085830934444379186570650215212783609639205945" - }, - { - "14": "5776684794125549462448597414050232243778680302179439492664047328281728356345", - "15": "8348174920934122550483593999453880006756108121341067172388445916328941978568" - }, - { - "101": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - } - ], - [ - { - "101": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - }, - { - "101": "1" - }, - { - "102": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - } - ], - [ - { - "102": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - }, - { - "14": "5776684794125549462448597414050232243778680302179439492664047328281728356345", - "15": "8348174920934122550483593999453880006756108121341067172388445916328941978568" - }, - { - "1": "18391027390011658052434319788313832985258800726793609915693141529846616218392", - "16": "39976218779278093542861309592985538942815568401541314690583304734527154574303" - } - ], - [ - { - "8": "49525108357280539459569165668684164497620522385376616561300824389215851676972", - "9": "32708508311735022941325600146712381710542921827904536994669574389708558585369" - }, - { - "8": "2910766817845651019878574839501801340070030115151021261302834310722729507541", - "9": "19727366863391167538122140361473584127147630672623100827934084310230022599144" - }, - { - "103": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - } - ], - [ - { - "103": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - }, - { - "103": "1" - }, - { - "104": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - } - ], - [ - { - "104": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - }, - { - "8": "2910766817845651019878574839501801340070030115151021261302834310722729507541", - "9": "19727366863391167538122140361473584127147630672623100827934084310230022599144" - }, - { - "0": "12274216425815286338344348482336276117995066724093487299512262174649563976186", - "8": "523275527750387873590246815039557098366775355168517172710947678319526243896", - "9": "29863921153103613859282946949144467702712859644865163165806334700261602539917", - "17": "43286615009455031784719797295956349308039236872337887241869415538128324649873" - } - ], - [ - { - "17": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - }, - { - "17": "1" - }, - { - "105": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - } - ], - [ - { - "105": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - }, - { - "105": "1" - }, - { - "106": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - } - ], - [ - { - "106": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - }, - { - "17": "1" - }, - { - "0": "1050758930252644049914605206403427631313247657935800041327915318465466974783", - "8": "14647793870698006663788078479891969955203674662705784514362579274748145255342", - "9": "44013170922976527852837802860940497245253994239591664654910384441309179470950", - "17": "44618527102236044714623041959177328003816642867354995233005889480954713736162", - "18": "43286615009455031784719797295956349308039236872337887241869415538128324649873" - } - ], - [ - { - "18": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - }, - { - "18": "1" - }, - { - "107": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - } - ], - [ - { - "107": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - }, - { - "107": "1" - }, - { - "108": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - } - ], - [ - { - "108": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - }, - { - "18": "1" - }, - { - "0": "10607585076226348745183629788245008577438579576330359487986117688517235408881", - "8": "44086260898869827729461696793073698695171266839028803560440724451968694562878", - "9": "45609318636240826618104404231943273075745914428284787965153199837375146018682", - "17": "34689122506791715776071270321964384164520116559785394498199709612928793950519", - "18": "20500302356120126646304935893371217543220209172169789961563086856428115597248", - "19": "43286615009455031784719797295956349308039236872337887241869415538128324649873" - } - ], - [ - { - "19": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - }, - { - "19": "1" - }, - { - "109": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - } - ], - [ - { - "109": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - }, - { - "109": "1" - }, - { - "110": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - } - ], - [ - { - "110": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - }, - { - "19": "1" - }, - { - "0": "20509009694313778489858884111978287368685508463213168251197030962719682409205", - "8": "12607992335172491240600546551739327470767846536047868017918750947802282287335", - "9": "31066938295670410723054688528383889966878843405893792865037517011432310377109", - "17": "28444766044878297851939071031958436774401690490685803415980417810351285288175", - "18": "46992068175337004244612270242884504024052307296766029016148008260534360879369", - "19": "30998169063668865873788972002963240699852520757570395930308332452580097054007", - "20": "43286615009455031784719797295956349308039236872337887241869415538128324649873" - } - ], - [ - { - "20": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - }, - { - "20": "1" - }, - { - "111": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - } - ], - [ - { - "111": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - }, - { - "111": "1" - }, - { - "112": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - } - ], - [ - { - "112": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - }, - { - "20": "1" - }, - { - "0": "4563680725198793251172562411623420668869330885202539013185566348548255071635", - "8": "23438669612988967744394891095824517659633044895292482524073888033526829516987", - "9": "27332645648899331453309944209892012570458875279051683414008536707518527791934", - "17": "12756820937250441106229187825805449016933357428518379195050618891473007065071", - "18": "13558789015118527713957930658666130417146533292655698478770748946628830945170", - "19": "133745349276713264300556318422135592110994373519420655612344503638515360841", - "20": "37362060064797730184705915714627896388964928616834195472855686100461021030130", - "21": "43286615009455031784719797295956349308039236872337887241869415538128324649873" - } - ], - [ - { - "21": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - }, - { - "21": "1" - }, - { - "113": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - } - ], - [ - { - "113": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - }, - { - "113": "1" - }, - { - "114": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - } - ], - [ - { - "114": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - }, - { - "21": "1" - }, - { - "0": "10132793020925051358967312895903080509340240497490871556356344077873835967889", - "8": "52054038910215594550350530851443290570393185222311899738807668115885166480748", - "9": "21931046423993759091296806349383041916752643429514287099782135231963611419871", - "17": "38509896658554346735701809271368460008849036093648740709803418559671241746918", - "18": "10404084851932792103272289385895210754252900162091158424484943133514023472134", - "19": "44197672073455331141053970220012513759520245384805484238044711836794287013028", - "20": "19111610154350099526360715550060897883235808614222370357672419532172792868954", - "21": "49197749829697731916730220414012305162239148718896561097266732704587314877562", - "22": "43286615009455031784719797295956349308039236872337887241869415538128324649873" - } - ], - [ - { - "22": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - }, - { - "22": "1" - }, - { - "115": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - } - ], - [ - { - "115": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - }, - { - "115": "1" - }, - { - "116": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - } - ], - [ - { - "116": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - }, - { - "22": "1" - }, - { - "0": "2647212931513679432767054030363504863222540192567519779190979594289737202550", - "8": "38265739260659547545823878167336804938380457069528607389540990840187913625048", - "9": "10977316254521966142079585524801037072517021878534582944038349171363363010424", - "17": "24670164380422293972563714110452662424227945793854522951291921694212727913657", - "18": "13428317419311691864066108855224782671524209281113452406120194086612645933003", - "19": "1306729606245615638105902628288325102878471788441872534753886762054758749285", - "20": "42459919049932035097624789991134573942266120535137515909226761181108272964659", - "21": "25996860871921503911544082043813396243054833166873924304909577117740333819085", - "22": "47042174649050522390501748178762639544653448991838551088262477161887058505704", - "23": "43286615009455031784719797295956349308039236872337887241869415538128324649873" - } - ], - [ - { - "23": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - }, - { - "23": "1" - }, - { - "117": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - } - ], - [ - { - "117": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - }, - { - "117": "1" - }, - { - "118": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - } - ], - [ - { - "118": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - }, - { - "23": "1" - }, - { - "0": "4359886051856780224292971980741406425492649906224296730054335077893394909530", - "8": "25049770149452949374467751744953832674825149247215267735945993077948422051037", - "9": "38513659637705165414771786174460269397288080548044442743384172534373206325937", - "17": "52122341676622476698989697463907931856383406157792658408512797620781224506873", - "18": "9839492682569537610819765512504521947628538257548325302727677139273265827420", - "19": "18655316350694565641464950746239354029108944720339362036628705957794260437160", - "20": "13600593241380128370270779462252518827942753136846635056083174406395864726952", - "21": "46866975249348385204996497054439891787355641311250052516740548639217601140643", - "22": "18300223650325421706116599425722186640066050378465269788761004957205339296481", - "23": "1108292025407625726051905408276804397965195841607222412643343962994880725416", - "24": "43286615009455031784719797295956349308039236872337887241869415538128324649873" - } - ], - [ - { - "24": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - }, - { - "24": "1" - }, - { - "119": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - } - ], - [ - { - "119": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - }, - { - "119": "1" - }, - { - "120": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - } - ], - [ - { - "120": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - }, - { - "24": "1" - }, - { - "0": "3054847578866604975257033821021111653343035155943751913629683293137065347688", - "8": "1571814154268717365911099353068294390185480398602746730473175244831011913749", - "9": "40944618482791468333288891986527612484118332155090636872070947106473911680823", - "17": "26544098650097602827530483799155511251841273545311750035204314985181742249889", - "18": "47789569197925979394558652231068257823414656437532033330332972750615121224973", - "19": "11766554378928597063804608229791574923485243610554158815715421873522134958138", - "20": "32488380112214489255202140173040041641682802662141975501481890469994212878140", - "21": "43944567479141735733084647695553977221103054591511189509433824240395745824912", - "22": "32142445010587398401940223855151394061997208110235665894288758048966534773810", - "23": "19708456662923868506331135265189148400623717017138247198937389688645833119818", - "24": "8710199628819774376887170190660500058809235533705819821767327029930309306060", - "25": "43286615009455031784719797295956349308039236872337887241869415538128324649873" - } - ], - [ - { - "25": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - }, - { - "25": "1" - }, - { - "121": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - } - ], - [ - { - "121": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - }, - { - "121": "1" - }, - { - "122": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - } - ], - [ - { - "122": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - }, - { - "25": "1" - }, - { - "0": "1596959724864208462318973909411993812742702283705404297188991879075781032285", - "8": "597586323079623342021154412625898051176897679340023864083676874051668511934", - "9": "7856192806994717731873545182397730487544363372736958983631501652418019505935", - "17": "50264507679943839576798439730596962159743072300230863395483751916788544964520", - "18": "34744287446837256340485864020734126794481742487713181744756072870918865035221", - "19": "31536751286981380314003196150422144896562966034030808672985938732266457747648", - "20": "7011128262576212572413596762500166619168819400050749490847811724813985822562", - "21": "34393510166785394575647698404563761511600361745324455861299096957522581151882", - "22": "2539820945593229936033516859775882239362549663900519549445893129215823354926", - "23": "31830160268950011988415843485889916909435506750195255746749797547385071335706", - "24": "26163545989402538687163581171383833840715572972843592035966208808229216185106", - "25": "28655945309801649476497566762368779734248351654321856351044823474760502562791", - "26": "43286615009455031784719797295956349308039236872337887241869415538128324649873" - } - ], - [ - { - "26": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - }, - { - "26": "1" - }, - { - "123": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - } - ], - [ - { - "123": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - }, - { - "123": "1" - }, - { - "124": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - } - ], - [ - { - "124": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - }, - { - "26": "1" - }, - { - "0": "9261998448432672939016143689737142959950360294646800435636112732010430895748", - "8": "29187660716279070466787210448535577009279255682815140908027674703152603353097", - "9": "34127721494573460562626390511713334078644561604752937144809917178660613681772", - "17": "35579634117589868096465119861802472339972768591256890404363690519067366418412", - "18": "11775947800468834411936124909313979868489795258570487214954130629687446250427", - "19": "26757645007250378196609692159012133165990312145599643459296878881105757930181", - "20": "27665572361398849436113159412832395956213860747776629163045867949595935146953", - "21": "8605886056650093142247183627154593698747475493192517229640802595075646339512", - "22": "11827601560949004328045691063904382330623155862780950862428796047012317979804", - "23": "17140429297242487508373023503976741370501964850620935389018442218477773274633", - "24": "19197190215986596441799021031587930506996378487446902302730638321380074945023", - "25": "40294901855215798913817067541369435026574706677301803503482667829096473569933", - "26": "4428148813778707766160807786538068971023325871880931422496658130186965151085", - "27": "43286615009455031784719797295956349308039236872337887241869415538128324649873" - } - ], - [ - { - "27": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - }, - { - "27": "1" - }, - { - "125": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - } - ], - [ - { - "125": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - }, - { - "125": "1" - }, - { - "126": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - } - ], - [ - { - "126": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - }, - { - "27": "1" - }, - { - "0": "21811458719499960186771214587366397959723618323848154217660010569954685810715", - "8": "34401610499586966881619461335030210072479838401278499810788565707787185255455", - "9": "51165492728394242983663379841727284624200596842151612659217220816743963203538", - "17": "7957160447300388165441996578337445694734798230657804121629037002580485804588", - "18": "3112296533843222836687174985460829512830498821936912945097238271636899007951", - "19": "29763270315477331916450274239517016674168147839226194069123460836065020823172", - "20": "24045372628603965531640755160668422831271597757979033486858224704815861441342", - "21": "32656267590793163554518363289836788062436883137510286552200807360270842252201", - "22": "43240951980381493526299749711619611481213013101865296211337817937016731143926", - "23": "24343674894502055927091066748288339464015196069594062903545488680302215556884", - "24": "27993811030819184317552970940800960241497363672173730398321464726334293958097", - "25": "22152655807744473877336354872503231583082965085782926063629967178853133089546", - "26": "21983049277506886158038474252140360350322964821609907598656506573318739437848", - "27": "21453047890104560453118921752650875921831568987001062676099051267369559909495", - "28": "43286615009455031784719797295956349308039236872337887241869415538128324649873" - } - ], - [ - { - "28": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - }, - { - "28": "1" - }, - { - "127": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - } - ], - [ - { - "127": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - }, - { - "127": "1" - }, - { - "128": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - } - ], - [ - { - "128": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - }, - { - "28": "1" - }, - { - "0": "3239076967784329489572444293576130919094218653342995300018245457598795548917", - "8": "21029159653293094852701089505851760414211005952802290736984732909843171386992", - "9": "6099653689013781554444846056378306891303416933883925215025255810832583578247", - "17": "14966059711859970165987342015410695006396269680549529458279318356656676092078", - "18": "11669683921054668546877318202199638349464737070388825019929787329418089991121", - "19": "22936347808378387795366137433807865747318828653532980672336704756330391161390", - "20": "28520175517985735849599910603135742858821364584281742893610499563740602141349", - "21": "1972317795414592831264440063142528198019547091539219716133346020110636874516", - "22": "44725062420632732927700594162692920449497607746903752529035447992870531990083", - "23": "18245339776025151066698543282567574090818306067639678467857056028416982921995", - "24": "33666071778868081577534013820928639052489491575650584804439875006850755630775", - "25": "29006803614327437572794885934940680170158140010867839314591082355351172926441", - "26": "26664354854209176428562622367752563855213297208751125047536148861356830877014", - "27": "46540170169284300712416130581131496192458354284627871312157958099560592800520", - "28": "37552166768553729842078435667515655279661905811024726972391043179629665575773", - "29": "43286615009455031784719797295956349308039236872337887241869415538128324649873" - } - ], - [ - { - "29": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - }, - { - "29": "1" - }, - { - "129": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - } - ], - [ - { - "129": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - }, - { - "129": "1" - }, - { - "130": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - } - ], - [ - { - "130": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - }, - { - "29": "1" - }, - { - "0": "20462520400712402627709344205131902699657125916001693532154815488581362749475", - "8": "3286079220927218273323471015859387401808211963700847942237991184532915331062", - "9": "2343353509527431221332805874580459769477254227005869364491719637404275393556", - "17": "15835679397455681978852345178921585571770478820469628643245691677831111209106", - "18": "25968359525593086699828741390841462213842557620921661117888781513811006682992", - "19": "39997221789469405004859238830109971808117973193471679882930960070408268917550", - "20": "31156624005995695462950647443564568709839806438278546285750322523829652149710", - "21": "48034682423002979598595735627082470964414057999025019120237307914909210515445", - "22": "25682390162004926648126512596702300684563123040119082981667172060065342954604", - "23": "44102610159658350122248945998262639117012424368083906748594985507669525398290", - "24": "32726117160370677784865534342031335354951857703739264647854242975769839117872", - "25": "42972349808106017539874972881902782760034769830822810182078396041017049664207", - "26": "20250495254710141779242865554672450483083674839617623855788372037336087569162", - "27": "24649455789456834061394228559949696511291535147962937233602998410882693391013", - "28": "5273471053396930295336690560480093058729058663067686078914810556193817947632", - "29": "20828928976331033411088404076326953775690739983590058821574144317057361316145", - "30": "43286615009455031784719797295956349308039236872337887241869415538128324649873" - } - ], - [ - { - "30": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - }, - { - "30": "1" - }, - { - "131": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - } - ], - [ - { - "131": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - }, - { - "131": "1" - }, - { - "132": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - } - ], - [ - { - "132": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - }, - { - "30": "1" - }, - { - "0": "15540512546148021086829191867945740637058588949955294856096961322685397103557", - "8": "10594000682191512671561842596850404768915215951390201064000301330876956225481", - "9": "24273899499008308828010116107035952946652360552962421411901755620677425559520", - "17": "21652127980743386179775703988143498909493671094413131441765583886616678127316", - "18": "7618365555867366171095384948441447746830408002681924780236233228087176595746", - "19": "16755497111647544518527337728294241483321161788841150515946266621678200849447", - "20": "25703243218349343715292199910244143742862242078796621739382563676444918929261", - "21": "23660236320146546117199432747642895528957581325887281208357215695199920019617", - "22": "2357711090522289546598897234729386613974028245803311836473732510536217990527", - "23": "28733109390173555215828644728966016814697501232510340716340765521737962277261", - "24": "44996841128396863114245258798635116637325455782740466297389023640723938426165", - "25": "6599789027858686808158438761231663258872408335820355159742928007712988636038", - "26": "28875710406631775034784952874367371890072865222379164063203700341303660516619", - "27": "50284829871210718242517076663473729011651558626688537645367329442091428271822", - "28": "1762353919069896882843960674301272407929198385872696572475666081553308159800", - "29": "10472686281706988856343297719402448851837898306731319072661790133137287988625", - "30": "24651292315699772044621115381931231967266843563921217358063230858396457193949", - "31": "43286615009455031784719797295956349308039236872337887241869415538128324649873" - } - ], - [ - { - "31": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - }, - { - "31": "1" - }, - { - "133": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - } - ], - [ - { - "133": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - }, - { - "133": "1" - }, - { - "134": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - } - ], - [ - { - "134": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - }, - { - "31": "1" - }, - { - "0": "13865340336144010740428645313739899438060242120535852433410522037321843803156", - "8": "14528663677549217700800129164589240270333099357676222217378864147671364079489", - "9": "11838001831142887965161049711255116731008938006723415752393105875928823923846", - "17": "37732396118242725888244754173643394832944788121742165982809199686401861287085", - "18": "23530732678750065385307338533592755450395924605310386851176582649096474639407", - "19": "50550572544129774267342194539641465324961602768578697715406753079042513781561", - "20": "15681255641087045917754868438779202436188948959404981614827002801118140285897", - "21": "14475111171075784286728374505417106731551073148221576213378864831472932847906", - "22": "21892317478647426493537220403009064189435814866706096421969697051020450471478", - "23": "49689139596570110748553740997039927421991030434841221960385634510364249022485", - "24": "24478737364037223335741847998307333115695913847075173158738694618639327321585", - "25": "23620509942094221335144955812663113871607773353773706235850667951902394330342", - "26": "1107509655449675373141801514668550306082947757353068871276916185101043394203", - "27": "21672129563678408258542791106266827495094250942223150056012336225035897917964", - "28": "40747714802267667523974000545189899228541863833165007357953802391444328469473", - "29": "34248378101069309979817762929920866414732447870225799199323440508751612246222", - "30": "35737277879488434066315981735687763550656392649338564392724059919984168272314", - "31": "47600411446882452507164335670385039071272755848488217750798424417826323267472", - "32": "43286615009455031784719797295956349308039236872337887241869415538128324649873" - } - ], - [ - { - "32": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - }, - { - "32": "1" - }, - { - "135": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - } - ], - [ - { - "135": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - }, - { - "135": "1" - }, - { - "136": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - } - ], - [ - { - "136": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - }, - { - "32": "1" - }, - { - "0": "4030925228209360989389332579897661669475428247758360576989046703985980534507", - "8": "14858100306733543830524509776306521753986490895213794511117380368819770756599", - "9": "44671304073227997346166121088379726371809129767681143691985426306899658342", - "17": "17765016743291917293348217143848029152268927397297485711988339683993251565304", - "18": "12964095691769142343773882903970457860146509467297206130486623695415961710194", - "19": "32583882997290336890435533681419785790048166306686416329629090622958600343611", - "20": "49880726576714371518407866641215833961753359214898505221320997746121053278085", - "21": "37279684032623107713129904363178895751371645527786470363426886665677707859510", - "22": "8079583818299097245587658691508156392962956495276449575816587971727797972240", - "23": "35071035289962475935149929989628309574094420629916270880243266104415759539645", - "24": "42412176687250432268065419865405620074868093565660661895808364766176954784949", - "25": "1304424358085773743635032637550606677479958615291705462427564388111328643902", - "26": "18123863706460350717772224518948997136960189165692162918421068438515605331180", - "27": "15247387742143107700869638004684331078239062417893832300017981275653423992029", - "28": "40795696217902147849441066156704542391211206690054538600804155824232978810725", - "29": "11710849920167296168107062606282981359033447704029463211736528472494482512845", - "30": "44932719700262148428290218524399144765364648952886442361660894793170415079845", - "31": "18553267487594477155394598359202706283204821239882196657466733946723748123074", - "32": "31873539077738869484715001286646541797062663060098859143133736285480854825686", - "33": "43286615009455031784719797295956349308039236872337887241869415538128324649873" - } - ], - [ - { - "33": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - }, - { - "33": "1" - }, - { - "137": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - } - ], - [ - { - "137": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - }, - { - "137": "1" - }, - { - "138": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - } - ], - [ - { - "138": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - }, - { - "33": "1" - }, - { - "0": "2580022244547148022781785697172503311467595214847728559144402326933969076319", - "8": "27181109173473107186592951733888426823570839381186649897096332580930062940328", - "9": "46873524491435198200626502287228048075211204246868986503104136165641791091000", - "17": "16379919815991409619844626114342202540154347571398347276443860490806914645879", - "18": "11215928921155056297718210586372702721892685907533235168022562517029343843882", - "19": "1330974974779279136429085649456844951070467436753555818629827501355776171085", - "20": "32238479736710015534064993383806271116202583408359114092914087949533985207602", - "21": "41437026133774595801694975208558376439021336104883292767726661373231785547566", - "22": "39025745105875015827336770743473543127798310955808271568591940634510927401951", - "23": "18721971601977855606709983187995720067291812077544760484198819854632075982187", - "24": "48004104043746425951168520121815459983414955463778911459915037961735580717718", - "25": "28972033817708454024422908480199183522160189472616183084578941864288748112462", - "26": "21345098043036376894019519366487490654696740986373323407488281376162220863865", - "27": "26246535916519084883477629318090541703747049200756286242386224261745288927184", - "28": "25179221006167358111653074356355845330978933642545584176962297005149820651640", - "29": "31629172517777438013855581661549792417351427250750916176635759848538075124917", - "30": "36614856766820819896890097301420229891251179730422633418135500946732781252415", - "31": "49079342121772723457703921427965310366784818866056592450274390639804540556053", - "32": "41479607759050823926556936572770111002182672160916287369425357901766050133984", - "33": "31077921017487499812944246263959659803791987246355972434453375524498987390104", - "34": "43286615009455031784719797295956349308039236872337887241869415538128324649873" - } - ], - [ - { - "34": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - }, - { - "34": "1" - }, - { - "139": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - } - ], - [ - { - "139": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - }, - { - "139": "1" - }, - { - "140": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - } - ], - [ - { - "140": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - }, - { - "34": "1" - }, - { - "0": "337091914144357888262743136529662076143869414926179844835581507288841653842", - "8": "50723568755110666799389577571665792632962662873358369901279293864384186561143", - "9": "27381029524956830770342921432213488541773610786265562701437276274030509756474", - "17": "25157163025006675813840556669981528186049170754748173478938770434777396816988", - "18": "28220291531884295875085884990996904875205766936954026921709458393824277943310", - "19": "44224780248686618407322042303949308702686705189457533519373292643528805622645", - "20": "34788159059940347861135457423000269516445375584087753308537484728309055205449", - "21": "36660474990137303773157128866616209618111858267312087687536945007857245393990", - "22": "50291588391624888429074352082114698989490083229359406152520519227161145955752", - "23": "21842326528100635611925738545930902265606859755888367305503773667640452131595", - "24": "37760017257082409198026431670079351419967799140801543277979178371853586471369", - "25": "19711495222248109494758541085854341796257284924339506972066620427354960872585", - "26": "22749383682185903339974801655701739973700060135585844052178901866256719745285", - "27": "34789994377602427233110181307437542598456534172351365146979293543752814588779", - "28": "13094345979130343631637511943758586092439973481034244383319185982296702999440", - "29": "15337830510875330083847337164522713022265707297957458492661512584103490730001", - "30": "43396174188327815553236876768678164070817193476535712490535348655096541012031", - "31": "41879290867236239830078484326818488917092697577902934320004706674829159970527", - "32": "43709028298909923903233945287688330015343407681282869080265270326641824875177", - "33": "51861947707730949692802647980976865874872943600377291169311749144625240692366", - "34": "8949707910887086829156187647044449922875040275018614353774963009243352703273", - "35": "43286615009455031784719797295956349308039236872337887241869415538128324649873" - } - ], - [ - { - "35": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - }, - { - "35": "1" - }, - { - "141": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - } - ], - [ - { - "141": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - }, - { - "141": "1" - }, - { - "142": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - } - ], - [ - { - "142": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - }, - { - "35": "1" - }, - { - "0": "21128818615001540137263856211467089199630793116539340697922423416110745118506", - "8": "13845169169543960551325081618817383129322270258184351988862082266238056066234", - "9": "8226205125048119476598120382361959897256221918431931751979010784270410275891", - "17": "38906599262480486164096033254986152016544272824787712454349266670064444981418", - "18": "49524041899556842462282581562545444136513978713907354951476349361407804036311", - "19": "48901194030454629608041684006494516246491574008936092961415478070453381489887", - "20": "47017488196543169375853198988631926012157609120078177171042041709223045158498", - "21": "24704598254494170906978985158025975987627976205595592823858779473094965603342", - "22": "9235527723173510533028272015275429216226938838313413125926991336173069828382", - "23": "25694322235152318296973156623042105963668059641399971137993098938512850741569", - "24": "6430150625281502499263367545623485637542997100772161154937808907917540064794", - "25": "36818399479123338381126843754845473358867253932349754618057690777012487995222", - "26": "12026308592203002671755915770366143632281549246146936315633345713513495626039", - "27": "15027531273239294548135408743811046336953236319381205820053392255603275101761", - "28": "32209254178022292255523713989810816367286658061472011291723236143902874517341", - "29": "32631081872689453428850131044174664754415433374321656372630646887155927531161", - "30": "1687826072397159240028492639840412160099394262930722026586109382335944438145", - "31": "13644809454034025291462669073548153801222969383556126452124682748451034061288", - "32": "28378004937887085466224964764108652218018754514582192794350310994650993308245", - "33": "38336552971616893359511591087441539361256363627353678656085081581112517358916", - "34": "10616810266955583517128484139187353572132153666289449673663128542986554461416", - "35": "42178141075323017166567164113145783395347919199890554609075192669801121474907", - "36": "43286615009455031784719797295956349308039236872337887241869415538128324649873" - } - ], - [ - { - "36": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - }, - { - "36": "1" - }, - { - "143": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - } - ], - [ - { - "143": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - }, - { - "143": "1" - }, - { - "144": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - } - ], - [ - { - "144": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - }, - { - "36": "1" - }, - { - "0": "7314289664170998780822250984795805793842874757314808790503348444042779760074", - "8": "30662043156597574790057014846962168542122497721087294491438631696378782227484", - "9": "6488437480145820551431940315431694795565037465649971588553196450940297368277", - "17": "49813802759714230353405816804126837960809128426019442477297038364407471055193", - "18": "12517256310096590504853274600996957800188381529902527165118665831865424884325", - "19": "3079999161275035619855647255162768679151481673673206924605948290605869361282", - "20": "38160797019872468074970542291721311411185147474986686063412484101066367780996", - "21": "5277882749044928939030434407454708831407032006908971901866230098567169953986", - "22": "28697072126854734145391363710624207203902114564006887954633261484935914582650", - "23": "48579635183661323089798871550932517842869241978799981633144406563073340407994", - "24": "13552388601447211119872664938077459954366466316771215357085573378346771750855", - "25": "7544676925590293827155319499305944595115166818563932432061837869160142749267", - "26": "39701097489896980160555820140090032405804153322133866493612527491494076257191", - "27": "8832563016222880454812569305019351221709735174922622662818671036750521782780", - "28": "42750630213196396943917030570936433911208787315609504067186228862973230444057", - "29": "23497096521892953939825819131151357197272352397104394152594762199795198551054", - "30": "20758017408834418991609862867846704076072430843743186943621596868377820425651", - "31": "50536232266277788037039807491929537164213431138401466625614258399941441016136", - "32": "31218313643500738727668245785981366209495259120102650649284720166935247643929", - "33": "7139785482164772453235574425501179796160507201334608607359135451510956619769", - "34": "21426037128104978208428634291788170441480970374740082327269389698221829766951", - "35": "49826112753746887747948778170207209208712717450784818887368578292560647786861", - "36": "45239551344620027413844232442148888764664155410237779046456427596526516401713", - "37": "43286615009455031784719797295956349308039236872337887241869415538128324649873" - } - ], - [ - { - "37": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - }, - { - "37": "1" - }, - { - "145": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - } - ], - [ - { - "145": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - }, - { - "145": "1" - }, - { - "146": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - } - ], - [ - { - "146": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - }, - { - "37": "1" - }, - { - "0": "17774049587694351616089815110956846453850106707275621110789201687277957820281", - "8": "11850879631656382017697575928125109153224599045215266929537827948998316762935", - "9": "30232256179014136090139975203662404632649090238744268830894121143745141062215", - "17": "29242549176650905843987268756578212698779552564954022595678629296459076816962", - "18": "37306561170878606140625331462011722616894127953732802260861353333403823943119", - "19": "22146354924546398933803212205358772400522488608603479059166957231321700290340", - "20": "21056352391025112863656294310438454807175015260686937517359604598039224334392", - "21": "13704796627622720731527881330357435065100578401998242636187743389482523710985", - "22": "5664689534756509992878435762488602169039148765158857723195653635520482094697", - "23": "6322948406265495834170924118212099318935348969238839867732296789678826291431", - "24": "4419712412905029863629157295207997565622761346624656165912705778637331410931", - "25": "41879071297502907542631800868601571246202948262922140738202733956669232495729", - "26": "5539920222027131258347851840485044060148951658459501473708228553401494090271", - "27": "6034211861305683142766877367290777028665117612565775600623624258474909659493", - "28": "38566821641000356042175751663373312299611234373543418708190468954267686638449", - "29": "12484257238020764278248486244494647925813814307196031258457529180134522717520", - "30": "12088770739118911904854912539240099012516172519206902495787529669677212134533", - "31": "7575995152049874141648958945076998534958559475728407379234922619731221024203", - "32": "48745628520490379445538824630341209839229824311092126125927789112873273510064", - "33": "14653310810396563398291495591422972601807358119590621831471857245357386800611", - "34": "22776226952437673635196492706144875987471929525928842515109504733197962000397", - "35": "40224303441185971055810341996542504167102054070463552370880570999456706479148", - "36": "10295064350542447578898388503786417973681429914299410609181650632091658646327", - "37": "18858277053094609957166923567570958423513516228825289463735088131328490411616", - "38": "43286615009455031784719797295956349308039236872337887241869415538128324649873" - } - ], - [ - { - "38": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - }, - { - "38": "1" - }, - { - "147": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - } - ], - [ - { - "147": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - }, - { - "147": "1" - }, - { - "148": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - } - ], - [ - { - "148": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - }, - { - "38": "1" - }, - { - "0": "11019693257420674975398059669532837767980992812803956883547482631461450651960", - "8": "16928277678457539924493636008939749436959266355469745961219654360226967844472", - "9": "31272499369987139069488356498241864545917741852811322857225556083384913250240", - "17": "26157339263080822056836925159177176817282458167818256859411195220746299363768", - "18": "37739682229216337513312114384872743771114286347750114321577922031861638568423", - "19": "28706667408894407595930033405124374686594522481996221525610673305918807929027", - "20": "9686910624990245879390164764173842737690126858792111117231271949615157241094", - "21": "21275802581959010754878487450494975466564908081680224992245190540696983218537", - "22": "1541638644456041256239739688853664780387002832732752635059619008643085956727", - "23": "21352225176308708369972004089606744818312330440918733898737346445626543253648", - "24": "10920606669198589431243693740300420744709350994791405626257801971811240909858", - "25": "14101170012663090398234447491860091445224108024064538147352028951872354341574", - "26": "42149286697789005626888552551469332441104296265015971110906263835316730464965", - "27": "43265524969980899902328903531277380451722695171875703865404860244414634947895", - "28": "23539510236848902708905675777211808654773168756145962429198126822344226311292", - "29": "36566296967485201076823316234644921892676701998290561546349475040193815853768", - "30": "7074490561651379315451189268597430431955983028423277252088214593382360289302", - "31": "27553902952833260856639181376624219269377712692931136828431127737552995267295", - "32": "17985626034133801121789741334690470221178497155863162704401703190061599938282", - "33": "32632206181409287963727907342521328247863471041335478524341221560497273911934", - "34": "42867432770536947951309983303740179308274109337679318841065170445443110439995", - "35": "19279362180509195546034568039438280697103471368061754165548486469339481548199", - "36": "35952703204886281564593944085704058268385563155210117348953529202575188173181", - "37": "2865156237875579677226096900280071780422451880687485768734854411802841039962", - "38": "36770014299349415561677255349936090831255773519943501154753537073496539244575", - "39": "43286615009455031784719797295956349308039236872337887241869415538128324649873" - } - ], - [ - { - "39": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - }, - { - "39": "1" - }, - { - "149": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - } - ], - [ - { - "149": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - }, - { - "149": "1" - }, - { - "150": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - } - ], - [ - { - "150": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - }, - { - "39": "1" - }, - { - "0": "8945986251101329707360666524341408357502651307257925367450947820355368214535", - "8": "19621957599858283039228942414831234334418543669744174167780883956126851745264", - "9": "11782529295182019304153434378211039374798107207551458210521069024226098915337", - "17": "49278758536456266448250434641644395437445817130976722165653129757070794680478", - "18": "43633802059756764926680859109607038522312580612087153378466366499937250039429", - "19": "32835346063310279740662961962148439418498078693684289598954528231161153602154", - "20": "26811596446196172505626896366057669134451858814980164707356948392719202417005", - "21": "18875579163824385203072996097698536623049403899538511519101453842279441195842", - "22": "12842036419072882223015619555077392569923678687719068677619340623146866809352", - "23": "20088418219834287471426140997943921812891137929012273574990725293301492007625", - "24": "31288287700170681476885182152424994678424833044313794480042136427974213500292", - "25": "36698767274083859448273317493988168946131111089883483865166818417722883061644", - "26": "46038392220063806307879075425263041079377330486123603950198620212776389942258", - "27": "23032405341825757775578463429128633955891923289454007280640185908198749001912", - "28": "2186688854759609604361452016300647084218833634450843622824488202522142264895", - "29": "45727295000394372337156038973578866544817779387166483606226723031940532856463", - "30": "20341199797792943554641051033521515418250074811232164043118791622765364537145", - "31": "35782365127887941991118638082089445024557233685616522140596732819867487273316", - "32": "48565143725360679749285030214373807703870519138359664475372562200498890597725", - "33": "10866552960921177324838233872013586107530388890430387178835984713886548409635", - "34": "30243336147172932871675464029427792437425092184946484793249386344719415489953", - "35": "16808846921184437640264982516275108527068129185823665379139680240363584732029", - "36": "47662546468052363058224483835014419669361605046434986215334456201714459787860", - "37": "310352617817380162711792202336770411324252279493429063578066374551155484999", - "38": "11935013315395360329901434478257972609774780647305602986025372919628917585160", - "39": "14755321404618334499811932496197107494510782350478032457581071181404951210635", - "40": "43286615009455031784719797295956349308039236872337887241869415538128324649873" - } - ], - [ - { - "40": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - }, - { - "40": "1" - }, - { - "151": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - } - ], - [ - { - "151": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - }, - { - "151": "1" - }, - { - "152": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - } - ], - [ - { - "152": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - }, - { - "40": "1" - }, - { - "0": "16600645183477638722007904287733521074791737381945794958827103590279914462576", - "8": "42314825667221233237103551252644362643241169551710964499188403709980641444856", - "9": "28227699328129887719001794074043836722927747605197280790703598355096308450470", - "17": "1770400685919935114818401647108303603064747177990157725979935477782847071625", - "18": "21840627590191711326340995799848952233700153485768138726013208940285812715794", - "19": "37154751456668767436688129068272332702690893803770635598821410155289651649357", - "20": "27671215364757217206860459697877057574591132801612199789856265575418820268136", - "21": "22629003087615824156742308679714307643960186523753129308462402343490024260161", - "22": "15756634907567765620811405116637788161196758865320028919380129821726602851434", - "23": "7695495109244326469524587084883257683119941243876328534898678676461121070251", - "24": "28765174347382233218157963896294260934321968382359379351961967530079420939430", - "25": "3390844913631200580785296647074137597117328556753398565746952446078751450437", - "26": "29594491183238071861591287655584547894683749568167079241274595681372336956266", - "27": "28913142760178868502173166118136685339224790086189785111346811232621189532728", - "28": "45832861973683163004894948677391515171904179441823510781756805122947314442567", - "29": "31558004927815607533973532986675246387525448767302920227379319447169914134083", - "30": "28028681106952368280944961917656853287140708870462904602587487006160033513546", - "31": "28907508224624220904701568806509798149998887253368752586648220390082805265286", - "32": "11610919656117098898729531747702941813925211479421781261973856161589172667729", - "33": "3231369469534854164266393753610546350768491779350919640154553933009579696683", - "34": "47169630653861250814855656637100662267446690330862589731663803506456155773321", - "35": "1452247855224159301983841270820901528408784252971929590282721646087721441821", - "36": "34677618128559183808305500928931160410323459304586421065054364094808869251292", - "37": "50493826240639096618686945080317734960716061154872562135852866956741813571169", - "38": "8817074993573976128477339618065802477287398014200413035703159730211889016320", - "39": "21978630713134704531716360243096572055955824692889309463025467261314860112262", - "40": "33725811018185567734055535291884000512942208143309228910982194679963823389667", - "41": "43286615009455031784719797295956349308039236872337887241869415538128324649873" - } - ], - [ - { - "41": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - }, - { - "41": "1" - }, - { - "153": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - } - ], - [ - { - "153": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - }, - { - "153": "1" - }, - { - "154": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - } - ], - [ - { - "154": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - }, - { - "41": "1" - }, - { - "0": "11176512602209691417636272193861235132071391793840565562361651389294061042059", - "8": "32926019421241766115886759857390645421406956354213986673502257002928118703355", - "9": "49322693414070112361358512281698665879647459215440267595469827738584492496854", - "17": "23885849599750960614195676552190104781624167550702792930135278782551959680912", - "18": "52027993194289789878721235717901002159062426489631176312815162515892383178934", - "19": "20662874286277103781593972467614217896771610380042762792042189495340567497488", - "20": "11024082539078060250302434570254659466759872833700654022273959674816033156069", - "21": "47900169680356514769473251177281839038424597295070992696786332498725144115126", - "22": "43128414037998903280225286555100738166628908168028202345158002838473899035005", - "23": "21873737877315362585111871031542075627401623925590880962326448593844173256594", - "24": "45827880819010052859682398093934267495546231911068322924583720842333110096830", - "25": "24708341089878163845604900673376650077518903274152284425587869564432263833901", - "26": "38249719634626947028774516518377519238907029494092586227875997773569916494095", - "27": "38868657591738799754450976530845541053781177916338697072279523785882116264122", - "28": "24666967491863823872268894608464430357570174118224274426374241769221722189445", - "29": "20948467994356262746790204439320110128618944925073494537213964454790641088716", - "30": "38488727835666536472587626179631781040410387452477608884818993755045121332319", - "31": "30599267710866281727840624159446152374249752767773192547878239672562131840684", - "32": "15807707260705186026934442682639374891852306650080608713250092557433280318851", - "33": "36757500844957109817886920806721144962967018390345783625309683336805947217031", - "34": "16195063003814745206853764524105132153073190480596855335367388375459538763071", - "35": "49955515121534659574626278012551066333081933898174102697619904729893950800521", - "36": "16770123621612585525700151395012787120342672139714596759240361110446120457487", - "37": "38732057274905822561165004373199701615249935934342560547372833408650701030003", - "38": "42037880624657949431082276650057149517219633097126345695858606280354618584649", - "39": "25523153092056605796472789240196574098233133459144651411703225514191886783058", - "40": "13208626618338019841673304812021246827423373161375018886007460504304112417024", - "41": "34799616780256086016754470315050336574258377279361730937204119314378252243228", - "42": "43286615009455031784719797295956349308039236872337887241869415538128324649873" - } - ], - [ - { - "42": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - }, - { - "42": "1" - }, - { - "155": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - } - ], - [ - { - "155": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - }, - { - "155": "1" - }, - { - "156": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - } - ], - [ - { - "156": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - }, - { - "42": "1" - }, - { - "0": "14889617408341048364135563212181036206522304313837983213620424531022449761460", - "8": "9821978372370396565607801129981669222334664256788905950789229827664602932480", - "9": "44862232445783687748726242704207292341327785797356376593676494830800916057931", - "17": "8001707016140060525533368639759466050815245287169753349367248364608803361893", - "18": "19434270661729297800095840105966764422631322265254599438375963928012041689370", - "19": "31037746839541599637614789781208480394244415226575855916289025913966388902435", - "20": "38086421464767233228206031579834479573435705416833136798931821388239851989438", - "21": "24515471995485056967766024106378888453007265452167891603007338553757952287356", - "22": "44335491948290344772488376733372694528692413586275385475595292284760762478234", - "23": "16105744552339749408775181532270654053409499087931502174638318673614801557613", - "24": "27062978657722122306789121226197958510515451476845272398605686330265121100929", - "25": "5520923236345478641996786741370818820503973997372831479955196634617592721773", - "26": "40372254748615791326162736375394137416565170834115680593886351983307326680263", - "27": "3313230171378708344916878929359553603690478115731810973246369027418791684592", - "28": "25168768161733154384401824521470485165397728927771272393402763957272848436196", - "29": "7254257427238599687012675713692488682283170336131220142956080989373488208915", - "30": "46529158845605654480670634214474125910620651936182760464285801117685844940226", - "31": "40165790948437272557581734132036130141153118107590583938048574754391390752399", - "32": "13766184923483059424872988105181982574114466203261560587617546353967345033685", - "33": "41314350190765687995272185097953747686249237407949200695918953887204610127421", - "34": "27436818555742363029176064996584252941471030296862535710006175920585573493096", - "35": "47817333641969328854732962986196346305893730082899700901100616340685751184060", - "36": "14655556891179325738020446357323826975238866008126384323263343552314900265044", - "37": "29841105892017310032127252269542861436562066344755093514110688347137715362628", - "38": "17301699307353836037259823782188805909487958290808515729827120876865150598413", - "39": "12218919189459031534444047442125277423180155681078368028325283640039624669329", - "40": "32273155224374158801770363506480180554531983962048253716369171577946453015556", - "41": "19189352981675128447190344166435719843207892852012150882908045445221584611614", - "42": "22639478768968659661961089202534664418483898785848419662908078854285901317920", - "43": "43286615009455031784719797295956349308039236872337887241869415538128324649873" - } - ], - [ - { - "43": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - }, - { - "43": "1" - }, - { - "157": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - } - ], - [ - { - "157": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - }, - { - "157": "1" - }, - { - "158": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - } - ], - [ - { - "158": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - }, - { - "43": "1" - }, - { - "0": "13725846858893036951158965143583678272726150787955443316044240114688865768260", - "8": "36711147995975920595872917053759219718988643029692576312671462774480141174686", - "9": "27172094241862832421672996325553855005375442296725922842350599103927202236425", - "17": "50539431486023871778126791939737628041642579364406999647288020791451610107862", - "18": "48015925587210585502497747531311096565745330244328620974852467179372687363617", - "19": "12694811585244082672882319007797720760771830847145535184152402024864247304120", - "20": "44433479935271094509501806160762352061113545734692252336335228153800612643096", - "21": "37386656397900222485002324306707781812485208262771064720704260799052224672522", - "22": "31206282322712029119630700061865260856303456354616137846427295407279117548273", - "23": "43763258151721165051273034133497210148902677599769646893855705295584896019849", - "24": "176577944156624346035173047433732315625625583936739762280238588418515483318", - "25": "40096303191267207628032101582573559356008900308432736858384083963295127729427", - "26": "32426050228559810017376986417514827877603720450503828415146126617938123435633", - "27": "7983822328991665313700416344046138157339466452182712542571151094949742362394", - "28": "656962417534521476066849282500450979079944508788912020618878626450111853800", - "29": "51458752810552025572996496677206893011249949517129489406190705102025798619139", - "30": "44370342490930171370758565971532021877479785502771011230219648034171740049220", - "31": "30212829044460511741571511887257717278125615814334995886433062816531420087166", - "32": "51462551491324820088567192406299811177339527385124975612463745473417906344511", - "33": "18202574542611783737362937477361408124277147541915626784961912224105659198445", - "34": "16506763906557014790524743957685465050643297150420689535701328289711220966002", - "35": "22538466767702464908481839631359603636701979452058713396181692050522590708232", - "36": "2889737371787126059259758513601934129969758783239892532255711969094005311856", - "37": "50387068289250485498381075697928834591329919392790185570508879491333962703881", - "38": "34607726097378365187641414136907165409176372467005504221209584056799351224853", - "39": "31286936081466490687047104501942782791001960334578372043308625198999889395140", - "40": "12646589838472978096138338501122514906520852391060195608825005144490511415274", - "41": "15608735804665700466643300779646889978518192296080682377287443381139223335371", - "42": "49993459431056528522590237504686568710210356597254656865859431600683266684920", - "43": "17161225510441542669896798530158788679153679836317295003128575350675787351907", - "44": "43286615009455031784719797295956349308039236872337887241869415538128324649873" - } - ], - [ - { - "44": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - }, - { - "44": "1" - }, - { - "159": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - } - ], - [ - { - "159": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - }, - { - "159": "1" - }, - { - "160": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - } - ], - [ - { - "160": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - }, - { - "44": "1" - }, - { - "0": "7031966701933394996335226463904734684231938370740242159297465282149711323302", - "8": "142012157162732790476712995405203508236839755439096208359662992891357920076", - "9": "45512549781131894787852726095630356935102934880792575620757067129369241134465", - "17": "5262593509101086771419549406334548268667695077124764692098655571429760875613", - "18": "4962143974369544789461484771893115416145749735198985940119059033798638683565", - "19": "26489941391968162577700795132149135904666886136198429518668309055258539820571", - "20": "5631286445105881041551469083518029532985232239958802797853191393969982129189", - "21": "16933341551246723932075808548395826172521797403020301719416223335719271499776", - "22": "15047808481617831302684792227077542378281757186745790146735777283855485797715", - "23": "47406587404723779473212568051089605220106692098616671654508754167495564631815", - "24": "24841980625906827075911289162239918083684419155029097953141657744254274520929", - "25": "5691167378968777327238142672202348693664591089695149053054740888775836413668", - "26": "20602960291808973971745409950139760614584745447700843206081823707441531906318", - "27": "26056642317876551124932757267109182497254717756225955270427164091195222966678", - "28": "7282735191207013916587502141658990009093363787239802764138135976059195221469", - "29": "46677591209305463328428007775592826718062206778083989246446546439705255971652", - "30": "38378418323519475723770544359600973357768683300447638132540759524233742520169", - "31": "42642949952483049707453255595597615279424296789275207664507626499734816035190", - "32": "47218652939016341317425777778014400780436768771128722468616430386170557150218", - "33": "13624129633543806012332981695804056963000035956067201901626310027831375170020", - "34": "35930755608853632707003306945556513160629865385051788381691419775567527806354", - "35": "45138358027759727736114240842907199493686963710834064817571156309429114137551", - "36": "5782365809987060486144015037645983854378634345782370676036420827593408852153", - "37": "2667579255481456752762628214670847533038342860232427263641149401076611056726", - "38": "28459610922089974098541159827694267186070956041726450257767444965924612436292", - "39": "39125557997459914703736221925330539592561246508548203277041418738859583645077", - "40": "3738880590785599133153406905368577425751147523729218981253385839886755679796", - "41": "3100785493698492171850829896449248633539415655759176279293995796633568408735", - "42": "11052236898562956832925465497615751865853367445834666626093853800894272646616", - "43": "28293801064931662160623436755904130864413208165510843867734909083982942429050", - "44": "11969561556661521394075796469760243514765960948235377349874003978124234398371", - "45": "43286615009455031784719797295956349308039236872337887241869415538128324649873" - } - ], - [ - { - "45": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - }, - { - "45": "1" - }, - { - "161": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - } - ], - [ - { - "161": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - }, - { - "161": "1" - }, - { - "162": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - } - ], - [ - { - "162": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - }, - { - "45": "1" - }, - { - "0": "1598623817079294552053879422210947472173483306271647853752616124799192256195", - "8": "19357375764928658486125492312903159240962516177547653766639560342970447513609", - "9": "17118701616051148895425563017719752116688081282488191314593730879998997672528", - "17": "23903302725285530814764911488019660980310125807704778650028442951921783028799", - "18": "9897263044193607155627162695017667381801834624220133564131928889622082388385", - "19": "47490516596625040873684685722291707875685916662882003823468373379942203943244", - "20": "35650378556195341492829421851580373180514703389856223209693179747710610666342", - "21": "6442299495546724762090119607188928448279286781673837336592301342828742402887", - "22": "51671889287264351694348042003912808712722331694462281256678512026240380035362", - "23": "37366119584409138370974470095618562237665824326794809311160782371088705795442", - "24": "32791788447625478084986353216057490565370704028609400145698666511146163062616", - "25": "31075851774814637707598884955930591295308633473932978116698622475944127812357", - "26": "36546464339158916041350011351352576231065070984191552828215525031671446135349", - "27": "23936470630097230164450049645278622502326699395308969546378797604571614370544", - "28": "12863019227041163679357132342403072833493994330243086370189741032121951009865", - "29": "46396999292767829232413635208160980612505988303487669327281747452575151087801", - "30": "51774417703689346175269622718539474352936079983151706333491620927359022446243", - "31": "31138846263665044144447764956866331329116971856785098041226111389415994111666", - "32": "7385118339516078424348639584862546644726644641358359613155622493151278694598", - "33": "32772595414665738987529295269541840911904817580202721890676403815428992374992", - "34": "48609450298102975410878002947010993415780901701734494293424138640093185264497", - "35": "33065909175909175231243458130698368815121775338564547511889315722304937955810", - "36": "51476353437800693676898582629048518729381383092760728912765830528570010191884", - "37": "47332120333366057171632318477468055992141535858476641095121598375949582189052", - "38": "28379018099761924258193916621470498057810366514738305328467687876634890535045", - "39": "20442923051403164779568983221233112502659333458720967483565560818617854662021", - "40": "16778439902420796923288572157417285152644655501314307730105153774655178486465", - "41": "29590561817755464173884206077046656028869621477687420126651906676567779047550", - "42": "50778979844974474744310943616990541216931214758741462071249181328648059247853", - "43": "48327756960186655700029610093525378413444717435369524613947391343393464331474", - "44": "51806651970964959124394335285633687623390235447738246814070188395444571343012", - "45": "18602332275634592716672086837920674817730217379007489248168742572627279248304", - "46": "43286615009455031784719797295956349308039236872337887241869415538128324649873" - } - ], - [ - { - "46": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - }, - { - "46": "1" - }, - { - "163": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - } - ], - [ - { - "163": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - }, - { - "163": "1" - }, - { - "164": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - } - ], - [ - { - "164": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - }, - { - "46": "1" - }, - { - "0": "19774466718056564032734488707833648551011540998602523750214654491716642104404", - "8": "16227282192285380784239586845831622786641088857048607816373423540961705833178", - "9": "50622252848272042658715088266286380788452842744620902788927155637878454849679", - "17": "17258180871075628593496773525563950820511982198732771812968569892157559106177", - "18": "45389738865430948844944923011329608447316758171755954381791240884705086941337", - "19": "4546374487117493137600343529585842613046040850218517029809310558948665479612", - "20": "43693730665788888221316550144101092797505262253722201038781041771484946266477", - "21": "47395612810614895046124839754368317666814348510703666653329484040637490338740", - "22": "32613789337377137132593303034618216811062156327266179921959981653424153731504", - "23": "20961534624814738790940043886091546454983536368386801913924874812306470719575", - "24": "34980387216430823971905122251032436335341226455311383589064003849459268442873", - "25": "38093528760732579390346070830415951623496227298591897320095481005009929098157", - "26": "48993565115964824721977709313166617227423679784757921262652161152997201094311", - "27": "27290507081691080659755915833677151289006857969790653544104965201856832774627", - "28": "42080594259869551656213236912377675437208312885996812520150816981125551904468", - "29": "34319759628681464785281049185675829809846555283831650736307510954127411094187", - "30": "36715072233611125792468802308617692745150472758285162905942150811274903666221", - "31": "38692397173361669864504675882326418916578246304109065414701919708958344628017", - "32": "29326103779314365483244268789582955730433686983794469383867867523696389840625", - "33": "32695501838507157153826566959370390879471215395376616760000428287943710088071", - "34": "21078426719703939258054705017567310573521158020826307557557811024396181708359", - "35": "19256272795231341010002747488580365875520880544875197343295417395234076942720", - "36": "51105112782644593471995659170752563254787355732144879955449614778392594359142", - "37": "16672157567103979778465856123393972043004245839972237888512432209886157335352", - "38": "5132718335644465242765393801105473255944018728137391526391882331617876955591", - "39": "44702633937131440423951237378783041350021520379895635577392201297345105096513", - "40": "12253910526746071346700304786036386012889485879232095534046298822351619499842", - "41": "604272257488378493608435388821877965592309006351284850634467177233314107681", - "42": "14579891564640186122012802035449470722979846868490443252818087990401335565873", - "43": "2528555589907304480567543995740873254269943236110359458607521578313185483455", - "44": "39932222907452663354169180873737483411772733705167487385372693194233755169387", - "45": "8878563326952107781211530709200446220236926168591198151805735789344752724931", - "46": "23148774627323638096156968892438537428059434414982163460401389722540031123577", - "47": "43286615009455031784719797295956349308039236872337887241869415538128324649873" - } - ], - [ - { - "47": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - }, - { - "47": "1" - }, - { - "165": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - } - ], - [ - { - "165": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - }, - { - "165": "1" - }, - { - "166": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - } - ], - [ - { - "166": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - }, - { - "47": "1" - }, - { - "0": "10833489778171075947445720806211760058004501131759886332251918804308190690081", - "8": "12485065852966330612667926249192926404519223821600031601817032788228891751173", - "9": "10168653637090274400132323743446697752243041907275221524744554284791341285420", - "17": "41453060575518500136728336976042780403730184505616890546207211019509276838010", - "18": "3938423710941079636991353088832759036727715661432461483499029976398365656156", - "19": "491541280244853203172834507238880271894148136506540124692605895905014660155", - "20": "48497205504597853575818979888835119588852486763460700459357191803627976763716", - "21": "7219600033858943759042100507882608202841692249864595510049142695040104225571", - "22": "4688629064620755656645374566897785120106912499694128358988120826552278586445", - "23": "39201623907570466488796080979309584010081500074188715075395158757700542180316", - "24": "49074992071532227596043579046673235209644648959565117235007669356528394990476", - "25": "48716948409848034998049900660312197575769034220903857168894035433554003012425", - "26": "2177743461631360281223354428537531257077701806775378570805194061184599829447", - "27": "31675794719237103210178779411581622881574978706409981297741504044072856807432", - "28": "811579569821149950334432326133607288840384635575885264253518708717387072503", - "29": "14435231682989099908668465716963816605318278372862317981160513302516754947791", - "30": "43886947675217745271113944732849795357387235755374611934564453691327951246100", - "31": "37318079882616467187103745378659794006255299213551432520229535613827126805107", - "32": "26705613097504594196241984922218932755879842131529070424378182775736488838823", - "33": "44381509634746186727509445274744396626696995163947224142541586586947775674057", - "34": "8921157924943686401802427551025836126174391280714568357295779209147616560889", - "35": "37489743120529567389970634350129533772311765733328362538523960919047450838569", - "36": "34023203323877654128747935165011354052658439645515440557742072195157993727030", - "37": "48937644247515191079345779508999693422914276623222677706840909167356884726099", - "38": "7980942975810932378585036412845287768191052967369455151308522889980260340748", - "39": "19597085003381045547978930945751050195838083299295792405300527537583188453008", - "40": "6489354869503383462041844038090974303720501359012215067361568777452575487416", - "41": "6838561344113985155114368695325331235965571993011010125873393223676748420593", - "42": "22928325978138128729394435268656540845199026426117189525905859542652758462571", - "43": "5389936583667132916324241186677008497789274957691366372006156196544340983353", - "44": "9173137713889742705446604445573410638315174421328481234661353292979240463503", - "45": "45868326017838507058834427152559482978484561776178826334391024375469665278025", - "46": "16832804545574783575930464465446066370068144932129311780865410396805403252955", - "47": "39339549402361046520356004604034987007221427095395601966119220299226094274826", - "48": "43286615009455031784719797295956349308039236872337887241869415538128324649873" - } - ], - [ - { - "48": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - }, - { - "48": "1" - }, - { - "167": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - } - ], - [ - { - "167": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - }, - { - "167": "1" - }, - { - "168": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - } - ], - [ - { - "168": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - }, - { - "48": "1" - }, - { - "0": "1643230281589922929913461313269721677840560726699527243824368999070606462076", - "8": "9150124100202883496359667368289649418858428348285828628727641093544362969306", - "9": "10374480017835766120640512168723903721981209848307028438552563020656473018860", - "17": "31315773388575221903098703113779066631655938513490713759459613250454585798324", - "18": "43936062048912166867549848842467200820501804318748178088898551696577170694204", - "19": "1828076773736506060829539308554111412837350780127459773619575726383250235271", - "20": "38920045238477788386630376348159850463740461860153994897137085336989816809733", - "21": "386467343220786862253800105704189636532522647485797147798326502661554856122", - "22": "47165177610393849397585382068993459802984220782267731789977278902028059960177", - "23": "47596726018418239838624119297346790990431090841872748084682226400752169518755", - "24": "47079066136437787747726217152500800014350215339610046526487385750721400723808", - "25": "6944423624350972837810059700762218699492055009462260502998172451910189178411", - "26": "48329751551891319440395668063617981799707909857591627421763975978828656201472", - "27": "4547274341935321648683725053485757619433819657294875366334675861170620115302", - "28": "1204461565236492774643423119226833922149069210357752482563346534582979999916", - "29": "51637481141688044174696233988938619965098833844124525570587758719212515268855", - "30": "48781594189550063345783163596186923401920942089227292649931543057294164251676", - "31": "23407735082211443838806599823221247441634437474246751656435588997042112949012", - "32": "36446872433866869878113284078987975458112078466781651601463757078376679305151", - "33": "50823574225017882450332143233678616897569768344547463819008641869165329356627", - "34": "51791322192639338826528454043887610610124124954033768691588198292856285006349", - "35": "50302977761241343969938129534033638902350425994906136627925824358240485055781", - "36": "34383150358904224955813498808455586505136229354308635565211456463999684991449", - "37": "28919085422878005286860466514092737090752013910694849285993469115132210944886", - "38": "43370767523315502286746526799047213374006413514885574666559342516023412844324", - "39": "27916942360791600208390409813772600806996114342820568405459090556866802697695", - "40": "42968812092627766453438027113874087026410138469580999821765729666522832886667", - "41": "42286969470473001982885681062468171248082670978244270234393936681378890733893", - "42": "7436595962763155960284980707299893121113475361001948563296637185978678135853", - "43": "34442102657158387979474935955287866375576048980995352221233310611484081512290", - "44": "48965567413064480415468552016520697916069754987910033805778587584149805830527", - "45": "10110748048509874446200626286757642340521009132908729511776740165593480869200", - "46": "23821455589710264137993374241564663787329505123082215823798122849687775323818", - "47": "8836922678029898678801888127383665769778240981576043425283136509491716796228", - "48": "22848952150216221657278322388055055127341707464964944907439556694761909917829", - "49": "43286615009455031784719797295956349308039236872337887241869415538128324649873" - } - ], - [ - { - "49": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - }, - { - "49": "1" - }, - { - "169": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - } - ], - [ - { - "169": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - }, - { - "169": "1" - }, - { - "170": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - } - ], - [ - { - "170": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - }, - { - "49": "1" - }, - { - "0": "5529383825244679302765929967407905660423153508826597450732592666784449667225", - "8": "37055462750066689003461043268482093768255524330162480752380091744775301088625", - "9": "2868687757480077334032362986069262808410660948463783084539963536058220231809", - "17": "18061100468992391736440440646368959899497826542215191389141247863370658943938", - "18": "47420493926202178152648909058166454197033548962877870937301621307515594009886", - "19": "42620731115860326370608481394974037636684117222339165479158928550882646647179", - "20": "3039478907074962470472079110870376186260520791501699381050807827362409154969", - "21": "25473289429482678472777713523788213153751319977754872366595368764257157193750", - "22": "46422562909458209494273308239913451970949078380982272138128658581220235122272", - "23": "9128765778263001237008255350280402035217861414456145324508896638993613012460", - "24": "7455669787603810821627829188526725058592097054309104291564175921036624334455", - "25": "14353798396702170852569961524848286586466831194733411090981745429416157700095", - "26": "5424141662401179925871596514666228109669050911728604012809610457734604459333", - "27": "17167460947612164759578153328942490216249175115796801124253018286563859905743", - "28": "18468904687752525748882063428017842286224731148767165756090751574467136647711", - "29": "36430364577432022342495838191696718843117338372877184232481871587699764210386", - "30": "10422876835121098669596605121475209716844737372362721502163187847904988517432", - "31": "6849549442861257735207428637094791593981959650991526519412015270561430668532", - "32": "32383336136694996847732898211450163182940030011613106346867600164489195536246", - "33": "29463020886287809181977004269040535691584942691717167339556975597839484274571", - "34": "7064777828360925765798085877300103558247219785867199374290137175795956586748", - "35": "39804699850084118816746474891691207549708458259880821034002552028448868090155", - "36": "35622346333162668044567745298489894606904047119572809148689811878521960796057", - "37": "12002751929148267900185461127103679795788513307375770233480590674844237734319", - "38": "6238610245645407134238174062018982427556465933498326530100267938116756389765", - "39": "38223334768324367500451957568928771935518287699975570445422841288143576825204", - "40": "17569859554436091520522306831116608885764842349847700892479876557069302161074", - "41": "10984176370297423938620763651899444110304955439092386135700506339663632930191", - "42": "19781856117837581188239633563253835326814048142930359419504123948510918332513", - "43": "11493673264049883921135845884001518168836515531075901068950172865812266759915", - "44": "9857065403299824442261916026741552823607819106464157480898257584216083359060", - "45": "25219542011495972260220822665248381963923395508188510715436078085710606476784", - "46": "45467884437327280531791482804876063238991302892630751555933243104593120753680", - "47": "5396042962590938500697767758869095412354374487351525793103253040485277911785", - "48": "30284619413030338066398106943902740082968037974727604058918565599755308417687", - "49": "37974042889562811290521989283153743935601079177541358157166481519370768542906", - "50": "43286615009455031784719797295956349308039236872337887241869415538128324649873" - } - ], - [ - { - "50": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - }, - { - "50": "1" - }, - { - "171": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - } - ], - [ - { - "171": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - }, - { - "171": "1" - }, - { - "172": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - } - ], - [ - { - "172": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - }, - { - "50": "1" - }, - { - "0": "3182718166498008812418586185198779390426386793456672324496083987133297273028", - "8": "32926915752434043089867058169666297752313776372016219331728563277690241534083", - "9": "47976875954232073336554205585086358567803613462315826321029621315345058443524", - "17": "5013767729997012319537535223742671376847216036883396905593755311750771851124", - "18": "4042102599875199704360227070290810262077356882668493057202162483802847551247", - "19": "7523167637422404073837718433613161800922502611402235838506500718978337278060", - "20": "29363760583617431835031510144827098016649152662807776793457114327749053170730", - "21": "51793675347194196027107483187723312731029262058165743625042792930272256346227", - "22": "28784486182402121939677102428494458446056217346449876112534872083729197737179", - "23": "33196842299770698094777836345369062044376762594603668793910295530004395860817", - "24": "22208414789330235684212724304396966782410301945268989554076441457783721016667", - "25": "28580447473241086180594740858448882001369133488186246055180461214635205909561", - "26": "34278135444415729840826722645351753769427628300194311781939262098964143660674", - "27": "48193225826157385232782694370852811173938937474981025814999570481072944081411", - "28": "52201237824720083752363860060076587793378140971033785093924679406652381363175", - "29": "47552906929785624091452435551737721455988804786883975584992420475007699810773", - "30": "27132472653502616956966642728126699195788400198103973948993023516431572589362", - "31": "10389811231893989052934357579831789017638868489307187783157199748358721415287", - "32": "36549607029688752653370605148221026360249830237184887264419022181287783306400", - "33": "42532666236031626673360246982240202331291830693927235736901700185198836760783", - "34": "46494840385138930761416763459839914858861862032292579750712123959140575486927", - "35": "34371304866850425822747943494500712401006566930164300319197998496807185126343", - "36": "42908265370426739389831127013082888253076642651088871623458013388879086873986", - "37": "11266282299598410997189206084139697517390930788939438234281336855974629584778", - "38": "13805468254465408450402625430120689646754186366290144302725820351252434866880", - "39": "43434726263656988720248000007749398516863399008523143018931161020271912800792", - "40": "647731032233422217558127532773373463300504203189397392298264292595875487383", - "41": "2905659771028298817004550722430838979198457315870623726262319644807624942985", - "42": "25698046083270312750636259327184550058421111519323309647198223674899960287011", - "43": "7855947208728139557226339259540416057017531347482278615209160307519622148288", - "44": "47708852949726384190581221945733806953051097619938074628876833805174210141435", - "45": "39479565339885969083033734435984479668373915952688061804811835305230118135333", - "46": "7842387998943086825378298185293619679502215847273077668397997731014939121008", - "47": "40306784563250300758131954226384334558258162701206642695140064284918287330673", - "48": "9197092075254020697128929074555292623565702505297022739167122481872051270291", - "49": "27546004459242409128714464151873563221532990014603680095750652265227049128976", - "50": "42458762056737171250789835374827842987755060473775593241102738142319044877826", - "51": "43286615009455031784719797295956349308039236872337887241869415538128324649873" - } - ], - [ - { - "51": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - }, - { - "51": "1" - }, - { - "173": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - } - ], - [ - { - "173": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - }, - { - "173": "1" - }, - { - "174": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - } - ], - [ - { - "174": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - }, - { - "51": "1" - }, - { - "0": "14599400764263231688213618032482449522109778447311452342990557992771279312515", - "8": "18912742569144191770162346423650482057014722093013146591972376900150909929530", - "9": "38548250175464665192127540856411534634963604105490431104492468719569689398231", - "17": "44348538671285686934884133685986338744527348022344885164891332779321400366538", - "18": "1317035426462381896031095674260522491638297528453635216908139118977061741406", - "19": "46539207139660021075588674954360581639176307945744542063239224977085836649648", - "20": "19948988903310505461818670726332844174056822953145061275074663474023611784203", - "21": "32799341221863613713315633976844686242513156056573436764399772292078998002902", - "22": "47882070842801306810625066651456545217207722143553240827563066436616476338717", - "23": "37288456982982674791710885911199410879115779381419600020643092129816688345351", - "24": "24590071506477008281171416896588602430040047086534166442574329507096343702194", - "25": "21426534331298303745051173870539947668741631378123539841613483736269106030915", - "26": "21500915328820030333608864248737705227390853190525669612567282458918537468989", - "27": "2308445996127659243095771873055937834746528951850901014580599297683329698668", - "28": "37436558193041549499094419234762480296256637673958654680499876580003225945988", - "29": "5876951665448425737513934386595289755669601152481593379720880778812976650347", - "30": "2931207815460508121850433958866787318687555432772051964500636556873218580423", - "31": "39297787268304865366875080259169708651282955033008844315219223063622811132742", - "32": "3815516441516166661642172869930941161203900213418247385063959232984890767063", - "33": "28286489692742642209328005839749003580767201527597857886363299992740921956273", - "34": "30362392711822458116774494325638110400060105826693561155539104482639338528376", - "35": "10080174423788243550547718097463919226130273089939311600243914136125286667950", - "36": "27091549112553130991767524660764913489766818069077890225413351581320027788468", - "37": "18826009612321450242655041363080133503507468988795699511697464020993344212329", - "38": "15953098987300962448626455676063143253197121142297994297124625590770153630274", - "39": "52226479997039263893288195155905971351959680139821013658974470901311902455741", - "40": "26919088194009920442887843339979507927310781225351493408216270427103744817440", - "41": "15958228281395294969968506938836163432356122968174445529633184099557083551354", - "42": "24026242102138439782480649244306880158304466217867285135586377535978229645528", - "43": "8372630119721329513598071506313643611486345273849051306859636519105003283484", - "44": "45222015296981788493755017514116328636458841196562673595761093374086989985476", - "45": "44577972078797192698174046205333304946212575340719842707581790406040507255641", - "46": "40272514905000928736293458265218941340231225508985571798376485411066362977764", - "47": "6709837189341035330553101939098937948377238343260378126852853557079310516844", - "48": "47807295039052943200192045607452430857512347687674434292683582960089519079597", - "49": "17586268334218852322280026530709274916427397519665142185944639475824175461129", - "50": "19493625175981631156698363301813094890232951876979700015593327200705609103261", - "51": "25683586717985565161948594091827604418697644794370484562766993783686395908239", - "52": "43286615009455031784719797295956349308039236872337887241869415538128324649873" - } - ], - [ - { - "52": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - }, - { - "52": "1" - }, - { - "175": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - } - ], - [ - { - "175": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - }, - { - "175": "1" - }, - { - "176": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - } - ], - [ - { - "176": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - }, - { - "52": "1" - }, - { - "0": "19971122460007657859508211404523175040055308424338161607196356611508685672711", - "8": "45800820128173391792185096204771604566910744248498168395979465763434726200074", - "9": "3296593061958405454222623767042377492294151640890709792162681729282441860959", - "17": "47577554868383470173772963558858336929033862232427644487064158734754936268283", - "18": "18477055984664591791168426741989663816098043141357366638520053497338079402575", - "19": "25220082235435097624027415701773571462515511010670278999287335449451268531191", - "20": "626711679103864410439713004447866840839704333952923655574744443194054128520", - "21": "46168177344309306601260358932398362664384532320511350508189345939910842708899", - "22": "12210542597760506638588020455529460573339586738742651768549690640596356472824", - "23": "4382854497241020342368818720791628600373586432302204096445971852065242242109", - "24": "45682661849071321924503119515687782731623959437074118200276414169882856637949", - "25": "49399949275619986302265977830833405944277499222523470864100997833855183769335", - "26": "18450413655447460334878199409335636421323547099473359223820431819470746119311", - "27": "1837028932618437220297077170968059055290689679994891484332187122910651899938", - "28": "39622122628997086779775300897944353896275898579263552469736968131323183682930", - "29": "10948723487912278740535296997019334230669113019607642499202210519535806030867", - "30": "15697358055270789222913660525066260537271159134069000173252065709498065979446", - "31": "27857821019300782343485018756105818966357710298942387798577048806298503028817", - "32": "17356086639314663282692549189543132986148130847434722818463421244817259728223", - "33": "891327086656865113381281477779838422670221314500243227129808492328666628854", - "34": "15143558804250167271859225448771729546438234112652322237017579473405520284811", - "35": "16390615825144014250159192152580361697013774158644572682547086120575102613470", - "36": "24884018759847835638638532005804645087825510580402991836802423535026848890402", - "37": "27884325742779882949054032106561462868317711852477810554036909137384089655002", - "38": "45774013456159699697037248566545754692356300584177254754477820182750814439767", - "39": "12456232222288471227144367158891184160679107279386793729713014786582782323815", - "40": "21622819518092347312784241151526807226685736830852547810228089884173491716460", - "41": "26701937558854188229061367392442715915411165328623636113257176896654406684794", - "42": "42033658794673269440783147627697273078428793909581651885129093452456370823044", - "43": "32601587741740562494877757669929289782841268464357082837787932579766868623872", - "44": "28991960260567965231978744956343826810629279953958189256198349905624048560904", - "45": "51737555115584236985075043616985711544105687714282862501121480196841248355425", - "46": "34201144676260319605708261812656239403953964667988043477108767974993143184197", - "47": "9906106918365321017105677790435123615339082526894244822796091051050072605410", - "48": "31260639885130878794843351623708476260309342082607367020952318208258443597815", - "49": "6977163481927963691187158449685853311426752776578931518685822891256410250137", - "50": "3514454668016928522178975669382735701277456742852963606568745948886411661948", - "51": "26964653365035093099079233300737906096765913771649525232456926040546945369028", - "52": "12497266611968540567111644893085223797720357073685312991428428732122834393648", - "53": "43286615009455031784719797295956349308039236872337887241869415538128324649873" - } - ], - [ - { - "53": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - }, - { - "53": "1" - }, - { - "177": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - } - ], - [ - { - "177": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - }, - { - "177": "1" - }, - { - "178": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - } - ], - [ - { - "178": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - }, - { - "53": "1" - }, - { - "0": "12801369279155577131760037436686619341421686678830792595575775840669776442563", - "8": "48314670915362627199752432936376680501039827958805748846625888559233657950802", - "9": "36193325099956969212371624565796622990386626924369046066585654744916067956227", - "17": "50173205642135171924920681355846645523717857709157089501646638240843254608466", - "18": "48667477437227384968606401517801433351367041061184844460733929214411245515153", - "19": "29468633431938254341174210027244178345528760649133603172754558558924141821880", - "20": "14345993343623488938290976856477841428797757620812366906795990921674695156417", - "21": "35366090867308845531673767619698229594652729166029855327366968050336069683389", - "22": "35511542731973774503017775596344705542250731048918915430508219562259364736626", - "23": "31663516183477238231908557668110710303588643107783599067195586215579342442991", - "24": "36294237593843045602877574052408930235865392795896448996051948224974258999816", - "25": "37264147634278328055643958769928693472117849154812706124555185877271734880109", - "26": "6405794658226361651932970226715465952688138048776789154099691258582864265581", - "27": "16644667594192474495283244120292911558085205029420622892260221298971905554587", - "28": "38134538098278005702493298765434409251270322704749634056448927576445009842737", - "29": "45826906271895256079093037232020869649557982877749626792408970774493043871574", - "30": "1593256909260181279006918088898070339693815483577656139152349725128201040543", - "31": "42991136075009807934268348038867327634824416770002788686976655950875250823473", - "32": "37708865735284819187879728055988359746024244646477934111398423647452361709216", - "33": "47164384664177555697846017933874747451603461484031012137458439862459381805074", - "34": "965897963308505670259252396349758849490684199114512397124646286838592010104", - "35": "30527006209889105742014547584929936775107102185333973362772965349019086644642", - "36": "29229608477861642923912423043441599810674586380622648291982756970402380291271", - "37": "1953211450577961224325433364858897622999560620157734175054307880950069477946", - "38": "9353519980616867754753210515127525477911643995317359005875530721518736307544", - "39": "16185706506967175686265770066846559450323124283797699141451776739272925401570", - "40": "38022062023407203818525259102611229069856916794072976458543714108368590343694", - "41": "46465208811556690916176364326908979382487747551315561629727874724669460928081", - "42": "18561524374418425025293682348210367189154475189225886149014162833411012225454", - "43": "32475787033882626937186047914679707543688806472804855206596574310071848468173", - "44": "27777254178959933148238688998877629299620445205960491876664172162600227405703", - "45": "42281456474174918556176940606933783615885059091629897872382333450022464607768", - "46": "30220411442471558101331871257374116184578780630167048780559754194684624191702", - "47": "5903825956785405493059608031611842077972649879646080175587220918915621220574", - "48": "17260187455220800157500219115622945881734720793662323569058006167767093361336", - "49": "52128968513794320763886436531635675169094572810123717503985686939701742113750", - "50": "27688659169175867744387350098721751851726897990151039928118164768438867661644", - "51": "29060634690866525617320518365559890485375658123513445532987097910753124707637", - "52": "12674183554060579328168137719987416387732764071784921154729503956463272509612", - "53": "49032264524367070039979300981022405674483090528243494919276139725840577629803", - "54": "43286615009455031784719797295956349308039236872337887241869415538128324649873" - } - ], - [ - { - "54": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - }, - { - "54": "1" - }, - { - "179": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - } - ], - [ - { - "179": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - }, - { - "179": "1" - }, - { - "180": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - } - ], - [ - { - "180": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - }, - { - "54": "1" - }, - { - "0": "2874800145470695258580041840911551878272283565060591475417515522159069870291", - "8": "20856609100865185787751612448733162360224271878987395766388433936362043695461", - "9": "12194183133497856466174688191209059807219766320629463385834794408249963412301", - "17": "31120003419260810433513376907978875667641771785924900128059201650129025002917", - "18": "49075744729252835999324841051845888451029339824366977433296527179178796120204", - "19": "26649798772914118313774692572897728972996138081192524682225116227379955805723", - "20": "41481613659966279609324734461745490978016031957504218984109857404997551581190", - "21": "30946854478876974127030466698546304482867161543226115801559905435036618512713", - "22": "20306023826684576400012600130891657535312452106355231428727797969977899524386", - "23": "42234672811986758149547693373931303325722717544933127080177051976441786012261", - "24": "9968736060683541482096459058594514828048520841619612563543546965290819049926", - "25": "419159326803355504179044755466687201380679803539890028835597793036028148670", - "26": "7396879683204645534515863273956505889287928485285985265001562544458108372264", - "27": "33107708437166453066711746978644129600215459638571298655909606369328774708558", - "28": "1732395166632007906540260951336623548518684916962654086166318956454494582612", - "29": "38038727361781312909939564755112728410225794225274293695641011853692913942887", - "30": "47064636956155159711372043655525582593330987748710505959772210793150844427629", - "31": "21094028601983788282466965388694059964657012242126432762809912658058837513025", - "32": "41693947444146048268900275157088917679898621087439750905288151198471636691825", - "33": "21395561571199733448175766131346558181089532251915490406827735912339060481275", - "34": "38344438168145259744035490010153258028894823122889981318274790921047311177021", - "35": "36258256020328210054225953915467393970280126992885092782600759336076807906414", - "36": "5148818709280297982538149421466868438634363587044453389440824490919438380476", - "37": "10278869912806823810196303006899201166586662306567305858617877001030314127551", - "38": "523562038924235281409734882151422465950556227071179417612305001927314831554", - "39": "44169929119578255992801129855518330082719838779908561108568163718866855441244", - "40": "16123470698057306807401503360854890044980663506556273137399859491248793076711", - "41": "41377980663559068169285432194800492798724863252183212831370642716258744672546", - "42": "26474215360742893662054977905863824919387896215596411102532934134275961495347", - "43": "49964246569323779981619465035875384729064815705712628415579200513034193278638", - "44": "35208116100267599078440655715509657194269847386778597064885133844425211587329", - "45": "31060781036617704279363296484561798037376197962341776875664819457925942107822", - "46": "3187565374732278756401028773377528675775290133292385761572821738691963201647", - "47": "50370578993513916347001504316826660523542037902909360494292428417978980967529", - "48": "49609829308379409108571524386113107966178825462271389944901570747423379187910", - "49": "32837244640867386656635274474487603222200973394948633305679410931669689931245", - "50": "20093098078022279649854787717657644175357059710060694480824121550302320328051", - "51": "43628106233154507498986159342755166333941765449497175563291834244511737379217", - "52": "19934393442660559057401067523764892965923164220007314439356829698670167778640", - "53": "28954700295118130234512355849317298799232179094852566623421541652106186276562", - "54": "15958782681044120532233862649699354067562059482538815462686088366110135452008", - "55": "43286615009455031784719797295956349308039236872337887241869415538128324649873" - } - ], - [ - { - "55": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - }, - { - "55": "1" - }, - { - "181": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - } - ], - [ - { - "181": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - }, - { - "181": "1" - }, - { - "182": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - } - ], - [ - { - "182": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - }, - { - "55": "1" - }, - { - "0": "15558578381003392888309173936039806021949114680855373870727168432126175091041", - "8": "36958213691714857769216413913717418756278650557262436863112402114560266322453", - "9": "45014442397094611256700146330680183783333287366446722041429690667721733015947", - "17": "36699347915436590119012342223007322693871385423905777984001272614601809060469", - "18": "4182050713175943474869699871949603240467902844541127021822081383262698883227", - "19": "1273529694669713607728232233990315371713710688403897798989378639884378802626", - "20": "8672800637664026646743353507843702204571133196091624193841028239432876612002", - "21": "27480134299646099452152758729175766347604505303723655843813415817155731692605", - "22": "30500110147764869241663641725809932131980022349733875173938526399578630402695", - "23": "37030819562059733930764362059115754276660327317281064767897494458549206154039", - "24": "3660739564462922177054254887881893920831064401602616405594399048113866124360", - "25": "49831051361945158893120755370319693358884714166399824350805931828231122758907", - "26": "24567375305900541593389151988598983318358988428396664010009286087762387169287", - "27": "40754943758301933511617295805822605115493844891518129662616663106045623085079", - "28": "5626873820042429608006775761281872331410675450527484263670837735872600436437", - "29": "34863835242065454762537466692915586106813265652827290812350232547736812461308", - "30": "28828205476385997846779928983272003862852760906233275569602803301408742183403", - "31": "22404421863587080361386335251285610425901872423967912438723918768609701754090", - "32": "49742800412465320613190097571905223754246690598046716044092048951594495144283", - "33": "43303826440986422772306777141453898681091336743660802199226193716736468111732", - "34": "6887733418436517002533875615346854133651070936202786825206750583634416865435", - "35": "49928646512630617197668763320934773675279798078327894506637275425144811858980", - "36": "31195925800150561447903998324756407744939544137813193398111834438159029670629", - "37": "50728333294076698795328593499497941485753817730764946846097348332971820028089", - "38": "47431846741560944119759707044268607324491358823045671830112338234018569771208", - "39": "11768075163522120199872609279918140574977309412921216959940230218079930033864", - "40": "41473359991692454826320644045116445959346115572840334171707672586867253916594", - "41": "34193163133561962307658969863144337775495246144895307564894490707896028980959", - "42": "35867805232289326499030564831339998043659154145919031292301374748120022854211", - "43": "14993180337074960720200686515994140668145164628921675157751616402398509521981", - "44": "44064398282333257020730328421876041904474323246572881596558050018032899535332", - "45": "19948524166797692818960016080188213484029547942940502496106522580431568460692", - "46": "25789203347610016219245228643172820780131628919673031699868379621320034965224", - "47": "23778934183651653875855073897696630838083898449618010562925986432739163562010", - "48": "42873329740499792269864769078473886692959327353108742535862616780175502798850", - "49": "38446814079139810910457761664258995461481738309202125910212759757408039109745", - "50": "15169960863969117799681893272465749807243108331232990797941252799802904789983", - "51": "38622443651082277770338516920718920222092540374828892382687036544580326399635", - "52": "39012125427428523625270049677804413638281221676939693649555247230928602879857", - "53": "11009779126651618604805444760109344004521652798024360065291954761372826045508", - "54": "10333640033246829710459890372538173174968500552565000956697270018521681136132", - "55": "33085109261284620782260186134834786881330708467426846946188432828680927670741", - "56": "43286615009455031784719797295956349308039236872337887241869415538128324649873" - } - ], - [ - { - "56": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - }, - { - "56": "1" - }, - { - "183": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - } - ], - [ - { - "183": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - }, - { - "183": "1" - }, - { - "184": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - } - ], - [ - { - "184": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - }, - { - "56": "1" - }, - { - "0": "9811599964264530187304305794911397412166832842489170888364387877850235210426", - "8": "35257600960717538875671174989277404503928084911858834487979693639941982630379", - "9": "3511536225223262604609683995454835423729933191908823349529006198749754827458", - "17": "11140552643295538623324758330780884994169601899807457822393096174630573504170", - "18": "46499105656219891570679669047008880047550492622686403885395088124165904580949", - "19": "13199005296328705942742673700739398316276900099170163010895756592340180569845", - "20": "38964461774913690861526712655704437143704150074595460975020257188853694562186", - "21": "46887995575115075192085668564705929400665308797905212794851598240413114450963", - "22": "43882743115731196202414273703052922012595016859296339769740332206157263189122", - "23": "20397062127725501361465306241040596120458947829181053355790739032074705276821", - "24": "23160115496829174048084493986276064970641698048212834450299759532374273843073", - "25": "4268056679769359643937094304142320277221752714811575715839175071336588346582", - "26": "38249448006087871870414075597249197962305563763288948092987171768225606048056", - "27": "33596237573662860295450833846931282978098516966413400438078941425910835629498", - "28": "23630434733803689743605699222542434625025001933784843468220959267528142872101", - "29": "33575464307243128981723379154379172357079406922008987267817849443623380855631", - "30": "30626007667114502838585126358916475666886034246231519650495792272912006029020", - "31": "13633205442678024490116374717532131352359980411562278664318957818815276920767", - "32": "12161045078858759438780510398498849284840447054488718213829402694006262441613", - "33": "50030805989570793011632246156070787126245014493357494630801666217569732219304", - "34": "16967256452093040311257114695634358181533509810798447616355511972126811938368", - "35": "37159904135829956970090166621705162746922718418428587224171612039491511282234", - "36": "27082508832330131584428263875403436892556356800227622939998462667466888078376", - "37": "8621624402418440223444480777677121828796697294554054054921746855244960962871", - "38": "13850968289508252572346818654663049160399799234068973515041336808130117998136", - "39": "48348378963979534612553729623784696025942387346766985612264001588619022509895", - "40": "20465842086964107698124849734843009969170881610962290987835753743429296723691", - "41": "8670623148692012434609961242682871457964873164561845443717617228286906566132", - "42": "16145238248104054550143407880643729479505295189450933988102913064388173330271", - "43": "10145441268572590579208613134932101955700079461488272082330201550579502389725", - "44": "25679027856520546825358322768399413062788017707148561415116457406931508103195", - "45": "32193319629788271552211183220519976653532663919955926627209228839062721385556", - "46": "13345002706260255504834651581939602387199150714457171852797771708361543609008", - "47": "47061450638514375826215811908744866879258526610731896947900612310952630500848", - "48": "6972801335038734140453678068767945491926699338337421365533088755153382412709", - "49": "45826496760241964327805018861710011778187306958856979803130891325927228280974", - "50": "37232668388052912044859799313792650240045356124449905327667091406656129739611", - "51": "1532679147724594765474517925527692031171670435697271341619284126367634189102", - "52": "13155359903310327012531704167967345564423841690627110816735525415741074577023", - "53": "2643346701398652130190188285932478236786842320402775663801174175616386099833", - "54": "8450242745402834923810032977394242745150947624276083478110833191908818570550", - "55": "22650864147209266095665093661522062646273819273669143119794073301199367811327", - "56": "38444507686116491782456252560622280787395517083096505398020732946145982829252", - "57": "43286615009455031784719797295956349308039236872337887241869415538128324649873" - } - ], - [ - { - "57": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - }, - { - "57": "1" - }, - { - "185": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - } - ], - [ - { - "185": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - }, - { - "185": "1" - }, - { - "186": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - } - ], - [ - { - "186": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - }, - { - "57": "1" - }, - { - "0": "11797479380255190086030457967941350172480879235135372778366002137304980472711", - "8": "15903879427501232694255638422894771343347562435171741841900994323840681792831", - "9": "50933137116934872809912330682256821781357990795311511926646529443934874684866", - "17": "35996553932923897415086385224589569272575176623262682197040707185932980608310", - "18": "15673980864813038456649688853613609271108903784258878253725217485084375952374", - "19": "24174431504151113175261776274814121376130441284859089138757876354435937799758", - "20": "51771580265810105335926249893197814447984473552914220937219071265403434683058", - "21": "11290676623867776344114482395303171169781518793948295324309426128608604849031", - "22": "8409495521111008620436530099469680340946687554032749058284685581311015911667", - "23": "40277766370322337046224132367735432681011902480335046503085989917085258671141", - "24": "32351579748207146295947573665198897103936797798774700208149653140337275101629", - "25": "26808655080887219095537193056365166400344791460751387396265567694499495682557", - "26": "43939295993427639264523982908312155952270428904976951179149363619349171568699", - "27": "2615928250125534253983662023398397482642537775194048496353954712112464381585", - "28": "24665117811273098571791268405924882650770009640818441195485571661529124368653", - "29": "23310083990793389709278189173553809917938459280773029801578963832080194662847", - "30": "38897903662727474762999506260664058810863249897094730433494501194313760658978", - "31": "22152896730736334549888318638692287204513515214316028404603478706319246406821", - "32": "40841491001727582810923937974220948519414818120996801118165557378803379209065", - "33": "45804241428533841200143162657819328022465976214145860566982147891478535457215", - "34": "38247945248539047400507866706601033492064308564388986885894572889109560162144", - "35": "47940123283699016257558947182409062548466132367938684643112467714426120763777", - "36": "12092623607389999641850228730930499684025853387799891848171999001242156520113", - "37": "49612578069481060603003816627697197677305941822287909347803282058610057851646", - "38": "20808330026913643960790880775016541975386323877332274178447810961387653739530", - "39": "12509250239645937872135956832140815144675242147625726684019380216316765581796", - "40": "13282348156818073297501589837760392749731423097724829433731087807561077290345", - "41": "8635613494893295811108084342410205802605449093122787212547598535283126066382", - "42": "32921001631426041445676343899570583143896202738541110737370163455053198523276", - "43": "15072150072951460790252803724636999781589895337377831063611633819852290167493", - "44": "47738287137100779085704833103435594777837426768554691154123749531090265614528", - "45": "7445854682870371420431653252737133712854292373432054892095511395705085407679", - "46": "21419594226096369102011540768316311221996509834983414160253966398167483277798", - "47": "41412475794020893132046501869760288631274866268483141282007604116478522130215", - "48": "29438264997412425909247180540071295742601686211397643346247079268958911123793", - "49": "45548144845518422625308249397535081611763184016229732029888668317030817383283", - "50": "41927051337616883142943994926384936534125160827462461617823879167958338420243", - "51": "43871397036862176880455740854832172473688538889696440670583829898652011761449", - "52": "35207008718646426805720704075193183187236956162781027008678627115192383928486", - "53": "29994332457423934758231668689495943901058791066006355079939621091191349632753", - "54": "38772336053192597942783585627048220283047009949910621214754172288582954650815", - "55": "23258426506563365742480079527437241168385234319706845454083583771341346157344", - "56": "21925302142198178264630423325182135754836599254707043729183366482558865259361", - "57": "19658305843685569169430362365353837930373042553254857801602254381749376221871", - "58": "43286615009455031784719797295956349308039236872337887241869415538128324649873" - } - ], - [ - { - "58": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - }, - { - "58": "1" - }, - { - "187": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - } - ], - [ - { - "187": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - }, - { - "187": "1" - }, - { - "188": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - } - ], - [ - { - "188": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - }, - { - "58": "1" - }, - { - "0": "15812276861397201227532067085271584728094218111982315967331671382133625425702", - "8": "18535644248950183777137251290930303361902992375131844745869851531133871101591", - "9": "51279430787531082701705987084781412833104680004081069854155329187001349341305", - "17": "4620280059807225859841498832568948712166192733167194648593693014954508365885", - "18": "32279062726889456243515576789074612857448705807164488364752217196476984385336", - "19": "44184272794121612856301067916009731591444999632122835020008743833827814863552", - "20": "29330345497656289885337341599199864997103599202957467011994075379964143536392", - "21": "5810129635340722855995660842622571514496893097535104267342213526806885038139", - "22": "22093601765509068412097730208250625216888878428029620474620624391082030724566", - "23": "33823292390456074445857176711494284388031659375969059374615101024483851681562", - "24": "15072443219894216835360983777898383891557547579089367459205122866145685995272", - "25": "11626606821172032584621965350821246286763970077350448014993294448335424093939", - "26": "25180933374455752788679644005606966820561921667480756716673289054562010226309", - "27": "1293692419630817662246021156447890909935006109796007300336215233157165802530", - "28": "40038366087558648585659130172915749952856978749469196738924528961463907971374", - "29": "47485661197731712229971694659034918012631606979817871191437440272676288502187", - "30": "2266783342230428215697200199668626694995414935224528224332416477665003813608", - "31": "38938621350782041225841985605041242562204765227075428144303567109368785432651", - "32": "11750757641454844391624222022215998328321386209820668812170311710921023483959", - "33": "4991232372153516775627384368557315643226357632901061310999707044873437432007", - "34": "39792158166938465032391835383243783697520243744886894060261989761329089919021", - "35": "49219084037788441430833002157865002740643066075395238743097679890017637651197", - "36": "24290370875537977927341659948899202892675773969715971504272559801894997624230", - "37": "821568644471580341005322773319808354960034337318505717800240692934651502145", - "38": "48762614452492834136469422970576561937183920526135495771505488136430876876113", - "39": "42134329839847921670579319483404900443425581513469279384164672676108050967277", - "40": "895129569681074695138752992405210404493215552983063837481217347568002758337", - "41": "7127544495965029401646717773079951414444422596100117426315918732427731834503", - "42": "23561203008659710736424481520837744068715071337987497033620756364166920628005", - "43": "8765990301331921992840574586282673763273298928042305630462451059175060999866", - "44": "36967831456732430915449372990747641804529407171981008266600435052906003828059", - "45": "14011901445376071324335811805745684445627094483111232940105054388150045797253", - "46": "50374876510000627886928660363770266275324035283249568246117252443100228691096", - "47": "8752478880339119208975018249995394197518781758545556932041583769657207755366", - "48": "37462509494912456254133476550641732048029849031342304242982998805380001516880", - "49": "28066082151029757310758300027320057333871698171350795423585946842186060828127", - "50": "29266447935243209577950456344099974544965022864956546295908626823688007890951", - "51": "15663062836082338776813337146561950964713663215969728180399468063126031180017", - "52": "22152069226694884890791439616391555314316473853628051925772872910546792004939", - "53": "45342217178073138250295208119690541363360396224002497690104428892154216307304", - "54": "5140994251500456411231211828824151820179055227524877764851708249117356248925", - "55": "45650138088064769636030235114597838277767315488933920125305256055766941545400", - "56": "9406552773588238504368824519032723679370593795249549746547590939424200942174", - "57": "34304589006885983711696510309541293412660869701243945658506473451337462560973", - "58": "44906303812554767121099823723834814371787058510416082935177272166347207070672", - "59": "43286615009455031784719797295956349308039236872337887241869415538128324649873" - } - ], - [ - { - "59": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - }, - { - "59": "1" - }, - { - "189": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - } - ], - [ - { - "189": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - }, - { - "189": "1" - }, - { - "190": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - } - ], - [ - { - "190": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - }, - { - "59": "1" - }, - { - "0": "14555033526911765831054005951477598685743695466168667850533171876658643729676", - "8": "34863523424364231614938921732466296539096491121427033596608711213460107724357", - "9": "27207535934594294618567784126810315469019735075077155894544655864054151572902", - "17": "5750256653513464772270728240767106259049960238754078985236533960553573217603", - "18": "27966986139375070816317855599335811167712069543547616545463636483849903171319", - "19": "48996383137165744560061674561699476843486153794209246017313888406269221666270", - "20": "2476119499950347117965077281283419172976844485414883598019890992416300049927", - "21": "19605519810492726935429407060044067856903606542149496202792719949332025169386", - "22": "8613512556546439080890935591940424299608376457901416051960964138272274942071", - "23": "11436359065316862937883286899732082402214811066474061966990279097030365329070", - "24": "39554166495063583479815671404631836465055498965504028848471474929024611216042", - "25": "19523470983586898988240630454875779055340295958702401903231329869131873961057", - "26": "26348171561658105438770458063226156174491270173373023323901210992687510469166", - "27": "39000576329217846269267382972726852667891052881859239144898377686097306720074", - "28": "1759539451344592922231121524951202200686716472285931383985167368459252745243", - "29": "44215007384708812547026515417691940988308520425821705337635516823963224312320", - "30": "3862928018832750321923891854067134902921544629899703616314984218766964942138", - "31": "21901636045985803764716269921651167505902593405337357105978024841479321558088", - "32": "15277032463621263351820813264538405039452211480260088892733451046931132793815", - "33": "47980315532056274693469074541315585470204512827658914626207874233482560262938", - "34": "13928209294676374986354534054581251377364148962389063419717048253436134839604", - "35": "46507853233669737936947896284949410488000541732414151172042936403069736895843", - "36": "5975339878438961254510761711737913220057327327920429634635847855679974385968", - "37": "46008417865678207009465851644805333939374068341600672204260151528384657420229", - "38": "32503514419112369135379707976457969627621664842193727041696160656391827897762", - "39": "29769885641142348996066484628958347559121673431399592937816912426690987936086", - "40": "28529485287992002327998886451815402427854447912805386641154547997477587424000", - "41": "10966056069092924465755325624999776666254573866314064813816398376495429392400", - "42": "23532024143992470589447289005983263662372741992402502112047294085724646358400", - "43": "6235066832243651718711793764111718577564538023035110780311297583819589637393", - "44": "37661601410810942143169047226518599079836676761701770864494875029400923702229", - "45": "51812320359747782931547711187698452386999889004542292053699082845002721608565", - "46": "46176950838959732318853419882362989611918701877381595807619426338789099528392", - "47": "46089031264899263959080610837239385297077066650560830826453176977157677727376", - "48": "35263622527179513804135349218413880394118823203804729405259008337527426821440", - "49": "23095995603625684421889036204755221630019576810534568201102507181887473131855", - "50": "8386585992550556921137720806880337531315050013800179766616859101176600258161", - "51": "45286850868675883216907245060188269722019657115235649596763286723407492330126", - "52": "29999732457492875872545122490568514877382586569195772952675283928339147878608", - "53": "15482532462112043627191388992155886864269024630544596242863184665227394845065", - "54": "25634082300307938257694021266206682661095479777731268539621430425145306759008", - "55": "40809550935485141086337434497663859640055401701049869955361322997962814204623", - "56": "42599837123830750380711027863755497897308646321548115070195289612716615924754", - "57": "21977168978137609776256406509083264783855133110410345478252231994821428414661", - "58": "36545003017022304986596970369089955362889935421785592657720103615656385746601", - "59": "11264203372774413051457764167603997729900179027056370285563478883438007644364", - "60": "43286615009455031784719797295956349308039236872337887241869415538128324649873" - } - ], - [ - { - "60": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - }, - { - "60": "1" - }, - { - "191": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - } - ], - [ - { - "191": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - }, - { - "191": "1" - }, - { - "192": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - } - ], - [ - { - "192": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - }, - { - "60": "1" - }, - { - "0": "8453370176619636688730899865196838058367647719211769227158095525122045566926", - "8": "14700273049248148503004145417342472819489376223376523850579514678795192227081", - "9": "29499326547663415118039654483404228825085905754372888154597331620186324201400", - "17": "16900751352067322471321849692971462399854556457660170971998689894833900813331", - "18": "16689479123960579558525350640076788009266299858209821170307162127601213020682", - "19": "14386894150226514038993853486320555463526577351517015808534222973907504459731", - "20": "14281827511074562696250851356442184389645138997631056247147800856186302003365", - "21": "7601252818192062195187621691597227288571904750280742359906535937941906367017", - "22": "4950822731455444783467234639504457829374996503506837168099325001874615550183", - "23": "51837903246315225107713370053074430254423132769542085893706060555586792897700", - "24": "16546152870877373244122296708221062426096626308631253855930455402080796562574", - "25": "4267865283645358193406033116664140612846132199837271175626375136847081417872", - "26": "31454641283437352122887508382533251804031425393665228594397434200271283770360", - "27": "652351439019401607116973282173938477922922008986176658355519764498125477040", - "28": "18594303503105103590816782598057378876343333312365732299430823372586744082303", - "29": "20533746798332961527887867601021334314023977450240156997756134802307938010802", - "30": "31730678564668931612365424731140004183405310786489510937867001188362404418596", - "31": "48479350898178129790326533098191184443453539697488280147441712352960713016015", - "32": "3701397702704437653048502268996115699107880672985127178774683611658752663787", - "33": "43460925668457066802977853258916243668192492196292654929076745569308999855359", - "34": "27733837539902012247848527574232219638074401231105290178750803799129180705787", - "35": "30083372598883073357569659985884766847092469428102463974683224520788843750263", - "36": "13416139037409930437710452642455058429395604708926529298923428519360184552116", - "37": "27623352308908286682197442773696828064780759541825113166920190830465760115310", - "38": "24467178523527661412490840403895102707219693834308197483111012821849214625368", - "39": "23728113528246855478500232569870948003639556873931983873702722387224671507274", - "40": "40129666380656807062809606434513663321184682779877056834215304787161622530992", - "41": "23196600856704274435727136372434040707394830440542391821430543031726044902028", - "42": "8215865162361209177076800640667002682904703694618158726144936914506095863437", - "43": "3321432016766039564500829840965935044807999892231160713467534524496739885083", - "44": "28396606736882264536595363707806916216399321241793162972947130804700644375984", - "45": "11194814843932421015565967864086907107533146380253464829460895377752980992085", - "46": "10247629864320245984498290366880059792803521307928493823642220920516164182974", - "47": "45540947434270323589980283215503197959394615493726851605142801290557895945522", - "48": "6351410723033070278915663444147684783159571748995149346685113381367496940470", - "49": "16646450007547282223758767567849289455552591191901648461491104448301851645160", - "50": "24889229155976855852906874602243636161245617243340644213538531529932171805657", - "51": "16505800654202230305733090602177678389553495759416075701766877632336332944277", - "52": "7675925178062114939817324806065476013942155510710362786272913586341990387463", - "53": "4359946372430343557819891012909070894466214109202481725255824345350007819348", - "54": "45310328951976216802832977340955247700000185785697448432973364509588835227620", - "55": "1384466495725852204781033898726991475424675908064122876245360290035272122590", - "56": "20546022705070598934171404288007102076165295582340576848525332838925487625279", - "57": "31529351690707589786177302613748003542923394436330434021084483472278799068151", - "58": "36579664282908550417057810780214348432407464125259718233915609127479074532371", - "59": "21731188521940091435304189115941138939077038176310508314733831383137706473253", - "60": "31242499920666589644175623123420152488672982280975416311907961288030179258970", - "61": "43286615009455031784719797295956349308039236872337887241869415538128324649873" - } - ], - [ - { - "61": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - }, - { - "61": "1" - }, - { - "193": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - } - ], - [ - { - "193": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - }, - { - "193": "1" - }, - { - "194": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - } - ], - [ - { - "194": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - }, - { - "61": "1" - }, - { - "0": "13746050345785052659407845574049356403121452981555493239274450074369130425982", - "8": "12160298752630338254530407945916766563834922532629505948426056026762355688759", - "9": "24489210578788599746303010662424713680859298825664124500851658126460028611472", - "17": "2416282630496950895368468643656141052319363876716233763023393100414280100706", - "18": "42981599081066594574328294535999978881017233028753835538536441541950646694255", - "19": "15212058715196543168691534562115742556260998059165932940223155050835392905871", - "20": "7202642958849074041149709775648076784468091348519822681347269528720073196143", - "21": "6522432676358732147478141817940573321601463188148893724131345313299366277943", - "22": "30767391711383501088521957411965231355303322859244512663417573036486769560650", - "23": "25746217630755160409444044629613582263482418055996449213633577488700523972799", - "24": "21021489915403929056022381202339071137483935615957803312647510804006503608618", - "25": "25542488973179203547537575381757126623160646063126252149260646642491985455010", - "26": "39202783094033560367251586850007418595231762966762456877083689422103114302873", - "27": "42659979988686870538834442012951157000488835088586275318454859917364636180137", - "28": "22430322677324130181763040670691241668786011709052687276545407961918692563488", - "29": "48406987307229118673240050520579202466569377333779345715053781378573549122625", - "30": "32512762645259969846085846374668170239637901875870563058345044054829768690833", - "31": "12964332751459612423825674278436953024702343697445272589499909315219193614844", - "32": "17674929204239727197236875816434856294782854402608690167877003244499062707806", - "33": "35996901399392717462862132250727787257166816817003415696247648759044238397277", - "34": "28325316069932751182520989939110557399260474921579709781574209643558465758695", - "35": "21833357143948235033591121406164464285108953595523603678956916489804539718072", - "36": "38331690060583629680685237980166619778480849178533510350226140209194952416669", - "37": "1910008285596138509433156975888508301958334203765702016398433769673831959592", - "38": "20529999853741449522757630250231340974686162798879163149077571373915494358582", - "39": "47621487574637106182778429441529768269093324718613739643526908500865444681430", - "40": "7640467605139465408312247914559549873127370928782720115148094253764904080185", - "41": "35573711437974105808858853822651224195828755279535917162598043903770539612809", - "42": "35715418186459305889507607119939666907048938427469427022964475999452421772838", - "43": "22336959598789106495327873836901651981071934364977905040499914092266492901141", - "44": "34511547816643469568866276318926640155368224942611919472019254390342347425444", - "45": "48824917938452478622888631878667621239852169635968133501076368017563996390439", - "46": "20853660078940660344920339985103970034680290671611986933111153861490558697822", - "47": "10571432867175603940150031107922561124766978468619575879683648289217524381660", - "48": "49493276046799327428140895006466786215873772846362310379203146808983456575065", - "49": "36713702121150926082432966348187867976049574274749083575831213287992908318467", - "50": "26713043222935439313168338297667161549663121998436625146885077388937336772475", - "51": "36921874834948147602387153908118301866871622664597884998360102167718472222034", - "52": "21171928867526122304893463972788477735084019247057030465089017334283634744053", - "53": "102857812645606371641637208241588513525948993002616265152481194966021407713", - "54": "11157164996773026209481356607883839838979228741589344866739125352400725182348", - "55": "23717811700099572458850518496200474317486991557110676129799763478332773039046", - "56": "39414262539969457155290717359724715561792308742773407579245220001788434864973", - "57": "14340877577137189560256276309492674267175395555397058879478961925444677924606", - "58": "31915161442890750462810947067448112034018865009430288942708990316341338025344", - "59": "15874618092216782249393338768265106006925014950905031167874131481528276254681", - "60": "44678910721548955339712967126788169304877529192714225642695406925806633973101", - "61": "47156379770217313095519756335734358221876217483869599784859342147494897869852", - "62": "43286615009455031784719797295956349308039236872337887241869415538128324649873" - } - ], - [ - { - "62": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - }, - { - "62": "1" - }, - { - "195": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - } - ], - [ - { - "195": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - }, - { - "195": "1" - }, - { - "196": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - } - ], - [ - { - "196": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - }, - { - "62": "1" - }, - { - "0": "14759221361221742392877550517283338785302973165056606116918148040408389214809", - "8": "202407840431875867766643426059833405749180713740029168212121726829111456034", - "9": "4389625065657449317030702350118528836723412888175923456795303622692603348461", - "17": "22124547763030614956723956856326261205118864432496713670905557893356629288740", - "18": "45015563437869612030875693329426287865677755054832015531433903251609211909263", - "19": "30132039976894230991911643394121154959353962970395263040064521052303401753375", - "20": "33261906612104730940158418188127527192912654771923390369884450357809733512018", - "21": "26075012561174944464659081211721167856622575581389757714824703074162184755500", - "22": "34422329714935664530650383509330601829983540590145237423922650433433290306244", - "23": "22826653351476414916129886030807788398849717147480080565816228954869033149809", - "24": "17143881673837099860443000027845204658537360143875256170701468782769380870994", - "25": "1155594379190122261495268389870511496052391222425417245436215318946864734397", - "26": "27994354284010570886378705670130542222881562968881762784737961665295328109152", - "27": "45231133596052197635441293735131396095159983596261768316187212236318546544039", - "28": "16262625025122187759545307417174368345931365220187530099940772777713941503773", - "29": "17744349920858472243804562537874098925427018001413294470494809327274841825536", - "30": "39349295918782787440679267240707908956600518497269391393014839001610583708951", - "31": "32757745102683417706190882994574011250024626837577371684864966374698366374416", - "32": "10378422045359168268143655276949180380523554526659479511729656315605983861813", - "33": "20295155718568791125722514790729739347910862321393260313425396612833381792986", - "34": "23673313318641707960531293961064692186143094921293655158657384119991662470445", - "35": "8785396070667577989992666411528897019609520717717293376504614371173471548867", - "36": "4105435180062589839415042674755865976958947375050106721326799093268850695917", - "37": "45202079445709425613495500848516752301308963291977529462377843726961768403108", - "38": "44583613145060041115161155641231271374067442119672941778587718531269742487124", - "39": "41082691868835919128935860287122418783595901946872388164274532321275156677148", - "40": "29046324304980759156362827555335638076628152980071794850825882786495925128687", - "41": "18667535964814177829900659772648241778938686873519814281138220943998997612903", - "42": "40387146549309737497467663883891468396539144007385755558789294261647364013939", - "43": "13979902850909523934126392020966080066041119739795649557053411463088861758217", - "44": "17527637028087741448032923941231911813916383117712210814057933375044595533521", - "45": "8093583585228186186026281210884812121230182868152944202457872179246298741037", - "46": "26476544518972331283253815016259224039059162169809722353651089996545464056047", - "47": "16930513270556706168816870780416023598389664126082227060736388156415338467046", - "48": "26378167911658665390128023595423353673806432673057019895828190214510813544676", - "49": "36438971143067186466459211641069632178659359451497133561777853824712959150707", - "50": "31219984588343227847567592575943601250271080362604322004882881874762994202476", - "51": "21531408845797380042597696391618043923815122995271396214228758000829838292801", - "52": "45005119603522978749818109516984003357286054479567303857884173247462067307659", - "53": "52158206475962808364205342072108229411532691697245532292168363626638390530973", - "54": "22972056838090000140748294757470531319524401910776998371572311009586332693783", - "55": "43229442928392347842299135599657644251105686012690357203224589867278468699484", - "56": "5880835793078016588051561462709350330896161665659458373952555687144518761911", - "57": "24568610925075405039864661949450358667168673161553757339074381162686686643210", - "58": "12539646897245244286054609727668992110829914176219447921630768207798677120345", - "59": "22877905117837514250317404440252599030960777959680398572233601350668547841979", - "60": "7143486405429330723189502435109301316884002952786063678248225165035176463854", - "61": "27419536701911088779893332686301237261792647785541455115979948637938215988950", - "62": "22724626402939750092672426482330819282956047222097810847261056122387673907003", - "63": "43286615009455031784719797295956349308039236872337887241869415538128324649873" - } - ], - [ - { - "63": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - }, - { - "63": "1" - }, - { - "197": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - } - ], - [ - { - "197": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - }, - { - "197": "1" - }, - { - "198": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - } - ], - [ - { - "198": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - }, - { - "63": "1" - }, - { - "0": "12918384943133505870596332126482017188382093626741625148832869110825500058603", - "8": "37884471416135629084723192987622969071086230264500793597466006263030137077950", - "9": "40997719029526128938822562421152492865154216291293700642781556044594017130687", - "17": "28422544056114001658103762492539660648378977517899528721398186317915948612359", - "18": "3777851713579777371958374617881770761271960394744080397007051823789849641816", - "19": "20827762194490943506410781321651756764545592934544937415436559374710741109737", - "20": "8539356410893075195659365992316188968031657384439031113700177936830697517126", - "21": "46557132888501387188241359432113836916710605499313174639000457027283747484879", - "22": "34978267062325686259874599292218175361180457311333429302345318509877047033559", - "23": "35219361896831725206042534504188001395081288968945915824641456462653969075853", - "24": "35596816375591274151942729752532234635679149886114395569813380802867966213102", - "25": "47148162502421090881221294300417151695945096449423734460024056913379613668879", - "26": "29651522042631797561422500254422319203910570148836217744653014334164403772943", - "27": "1847631130218946164597098437158686334545365640077428838105219075711049634604", - "28": "29307134038554350007637632447404944007917016757747002014845600111364985803941", - "29": "51348434721896323438054108232374976710317278715495295660141702151571596569055", - "30": "11538985237996048437856555123431754598334681850862992672846414529606314266722", - "31": "30452492680584036611884799638558477586232445724533220827796413838743585021305", - "32": "10738371907775639766377402684062060893726283997071754458567283588906748285425", - "33": "42249561372145875564584798327160785961939181991103025819325731623408043502846", - "34": "17384460043942341460753370117147480611077456175890768866882184555085093185196", - "35": "7324475402400776752613422030297234561498002095837482724161990369315988202836", - "36": "14871871983284849586898314185895862721722540673412961437338782242046066203720", - "37": "8791752674186288337434719118017720160276642641087529536728799257142194019788", - "38": "25863809413522720318490550545243958988225776745373671280593540276989282981518", - "39": "35685300507826852096121812557700271526493381219144479637112669279809775634248", - "40": "17960006980736475815000058245874904596782169554537034715563879238696961989605", - "41": "15147995969583918611422482441824641725306720470228930566133362853229743868656", - "42": "37939574642141313900644464917258647561262068854008519949337337794397706450390", - "43": "24472467249941534687992611259859372933036087014776849400815946180270625819405", - "44": "34795645894334562558341714221686236743790934315646650188430670601999528951212", - "45": "38767101515793527839724033728231422891446012493820979522040624599261645637887", - "46": "51842944408328664131488103675314170545079642533026534611369105342485959007095", - "47": "26467268014976196626683570195589840758388162413314253062706483680791632384255", - "48": "36184737938426586089667113896921975582648766381854376917666556710832395679768", - "49": "22274491065043784835874413348941313529531557064357999466399937653111349248361", - "50": "19181509388264500023489727959063259557029217617589168959461022249029388383641", - "51": "46335338763635641898985867217132651878286217593127042861085708065485767667048", - "52": "19417656836045927120748987511559080650879372396173767406745654045758414567495", - "53": "16371970590349766009874643690622166771091797777676607265789270580527510135606", - "54": "32461066818222490038688311538299905988991813620133592752338220509248469684698", - "55": "35545969304008272171542829103829330482452203789770741905697711244411776816308", - "56": "3585214683093891494729552695583340390838793369189982744620754034343267965683", - "57": "5717250460095572680081036252697272856749341439526325479554925055633393920012", - "58": "45088810464445420970598504738085820976478745955450740198720035851613285424806", - "59": "22889678906707205465882207002697731324433984931875973764116893412533852353562", - "60": "20246726150842890101123626292770561959755012050100455971819281434059733660399", - "61": "37308293285466307167049349220612089335089218718450562346102192007264820097303", - "62": "11526891281939313199283915366936608788576466278315840466850096701862306953345", - "63": "19329167694872896057870546841567444325193072978814365109208504264676308420872", - "64": "43286615009455031784719797295956349308039236872337887241869415538128324649873" - } - ], - [ - { - "64": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - }, - { - "64": "1" - }, - { - "199": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - } - ], - [ - { - "199": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - }, - { - "199": "1" - }, - { - "200": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - } - ], - [ - { - "200": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - }, - { - "64": "1" - }, - { - "0": "6090669736471884927589246515939581624927624112611999595504716515722644260809", - "8": "18105251099585817569835043999385430215806563651193888127073386558902396596344", - "9": "41526291405968308678221194608828183005211853992667515271879265890974408750226", - "17": "49219509368077229192942071442807422097839945222055444899081529892516169321458", - "18": "11334560377258205833144348273135474091373431022959076454854066258193704327890", - "19": "2506508100877849499651303207301636670026230837692870274229885413361621870185", - "20": "40246447128506644978850804572358196374832247163743673671469340334972570231358", - "21": "45222439883624862587946396030080444588624721024093310773966604333822400945002", - "22": "23479663061856897000062310324514637771716867367139061550699779575420501026181", - "23": "23338022178919762668066885868754666854455717253264977457455256521992460756170", - "24": "2814596005228444192350999725740468647017485705068745797397179203863814478623", - "25": "32442123377203054038834281710321840020860763228199753516852116146595162114037", - "26": "22687386648875819875585015987517622435020679537314158956442276325533979531821", - "27": "42607835634976770906793119482717689012209402340391075236842715291294189368510", - "28": "36047107014043816788456772264444918742940686969403091181804639631605816343675", - "29": "22969419565233898168994871240159902827951596689703514828432608694723116107203", - "30": "19924728791436406491714484631782892851789178531173809577475349495899031819818", - "31": "7335824083778420543976623335391766319255872057519501385585542331257382709970", - "32": "14494490028954701737605930677483499025021395479613479506863034869705024032280", - "33": "3112583029899138538660285395956980830348094096912762028808367745082961581141", - "34": "35291127916319902928033830274267769857513523206153555626135063764014496603616", - "35": "4400714566440374574744561101193006149424032925832886003025102748254307346989", - "36": "36469376918303233933766274358971415066132066433752538835540242385238637431516", - "37": "47024657524237091558130579012841298812529206391204800189813171179474302353016", - "38": "28487291842040381413744735037427728615151302117837067425702500383621279598662", - "39": "29561222909992054111416571039982623683374336269353761486042174929834579869735", - "40": "50508781043589651771011556653927758176062057933242227547157410243315868497283", - "41": "17146615808929550296773456530763421355236587048146265440186773271443776958466", - "42": "23831093463760166157409078240763132662730977225061749719269163834674590937102", - "43": "9104073966198879983757819132314587834146985487258225551145142651317073690951", - "44": "37421392875808188007537851763884460124426196384618642693564751819575990531588", - "45": "12413895683646046925942597159696063185715352142515623481609090218549742921064", - "46": "22696357473199461416570771076555296991426639967632882877973822166407886576276", - "47": "44352580154064927153763751358768744093128095445229789832138430581527101309215", - "48": "48517453146321583484203670059007142342037697323539465805659974474904809507251", - "49": "21958683350938132268140867475785616243873969149206206814383773314047212966971", - "50": "1173836269856856295028964019110711116960994767738047215334016502772772047504", - "51": "23771401865152851318715410824609768029118131372140592123498772566769121634204", - "52": "31186850478596835615755162198441498862549854163207382962064308273977816309120", - "53": "825844607743784307945347104527361817104784350321657886580894118679002291572", - "54": "51134953916632441060462847180166796025218433023741590593889493636977505417173", - "55": "35619145164174505356266704107412060808684011660269423044995482227874707825377", - "56": "38006681173682221167228564483182169105601655732033660936879138556643675660209", - "57": "20160096619046027285451124384487669973338585183637016507512971648785247355962", - "58": "39339024394244310857002919951215253370549967835254042446049442860469731544899", - "59": "25412228166228200904079093833840955100111534127125963016657062390305530944197", - "60": "26139634550891915320553656446148719429708727411997914181042225639970370428498", - "61": "22720938231177202001939796125063120366308497298844865491888693096075225403213", - "62": "2137921758406683954443404933020497542127313276607299321099632492248859975302", - "63": "44525510071135339933687851559324847511100413984904835875185688339499873225170", - "64": "12441252914149718702295129466443083148043622311789141603181901700485078285379", - "65": "43286615009455031784719797295956349308039236872337887241869415538128324649873" - } - ], - [ - { - "65": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - }, - { - "65": "1" - }, - { - "201": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - } - ], - [ - { - "201": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - }, - { - "201": "1" - }, - { - "202": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - } - ], - [ - { - "202": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - }, - { - "65": "1" - }, - { - "0": "5818256914990297609452278687275570020517086210519703828397962481130049333349", - "8": "16886079687541724562690581328344298917364793110879854895831399777116893637240", - "9": "48120063633031812035166768434439480639318970907693851558904662831111112572919", - "17": "11549473686999029992645724506487348379462282945872767737452958571673863440187", - "18": "39497379618000149296711855719469117254948881653652385085599228212306691176761", - "19": "37626527554054003640119820408171354262180845137269474728630411854620460636604", - "20": "44176145540096700676915226978695765623228912160013981356498432208717092862842", - "21": "20172138230818294496585292994197552446414738915200862331716424599255839772622", - "22": "3539763778150435811442849303703688861172949084256459494751327464542589249373", - "23": "5302853349950207638849857393260799479358798568039238517212315122442580998843", - "24": "51395342537742067628806524007746481375506598953772175556135273664459639749332", - "25": "46351734528949099020871844310701449434921999874202492674249947563040312692035", - "26": "37156572147558103991677908746740193329131617612478837467906922459217526430096", - "27": "26541857214625046890505606346464772162956284166089401764860866768125413560399", - "28": "49272565304209497471309843271642650670664477617740860126201160730526378524153", - "29": "44641233901811704474926795588119527505940031447103674055906826303067924172063", - "30": "28928119520625370614448507762698367735494676561915670084893760419469006658444", - "31": "9476171922760572905935739764332931346265314610313639961960034605297840304651", - "32": "16625567146320184745768815198749953598851527559448099631478480473468954425474", - "33": "28329392871781854888581019046861902447981492336476457427733271269769529906676", - "34": "1369661764262020529469445937917483207891748653198732779280365052042746176564", - "35": "5483583649041200701150335130100661262616965605597784004779741143891263402163", - "36": "21951495011660570322796526825334043519803200448462712324197994917016906180456", - "37": "21398636118643343201390659672066247311875318621993092564779843538570692735008", - "38": "13307876292837631461539961906040696053480825242606323465448584389064203081183", - "39": "6236991249635313076879767685753337320622240980129205013195900938545317467253", - "40": "27835397807967762240261088384156775751784649430355373484614647602637774534671", - "41": "31790069572885742795717808500100960455111161536268504809957780615304573694908", - "42": "20071865382219335471577788449738322920232578359084575210043654623954973932390", - "43": "7535528675038112428031242515022606349587975693734031438879292876905951865144", - "44": "24115460360816387445486595341638181040754598857512413453005479045949963684894", - "45": "48865535761889611418824055480388360015872134554790443375518319438802297547250", - "46": "40456865076202821997418028814632984105252177423838328529121856187471207760025", - "47": "16943061436318094287561257477872730952359684242562932773702873251838744337353", - "48": "31207628300866901298463342096415156137057489675842168463213931480649376071490", - "49": "37829864701184972740812235598093157528232987956785507447008623045734747424867", - "50": "20737210560007762890923805944473968159483894557805546906694965050661342446736", - "51": "44725602815927210898029025256526434834414763708454075680766616021284482042493", - "52": "23822670638298706943959556384153359137573323565974344864429928793545362373032", - "53": "23490647444586733977238678750275615435881433066126286406681597357542937199166", - "54": "4442368127083219467359858382639898829367068849341350637593491732272100353862", - "55": "30002259305586097863990530495065626559773373913960658802953486745450099759553", - "56": "8801453602701519111052530988408105862276180928197579354183531401224304573101", - "57": "12532869501606963398797723910612090230718937099261431263334640622293692290665", - "58": "22317619582477753815482091271128515364228446601650063371190563728683969728070", - "59": "51551507597405477301911508867764058885057517166346227055341170599660695333090", - "60": "12487389287258251428934795629306210046122136011424889557054482782731930839930", - "61": "50049286197091948785630681053055790564422412751107260739903691917452633628501", - "62": "13097122013095355616930354035744060399033782903029472199660181880283766937835", - "63": "15281855757331157959533326633520498980204938317757711805812320117113995302056", - "64": "7785813985806907566232330210379162160750217947757546496340799009824587830275", - "65": "16275755461783187583809512826957500092486877567197714174133477045457407719651", - "66": "43286615009455031784719797295956349308039236872337887241869415538128324649873" - } - ], - [ - { - "66": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - }, - { - "66": "1" - }, - { - "203": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - } - ], - [ - { - "203": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - }, - { - "203": "1" - }, - { - "204": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - } - ], - [ - { - "204": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - }, - { - "66": "1" - }, - { - "0": "21665668991006174860786913595488512200171275130423190085616080866607498513357", - "8": "30507404025381522269600131880367962444871567514246386809469375298884173043110", - "9": "8875539339103436460218999392495639490304489488901678588388085548698629290375", - "17": "31204636303324709434999301738982489604338652711355462544530211397885986877711", - "18": "51846145419202947923327749015372367419306272995524625817328537421320430442239", - "19": "1111718517356132532386590672129414904199161187422505169286434521433370608479", - "20": "14544241264016276951029946978958111569079385799757550086488106899444827457842", - "21": "4267787217702637664101856010688561941421084834277670936342248046379441389167", - "22": "44899608136002230894196449081929888490051554629422855436858915883604068466546", - "23": "52330272032963331145889624003938742272270289693598161106128351449541450753763", - "24": "22572195481986825892758038478573625010204260601764183633754280744995582703762", - "25": "35794334857998514323903583605094999232820689675686940869136423897134931675488", - "26": "38785414255245004492635961001004501653408126959239598918419982980859822274054", - "27": "50191488472862114610701949559547755110863200324993249084218765421223986993350", - "28": "199242690165686288073913244503430578113264484790644664780849154755014186792", - "29": "46383900825122832006482745001285028074169464092873540455868614874656314567126", - "30": "850983914612468364627065902686152028620080509323729024544645589854110617387", - "31": "5836497826779138043522959714413400435809938416927786929179703963888146228859", - "32": "1784393604819848437454000489646311656328319520328328583736364716606488345507", - "33": "4146278025383699246168139850447714143023267822452214392560565435738887876599", - "34": "23431500533368174412390803946628180151063938913480568111285430064921914692158", - "35": "35888234271154605161351721061806105557630325284349245823055538970225020471026", - "36": "4469363946371184696492043002684100839918548381996842877990046449209210002638", - "37": "38071831341898010261465071397541893469847318059491195957229791252292399356917", - "38": "39567776739556138363273968517160372369626996500353570946604384895739839584059", - "39": "40917172085802706071657971434472012495079766543834967122796678595363781360904", - "40": "49389974474944355249948888394445555273223933347564691577485627516521874006781", - "41": "1932404071083105703701524852002897608493085449532510425826262742997783288221", - "42": "399534921351879543424711924539329248284479211244687002564025160460570901805", - "43": "26425394299857290643386038211766540784307009640320527060942463427276745301379", - "44": "21859962362666448427681422373195733201404130832094422848590978814638696650587", - "45": "8526435779607488789490052945552966754318815158341604630915538292938845526498", - "46": "44576254071620971100146600001786112291291380389478450630028412618411598248620", - "47": "17106549514958316430045624567551382874469495708991882176345867756050584090460", - "48": "32404030672645571745776353209772645610996757728421467654913069647807270673015", - "49": "25372360495812822398972241094506552408326261090071227231284863564308763830455", - "50": "30046288344231147739273279154052856278368214827110836559539783191839250091743", - "51": "45084861332213492865636229857128093772131937665846776582036980131414987663493", - "52": "45765275839105241652834088562886647103655014791876098579044387490654984850556", - "53": "3124055724428637395135659647837257059523349642196739624095905473716900902599", - "54": "25004876446586064813934950007146289962807962920369542094480609911962504794042", - "55": "47627965322087120269019873344546673114991648862075255773883849924879527522125", - "56": "23132172113907149861532396473924934847100690812932747379469146711379912405042", - "57": "42629253066145047719028230487301821654265759428883944266596267612570718789348", - "58": "5943001090118508162539351810387751400551993261793535136057052569165765825830", - "59": "12460475920522930922004415615623509166787837909275476044607188615432040726225", - "60": "46843820541314873774898523881983574521088091530972191296511045440327015033072", - "61": "14067123983844959682538161310095965964167648712957484795009820026164582381074", - "62": "14119055534806965132731330532400309559074028456071264674781592874812544450141", - "63": "49050943537647976300239469521371615224390921443321014892080731068069501025948", - "64": "36562725080363385624164461347437875870902427394549599270213519318021459865031", - "65": "49337876944525854017704900047138067109843203981615246324454897016961714568737", - "66": "7633106466419211795855349573109400483930333323377713263919614912900007318455", - "67": "43286615009455031784719797295956349308039236872337887241869415538128324649873" - } - ], - [ - { - "67": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - }, - { - "67": "1" - }, - { - "205": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - } - ], - [ - { - "205": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - }, - { - "205": "1" - }, - { - "206": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - } - ], - [ - { - "206": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - }, - { - "67": "1" - }, - { - "0": "2268297495968854614780848291068505574765456615293615512346180903730018964697", - "8": "35877990414132628773605193313470305323317814794935389655621199110738954837930", - "9": "47508263265065616565570489821155015752960484475610334461200710438181333320611", - "17": "34993532542453944782979069798391451723163362083392903233557958548457185664461", - "18": "13946790474995103029760579402879948960490746331685120471338455859016735048311", - "19": "5901599305185159007975560094616575851505988538342891876110343594489658570608", - "20": "29928404026726806152943356875525932448637626674882100757214354576984966884468", - "21": "49920664144093215447123878523612335841597358292738938595431798228135683243263", - "22": "29183082832215219415919710524036159512602704549163381090393255614084206512487", - "23": "3098312088003111151651874596986880311682801092261129304427154548059658543501", - "24": "52355399735396209126434734733906557274349910395682880401590612141527747011267", - "25": "20311895988813568086604447001343791076437636703294720569920436354588084488412", - "26": "11317097308005776684955185499374537793813088950866298750942236326874829776010", - "27": "18181996769109491533831183038613760199412477790365964460880177327246591485895", - "28": "16565696322261620813963399706583313549678131517253913742133618117724633126384", - "29": "44178028625176747457397681326359683868666696971148465888162563174035251275565", - "30": "38966834234439017273064047550249941995200699743111551759739450629829303505192", - "31": "39710776913835611279773173882878998455404467744588108206406048176458944163414", - "32": "26307267385156089604749558269874327914501651529986548980701524025540511606704", - "33": "9770225215092028816593376862101366977309661521800335443692058949454680095983", - "34": "35261394279360843085455004570849171890873533654631950487518419775167834053794", - "35": "17032654095748550186921526559790477729405407243044450010473191183935220095626", - "36": "47267290706759194779749403039573114633535476855059484854433648428364969127846", - "37": "49521191618268604122833499491546422926314297069208628898242672088079247257546", - "38": "36488870844486706860274103279503297345323916209771043884753231642539946511567", - "39": "23661630248342976796394953001897902025975495504377549453605686753250476342768", - "40": "2442309207508135740580060584660569878787082124636639469460095405504941546415", - "41": "32433039857633585405348572521286352753051022553763587133322973179785228004702", - "42": "23373430325134341564374600231059458367357804740037904695429377573404458598329", - "43": "42126889597291198397251298863497822342481205374510784432119973740468648212136", - "44": "15859486764029342941273439325843903606406770011573061562310801627352889111151", - "45": "45087109542147330325668865737995461133456581823315464974325553553704382801784", - "46": "14452179250861604040765499785175408800203224749862047924816304318262621594956", - "47": "17144917191806268086959561125255161779452614799253146195852421894573871720932", - "48": "10377364284453390405152984373158716609162878046888675265631266476888733118630", - "49": "7358894747676596288502669068422147377112035379280138905658048225146999120370", - "50": "27104349451124518266527554031794280080077406188302534178981859553938710405172", - "51": "13548117566822831253753893830960538144353363808076203114265716335983139660913", - "52": "5813665685938423371174187948977505230669545995930835790709433610904873891158", - "53": "35010200153801518012357001201569280228032473364221572970546426171016514727260", - "54": "8785914674829638682485428870526287117110406229548059834565682842442278791520", - "55": "33886343070008821587037508535012919043970555361603486500896135983365045881841", - "56": "40471542637260246260337945970835116683190556911775665131481846516348337210818", - "57": "50772393795118986662637878085089223691951270244637696147762773051405511599347", - "58": "50753684863262172033541609577795092927309370385750442004030999087359509178024", - "59": "41682731753085550046420817738828916289699410156228435898840882883631225679282", - "60": "42131816690084251573065091413248422804960032593693648819011413706622920230327", - "61": "49097085622213271749682042666943527374252071464076995078188797361569829418449", - "62": "10610863780848317008455595187171729990515669742149544234074900074746732993427", - "63": "29781002550204903658383932783882304271542732656029231451387470735822187075338", - "64": "51776527002339441103956894491367914103406030116412254049590845879491484759220", - "65": "32736693839639572947835522920629067561896969430884632639799539126874894890057", - "66": "32771828863095421310334648684039980848345628200719161861599593264420944609532", - "67": "11837011406228020095853680681968268735481893787424034497353581492517584470585", - "68": "43286615009455031784719797295956349308039236872337887241869415538128324649873" - } - ], - [ - { - "68": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - }, - { - "68": "1" - }, - { - "207": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - } - ], - [ - { - "207": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - }, - { - "207": "1" - }, - { - "208": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - } - ], - [ - { - "208": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - }, - { - "68": "1" - }, - { - "0": "13973211016421000871032597822807506910708863418864348758128380360422656258892", - "8": "41118576002653327499770609016905077860087172937406361336819233815720214462936", - "9": "11517314634182668778771923197452888350046553913028069763707252617830626027291", - "17": "46587966413583243472263752481180461965826054643500807161411831304366826918546", - "18": "39436213021122964038014245952841684080810261617186442986621542783795249860062", - "19": "10018932179513029553888257484959005143128826038685423381503216025190052086095", - "20": "46460985945321966592464567466362026442234216503146633196537877181564391083476", - "21": "5908883549086915351765209385369358621971094877303381421917924256599819056410", - "22": "47960967091060216008075176276692203902003057182173975056120234521311565439593", - "23": "49883038427772347064611367186764889233875975879611438271784458452096273618434", - "24": "40993746929720535562602719169864240408884758852996005295971689780806548680134", - "25": "35688053528774992662759004656975229975934343876058043844830810857648560015072", - "26": "44028534546221291488523454446789123620353471121272351679708253761199035390732", - "27": "20346104874714566637789752714312695131443184600416931555253511747454845249125", - "28": "15812839473998102219043608654885406364356613079752581678150131820915664665163", - "29": "46286554907408251919609236558891349821694038099582976464342711939836906152887", - "30": "31702649422986590810892657089742984286878491272823648297179723179481267741197", - "31": "44780221370017494066854628494754555526078528612759659270267812530788072156742", - "32": "48619117205137954149057858905660464806810241495253957117013513237262764634930", - "33": "38560325351523745974420206197086782236927412873373082012481006118556803825594", - "34": "19740160354550333981745868986277470578854391505450441041161324441829111815779", - "35": "50931635444526673034687009335803999897525768626455469728589872661682795986450", - "36": "45439521820153132828424846573450275866573360061787681356223254678804194735919", - "37": "13271244582374382551016842801156570419421681090068697938089697133288725563519", - "38": "14752503463130422563759796120682237281392968535271599546430110081533286343064", - "39": "6911092654582091827842824292020762604327519934363302553266764620429918730636", - "40": "14340036000420207043348163927383816914746220288770559736021681161648993922410", - "41": "40083516194373381922512640448412979416663195485914765860317920452914894137528", - "42": "52344664480053029839987054306285306390036343318460932681161781276576860269724", - "43": "40578040612578296951712727693847954128466356338067225008030144085494953494740", - "44": "7648018416564067493763428363704200774754966109444175367096507168335520864212", - "45": "21680403839205765914026199590281113186237655529026051577642916763972951484963", - "46": "29828865668769799544354542262967935361939111516793405358539406238619305184889", - "47": "26572034157615763740316582558341821743972964157027373462938375555888956297754", - "48": "29735951033867555626301148938281567725499380253026544707851743576969595221161", - "49": "37525459778110246792534870473864244146624364672574355088530612450474543472408", - "50": "805462877325465322522426328091680911339128260564495972276581418717261478452", - "51": "25486437027630641623716268653384580237387229341488144306664661553146846157871", - "52": "13985216014824674207436227744683187903200441927312966213037656845990622275732", - "53": "37181615508203272185119848305681855482311707276789788769018606670808903478486", - "54": "8518023537973289982243295684343710770775440879699134108415153768499953370401", - "55": "10058459074016898471452840172912664549765220738943102031231816162822299966275", - "56": "22917775133588387535647206655930531757782137657005273743589892010467331324730", - "57": "11961339413556332976088673229764685070633315809136005504541134060474735253358", - "58": "47218599654066214746513620730589797681782218986557446034580471365233969709866", - "59": "5589922617318229733440721291139666829941324114830458247390575227895077000996", - "60": "20058535264368568287169176021088436914579925121615582694002098364429021007324", - "61": "8465883864765429819610880788915776714870297415231000066513177018199257137978", - "62": "23699955268385785740204419017620104605659712114837548974541208370921358568742", - "63": "44265759655466264888409474845867917501364819735158580748689773146308403884072", - "64": "9779152584303729929638748541358088257552442603356569841424553548910411460961", - "65": "39520775488934540054907132289078015595467767018120758223570587099591342304980", - "66": "45846561339321469409997990214872591231331282129670948960544001629672878200483", - "67": "9425348310700156528585241699227789683580378754416749705070669108621520025009", - "68": "36884896577898408680381200313233480677158898830630579614146907563189332449201", - "69": "43286615009455031784719797295956349308039236872337887241869415538128324649873" - } - ], - [ - { - "69": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - }, - { - "69": "1" - }, - { - "209": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - } - ], - [ - { - "209": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - }, - { - "209": "1" - }, - { - "210": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - } - ], - [ - { - "210": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - }, - { - "69": "1" - }, - { - "0": "11877184595954796005081407550764117823731241224470762866371883626576306508793", - "8": "21077572762623317419569220287919719709976640199312587691191332146075002902469", - "9": "47949632535208881392940201692077097151432614187331735396014917149206176691857", - "17": "38368477391238281893446748243478931109162269572982511754265021508646667481923", - "18": "25010413504798967911155724953359844704156939603246355564059432326851066180613", - "19": "49156343244768099675507263330427440646916355002618236878522293867646082309965", - "20": "47398467239961958234466795312751095872158937550036197115446880202038492448125", - "21": "14129873784233010472773499602376510597095087957746542793734434604295373938675", - "22": "29645669308682900740034715715100948145589549169030371988537832513804587806863", - "23": "26916306213900631366930900630691008901047983616874092235502421882352927377047", - "24": "39648316014469838046546777585395244407900626971317480765008940921476609101544", - "25": "1075805810326825888038456416810460180459853549169321212979680442629714834774", - "26": "26742611581046357919373890278067256831153690674060993158535463378878746430898", - "27": "30600176628266458666269452096167292830369044471722493024576650779825622135273", - "28": "23159865426933824627394912105045390189837788685453318812972678308193378695975", - "29": "33569207124589688449356319675784204004766931668865931971563994396529906403187", - "30": "34389668036370475501299119755998547058607688303732622516542000160673745284178", - "31": "1201798707829091660443727337089362654847125778464792356989113770070872288364", - "32": "29845572639165180741694798393928453145630079926987904517665346038212264126028", - "33": "14631160877490948079244942474541244801119854032928433756504111740729163437383", - "34": "40080609753519506809463229070890236419441886724017000051197473396542219151180", - "35": "8361138371948448311285813469916607579830825766161994386963750786396662232908", - "36": "12650985342504995539624849254867256949345148623701600985535203429051574018632", - "37": "7576169417291040709717076728774831320541040624447298181739202248122313602171", - "38": "18087080399845775229095049378903056573173506668471196548912476771635857414001", - "39": "50946534756837223531540116488572134938982654402735777739074427414259230825084", - "40": "36152430850120088633186488364406493550709113103942086102379778957087739335638", - "41": "993276771261256858857042169222506230402365302699752273013455852746280656405", - "42": "48568789680343666247707229903211391584965211102334428224951392271516929758637", - "43": "40636188859159557363665675749214558759494650452686193026013807368515210439665", - "44": "46029772444293865102396247527894365699964101588507493497848467960320314917280", - "45": "26249266637970821250701791146259620460777979049839894716272063798452506263392", - "46": "3588323013854012084629250665015774914417947451028956050559283871784121755564", - "47": "27109590105706386751911368719399129408868932064117313119612544754388330338756", - "48": "22842822545771342020646655049319906953380200972650402611554077235462209818744", - "49": "4481701207614816479212884529522984229788773295017285833734949187833907248712", - "50": "10213800354335574509890090444126910081412655199462394453887866032412554538590", - "51": "12911943786842739437184473431475581999292974959447410640796233279857677718817", - "52": "9953473847777408282039697356162319099764007545595606707817786873433355552161", - "53": "675850546279934170761900219528949739849162557392919195060995017481269405765", - "54": "37630393373926502771973655787529943576221548029162459755560034000722212554220", - "55": "12764348908927968630335282140605320883671604457737073667542217034924708893770", - "56": "50706245930980412763274865723812969485072355822647075620217603803995672106738", - "57": "1170569840686578571593783892921588945640564395618550102898663714722873711940", - "58": "29844653650692950920043964778460482972720627374742711739308279164116089505999", - "59": "18464885054601324998317635257092757153354585078611883357060516344786915729621", - "60": "51034031034174421579001461851074144295242117129458604025079465337815247802023", - "61": "24778955697552677253359729766728952442064159772063529404597405190760506153776", - "62": "35206763652405859372781596551484094937589677394707273006733342175552221105667", - "63": "31863332156332650826523486148821675507937894505329655680428907528825075192970", - "64": "14038018066923710868945460054543651344949412340612251004206451240197946620657", - "65": "23525190361144736246461695112605588687199766313886609569060779022129805130977", - "66": "17484638440332673076423669239950516695781461642246077937558618388168006796953", - "67": "189980255182371897597600816168632001776805331922811265704365136911656262124", - "68": "24043117171463576857503106808380901670448450454427930556859617127529380363547", - "69": "34149607076551728407708052728215864049135220529716137798707999116075456387995", - "70": "43286615009455031784719797295956349308039236872337887241869415538128324649873" - } - ], - [ - { - "70": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - }, - { - "70": "1" - }, - { - "211": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - } - ], - [ - { - "211": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - }, - { - "211": "1" - }, - { - "212": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - } - ], - [ - { - "212": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - }, - { - "70": "1" - }, - { - "0": "2986065710695845701959971763474802354333410127136499986005712979427956719303", - "8": "35928033466469113915043399639659388115595663327283483801836439615479370360451", - "9": "40455696369433843077729463423522566799126738105096803929288622280384348928712", - "17": "2070377821046805236887511169366792863565516199982155060750665611222443841229", - "18": "730266693970874542560968442839009003247188377557906130365397968370691750417", - "19": "21138048534877331069986216811680624563608361852946363167193856132642350499660", - "20": "47164546506419339267836876747949063093737113469719759935655345354175695026251", - "21": "30689185748703448234262132041340737297191063057911488291207396644118837270761", - "22": "18508814609533103129334687336852918949450800547802509687407995851128967364963", - "23": "5852588888356612910346079734468419350005206662329154334202689194112205727300", - "24": "3555047135542761739892507541791743801945794749751857066182727427049933710893", - "25": "6639181535566491953974324401044743150841192993343491074436125450940227135055", - "26": "5851444073600198297694472813949071919006059063818543724416081827962504504455", - "27": "46983190224538329950814556464139887068003818146049485849434972826866168614374", - "28": "8697110141815640923900358979950681479129278715519856719055192743061215189617", - "29": "42530858223969591383395652764346595577610068291560698348594871574243031887285", - "30": "28860976429283117875296951042629926267242349167693703055283898782129110848792", - "31": "39222892625571798302193951119855653828010755144089781462171043545173093187352", - "32": "25623920506484621652151013395132601662109142971090867154192510091472183782594", - "33": "6391294981766980758283359864598595564697710719915010115264829367667854611192", - "34": "46451619290714364797693593642112763687588469131175214823103449147068414914034", - "35": "17240676919560983407327951682391068430770222764862316748636651866053990698555", - "36": "33366828914434098972007358155103089980169384334739186333007962645150352073419", - "37": "10632265051260395101050517032159416883846080570056035198885104064652185608820", - "38": "14416105698668853344640206915482771885878288795304414277409458389310797881391", - "39": "38088285821811880332554533768367566906014274035598876032707127345197671837489", - "40": "46551418669439584200367247624531157159271564636676819172694701775220490399194", - "41": "24069389258381007174795235175796955597203591201567527460663172487081987433085", - "42": "31753491251047646634605532864497859067427711586357734480288798149316140483124", - "43": "35178258159430024200242830781396645242844466975304637025752813321412734099007", - "44": "8369759564623907108809020485256827974567250367000339427158839312625933946525", - "45": "47411799307031349211535248121832800944589222307800413519516151428178838010107", - "46": "17328197013774692120227851924722557076123775194668734088611376596866252307296", - "47": "3760127204406038742193762424839913221251768002357627140397901595701522492263", - "48": "17985365196672519940580341363958816473242798899364389661910095578528202341893", - "49": "17693147066671922323115166114044719695751648431858680522524038525195464472332", - "50": "5489310968146209531357143264968113861759434868091837256787531634478361489244", - "51": "35375607401499149611482639914370819011725504562403119428726316554210791710004", - "52": "51351628495562155149497698508457115177297831960488961711778767913139690999193", - "53": "24957314171194706081102671073644796793995739859511589051864181183579592270868", - "54": "12105214646230891131780055672970355755166280383461428072606712071701107820279", - "55": "13179785471006693208604028153235715933529590066614188792204748586533559343592", - "56": "29893964794106339042552443210878469249605406390493565693907590145799849581043", - "57": "13596248850589751633857848518076689125697626157787175263485056558458033483842", - "58": "8582371658824332645706752589609387815429932737009768296224831719122569432762", - "59": "32562867404828714525984124243971410561791224667243626040633861820690208762364", - "60": "4453816334057594981764115291155509081507978851175408950488981839489846059036", - "61": "36861558607422300624556589326824847427492533044157555614976027764147427571011", - "62": "3455617056076236170652612209496300062617326735995286278209221884554029737307", - "63": "49921233129480260768874187309792633646731332813540716331743011743937278080127", - "64": "28268135617786064985980604001406691848047249873260611671862767944965542978866", - "65": "38263875450537017129854351492513619516118371271445500512663148301365220256408", - "66": "23367921261126045456779665113846185824412816840217202773358575734522557992141", - "67": "31572013062006945038546150522672886613753816005900237824931889114428318884439", - "68": "33586798388780967328587204551546640863662079613148093423751107344313410784776", - "69": "2243339108379557042512080931962882760520111113917229879273364571107655112334", - "70": "40388393748571163542126751569869832775620735909491689280521974161775256552780", - "71": "43286615009455031784719797295956349308039236872337887241869415538128324649873" - } - ], - [ - { - "71": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - }, - { - "71": "1" - }, - { - "213": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - } - ], - [ - { - "213": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - }, - { - "213": "1" - }, - { - "214": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - } - ], - [ - { - "214": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - }, - { - "71": "1" - }, - { - "0": "12649794127562509279197585900369868076945168057811626745015439261348449322516", - "8": "18460568301597491007878900164052362248927462612777587403745087254932995669265", - "9": "10691064190862303452862603750199024193361775633701698219738043331397052322546", - "17": "50537756005483669615362774033579150435007325241201758409523155475539598923852", - "18": "44151662351522130204771062960801610572369635275211081750592416116600686257816", - "19": "44612985953893012963177362368347100205547474056889298641137417191180618350823", - "20": "46418612556749627983869955597143431904575741384127508709314192592764927221051", - "21": "46792430355948421686541864746937514997785791612605035230581794650060166440164", - "22": "10372014107294763511359448987347871729024609617665964681852057762277632024668", - "23": "15746914484703176943512617861662741217218839026709246646219030771298623339390", - "24": "949837953556186423580297077566238417367075194047097605831825235696240068504", - "25": "15936180135839524274618445341411624509848805467084480095563639515175327863019", - "26": "35661399052929284108306355547044524619400966780651402243253505975861253383056", - "27": "24303633886164997277195266046450010738147861602550592940768478514120593265339", - "28": "40770339283421435760717228293893053420022897746389072894172136686544980088755", - "29": "33631671877045108448110442153973608669336179496669197300149420913676619854288", - "30": "9735435212281313786054816818889380009732606741754008415341978388282611227769", - "31": "51356374076127903498371554796174649537256039802839729838833628877209893874730", - "32": "28603229448651344033544366114133355562345596220279885038057779594832037566804", - "33": "23932726774945329407028948443366546176932844943954767819203057641876144392801", - "34": "38048486477770062168235160169293363458518451096350594627783892339613045725906", - "35": "36667819662104189201565795928529578367737674212176196308756611302364276026301", - "36": "50312369276709306482447794879275303865858679848556909637163630977683758889073", - "37": "40640974594601385122998575282605604993372302971083166882961772980998039680049", - "38": "46573152329527245401147543333094999402550228387130577499113770084243033958320", - "39": "4923275582039348818678448104674287994231344646794752274649248326309595068437", - "40": "12470069672650358918724179454112997355528814449425359438655565554900493437091", - "41": "22518837206951281316923614032592840442877457370143896669898972215304804784639", - "42": "32786387939491905527679653399783341165477604395489634875885725964058062164833", - "43": "26802895997331561650984867742399229411641120218293853043084773960577450868179", - "44": "24838662905607941363605394876572784693539918934144065118606577461309780211108", - "45": "31216193637639764020618021860660659250283820023138966889135307674873264929725", - "46": "11911191095174083352687969707419195808840974547678014728491981206268612459777", - "47": "2362080850717757194188202462187416733947181980738241126975915103449537095882", - "48": "25745662595065700561191293220788139662001942770437723891000012600653461719208", - "49": "28140245600139348990629268116527996643000598625891819270010751243053721747648", - "50": "51751436096363252081363858113432111194503598836372712351332122404701061867817", - "51": "42108778529760768577798404187086628077490577913866117385024205732960377183078", - "52": "30813992311307374443071098934374584071612224649436358113020328600892876683139", - "53": "23492712686485208764902374462231435151209281258529959599300917398612926412966", - "54": "21519074573923031735018942202953742465795451099115310478204881198120764443717", - "55": "13279171940868488849400911348327262210848933822776793527584881528283026293779", - "56": "12065508657954377182012442695846643668233069476721162280958082114700635570417", - "57": "33809005349166058723728123142633200741310625521089992988520343036834657925652", - "58": "8168672220202151212224263402279952306120357211675188061793483598915640145790", - "59": "15840669771013121078993052531823132314230810375787734921546473546120793141259", - "60": "18811932207167141283135007664707227029592461883819414932716707079663507029494", - "61": "21370791779943868955072368082811193781258619493887306245699009542099935401902", - "62": "49030337676146277014704635145026496918996124255134633124406378833861771875818", - "63": "51515549193781248407019642817274561265310256393252259112547694057575168432224", - "64": "50706129836639190576468860988978635963401544111522556439056991219437760065548", - "65": "13819020399578701866935680572677911254391251122844270501318808538845470781335", - "66": "34071492461230958221148022014425493214874680643170300895570809304852071684454", - "67": "4262010540554351033770919624358198470659271395352575610869803763404444267802", - "68": "22508706532220565134086795478143129387737417566374055363406924625643630876217", - "69": "33785686673947073827057590402236732655718193466954214811102353404447372117226", - "70": "12113636918360017528534055150675411418158770816781946695031665943694998962669", - "71": "24250644043370111356525371269458070014942797652829008403143883916486475614791", - "72": "43286615009455031784719797295956349308039236872337887241869415538128324649873" - } - ] - ], - "map": [ - 0, - 1, - 2, - 13, - 16, - 17, - 20, - 21, - 24, - 25, - 28, - 29, - 32, - 33, - 36, - 37, - 69, - 71, - 75, - 79, - 83, - 87, - 91, - 95, - 99, - 103, - 107, - 111, - 115, - 119, - 123, - 127, - 131, - 135, - 139, - 143, - 147, - 151, - 155, - 159, - 163, - 167, - 171, - 175, - 179, - 183, - 187, - 191, - 195, - 199, - 203, - 207, - 211, - 215, - 219, - 223, - 227, - 231, - 235, - 239, - 243, - 247, - 251, - 255, - 259, - 263, - 267, - 271, - 275, - 279, - 283, - 287, - 291, - 301, - 302, - 305, - 306, - 309, - 310, - 313, - 314, - 317, - 318, - 321, - 322, - 325, - 326, - 329, - 330, - 333, - 334, - 337, - 338, - 341, - 342, - 345, - 346, - 349, - 350, - 353, - 354, - 357, - 358, - 361, - 362, - 365, - 366, - 369, - 370, - 373, - 374, - 377, - 378, - 381, - 382, - 385, - 386, - 389, - 390, - 393, - 394, - 397, - 398, - 401, - 402, - 405, - 406, - 409, - 410, - 413, - 414, - 417, - 418, - 421, - 422, - 425, - 426, - 429, - 430, - 433, - 434, - 437, - 438, - 441, - 442, - 445, - 446, - 449, - 450, - 453, - 454, - 457, - 458, - 461, - 462, - 465, - 466, - 469, - 470, - 473, - 474, - 477, - 478, - 481, - 482, - 485, - 486, - 489, - 490, - 493, - 494, - 497, - 498, - 501, - 502, - 505, - 506, - 509, - 510, - 513, - 514, - 517, - 518, - 521, - 522, - 525, - 526, - 529, - 530, - 533, - 534, - 537, - 538, - 541, - 542, - 545, - 546, - 549, - 550, - 553, - 554, - 557, - 558, - 561, - 562, - 565, - 566, - 569, - 570, - 573, - 574, - 577, - 578, - 581, - 582 - ], - "customGates": [ - ], - "customGatesUses": [ - ] - } \ No newline at end of file diff --git a/crates/provers/groth16/circom-adapter/tests/poseidon/witness.json b/crates/provers/groth16/circom-adapter/tests/poseidon/witness.json deleted file mode 100644 index f1f04e5e6..000000000 --- a/crates/provers/groth16/circom-adapter/tests/poseidon/witness.json +++ /dev/null @@ -1,217 +0,0 @@ -[ - "1", - "31232273693565690933177443835503636699764964887306595080004406327965362624380", - "100", - "9842592072096562740192982053780847714335170252748807855743009820904302908989", - "50701446129004058070861723679851844138491814571487424253622500774142707537960", - "2795251894010432905713865185664174299979130231545922463278597246216967809956", - "5761111059907949228974380389013385058975203075056009695887478576708799734379", - "47683181824399223325886335606956763639945526815456163296815820245553590268639", - "34320389318831151809324701654274261190953453403523552511510277052412292906271", - "49273806160521508760006268180912646801166473150686521525395439223992186486002", - "13056267851620141690709076785580648490595766208109774598967795176437311990768", - "49834844679966166072402550652625697903609585732578863130175942958657169537138", - "21349633007472877161747317593693011183910587800455122273541877206399133236902", - "18977998262159322181784324407336515834257167517134967615581319832601511333224", - "48002497218979526538923502322922709173768001388179318976853080849091128279903", - "866587974529386155980844332506695429771461778955877247849651206764444256959", - "20454076047122091198988344101814798393935424290186179890554041978873495892281", - "36212066407949692221727221436726511992381622022397748367353000012107873016577", - "14334497956018842272120480205060022698432281095912071033264455899667729793807", - "929724551545173062077768202634020480804884496831222762066347128046017141300", - "26204955778169658437835079301940997687412608201021299242426849553255879290411", - "22984485593398167382238972608560828841818025851611997173710772948004891095811", - "14363270014372603707871031000113963861409881419075777287268190796116095604023", - "22950712672973464623959364431870011027468645840520948585569396199939564530150", - "48582723826787318769293548505037557170748031825286037464838814197557895730522", - "7606900371885556209266478634801425110876551579485413897713203920590035320470", - "45301542482709858565191417322468431418157302373308524882903510016583831997173", - "42096606039310802466064646477728471968411288651756223725218259925760016917458", - "21691455555697701595140005376418072492613098491430616814912090233829427116555", - "8210951631362669449026667430173490916557983711863298358351381384289788199696", - "702522621901410665043833704231627494906773781171097084247728493640069931551", - "29399613134394562964868697558994748672074801013175726015401827583186457945860", - "476528712206082145016628705152322749642496995389179598087210329843776358166", - "11217389333127134692113680512859397814203349445574315503710644642714791344972", - "27713692054109098588361636930227665300943737959184883163753345042855058580374", - "9444879973318028910038079701376410592439490140929853125626701599900740414271", - "36224756556353870174128953180213478395673323268230224306022276203083464776836", - "48025603426892873267275436565222811037106028412601123616279956708584063971907", - "25738249534595389486234960434870736253535941224460953769723517483152463719433", - "14469306302412218587516382869518321072730350298073995429242671334453114841879", - "32248613324458644110375097418483624910966090068385137811361167113935700836562", - "36712432971349412465527840270410192601008830708216830332021059826106983784944", - "19166905196930194443242504819718121630684738367495424080176733129532354998834", - "12015571556950438237908712150293211728436370864101400631464770437513940187751", - "21879622017058301198588630953406154410691764493934932307981923637023514309411", - "44750918017295010468514060094789405000169855376168380376049255543444907067833", - "34009990478946879052209740900353089716000469787102064456221703724058971495325", - "31021015778311537495203378969201755456696694070301026280051898518614054396874", - "24775639773405856234228483389377942351063200988524130116623737411088505094669", - "23801335448538040172382980662333384912718113086707834308602514631491691964944", - "17922780681299373510388202405899873320175111482374564382218672293085074921081", - "46798460645370436475624630813845915430346536492216998499891409786363970874145", - "51888303627436834761836802107422955992420158912962611248827405605189272261243", - "45660774071110522522335703633814770510899580835824844961602235831367457180659", - "13597656000103804345718254563215441238592762081921954482365116290721284097336", - "7578213906319187847565772700860592457092595453628128206002088918779510553880", - "666495703888215316291874594391531642437388044043777457308159543012685365613", - "23391709620615706797203060991482812649718016244323626573272335489418750552845", - "4031644427541161735052613214302029129168967254538116159376134081978886775999", - "15011469156775200028309530965793040689683318278315309997809821904837666337388", - "6028487647946416800672279619688012070265258697454053371791521211422722165500", - "15872896476579481508907808853245035879948017612122989108112677273560160451148", - "49647860138479149833123738847314980180028846188512368740031824473156813615667", - "1766266619268431959749051692363218439619962688253739833717853887673119465217", - "46938901340287442397733125343391210972416243519458921889620617305181972679744", - "34974536152269264895390581538330583372308416706129421712044023878606494630938", - "32997050125731644356722452154374471677542181203427367133604537644531308591047", - "3504325870227981058651653893451666726101475357008120892938161712789500569307", - "24778770768250628900208482674853136536377931080863688970674501721783679604822", - "21379853174140287544828622329229538639628736294837380775618613408218304624765", - "48264091505420999636692955156049695535621861861087266863470213896462131323538", - "22556258401698893123597325402440096989709759698370506901384748051312079207416", - "16468564672283104448200466929470515559281318596645826058496417613750739556889", - "6216173916395818935558406768177722299666554535076872479228467162006736437912", - "51919839550086546060576838728042663787666489595474746175037591713189893785582", - "18394099542217429152585616676803562851788842374526875587520996533770378239013", - "43644239287154424684875063056883414262437976384949318691024791424650338769340", - "8418116039410524227724571451931975174380747028305604528786664030423105655045", - "24571480203008317501543451310080404273065870519445365205441559169451439859912", - "35470655702338046460906834189801593265013802365881913209281400793995169307747", - "44246302840851283810532477851581135841538922056492748628361012912082806353234", - "34711518017392048845663913904065347404273532619794114991653329970077921732035", - "24645592881196849151540495841540752399108940838595125057353429999053527707078", - "50231845581601874081844922235615563337290372498374796654375088957061009627574", - "37175284189340196595255643156185131608964165254843027857456309688799044024674", - "20965996800450273745095079018524567340310186794959187784879372532751231691584", - "14234894527833582726033074613697939504651529032484363165941916051823505924812", - "38374804115978039510433687666624568137010442005708222407992784536463097166017", - "48201947759009501961165411480744753071537780510700977852997944415546713631504", - "44251678347091898183721177005165159276494500174227320584535956097583332543362", - "2271941035593571682830196386531985122891399334853383248407798113804651646847", - "33373585036328889984274846096748561849742574548232200365224872630035536347571", - "37096118650826153141657195596233542427917508301246539898005805129852136950774", - "3077274877971230942285062684379420567945237706142211970653351514630484348626", - "46921896988671516121140953614115882047970293240045912641077805729249118544098", - "8278986561237945812184690528140506758726766310805659579444451103195435620908", - "10719042859978853922703277473397067229715789355540044261647700142836589878752", - "41096666790799052215603122485174524449256082635555249284292563440632708640201", - "26114302805902873370186882946861298202311317639142124141994265299504240017186", - "24122089512958765547140271133427456387400396624717547430816909562162956737687", - "19669882995535161591181680425103613063685882860653524607446239987650470631110", - "39649037465079967393973261504745617156177230607389388858294999425766221884747", - "52383916787187786777911889920052433504310341347123165209988318590578772649200", - "50835966522501771665584221136036348564226813258336005200522920119167958719806", - "46115285578898677998446473635493929879731329658843548459614979290530355187018", - "43616205091061492636289988059797058480605925926148236679714123575360877974373", - "44329530522827865084043335291234644088023180578221103175111303576988928582207", - "34601581792860273421709974153062769751683305687329509483286268946873019783233", - "24005687450578188372096541063857260559392228767249957409952720880148585683581", - "8401264926416768920840883132755038222173633780452223476084072362151558923403", - "39241562181267423281891483911695916284984376726268807628426727244446257961888", - "46241086060121626607387995801040873362396776854423983806483772384868947580579", - "25613605724693892831510258897496413254724322824359351121847606024217089935579", - "42811144657604425190922061999550634230808010659364321138936479478591900101822", - "16967560316782518983352198114986826778432314946522001211102536367698239912931", - "52216453053889420494115803263466468524431579180812235570426880876696630251519", - "43395431855327640930634396437238403029979719461142355825791073197057828409413", - "5739979244754927427803186734913436480608553861749846762371986686752721210469", - "24861857340938336950472124632749794022378090314657719191835537682862812489123", - "8060818261091417113785129860296513730701142001789355832909692110770284314020", - "607514426292577541090875246119436062052095772731827932157937129625141200848", - "46947146531784376209506246218852897611270905039780208958166066630425464018764", - "29258328625331693259224429120060181225532328778912000947935894453760901718233", - "34806313690274376458919670046108712124191559739357240275203701736431279154836", - "49986497913829262890440918091878961206525258604682890252857042950186333524164", - "39766543272531901416883469971291804207921139983829161911728567268740086385925", - "28091556500797434844528251798008397338594560241576666281998508494117743632138", - "23186737733877849913867741510649126264795492742900392628513893287994542825970", - "34307992155923243755136414840228127616280400005454568026611511989115216434490", - "28858240001261724480951946947709289948516365042168103626249943382483982603698", - "26718087421771065451980370117273164796818110337538145204977944861676442564142", - "11529007572354715997090009788499840321290160673228219695760274729008449217215", - "41818833610932056936257803996266601751120757390880177557219535829866128207497", - "11385931473640684401191236671377446228404497395789711684485909786295113379545", - "3046800734932809545251838586276556144556529945356443827748066188654982990819", - "21820866072902063659619039862390737896401848841413092387059291236176810030945", - "9260373451107750346918243179436845170737541806050625102526388075400013595057", - "50775870939862966414920375734388234221123592190948008489272156309651161088306", - "4749283609298891591756335047709117680971831257474779593848723947571957055989", - "7647335697008950350627451511980756910055899762665817491022221696834251051920", - "2544111829846209186666069772391404129589012716874935119896933198963815648470", - "4520524685766655716699001700924069236544665912498577185762497602437396574841", - "1489087313246746088548766265260176305578771974536948044498960354197105278897", - "49831426425372073590933151694013835620236449632038583725522006346268674211531", - "51829392696050679566277970049631175039430194572673773669460354610739430401645", - "22551753641170882458960054453729563667781821500594040339709737170503943293626", - "39875557100092776629801096431288228576369388718225980954102178240724866217134", - "27970300607167820402805648310763962720898738973942893119539310971184862035336", - "41961657773168629321045378298282773962430366222091440456655054209779301625513", - "15915927317605719955661372990692298243561855956238484819440300707062057130556", - "35081619576050445142121848342611865762555562695095734339145610480945840101020", - "27515417060815902459482553774040523829140545535853554007576394897375129107204", - "14971534453188424557783713771170815284718965632221559087315072218387255946773", - "32435382617980329961153843735379680389573276052103623237999975437161009088039", - "24081270123230281577299198511964134312484399302645197161531505252398293353482", - "36859364219134901873224235350286140497533981067115635481343031575103887189247", - "37685102373855613637277744523048040519209837448060790261321544729687705913611", - "8900710716968981104337659420301079323493929766528467773156234474349897676691", - "35163996502606394990930809092695311943564966788342266163746380371265947848936", - "25594106866457842390567220838919088968413507620827657476270673373210269648089", - "16375819521788226688684479037552614967016923578481011808934068116109215094622", - "41082095458382380055697971536536552864043155820507690887396239645790189639770", - "17040880062943650993756806837403103400522234323682130546066472543010527407949", - "16943480340246737691333212370674242197546296096764263949722785705430510279949", - "42723289032946758576739476241127107343790515137310389852872076202767930968922", - "41626342084680958232499623710152469842550047615888748141436044690870491378515", - "18929616358992919578953291121642564773136411726783841050680472233257448574541", - "11607858373876095938825348714340573491717184787981972269285132700310179306348", - "39099827772554846660452062548146063529888882995568674987535760694528234279097", - "35182794054648881043084793082591442639875943457417259729642732273618534765821", - "49700117715116691131491066606484243115374760425545685116633000976251898932742", - "45649948614393006349760903741801858405837234393661017814017081897565347946500", - "6873065548217483766438564807687724126991289146566911730828743455441747920853", - "43590314969752072547523831308613365600413652237988521319820151613024558449040", - "33177056274313996906268553318572773921345773735325945467758373664595405154812", - "45317344947823175743596883695049935268899321655775019531563574476884509669570", - "30099860084161055871573140725229111976496382954304194977341671015630743977114", - "22965624017498991332150649848367234689104556734442560598447470696236080100072", - "8341934048488732893238907749423888290972217250207224284943618060128100053118", - "28596129181161932920074998611591226396029216695232512851489436404991801744788", - "28180010922974985296871523413253362571579198315281244402799505586659125525598", - "43416037881910327292340841234854695526061453124069602341299982031620449917553", - "142651827142088011726795273680543595913842257039544161453729970603311878117", - "36439867830034330036264626692147515508059570404842463171828404368425290218661", - "14961134067431610039125780561713025836392484878585180040293433127524043987638", - "682376994187832549364895526422706043601725906702546344926459820898191810783", - "38317360690276628346364846415195763526433396143162835927741960265698793677530", - "34682488848092588069140607800609006466408394564018156382734818328492566998908", - "19609944132952033632651599437769929658506317910589022659413291230504613974237", - "14748561156412281284398160749605181174091807073522981418657410126477453665538", - "19329559201063048777619876416119758135546155751885943820635827738240479681632", - "6436260198837546375675605669888798105866010765714692171433926957397923003435", - "20802187185319477237667485678983729369078741509925637525284577250407711222574", - "7858122122831615910897064638870005340201720619640541346566778577938946281267", - "44834321195589934161494649415727792859599852025261753781877551400777678439163", - "32352311925569539666809361117203051438203598573416561652675871329827875011282", - "43508617397885347801885243798430386795013088918602555563258433310322947304587", - "10415694235159982976984229038010429999769978016974584333557887043103891460048", - "5978453131393549553634466138337688586272255061898920058118100646632355441836", - "29530236174532552135323467248613198536289765097404767562207315521821435363572", - "26462557209003402865404636273179319772252061255481675708062174728574857813430", - "2345373360113107965111506429577235802508050937322615888439068529404320575156", - "20847365024211512283688522832639387785539093689179262436985959207287879848892", - "3249072728083366810208388575844459458449706193979654170990364206310559766622", - "20795433501443590332152602764354483365500760852326704742421700979025782779179", - "29115999028157870628111689214578910759386210743994086820032199854490817921592", - "12695492055328164399944436927868513757651046445714684231140388197628188014074", - "24361364354004896259434935032004492034665400053713876975012793619708091992905", - "7784882525849889984038065976639661689748351515314556504743886456146574755616", - "662539596710935720549068336930934346687370201829538847271855151676512092151", - "19784938307617873904521952140907640491075564274762895345670919441525063416470", - "47345663033762055949623189794188778420225726754274304700948157724108255200574", - "29482350774506460834911775434289284636076790873529229986358190562715553147296", - "7355713467418524104551097122274915015585762546353840079921844566090937499189", - "8673129290913470474150926554000534596184420371100078985496163687555927065286" - ] \ No newline at end of file diff --git a/crates/provers/groth16/circom-adapter/tests/poseidon_test.rs b/crates/provers/groth16/circom-adapter/tests/poseidon_test.rs deleted file mode 100644 index 8458d81ee..000000000 --- a/crates/provers/groth16/circom-adapter/tests/poseidon_test.rs +++ /dev/null @@ -1,17 +0,0 @@ -use lambdaworks_circom_adapter::{circom_to_lambda, read_circom_r1cs, read_circom_witness}; - -// Proves & verifies a Poseidon circuit with 1 input and 2 outputs. The input is decimal 100. -#[test] -fn poseidon_parse_prove_verify() { - let circom_r1cs = - read_circom_r1cs("./tests/poseidon/test.r1cs.json").expect("could not read r1cs"); - let circom_wtns = - read_circom_witness("./tests/poseidon/witness.json").expect("could not read witness"); - - let (qap, wtns, pubs) = circom_to_lambda(circom_r1cs, circom_wtns); - - let (pk, vk) = lambdaworks_groth16::setup(&qap); - let proof = lambdaworks_groth16::Prover::prove(&wtns, &qap, &pk); - let accept = lambdaworks_groth16::verify(&vk, &proof, &pubs); - assert!(accept, "proof verification failed"); -} diff --git a/crates/provers/groth16/circom-adapter/tests/vitalik_example/test.r1cs.json b/crates/provers/groth16/circom-adapter/tests/vitalik_example/test.r1cs.json deleted file mode 100644 index c529cd242..000000000 --- a/crates/provers/groth16/circom-adapter/tests/vitalik_example/test.r1cs.json +++ /dev/null @@ -1,47 +0,0 @@ -{ - "n8": 32, - "prime": "52435875175126190479447740508185965837690552500527637822603658699938581184513", - "nVars": 4, - "nOutputs": 1, - "nPubInputs": 0, - "nPrvInputs": 1, - "nLabels": 6, - "nConstraints": 2, - "useCustomGates": false, - "constraints": [ - [ - { - "2": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - }, - { - "2": "1" - }, - { - "3": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - } - ], - [ - { - "3": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - }, - { - "2": "1" - }, - { - "0": "5", - "1": "52435875175126190479447740508185965837690552500527637822603658699938581184512", - "2": "1" - } - ] - ], - "map": [ - 0, - 1, - 2, - 3 - ], - "customGates": [ - ], - "customGatesUses": [ - ] -} \ No newline at end of file diff --git a/crates/provers/groth16/circom-adapter/tests/vitalik_example/witness.json b/crates/provers/groth16/circom-adapter/tests/vitalik_example/witness.json deleted file mode 100644 index 14597dff6..000000000 --- a/crates/provers/groth16/circom-adapter/tests/vitalik_example/witness.json +++ /dev/null @@ -1,6 +0,0 @@ -[ - "1", - "35", - "3", - "9" -] \ No newline at end of file diff --git a/crates/provers/groth16/circom-adapter/tests/vitalik_test.rs b/crates/provers/groth16/circom-adapter/tests/vitalik_test.rs deleted file mode 100644 index 4bac548a9..000000000 --- a/crates/provers/groth16/circom-adapter/tests/vitalik_test.rs +++ /dev/null @@ -1,97 +0,0 @@ -use lambdaworks_circom_adapter::{circom_to_lambda, read_circom_r1cs, read_circom_witness}; -use lambdaworks_groth16::{common::FrElement, QuadraticArithmeticProgram}; - -// Converts following Circom circuit and inputs into Lambdaworks-compatible QAP and witness assignments. -// -// ```csharp -// template Test() { -// signal input x; -// signal output out; -// -// signal sym_1; -// signal y; -// -// sym_1 <== x * x; -// out <== (sym_1 * x) + (x + 5); -// } -// ``` -// -// The input used is: -// -// ```js -// { "x": 3 } -// ``` -#[test] -fn vitalik_w_and_qap() { - let circom_wtns = read_circom_witness("./tests/vitalik_example/witness.json") - .expect("could not read witness"); - let circom_r1cs = - read_circom_r1cs("./tests/vitalik_example/test.r1cs.json").expect("could not read r1cs"); - - let (qap, wtns, pubs) = circom_to_lambda(circom_r1cs, circom_wtns); - - // Freshly generated witness assignment "w" must be in form ["1", "~out", "x", "sym_1"] - assert_eq!( - wtns, - // 1, out, x, sym_1 - ["1", "23", "3", "9"] - .map(FrElement::from_hex_unchecked) - .to_vec(), - "incorrect witness" - ); - assert_eq!( - pubs, - ["1", "23"].map(FrElement::from_hex_unchecked).to_vec(), - "incorrect public signals" - ); - - // Regarding QAP, we expect 2 constraints in following form - // -sym_1 = -x * x - // x + 5 - ~out = -sym_1 * x - // - // Same ordering difference exists for variable matrices, too. Circom adapter changes the - // order of the rows in the same way it rearranges the witness ordering. - - const _M1_: &str = "0x73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000000"; - const _0_: &str = "0"; - const _1_: &str = "1"; - - #[rustfmt::skip] - let [temp_l, temp_r, temp_o] = [ - // L // - [ - [_0_, _0_], // 1 - [_0_, _0_], // ~out - [_M1_, _0_], // x - [_0_, _M1_], // sym_1 - ], - // R // - [ - [_0_, _0_], // 1 - [_0_, _0_], // ~out - [_1_, _1_], // x - [_0_, _0_], // sym_1 - ], - // O // - [ - [_0_, "5"], // 1 - [_0_, _M1_], // ~out - [_0_, _1_], // x - [_M1_, _0_], // sym_1 - ], - ] - .map(|matrix| matrix.map(|row| row.map(FrElement::from_hex_unchecked).to_vec())); - - let expected_qap = - QuadraticArithmeticProgram::from_variable_matrices(pubs.len(), &temp_l, &temp_r, &temp_o); - - assert_eq!(qap.l, expected_qap.l); - assert_eq!(qap.r, expected_qap.r); - assert_eq!(qap.o, expected_qap.o); - - // check proofs - let (pk, vk) = lambdaworks_groth16::setup(&qap); - let proof = lambdaworks_groth16::Prover::prove(&wtns, &qap, &pk); - let accept = lambdaworks_groth16::verify(&vk, &proof, &pubs); - assert!(accept, "proof verification failed"); -} diff --git a/crates/provers/groth16/src/common.rs b/crates/provers/groth16/src/common.rs deleted file mode 100644 index a815dcc6f..000000000 --- a/crates/provers/groth16/src/common.rs +++ /dev/null @@ -1,41 +0,0 @@ -use lambdaworks_math::{ - elliptic_curve::{ - short_weierstrass::curves::bls12_381::{ - curve::BLS12381Curve, default_types::FrElement as FE, default_types::FrField as FrF, - pairing::BLS12381AtePairing, twist::BLS12381TwistCurve, - }, - traits::{IsEllipticCurve, IsPairing}, - }, - field::element::FieldElement, - unsigned_integer::element::U256, -}; -use rand::{Rng, SeedableRng}; - -pub type Curve = BLS12381Curve; -pub type TwistedCurve = BLS12381TwistCurve; - -pub type FrElement = FE; -pub type FrField = FrF; - -pub type Pairing = BLS12381AtePairing; - -pub type G1Point = ::PointRepresentation; -pub type G2Point = ::PointRepresentation; -pub type PairingOutput = FieldElement<::OutputField>; - -/// Generator of the multiplicative group of Fr. Basically, the multiplicative group is obtained by taking -/// powers of the element w: {w^0, w, w^2 , ... , w^{R - 2}} = Fr\{0} -pub const ORDER_R_MINUS_1_ROOT_UNITY: FrElement = FrElement::from_hex_unchecked("7"); - -/// Returns a random element in Fr -pub fn sample_fr_elem() -> FrElement { - let mut rng = rand_chacha::ChaCha20Rng::from_entropy(); - FrElement::new(U256 { - limbs: [ - rng.gen::(), - rng.gen::(), - rng.gen::(), - rng.gen::(), - ], - }) -} diff --git a/crates/provers/groth16/src/lib.rs b/crates/provers/groth16/src/lib.rs deleted file mode 100644 index 1dbdfddfe..000000000 --- a/crates/provers/groth16/src/lib.rs +++ /dev/null @@ -1,13 +0,0 @@ -pub mod common; -pub mod qap; -pub mod r1cs; - -mod prover; -mod setup; -mod verifier; - -pub use prover::{Proof, Prover}; -pub use qap::QuadraticArithmeticProgram; -pub use r1cs::*; -pub use setup::*; -pub use verifier::verify; diff --git a/crates/provers/groth16/src/prover.rs b/crates/provers/groth16/src/prover.rs deleted file mode 100644 index f792ce676..000000000 --- a/crates/provers/groth16/src/prover.rs +++ /dev/null @@ -1,151 +0,0 @@ -use crate::{common::*, ProvingKey, QuadraticArithmeticProgram}; -use lambdaworks_math::errors::DeserializationError; -use lambdaworks_math::traits::{AsBytes, Deserializable}; -use lambdaworks_math::{cyclic_group::IsGroup, msm::pippenger::msm}; -use std::mem::size_of; - -pub struct Proof { - pub pi1: G1Point, - pub pi2: G2Point, - pub pi3: G1Point, -} - -impl Proof { - pub fn serialize(&self) -> Vec { - let mut bytes: Vec = Vec::new(); - [ - Self::serialize_commitment(&self.pi1), - Self::serialize_commitment(&self.pi2), - Self::serialize_commitment(&self.pi3), - ] - .iter() - .for_each(|serialized| { - bytes.extend_from_slice(&(serialized.len() as u32).to_be_bytes()); - bytes.extend_from_slice(serialized); - }); - bytes - } - - pub fn deserialize(bytes: &[u8]) -> Result - where - Self: Sized, - { - let (offset, pi1) = Self::deserialize_commitment::(bytes, 0)?; - let (offset, pi2) = Self::deserialize_commitment::(bytes, offset)?; - let (_, pi3) = Self::deserialize_commitment::(bytes, offset)?; - Ok(Self { pi1, pi2, pi3 }) - } - - fn serialize_commitment(cm: &Commitment) -> Vec { - cm.as_bytes() - } - - // Repetitive. Same as in plonk/src/prover.rs - fn deserialize_commitment( - bytes: &[u8], - offset: usize, - ) -> Result<(usize, Commitment), DeserializationError> { - let mut offset = offset; - let element_size_bytes: [u8; size_of::()] = bytes - .get(offset..offset + size_of::()) - .ok_or(DeserializationError::InvalidAmountOfBytes)? - .try_into() - .map_err(|_| DeserializationError::InvalidAmountOfBytes)?; - let element_size = u32::from_be_bytes(element_size_bytes) as usize; - offset += size_of::(); - let commitment = Commitment::deserialize( - bytes - .get(offset..offset + element_size) - .ok_or(DeserializationError::InvalidAmountOfBytes)?, - )?; - offset += element_size; - Ok((offset, commitment)) - } -} - -pub struct Prover; -impl Prover { - /// Computes a Groth16 proof from the witness, the quadratic arithmetic program description and the proving key - pub fn prove(w: &[FrElement], qap: &QuadraticArithmeticProgram, pk: &ProvingKey) -> Proof { - // Compute the coefficients of the quotient polynomial - let h_coefficients = qap - .calculate_h_coefficients(w) - .iter() - .map(|elem| elem.representative()) - .collect::>(); - - let w = w - .iter() - .map(|elem| elem.representative()) - .collect::>(); - - // Sample randomness for hiding - let r = sample_fr_elem(); - let s = sample_fr_elem(); - - // [π_1]_1 - let pi1 = msm(&w, &pk.l_tau_g1) - .unwrap() - .operate_with(&pk.alpha_g1) - .operate_with(&pk.delta_g1.operate_with_self(r.representative())); - - // [π_2]_2 - let pi2 = msm(&w, &pk.r_tau_g2) - .unwrap() - .operate_with(&pk.beta_g2) - .operate_with(&pk.delta_g2.operate_with_self(s.representative())); - - // [ƍ^{-1} * t(τ)*h(τ)]_1 - let t_tau_h_tau_assigned_g1 = msm( - &h_coefficients, - &pk.z_powers_of_tau_g1[..h_coefficients.len()], - ) - .unwrap(); - - // [ƍ^{-1} * (β*l(τ) + α*r(τ) + o(τ))]_1 - let k_tau_assigned_prover_g1 = msm( - &w[qap.num_of_public_inputs..], - &pk.prover_k_tau_g1[..qap.num_of_private_inputs()], - ) - .unwrap(); - - // [π_2]_1 - let pi2_g1 = msm(&w, &pk.r_tau_g1) - .unwrap() - .operate_with(&pk.beta_g1) - .operate_with(&pk.delta_g1.operate_with_self(s.representative())); - - // [π_3]_1 - let pi3 = k_tau_assigned_prover_g1 - .operate_with(&t_tau_h_tau_assigned_g1) - // s[π_1]_1 - .operate_with(&pi1.operate_with_self(s.representative())) - // r[π_2]_1 - .operate_with(&pi2_g1.operate_with_self(r.representative())) - // -rs[ƍ]_1 - .operate_with(&pk.delta_g1.operate_with_self((-(&r * &s)).representative())); - - Proof { pi1, pi2, pi3 } - } -} - -#[cfg(test)] -mod tests { - use lambdaworks_math::elliptic_curve::traits::IsEllipticCurve; - - use super::*; - - #[test] - fn serde() { - let proof = Proof { - pi1: Curve::generator().operate_with_self(sample_fr_elem().representative()), - pi2: TwistedCurve::generator().operate_with_self(sample_fr_elem().representative()), - pi3: Curve::generator().operate_with_self(sample_fr_elem().representative()), - }; - let deserialized_proof = Proof::deserialize(&proof.serialize()).unwrap(); - - assert_eq!(proof.pi1, deserialized_proof.pi1); - assert_eq!(proof.pi2, deserialized_proof.pi2); - assert_eq!(proof.pi3, deserialized_proof.pi3); - } -} diff --git a/crates/provers/groth16/src/qap.rs b/crates/provers/groth16/src/qap.rs deleted file mode 100644 index d1261929b..000000000 --- a/crates/provers/groth16/src/qap.rs +++ /dev/null @@ -1,164 +0,0 @@ -use lambdaworks_math::polynomial::Polynomial; - -use crate::{common::*, r1cs::R1CS}; - -#[derive(Debug)] -pub struct QuadraticArithmeticProgram { - pub num_of_public_inputs: usize, - pub num_of_gates: usize, - pub l: Vec>, - pub r: Vec>, - pub o: Vec>, -} - -impl QuadraticArithmeticProgram { - /// Computes the quotient polynomial - pub fn calculate_h_coefficients(&self, w: &[FrElement]) -> Vec { - let offset = &ORDER_R_MINUS_1_ROOT_UNITY; - let degree = self.num_of_gates * 2; - - let [l, r, o] = self.scale_and_accumulate_variable_polynomials(w, degree, offset); - - // TODO: Change to a vector of offsetted evaluations of x^N-1 - let t_poly = - Polynomial::new_monomial(FrElement::one(), self.num_of_gates) - FrElement::one(); - let mut t = Polynomial::evaluate_offset_fft(&t_poly, 1, Some(degree), offset).unwrap(); - FrElement::inplace_batch_inverse(&mut t).unwrap(); - - let h_evaluated = l - .iter() - .zip(&r) - .zip(&o) - .zip(&t) - .map(|(((l, r), o), t)| (l * r - o) * t) - .collect::>(); - - Polynomial::interpolate_offset_fft(&h_evaluated, offset) - .unwrap() - .coefficients() - .to_vec() - } - - // Compute A.s by summing up polynomials A[0].s, A[1].s, ..., A[n].s - // In other words, assign the witness coefficients / execution values - // Similarly for B.s and C.s - fn scale_and_accumulate_variable_polynomials( - &self, - w: &[FrElement], - degree: usize, - offset: &FrElement, - ) -> [Vec; 3] { - [&self.l, &self.r, &self.o].map(|var_polynomials| { - Polynomial::evaluate_offset_fft( - &(var_polynomials - .iter() - .zip(w) - .map(|(poly, coeff)| { - poly.mul_with_ref(&Polynomial::new_monomial(coeff.clone(), 0)) - }) - .reduce(|poly1, poly2| poly1 + poly2) - .unwrap()), - 1, - Some(degree), - offset, - ) - .unwrap() - }) - } - - pub fn num_of_private_inputs(&self) -> usize { - self.l.len() - self.num_of_public_inputs - } - - pub fn from_r1cs(r1cs: R1CS) -> QuadraticArithmeticProgram { - let num_gates = r1cs.number_of_constraints(); - let next_power_of_two = num_gates.next_power_of_two(); - let pad_zeroes = next_power_of_two - num_gates; - - let mut l: Vec> = vec![]; - let mut r: Vec> = vec![]; - let mut o: Vec> = vec![]; - for i in 0..r1cs.witness_size() { - let [l_poly, r_poly, o_poly] = - get_variable_lro_polynomials_from_r1cs(&r1cs, i, pad_zeroes); - l.push(l_poly); - r.push(r_poly); - o.push(o_poly); - } - - QuadraticArithmeticProgram { - l, - r, - o, - num_of_gates: next_power_of_two, - num_of_public_inputs: r1cs.number_of_inputs, - } - } - - pub fn from_variable_matrices( - num_of_public_inputs: usize, - l: &[Vec], - r: &[Vec], - o: &[Vec], - ) -> QuadraticArithmeticProgram { - let num_of_vars = l.len(); - assert!(num_of_vars > 0); - assert_eq!(num_of_vars, r.len()); - assert_eq!(num_of_vars, o.len()); - assert!(num_of_public_inputs <= num_of_vars); - - let num_of_gates = l[0].len(); - let next_power_of_two = num_of_gates.next_power_of_two(); - let pad_zeroes = next_power_of_two - num_of_gates; - - QuadraticArithmeticProgram { - num_of_public_inputs, - num_of_gates: next_power_of_two, - l: build_variable_polynomials(&apply_padding(l, pad_zeroes)), - r: build_variable_polynomials(&apply_padding(r, pad_zeroes)), - o: build_variable_polynomials(&apply_padding(o, pad_zeroes)), - } - } -} - -#[inline] -fn get_variable_lro_polynomials_from_r1cs( - r1cs: &R1CS, - var_idx: usize, - pad_zeroes: usize, -) -> [Polynomial; 3] { - let cap = r1cs.number_of_constraints() + pad_zeroes; - let mut current_var_l = vec![FrElement::zero(); cap]; - let mut current_var_r = vec![FrElement::zero(); cap]; - let mut current_var_o = vec![FrElement::zero(); cap]; - - for (i, c) in r1cs.constraints.iter().enumerate() { - current_var_l[i] = c.a[var_idx].clone(); - current_var_r[i] = c.b[var_idx].clone(); - current_var_o[i] = c.c[var_idx].clone(); - } - - [current_var_l, current_var_r, current_var_o] - .map(|e| Polynomial::interpolate_fft::(&e).unwrap()) -} - -#[inline] -fn build_variable_polynomials(from_matrix: &[Vec]) -> Vec> { - from_matrix - .iter() - .map(|row| Polynomial::interpolate_fft::(row).unwrap()) - .collect() -} - -/// Pads the columns so that the length is a multiple of 2 and we can use radix-2 FFT -#[inline] -fn apply_padding(columns: &[Vec], pad_zeroes: usize) -> Vec> { - columns - .iter() - .map(|column| { - let mut new_column = column.clone(); - new_column.extend(vec![FrElement::zero(); pad_zeroes]); - new_column - }) - .collect() -} diff --git a/crates/provers/groth16/src/r1cs.rs b/crates/provers/groth16/src/r1cs.rs deleted file mode 100644 index c1a96d1bf..000000000 --- a/crates/provers/groth16/src/r1cs.rs +++ /dev/null @@ -1,50 +0,0 @@ -use crate::common::FrElement; -use lambdaworks_math::field::{element::FieldElement, traits::IsField}; - -// To be improved with a front-end implementation -// TODO: Use CS in Groth16 tests instead of a plain QAP -#[derive(Clone, Debug, PartialEq, Eq)] -pub struct ConstraintSystem { - pub constraints: R1CS, - pub witness: Vec>, -} - -#[derive(Clone, Debug, PartialEq, Eq)] -pub struct Constraint { - pub a: Vec, - pub b: Vec, - pub c: Vec, -} -#[derive(Clone, Debug, PartialEq, Eq)] -pub struct R1CS { - pub constraints: Vec, - pub number_of_inputs: usize, -} - -impl R1CS { - pub fn from_matrices( - a: Vec>, - b: Vec>, - c: Vec>, - number_of_inputs: usize, - ) -> Self { - Self { - constraints: (0..a.len()) - .map(|i| Constraint { - a: a[i].clone(), - b: b[i].clone(), - c: c[i].clone(), - }) - .collect(), - number_of_inputs, - } - } - - pub fn number_of_constraints(&self) -> usize { - self.constraints.len() - } - - pub fn witness_size(&self) -> usize { - self.constraints[0].a.len() - } -} diff --git a/crates/provers/groth16/src/setup.rs b/crates/provers/groth16/src/setup.rs deleted file mode 100644 index deb8c9c06..000000000 --- a/crates/provers/groth16/src/setup.rs +++ /dev/null @@ -1,137 +0,0 @@ -use crate::{common::*, QuadraticArithmeticProgram}; -use lambdaworks_math::{ - cyclic_group::IsGroup, - elliptic_curve::{ - short_weierstrass::{point::ShortWeierstrassProjectivePoint, traits::IsShortWeierstrass}, - traits::{IsEllipticCurve, IsPairing}, - }, -}; - -pub struct VerifyingKey { - // e([alpha]_1, [beta]_2) computed during setup as it's a constant - pub alpha_g1_times_beta_g2: PairingOutput, - pub delta_g2: G2Point, - pub gamma_g2: G2Point, - // [K_0(τ)]_1, [K_1(τ)]_1, ..., [K_k(τ)]_1 - // where K_i(τ) = γ^{-1} * (β*l(τ) + α*r(τ) + o(τ)) - // and "k" is the number of public inputs - pub verifier_k_tau_g1: Vec, -} - -pub struct ProvingKey { - pub alpha_g1: G1Point, - pub beta_g1: G1Point, - pub beta_g2: G2Point, - pub delta_g1: G1Point, - pub delta_g2: G2Point, - // [A_0(τ)]_1, [A_1(τ)]_1, ..., [A_n(τ)]_1 - pub l_tau_g1: Vec, - // [B_0(τ)]_1, [B_1(τ)]_1, ..., [B_n(τ)]_1 - pub r_tau_g1: Vec, - // [B_0(τ)]_2, [B_1(τ)]_2, ..., [B_n(τ)]_2 - pub r_tau_g2: Vec, - // [K_{k+1}(τ)]_1, [K_{k+2}(τ)]_1, ..., [K_n(τ)]_1 - // where K_i(τ) = ƍ^{-1} * (β*l(τ) + α*r(τ) + o(τ)) - // and "k" is the number of public inputs - pub prover_k_tau_g1: Vec, - // [delta^{-1} * t(τ) * tau^0]_1, [delta^{-1} * t(τ) * τ^1]_1, ..., [delta^{-1} * t(τ) * τ^m]_1 - pub z_powers_of_tau_g1: Vec, -} - -struct ToxicWaste { - tau: FrElement, - alpha: FrElement, - beta: FrElement, - gamma: FrElement, - delta: FrElement, -} - -impl ToxicWaste { - /// This will create a new random ToxicWaste from entropy. Bear in mind this is not safe to use. - /// A proper ceremony, like Powers of Tau should be used in production to get the randomness. - pub fn new() -> Self { - Self { - tau: sample_fr_elem(), - alpha: sample_fr_elem(), - beta: sample_fr_elem(), - gamma: sample_fr_elem(), - delta: sample_fr_elem(), - } - } -} - -pub fn setup(qap: &QuadraticArithmeticProgram) -> (ProvingKey, VerifyingKey) { - let g1: G1Point = Curve::generator(); - let g2: G2Point = TwistedCurve::generator(); - - let tw = ToxicWaste::new(); - - let l_tau: Vec<_> = qap.l.iter().map(|p| p.evaluate(&tw.tau)).collect(); - let r_tau: Vec<_> = qap.r.iter().map(|p| p.evaluate(&tw.tau)).collect(); - - let mut to_be_inversed = [tw.delta.clone(), tw.gamma.clone()]; - FrElement::inplace_batch_inverse(&mut to_be_inversed).unwrap(); - let [delta_inv, gamma_inv] = to_be_inversed; - - let k_tau: Vec<_> = l_tau - .iter() - .zip(&r_tau) - .enumerate() - .map(|(i, (l, r))| { - let unshifted = &tw.beta * l + &tw.alpha * r + &qap.o[i].evaluate(&tw.tau); - if i < qap.num_of_public_inputs { - &gamma_inv * &unshifted - } else { - &delta_inv * &unshifted - } - }) - .collect(); - - let alpha_g1 = g1.operate_with_self(tw.alpha.representative()); - let beta_g2 = g2.operate_with_self(tw.beta.representative()); - - let alpha_g1_times_beta_g2 = Pairing::compute(&alpha_g1, &beta_g2).unwrap(); - - let delta_g2 = g2.operate_with_self(tw.delta.representative()); - - ( - ProvingKey { - alpha_g1, - beta_g1: g1.operate_with_self(tw.beta.representative()), - beta_g2, - delta_g1: g1.operate_with_self(tw.delta.representative()), - delta_g2: delta_g2.clone(), - l_tau_g1: batch_operate(&l_tau, &g1), - r_tau_g1: batch_operate(&r_tau, &g1), - r_tau_g2: batch_operate(&r_tau, &g2), - prover_k_tau_g1: batch_operate(&k_tau[qap.num_of_public_inputs..], &g1), - z_powers_of_tau_g1: batch_operate( - &core::iter::successors( - // Start from delta^{-1} * t(τ) - // Note that t(τ) = (τ^N - 1) because our domain is roots of unity - Some(&delta_inv * (&tw.tau.pow(qap.num_of_gates) - FrElement::one())), - |prev| Some(prev * &tw.tau), - ) - .take(qap.num_of_gates * 2) - .collect::>(), - &g1, - ), - }, - VerifyingKey { - alpha_g1_times_beta_g2, - delta_g2, - gamma_g2: g2.operate_with_self(tw.gamma.representative()), - verifier_k_tau_g1: batch_operate(&k_tau[..qap.num_of_public_inputs], &g1), - }, - ) -} - -fn batch_operate( - elems: &[FrElement], - point: &ShortWeierstrassProjectivePoint, -) -> Vec> { - elems - .iter() - .map(|elem| point.operate_with_self(elem.representative())) - .collect() -} diff --git a/crates/provers/groth16/src/verifier.rs b/crates/provers/groth16/src/verifier.rs deleted file mode 100644 index b83c4b215..000000000 --- a/crates/provers/groth16/src/verifier.rs +++ /dev/null @@ -1,22 +0,0 @@ -use lambdaworks_math::{elliptic_curve::traits::IsPairing, msm::pippenger::msm}; - -use crate::common::{FrElement, Pairing}; -use crate::prover::Proof; -use crate::setup::VerifyingKey; - -pub fn verify(vk: &VerifyingKey, proof: &Proof, pub_inputs: &[FrElement]) -> bool { - // [γ^{-1} * (β*l(τ) + α*r(τ) + o(τ))]_1 - let k_tau_assigned_verifier_g1 = msm( - &pub_inputs - .iter() - .map(|elem| elem.representative()) - .collect::>(), - &vk.verifier_k_tau_g1, - ) - .unwrap(); - - Pairing::compute(&proof.pi3, &vk.delta_g2).unwrap() - * vk.alpha_g1_times_beta_g2.clone() - * Pairing::compute(&k_tau_assigned_verifier_g1, &vk.gamma_g2).unwrap() - == Pairing::compute(&proof.pi1, &proof.pi2).unwrap() -} diff --git a/crates/provers/groth16/tests/groth16.rs b/crates/provers/groth16/tests/groth16.rs deleted file mode 100644 index 4df7d1f93..000000000 --- a/crates/provers/groth16/tests/groth16.rs +++ /dev/null @@ -1,43 +0,0 @@ -use lambdaworks_groth16::{common::*, setup, verify, Proof, Prover}; - -mod test_circuits; -use test_circuits::*; - -#[test] -fn vitalik() { - let qap = test_circuits::vitalik_qap(); // x^3 + x + 5 = 35 - - let (pk, vk) = setup(&qap); - - for w in [ - ["0x1", "0x3", "0x23", "0x9", "0x1b", "0x1e"], - ["0x1", "0x1", "0x7", "0x1", "0x1", "0x2"], - ] { - let w = w // x = 3 - .map(FrElement::from_hex_unchecked) - .to_vec(); - - let serialized_proof = Prover::prove(&w, &qap, &pk).serialize(); - let deserialized_proof = Proof::deserialize(&serialized_proof).unwrap(); - - let accept = verify(&vk, &deserialized_proof, &w[..qap.num_of_public_inputs]); - assert!(accept); - } -} - -#[test] -fn example() { - let qap = test_qap_2(); - let (pk, vk) = setup(&qap); - - // 1, x, y, ~out, sym_1, sym_2, sym_3, sym_4 - let w = ["0x1", "0x5", "0x3", "0x0", "0x19", "0x9", "0x0", "0x0"] // x = 3 - .map(FrElement::from_hex_unchecked) - .to_vec(); - - let serialized_proof = Prover::prove(&w, &qap, &pk).serialize(); - let deserialized_proof = Proof::deserialize(&serialized_proof).unwrap(); - - let accept = verify(&vk, &deserialized_proof, &w[..qap.num_of_public_inputs]); - assert!(accept); -} diff --git a/crates/provers/groth16/tests/test_circuits/mod.rs b/crates/provers/groth16/tests/test_circuits/mod.rs deleted file mode 100644 index faaf450eb..000000000 --- a/crates/provers/groth16/tests/test_circuits/mod.rs +++ /dev/null @@ -1,103 +0,0 @@ -use lambdaworks_groth16::{common::*, QuadraticArithmeticProgram as QAP}; - -/* -Represents x^3 + x + 5 = 35, based on https://vitalik.ca/general/2016/12/10/qap.html - x * x = sym_1; - sym_1 * x = y; - (y + x) * 1 = sym_2 - (sym_2 + 5) * 1 = ~out -*/ -#[cfg(test)] -pub fn vitalik_qap() -> QAP { - let num_of_public_inputs = 1; - let [l, r, o] = [ - [ - ["0", "0", "0", "5"], // 1 - ["1", "0", "1", "0"], // x - ["0", "0", "0", "0"], // ~out - ["0", "1", "0", "0"], // sym_1 - ["0", "0", "1", "0"], // y - ["0", "0", "0", "1"], // sym_2 - ], - [ - ["0", "0", "1", "1"], - ["1", "1", "0", "0"], - ["0", "0", "0", "0"], - ["0", "0", "0", "0"], - ["0", "0", "0", "0"], - ["0", "0", "0", "0"], - ], - [ - ["0", "0", "0", "0"], - ["0", "0", "0", "0"], - ["0", "0", "0", "1"], - ["1", "0", "0", "0"], - ["0", "1", "0", "0"], - ["0", "0", "1", "0"], - ], - ] - .map(|matrix| matrix.map(|row| row.map(FrElement::from_hex_unchecked).to_vec())); - QAP::from_variable_matrices(num_of_public_inputs, &l, &r, &o) -} - -/* -Represents x^2 = 25 or y^2 = 9 - input signal x, y - - sym_1 = x * x -> 25 - sym_2 = y * y -> 9 - - sym_3 = sym_1 - 25 - sym_4 = sym_2 - 9 - - ~out = sym_3 * sym_4 -> needs to be zero -*/ -#[cfg(test)] -pub fn test_qap_2() -> QAP { - let num_of_public_inputs = 2; - let [l, r, o] = [ - [ - ["0", "0", "-19", "-9", "0"], //1 - ["1", "0", "0", "0", "0"], //x - ["0", "1", "0", "0", "0"], //y - ["0", "0", "0", "0", "1"], //~out - ["0", "0", "1", "0", "0"], //sym_1 - ["0", "0", "0", "1", "0"], //sym_2 - ["0", "0", "0", "0", "1"], //sym_3 - ["0", "0", "0", "0", "0"], //sym_4 - ], - [ - ["0", "0", "1", "1", "0"], //1 - ["1", "0", "0", "0", "0"], //x - ["0", "1", "0", "0", "0"], //y - ["0", "0", "0", "0", "0"], //~out - ["0", "0", "0", "0", "0"], //sym_1 - ["0", "0", "0", "0", "0"], //sym_2 - ["0", "0", "0", "0", "0"], //sym_3 - ["0", "0", "0", "0", "1"], //sym_4 - ], - [ - ["0", "0", "0", "0", "0"], //1 - ["0", "0", "0", "0", "0"], //x - ["0", "0", "0", "0", "0"], //y - ["0", "0", "0", "0", "1"], //~out - ["1", "0", "0", "0", "0"], //sym_1 - ["0", "1", "0", "0", "0"], //sym_2 - ["0", "0", "1", "0", "0"], //sym_3 - ["0", "0", "0", "1", "0"], //sym_4 - ], - ] - .map(|matrix| { - matrix.map(|row| { - row.map(|elem| { - if elem.starts_with('-') { - -FrElement::from_hex_unchecked(&elem.chars().skip(1).collect::()) - } else { - FrElement::from_hex_unchecked(elem) - } - }) - .to_vec() - }) - }); - QAP::from_variable_matrices(num_of_public_inputs, &l, &r, &o) -} diff --git a/crates/provers/plonk/Cargo.toml b/crates/provers/plonk/Cargo.toml deleted file mode 100644 index 18c3fab33..000000000 --- a/crates/provers/plonk/Cargo.toml +++ /dev/null @@ -1,15 +0,0 @@ -[package] -name = "lambdaworks-plonk" -version = "0.1.0" -edition = "2021" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] -lambdaworks-math.workspace = true -lambdaworks-crypto.workspace = true - -serde = { version = "1.0", features = ["derive"] } -serde_json = "1.0" -sha3 = { version = "0.10", default-features = false } -sha2 = { version = "0.10", default-features = false } diff --git a/crates/provers/plonk/README.md b/crates/provers/plonk/README.md deleted file mode 100644 index e512c7a52..000000000 --- a/crates/provers/plonk/README.md +++ /dev/null @@ -1,93 +0,0 @@ -# Lambdaworks Plonk Prover -A fast implementation of the [Plonk](https://eprint.iacr.org/2019/953) zk-protocol written in Rust. This is part of the [Lambdaworks](https://github.com/lambdaclass/lambdaworks) zero-knowledge framework. It includes a high-level API to seamlessly build your own circuits. - -This prover is still in development and may contain bugs. It is not intended to be used in production yet. - -## Building a circuit - -Starting with an example, the following code creates a circuit with two public inputs `x`, `y` and asserts `x * e = y`: - -```rust -let system = &mut ConstraintSystem::::new(); -let x = system.new_public_input(); -let y = system.new_public_input(); -let e = system.new_variable(); - -let z = system.mul(&x, &e); -system.assert_eq(&y, &z);; -``` - -By placing this logic under one function, one can create "gadgets" to abstract functionality. - -```Rust -/// A square and multiply implementation. -pub fn pow( - system: &mut ConstraintSystem, - base: Variable, - exponent: Variable, -) -> Variable { - let exponent_bits = system.new_u32(&exponent); - let mut result = system.new_constant(FE::one()); - - assert_eq!(exponent_bits.len(), 32); - for (i, bit) in exponent_bits.iter().enumerate() { - if i != 0 { - result = system.mul(&result, &result); - } - let result_times_base = system.mul(&result, &base); - result = system.if_else(bit, &result_times_base, &result); - } - result -} -``` - -The core operations supported by plonk and our prove system are: - -```rust -mul(var1,var2) -add(var1,var2) -add_constant(var1,constant) -div(var1,var2) -// c1 * v1 + c2 * v2 + b = w -// hinted value can be w,v1, or v2 -let w = linear_combination(&v1, c1, &v2, c2, b, Option(hint)) -``` - -All the variables and constants are finite fields. Abstractions like integers are not implemented yet. - -## Generating a proof -### Setup -A setup is needed in order to generate a proof for a new circuit. The following code generates a verifying key that will be used by both the prover and the verifier: - -```rust -let common = CommonPreprocessedInput::from_constraint_system(&system, &ORDER_R_MINUS_1_ROOT_UNITY); -let srs = test_srs(common.n); -let kzg = KZG::new(srs); // The commitment scheme for plonk. -let verifying_key = setup(&common, &kzg); -``` - -### Prover -First, we fix values for `x` and `e` and solve the constraint system: -```rust -let inputs = HashMap::from([(x, FieldElement::from(4)), (e, FieldElement::from(3))]); -let assignments = system.solve(inputs).unwrap(); -``` - -Finally, we call the prover: -```rust -let witness = Witness::new(assignments, &system); -let public_inputs = system.public_input_values(&assignments); -let prover = Prover::new(kzg.clone(), TestRandomFieldGenerator {}); -let proof = prover.prove(&witness, &public_inputs, &common, &verifying_key); -``` - -## Verifying a proof -Just call the verifier: - -```rust -let verifier = Verifier::new(kzg); -assert!(verifier.verify(&proof, &public_inputs, &common, &verifying_key)); -``` - -# More info -You can find more info in the [documentation](https://lambdaclass.github.io/lambdaworks_plonk_prover/). diff --git a/crates/provers/plonk/src/constraint_system/conditional.rs b/crates/provers/plonk/src/constraint_system/conditional.rs deleted file mode 100644 index d7f1ca9c9..000000000 --- a/crates/provers/plonk/src/constraint_system/conditional.rs +++ /dev/null @@ -1,131 +0,0 @@ -use lambdaworks_math::field::{element::FieldElement, traits::IsField}; - -use super::{Constraint, ConstraintSystem, ConstraintType, Variable}; - -impl ConstraintSystem -where - F: IsField, -{ - /// Adds a constraint to enforce that `v1` is equal to `v2`. - pub fn assert_eq(&mut self, v1: &Variable, v2: &Variable) { - self.add_constraint(Constraint { - constraint_type: ConstraintType { - ql: FieldElement::one(), - qr: -FieldElement::one(), - qm: FieldElement::zero(), - qo: FieldElement::zero(), - qc: FieldElement::zero(), - }, - l: *v1, - r: *v2, - o: self.null_variable(), - hint: None, - }); - } - - /// Creates a new variable `w` constrained to be `v1` in case - /// `boolean_condition` is `1` and `v2` otherwise. - pub fn if_else( - &mut self, - boolean_condition: &Variable, - v1: &Variable, - v2: &Variable, - ) -> Variable { - let not_boolean_condition = self.not(boolean_condition); - let if_branch = self.mul(v1, boolean_condition); - let else_branch = self.mul(v2, ¬_boolean_condition); - self.add(&if_branch, &else_branch) - } - - /// Creates a new variable `w` constrained to be `v1` in case - /// `condition` is not zero and `v2` otherwise. - pub fn if_nonzero_else( - &mut self, - condition: &Variable, - v1: &Variable, - v2: &Variable, - ) -> Variable { - let (is_zero, _) = self.inv(condition); - self.if_else(&is_zero, v2, v1) - } -} - -#[cfg(test)] -mod tests { - use std::collections::HashMap; - - use lambdaworks_math::field::{ - element::FieldElement as FE, fields::u64_prime_field::U64PrimeField, - }; - - use crate::constraint_system::ConstraintSystem; - - #[test] - fn test_assert_eq_1() { - let system = &mut ConstraintSystem::>::new(); - - let v = system.new_variable(); - let w = system.new_variable(); - let z = system.mul(&v, &w); - let output = system.new_variable(); - system.assert_eq(&z, &output); - - let inputs = HashMap::from([(v, FE::from(2)), (w, FE::from(2).inv().unwrap())]); - - let assignments = system.solve(inputs).unwrap(); - assert_eq!(assignments.get(&output).unwrap(), &FE::one()); - } - - #[test] - fn test_assert_eq_2() { - let system = &mut ConstraintSystem::>::new(); - - let v = system.new_variable(); - let w = system.new_variable(); - let z = system.mul(&v, &w); - let output = system.new_variable(); - system.assert_eq(&z, &output); - - let inputs = HashMap::from([(v, FE::from(2)), (w, FE::from(2)), (output, FE::from(1))]); - - let _assignments = system.solve(inputs).unwrap_err(); - } - - #[test] - fn test_if_nonzero_else_1() { - let system = &mut ConstraintSystem::>::new(); - - let v = system.new_variable(); - let v2 = system.mul(&v, &v); - let v4 = system.mul(&v2, &v2); - let w = system.add_constant(&v4, -FE::one()); - let output = system.if_nonzero_else(&w, &v, &v2); - - let inputs = HashMap::from([(v, FE::from(256))]); - - let assignments = system.solve(inputs).unwrap(); - assert_eq!( - assignments.get(&output).unwrap(), - assignments.get(&v2).unwrap() - ); - } - - #[test] - fn test_if_nonzero_else_2() { - let system = &mut ConstraintSystem::>::new(); - - let v = system.new_variable(); - let v2 = system.mul(&v, &v); - let v4 = system.mul(&v2, &v2); - let w = system.add_constant(&v4, -FE::one()); - let output = system.if_nonzero_else(&w, &v, &v2); - - let inputs = HashMap::from([(v, FE::from(255))]); - - let assignments = system.solve(inputs).unwrap(); - assert_eq!( - assignments.get(&output).unwrap(), - assignments.get(&v).unwrap() - ); - } -} diff --git a/crates/provers/plonk/src/constraint_system/errors.rs b/crates/provers/plonk/src/constraint_system/errors.rs deleted file mode 100644 index d091fc2cf..000000000 --- a/crates/provers/plonk/src/constraint_system/errors.rs +++ /dev/null @@ -1,5 +0,0 @@ -#[derive(Debug, PartialEq, Eq)] -pub enum SolverError { - InconsistentSystem, - UnableToSolve, -} diff --git a/crates/provers/plonk/src/constraint_system/examples/mimc.rs b/crates/provers/plonk/src/constraint_system/examples/mimc.rs deleted file mode 100644 index 96fde9ca3..000000000 --- a/crates/provers/plonk/src/constraint_system/examples/mimc.rs +++ /dev/null @@ -1,176 +0,0 @@ -use lambdaworks_math::field::{element::FieldElement as FE, traits::IsField}; - -use crate::constraint_system::{ConstraintSystem, Variable}; - -/// The MIMC hash function. -pub fn mimc( - system: &mut ConstraintSystem, - coefficients: &[FE], - data: &[Variable], -) -> Variable { - let mut h = system.new_constant(FE::zero()); - - for item in data.iter() { - let mut x = *item; - for c in coefficients.iter() { - // x = (x + h + c) ** 5 - x = system.linear_combination(&x, FE::one(), &h, FE::one(), c.clone(), None); - let x_pow_2 = system.mul(&x, &x); - let x_pow_4 = system.mul(&x_pow_2, &x_pow_2); - x = system.mul(&x_pow_4, &x); - } - // h = x + 2h + item - h = system.linear_combination(&x, FE::one(), &h, FE::from(2), FE::zero(), None); - h = system.add(&h, item); - } - h -} - -#[cfg(test)] -pub mod tests { - use std::collections::HashMap; - - use lambdaworks_math::{ - elliptic_curve::short_weierstrass::curves::bls12_381::default_types::FrField, - field::element::FieldElement as FE, - }; - - use crate::constraint_system::{examples::mimc::mimc, ConstraintSystem}; - - #[test] - fn test_mimc() { - let coefficients = vec![ - "1dbfc7763d69ca7d15701422f37bc6692bd01ebc4da42360f81f9adb4a91b01a", - "4fd2cddd334dab1c4005161c290f25a0e18d4175ecfa898b17095d8ec2dd344a", - "9cc76e9b37ba649b0accb508950d834af091f3d687c208d9013e1685075f092", - "16472c2e925fbba0fad047c428a4e8e4801414975e9841d5518b57fbcf26dde1", - "1c2e148c40ea201b748bee72845b349bfa4a4497837af0d569ae47afc6e4243", - "705ea7625cbcb5daf4d3dc5d0322e7b3adbe32227dc52234035881407825dbaf", - "1272efa088fdd0c941712554929ee2bf4e298fce57337dda8f4d704a8bdec1ea", - "4f966f7b066b2d838afab7b99705b1fbecff809f083be8a03ea1a656be14d72a", - "283392b9145c98fc9680ee035816761cb79155557f0b302511a928c221b04c03", - "430a47a5110d6ee4da087ee3291a217f7afba21d696eb74de6ce41cf50aeeff4", - "1c9fbf2d5b15f5b4b9aaa8dfb452a310b6fa3de7b2b7c68260f8e4aff63840ef", - "49c756d15bbf811f532811dba19f5fda9df678bcdd4017024ef4daded412af7d", - "3d6d63a3302df941979292e4be9a85f9a960698ce9a2e5d430423f4adf7a9bb8", - "5f6c2da1c738096eaac7763afc219965955b33e619ce5679c3f5d3aef1792b0a", - "32d630538e47bf4f8968170577a08cb1b26864879c86dafb652cce5068bdb5aa", - "2eb8b2a5593fdef777738374339441e112704f378f7cca12d4146d30a005b96", - "123313cced613293c40586b110f8e4244cd67cc4380c8f5df4ec60f42216ce28", - "42d1c99dccb35f9afa170ee24eb146903819160985f2460d7785ac4381ca037c", - "35375cf9debbeba36a0ed9286c67a18bd2112dc028387b905b36c23dce8c4926", - "67e693adf50e0e16fa03d5f9481d71ed0f63ed4527e080941d1ba0473c18bcc3", - "1d5f6a82f699df8c7fff5b5f90047128ead7923635c92a4849ff28689b6c7258", - "372a3d44e73aae9443ca680956bcd23dbdd5f790e0c5cfa45a0fcfb9ee920144", - "630b2c9009da6417963e8d45ae92e59322746e545e04f026004a2f76c12422f5", - "33269ebd4d0f0a2874a217899b11a13361d262c1be48f2a46e6b132f897a5ac4", - "394d93f60615db568325c284dd916d735072cb57b6cd2a0072d976154d8a3eea", - "14e83ce42e31effc8be6e0119ecc4157c1c44206e159aff0761e92a945aa0591", - "3495919dabe2a35059ef2e1802ae59992fd7b3a14786378ac9f622b907c6da55", - "5e88df9396c526cd97c00d7e1865a2175cf44f5271c85bad098d14013238df41", - "925666e8a081d7b9f6b74ed57bd8e41533c20d7715b0bb47fabca5a465c4019", - "332e4e5ff2e5d1afaaf9ef551934b1006ba305f26b5b35940e71605a5ebb5f56", - "3462e730e81f90ddf9bb1046abaca984656932c13d1f00c387181c3c9aa43576", - "65bad101fa269d55e51bbf694f5541225e26986350b4165ede5a7e1232355a69", - "6e66ec021919cde6932d3b0d4c2c63076f0da7e33b3af529548304096d127502", - "4c609941ec5da50d43b8d6d7d45fdd4faa8bb69929fc3337ddfc1bee29f7b94", - "127f12060eb1a416ee0d304c538e094a13eb18310a2ecfd0fa81cce82a59e43d", - "e247806a33437f19022c6958e51a172f6fd58853ef95d2ea3f8123ce9c2a399", - "361564af11cea08fdc3afd9bd53471561356ad5b62e762c7d6023fbb12d5b7b", - "6e12938c2d2d52577956a23d5df8a8e56d8e7a5bdfcc9cd3330835c9a865608b", - "2c65d8fca4105323322504d653328c6692137481e686f256c3acf98b8888edd", - "217451f2b930057065d940024678dabf1525e8375522a23da9186255df7514fc", - "b2dddc8994767c7d3632cc7bc089becf8ef3b65540fb4709b8cc78ba12b044b", - "5331126da252555886cd62e62bf8fb25c1040470bc827734f516c2f0c90fff3f", - "317a581c6091951f08e8580adc43c1b02729900d2acb2de27e0f6b034b7d8c56", - "6c6741993eb1d5bc90edd8ac667037865ea3e9c0788d3c319739a8bcc0893ba9", - "54420b8489fa8145c279c03817315b31bc39445306be7f48fcba9a46c9f4f3b2", - "2059da76bbb25f44687caae9f61e4afb811bf899a3aae060751049f7d21dd606", - "6a30ab452ec8fe1e76c742ab33ec57448512ff27385a0a2bac70a1686d573570", - "5416c3b67ec815aa7481f04a9b8dbde374786caea7d3f0ba4a98f326efec02a1", - "4b32a6c01df4fdf6f2cc3bacec09b008ccc5c3644b69139c4346a3e75fab3f4b", - "2e91572a13a6baf97560b43b5b862aebd8b7d95c0fda9c097d823cc9ef0599e", - "18d4e26bbbdfa70d96ed89322834c8b1d36a3b3d373e9be7cfa588a8b5c0287b", - "3d3f818cd10fcf2d1fe9ef125dfbe112e8298eca96f58b0b86f42608b976d165", - "31e7074392297131067adb72832e19c1e271d312c551d7e4f40b441f942da24f", - "245ddf2de52031410d4171e67579a57f7866bb3ae20a4525f82505d050a86281", - "289673cb2bef13266ce3f7179e624b2e383d24a45ef6c375ba998e3fe9286a36", - "3c59fba0c6311941376d9b0280c32e726d0711a734999892707246ba7b2bd32", - "144c00621ab41c0c0f354ef520654c0150d61f502b7d923b9822a1a33294cab1", - "68c6a95568f6ed64ff72c30387d7606d072448ecc708997317c6db6cf515cbaf", - "662fe152dc7461f350a0a8c9286fa3d635ff00931d3a296e358345595f72ed3c", - "217b043aadd7058a7e9270dc0a2f571a8d1ccd116297b85823de86d173e54321", - "68d4303e7691e3a4795db36ea36432f8f1075438e9b0f8d1fb5999dfd4974d38", - "1e26115ea5e4c4f066b84107cd8f6fce5792d77dda305a01790a54f3e234d210", - "6e1bd58127f00ccc79c3609843b1ad75de0527f21df9eacfe29ab4c563a67753", - "5a4aad3966fd75111f70775b9fa62a6d1c18702b26fe5851c83efa2b10954c92", - "71c059faec1533beacad247017b29980008f0d937370301ee2401018fc2aa7da", - "2b625e82f540d4603233baec3d48d81d9d855962b50771c6d5df82012044e896", - "47e22b67d921cd1626e262ff3c739c646696e6336f1aedeb95881c62b511268b", - "286509b96b3aa4a9d101a53e83b1b25fdd76dc3c00052c9126b8200d1449834e", - "476bb19f615e3a38389ead4b38e8a61665c089682e7f7f46830d211db2616135", - "67a0f1036c1628ed81e80f2493dbc5100b736843bb3a0d36f67d2b2dce99a192", - "956eedcce3f1bb98cc45a3ad88ab894ddb3f7e775a11b961698f73c8381e07c", - "63b48515137ba347cfff4389958351d07be7f13ee7187d4d5902d085637ee7bf", - "3ff35869606dfa185b81adc1465fc268a4f481f75562aca9e4b46c00a77ec6c2", - "729558bf05bd766305ddebb83018c6a52916acf31fd71085bd2165515bccaa86", - "557fce386beeea241a2b8bb4fbb47cec057e235ca733fca67935761e140c61b3", - "7f5af6d793912f4649026b8e7c55bafb8c14c003b296afdd2924c4540df0f45", - "34ae79f5d988f866842080049ee7af47c48a7f2c1466638f8800259b4f2af2fb", - "27fffd50aeb4aeac31469860bb68f2673d176f334f084440b8d806534f1d4698", - "124f077ee1466fd7d3dc1e15b460663820dfb1cf542988480333d260f1ead81f", - "12ad6f35913b3a56083aee7ee7e4a489ad73c400c030b2befa1cbc20313e359f", - "3befad3a0b8f4debf05a376cd38129e0c87d7b446443611252269bd6f0206da0", - "60974721e0b87c5dc35408f791d7843feed7f63cd5975a661ed67be0bffd343d", - "4db00887ffb9981dba5da03142d103d18096731637be4bdf1831a261eb4abc1c", - "3449ca4e443a46c14719d5771d05701bbbc4db571a3d7770240c3bc91c020dcb", - "10e2e709f73f334e5f1373567b95d5e3edcf807f613826ea7120044c8444556f", - "5d767c67116c8a0b388f24ac74212190be52a295c46cde008fdb8539ee58a49", - "24616115f5f6421892eccc479da1b684c6b525bc1d3ff3cc95727863a2bc035d", - "1f3973d80f425cf3e02e44930a273219df15dc4cb04c32ee086bccdfa6dc312", - "338900eb90ef72de7560c97f7d8e64a68114e9ee696c0141fb6a922db16353e0", - "8a621008ece8b2cad60e9cf048cb4cb8eb95e7a7c9d517ebaf165fec3387fc1", - "3b3472a80d728abd9758e42fd22a478ebcd08b59ffa3c5e628e9e789a71a82a9", - "167848c58dddcac256afdf24a93e12829f611534cc437bba34c774241cfc1812", - "18c263472f9e8f2f262f6a572e33723761bb322cdc021b2cb4b136b0b74db77d", - "1a54a00df68e3d7ec52e62b61c624b0b6951031d982315a46c444eb55347b669", - "323e73ff092080d3d3c326c037bc64bf3c5553af4817447155cac913ee16232", - "4b16f8918636214e2483bd6c0cac7ce1755891c8044b6a5a5848f8044382c9b4", - "55da1b7e81416386c36dd95b752c15142b1225c39f88d269cca7cae381acabd8", - "dbfbbf19841c1792826f69ad92b862e3800af884a9cc2166c43a8b02b64eb16", - "37af211973056b0ee14d5776101e03e20360924d488bdee58b840a9cc65b530a", - "33a2cf480bbaabc0529bd29b5ead59325ff6eb4eb99b83f8e4e52b8bdd8d8ceb", - "10fe5117d64559e99e3bc90440f1d0c87ee1cbc7d14cbf524cc6e25c54291fcb", - "71408145cdaa0a727a889eff3586f0755d76abf8e157ad07fb199cf1444cee49", - "305f5892133b16e865cd1bcd3ca96f39a552e9e24ce724a6679d53fb4d421de1", - "3792c249ab22a410bd9765026d09c4975767a364ed4ce8cda5c739d413538f4d", - "54051fb18e4577eef62592a030adedcc11b22ade24a32e76f8bf68ef96039c22", - "3562918322d14865722d461ee61e323c3988de5496d311e5e3b752a173d0f524", - "4966ed088e26f77208302acb1977596cfed466aa7021ee9fa455a1568b9cc8ee", - "217e57ad60015c9a4c3525239f1226f1a12b00dc220c2a3476edb9d6e33718b", - "65f67c734e6dd080b1490d748c4ca54c3be080f68ff0983449d5de28dadad1c", - "3db4cc8fd2f2f8e1478ad41b7c1e5c5ef19301bb87f44b49b378fe3e7e3a2264", - "3261a8cb17034b0c32bc98cc77513ad895233f70e86d8ff6df57485ad194afc6", - ]; - let coefficients: Vec<_> = coefficients - .iter() - .map(|hex_str| FE::from_hex(hex_str).unwrap()) - .collect(); - - let system = &mut ConstraintSystem::::new(); - let data = vec![system.new_variable()]; - let output = mimc(system, &coefficients, &data); - - let input_value = - FE::from_hex("23a950068dd3d1e21cee48e7919be7ae32cdef70311fc486336ea9d4b5042535") - .unwrap(); - let expected_output_value = - FE::from_hex("136ff6a4e5fc9a2103cc54252d93c3be07f781dc4405acd9447bee65cfdc7c14") - .unwrap(); - - let inputs = HashMap::from([(data[0], input_value)]); - let assignments = system.solve(inputs).unwrap(); - - assert_eq!(assignments.get(&output).unwrap(), &expected_output_value); - } -} diff --git a/crates/provers/plonk/src/constraint_system/examples/mod.rs b/crates/provers/plonk/src/constraint_system/examples/mod.rs deleted file mode 100644 index f5eea98c7..000000000 --- a/crates/provers/plonk/src/constraint_system/examples/mod.rs +++ /dev/null @@ -1,2 +0,0 @@ -pub mod mimc; -pub mod pow; diff --git a/crates/provers/plonk/src/constraint_system/examples/pow.rs b/crates/provers/plonk/src/constraint_system/examples/pow.rs deleted file mode 100644 index b1d298c59..000000000 --- a/crates/provers/plonk/src/constraint_system/examples/pow.rs +++ /dev/null @@ -1,46 +0,0 @@ -use lambdaworks_math::field::{element::FieldElement as FE, traits::IsPrimeField}; - -use crate::constraint_system::{ConstraintSystem, Variable}; - -/// A square and multiply implementation. -pub fn pow( - system: &mut ConstraintSystem, - base: Variable, - exponent: Variable, -) -> Variable { - let exponent_bits = system.new_u32(&exponent); - let mut result = system.new_constant(FE::one()); - - assert_eq!(exponent_bits.len(), 32); - for (i, bit) in exponent_bits.iter().enumerate() { - if i != 0 { - result = system.mul(&result, &result); - } - let result_times_base = system.mul(&result, &base); - result = system.if_else(bit, &result_times_base, &result); - } - result -} - -#[cfg(test)] -mod tests { - use std::collections::HashMap; - - use lambdaworks_math::field::fields::u64_prime_field::U64PrimeField; - - use crate::constraint_system::{examples::pow::pow, ConstraintSystem}; - use lambdaworks_math::field::element::FieldElement as FE; - - #[test] - fn test_pow() { - let system = &mut ConstraintSystem::>::new(); - - let base = system.new_variable(); - let exponent = system.new_variable(); - let result = pow(system, base, exponent); - let inputs = HashMap::from([(base, FE::from(3)), (exponent, FE::from(10))]); - - let assignments = system.solve(inputs).unwrap(); - assert_eq!(assignments.get(&result).unwrap(), &FE::from(59049)); - } -} diff --git a/crates/provers/plonk/src/constraint_system/mod.rs b/crates/provers/plonk/src/constraint_system/mod.rs deleted file mode 100644 index 059aac770..000000000 --- a/crates/provers/plonk/src/constraint_system/mod.rs +++ /dev/null @@ -1,351 +0,0 @@ -pub mod conditional; -pub mod errors; -pub mod examples; -pub mod operations; -pub mod solver; -pub mod types; - -use std::collections::HashMap; - -use lambdaworks_math::field::{element::FieldElement, traits::IsField}; - -/// A constraint that enforces relations between variables. If `ConstraintType` -/// represents (Q_L, Q_R, Q_M, Q_O, Q_C), then the constraint enforces that -/// `a Q_L + b Q_R + a b Q_M + c Q_O + Q_C = 0` where `a`, `b`, and `c` are the -/// values taken by the variables `l`, `r` and `o` respectively. -#[derive(Clone)] -pub struct Constraint { - constraint_type: ConstraintType, - hint: Option>, - l: Variable, - r: Variable, - o: Variable, -} - -/// A `ConstraintType` represents a type of gate and is determined by the values -/// of the coefficients Q_L, Q_R, Q_M, Q_O, Q_C -#[derive(Clone)] -struct ConstraintType { - ql: FieldElement, - qr: FieldElement, - qm: FieldElement, - qo: FieldElement, - qc: FieldElement, -} - -/// A `Column` is either `L`, `R` or `O`. It represents the role played by a -/// variable in a constraint. -#[derive(Clone, PartialEq, Eq, Hash)] -pub enum Column { - L, - R, - O, -} - -/// A `Hint` is used to insert values to the solver. This is helpful when a -/// constraint is hard to solve but easy to check. -#[derive(Clone)] -pub struct Hint { - function: fn(&FieldElement) -> FieldElement, - input: Column, - output: Column, -} - -/// Represents a variable as an ID. -pub type Variable = usize; - -/// A collection of variables and constraints that encodes correct executions -/// of a program. Variables can be of two types: Public or private. -pub struct ConstraintSystem { - num_variables: usize, - public_input_variables: Vec, - constraints: Vec>, -} - -impl ConstraintSystem -where - F: IsField, -{ - /// Returns a new empty constraint system. - pub fn new() -> Self { - Self { - num_variables: 0, - public_input_variables: Vec::new(), - constraints: Vec::new(), - } - } - - /// Adds a constraint to the system. - pub fn add_constraint(&mut self, constraint: Constraint) { - self.constraints.push(constraint); - } - - /// Returns a null variable to be used as a placeholder - /// in constraints. - pub fn null_variable(&self) -> Variable { - 0 - } - - /// Creates a new variable. - pub fn new_variable(&mut self) -> Variable { - let variable_id = self.num_variables; - self.num_variables += 1; - variable_id - } - - /// Creates a new public variable. - pub fn new_public_input(&mut self) -> Variable { - let new_variable = self.new_variable(); - self.public_input_variables.push(new_variable); - new_variable - } - - /// A dummy constraint meant to be used as padding. - fn padding_constraint(&self) -> Constraint { - let zero = FieldElement::zero(); - Constraint { - constraint_type: ConstraintType { - ql: zero.clone(), - qr: zero.clone(), - qm: zero.clone(), - qo: zero.clone(), - qc: zero, - }, - hint: None, - l: self.null_variable(), - r: self.null_variable(), - o: self.null_variable(), - } - } - - /// Returns the public input header used in PLONK to prove the usage of the - /// public input values. - fn public_input_header(&self) -> Vec> { - let zero = FieldElement::zero(); - let minus_one = -FieldElement::one(); - let mut public_input_constraints = Vec::new(); - for public_input in self.public_input_variables.iter() { - let public_input_constraint = Constraint { - constraint_type: ConstraintType { - ql: minus_one.clone(), - qr: zero.clone(), - qm: zero.clone(), - qo: zero.clone(), - qc: zero.clone(), - }, - hint: None, - l: *public_input, - r: self.null_variable(), - o: self.null_variable(), - }; - public_input_constraints.push(public_input_constraint); - } - public_input_constraints - } - - /// Returns the `LRO` and `Q` matrices. Each matrix has one row per constraint. - /// The `LRO` matrix has 3 columns with the values of the variables IDs of every - /// constraint. The `Q` matrix has 5 columns with the coefficients of the - /// constraint types. - /// Their layout is: - /// ####################### - /// # public input header # - /// ####################### - /// # circuit constraints # - /// ####################### - /// # padding # - /// ####################### - pub fn to_matrices(&self) -> (Vec, Vec>) { - let header = self.public_input_header(); - let body = &self.constraints; - let total_length = (header.len() + body.len()).next_power_of_two(); - let pad = vec![self.padding_constraint(); total_length - header.len() - body.len()]; - - let mut full_constraints = header; - full_constraints.extend_from_slice(body); - full_constraints.extend_from_slice(&pad); - - let n = full_constraints.len(); - - let mut lro = vec![self.null_variable(); n * 3]; - // Make a single vector with | l_1 .. l_m | r_1 .. r_m | o_1 .. o_m | concatenated. - for (index, constraint) in full_constraints.iter().enumerate() { - lro[index] = constraint.l; - lro[index + n] = constraint.r; - lro[index + n * 2] = constraint.o; - } - - let mut q = vec![FieldElement::zero(); 5 * n]; - for (index, constraint) in full_constraints.iter().enumerate() { - let ct = &constraint.constraint_type; - q[index] = ct.ql.clone(); - q[index + n] = ct.qr.clone(); - q[index + 2 * n] = ct.qm.clone(); - q[index + 3 * n] = ct.qo.clone(); - q[index + 4 * n] = ct.qc.clone(); - } - (lro, q) - } - - /// This method filters the `values` hashmap to return the list of values - /// corresponding to the public variables - pub fn public_input_values( - &self, - values: &HashMap>, - ) -> Vec> { - let mut public_inputs = Vec::new(); - for key in &self.public_input_variables { - if let Some(value) = values.get(key) { - public_inputs.push(value.clone()); - } - } - public_inputs - } -} - -impl Default for ConstraintSystem { - fn default() -> Self { - Self::new() - } -} - -/// This method takes the `LRO` matrix and computes the permutation used in PLONK to -/// build the copy constraint polynomial. -pub fn get_permutation(lro: &[Variable]) -> Vec { - // For each variable store the indexes where it appears. - let mut last_usage: HashMap = HashMap::new(); - let mut permutation = vec![0_usize; lro.len()]; - - for _ in 0..2 { - for (index, variable) in lro.iter().enumerate() { - if last_usage.contains_key(variable) { - permutation[index] = last_usage[variable]; - } - last_usage.insert(*variable, index); - } - } - - permutation -} - -#[cfg(test)] -mod tests { - use crate::{ - prover::Prover, - setup::{setup, CommonPreprocessedInput, Witness}, - test_utils::utils::{test_srs, TestRandomFieldGenerator, KZG, ORDER_R_MINUS_1_ROOT_UNITY}, - verifier::Verifier, - }; - - use super::*; - use lambdaworks_math::{ - elliptic_curve::short_weierstrass::curves::bls12_381::default_types::FrField, - field::{element::FieldElement as FE, fields::u64_prime_field::U64PrimeField}, - }; - - /* - Program: - v0 = 1 - v1 = 2 - v2 = v0 + v1 - v3 = v1 + v0 - v4 = v2 + v3 - - Variables: - L R O - 0 1 2 - 1 0 3 - 2 3 4 - 0 0 0 --> padding to next power of two - - LRO : 0 1 2 0 1 0 3 0 2 3 4 0 - Permutation: 11 4 8 0 1 3 9 5 2 6 10 7 - - */ - #[test] - fn test_permutation() { - let system = &mut ConstraintSystem::>::new(); - - let v0 = system.new_variable(); - let v1 = system.new_variable(); - - let v2 = system.add(&v0, &v1); - let v3 = system.add(&v1, &v0); - system.add(&v2, &v3); - - let (lro, _) = system.to_matrices(); - - let permutation = get_permutation(&lro); - let expected = vec![11, 4, 8, 0, 1, 3, 9, 5, 2, 6, 10, 7]; - assert_eq!(expected, permutation); - } - - #[test] - fn test_prove_simple_program_1() { - // Program - let system = &mut ConstraintSystem::::new(); - - let e = system.new_variable(); - let x = system.new_public_input(); - let y = system.new_public_input(); - - let z = system.mul(&x, &e); - system.assert_eq(&y, &z); - - // Common preprocessed input - let common_preprocessed_input = - CommonPreprocessedInput::from_constraint_system(system, &ORDER_R_MINUS_1_ROOT_UNITY); - - // Setup - let srs = test_srs(common_preprocessed_input.n); - let kzg = KZG::new(srs); - let verifying_key = setup(&common_preprocessed_input, &kzg); - - // Prover: - // 1. Generate public inputs and witness - let inputs = HashMap::from([(x, FE::from(4)), (e, FE::from(3))]); - let assignments = system.solve(inputs).unwrap(); - let public_inputs = system.public_input_values(&assignments); - let witness = Witness::new(assignments, system); - - // 2. Generate proof - let random_generator = TestRandomFieldGenerator {}; - let prover = Prover::new(kzg.clone(), random_generator); - let proof = prover.prove( - &witness, - &public_inputs, - &common_preprocessed_input, - &verifying_key, - ); - - // Verifier - let verifier = Verifier::new(kzg); - assert!(verifier.verify( - &proof, - &public_inputs, - &common_preprocessed_input, - &verifying_key - )); - } - - #[test] - fn test_fibonacci() { - let system = &mut ConstraintSystem::>::new(); - - let x0_initial = system.new_variable(); - let x1_initial = system.new_variable(); - let mut x0 = x0_initial; - let mut x1 = x1_initial; - - for _ in 2..10001 { - let x2 = system.add(&x1, &x0); - (x0, x1) = (x1, x2); - } - - let inputs = HashMap::from([(x0_initial, FE::from(0)), (x1_initial, FE::from(1))]); - - let expected_output = FE::from(19257); - let assignments = system.solve(inputs).unwrap(); - assert_eq!(assignments.get(&x1).unwrap(), &expected_output); - } -} diff --git a/crates/provers/plonk/src/constraint_system/operations.rs b/crates/provers/plonk/src/constraint_system/operations.rs deleted file mode 100644 index c9e207f98..000000000 --- a/crates/provers/plonk/src/constraint_system/operations.rs +++ /dev/null @@ -1,345 +0,0 @@ -use lambdaworks_math::field::{element::FieldElement as FE, traits::IsField}; - -use super::{Column, Constraint, ConstraintSystem, ConstraintType, Hint, Variable}; - -impl ConstraintSystem -where - F: IsField, -{ - /// Creates a new variable `w` constrained to be equal to `c1 * v1 + c2 * v2 + b`. - /// Optionally a hint can be provided to insert values in `v1`, `v2` or `w`. To do - /// so use the `L`, `R`, and `O` input/output columns of the hint to refer to `v1`, - /// `v2` and `w` respectively. - pub fn linear_combination( - &mut self, - v1: &Variable, - c1: FE, - v2: &Variable, - c2: FE, - b: FE, - hint: Option>, - ) -> Variable { - let result = self.new_variable(); - - self.add_constraint(Constraint { - constraint_type: ConstraintType { - ql: c1, - qr: c2, - qm: FE::zero(), - qo: -FE::one(), - qc: b, - }, - l: *v1, - r: *v2, - o: result, - hint, - }); - result - } - - /// Creates a new variable `w` constrained to be equal to `c * v + b`. - /// Optionally a hint can be provided to insert values in `v1`, `v2` or `w`. To do - /// so use the `L`, `R`, and `O` input/output columns of the hint to refer to `v1`, - /// `v2` and `w` respectively. - pub fn linear_function( - &mut self, - v: &Variable, - c: FE, - b: FE, - hint: Option>, - ) -> Variable { - let result = self.new_variable(); - self.add_constraint(Constraint { - constraint_type: ConstraintType { - ql: c, - qr: FE::zero(), - qm: FE::zero(), - qo: -FE::one(), - qc: b, - }, - l: *v, - r: self.null_variable(), - o: result, - hint, - }); - result - } - - /// Creates a new variable `w` constrained to be equal to `v1 + v2`. - pub fn add(&mut self, v1: &Variable, v2: &Variable) -> Variable { - self.linear_combination(v1, FE::one(), v2, FE::one(), FE::zero(), None) - } - - /// Creates a new variable `w` constrained to be equal to `v1 + constant`. - pub fn add_constant(&mut self, v: &Variable, constant: FE) -> Variable { - self.linear_function(v, FE::one(), constant, None) - } - - /// Creates a new variable `w` constrained to be equal to `v1 * v2`. - pub fn mul(&mut self, v1: &Variable, v2: &Variable) -> Variable { - let result = self.new_variable(); - self.add_constraint(Constraint { - constraint_type: ConstraintType { - ql: FE::zero(), - qr: FE::zero(), - qm: FE::one(), - qo: -FE::one(), - qc: FE::zero(), - }, - l: *v1, - r: *v2, - o: result, - hint: None, - }); - result - } - - /// Creates a new variable `w` constrained to be equal to `v1 / v2`. - pub fn div(&mut self, v1: &Variable, v2: &Variable) -> Variable { - // TODO: check 0.div(0) does not compile - let result = self.new_variable(); - self.add_constraint(Constraint { - constraint_type: ConstraintType { - ql: FE::zero(), - qr: FE::zero(), - qm: FE::one(), - qo: -FE::one(), - qc: FE::zero(), - }, - l: result, - r: *v2, - o: *v1, - hint: None, - }); - result - } - - /// Creates two new variables `is_zero` and `v_inverse`. The former is constrained - /// to be a boolean value holding `1` if `v` is zero and `0` otherwise. The latter - /// is constrained to be `v^{-1}` when `v` is not zero and equal to `0` otherwise. - pub fn inv(&mut self, v: &Variable) -> (Variable, Variable) { - let is_zero = self.new_variable(); - let v_inverse = self.new_variable(); - let hint = Some(Hint { - function: |v: &FE| { - if *v == FE::zero() { - FE::one() - } else { - FE::zero() - } - }, - input: Column::L, - output: Column::R, - }); - // v * z == 0 - self.add_constraint(Constraint { - constraint_type: ConstraintType { - ql: FE::zero(), - qr: FE::zero(), - qm: FE::one(), - qo: FE::zero(), - qc: FE::zero(), - }, - l: *v, - r: is_zero, - o: self.null_variable(), - hint, - }); - // v * w + z == 1 - self.add_constraint(Constraint { - constraint_type: ConstraintType { - ql: FE::zero(), - qr: FE::zero(), - qm: FE::one(), - qo: FE::one(), - qc: -FE::one(), - }, - l: *v, - r: v_inverse, // w - o: is_zero, // z - hint: Some(Hint { - function: |v: &FE| { - if *v == FE::zero() { - FE::zero() - } else { - v.inv().unwrap() - } - }, - input: Column::L, - output: Column::R, - }), - }); - (is_zero, v_inverse) - } - - /// Returns a new variable `w` constrained to satisfy `w = 1 - v`. When `v` is boolean - /// this is the `not` operator. - pub fn not(&mut self, v: &Variable) -> Variable { - let result = self.new_variable(); - self.add_constraint(Constraint { - constraint_type: ConstraintType { - ql: FE::one(), - qr: FE::one(), - qm: FE::zero(), - qo: FE::zero(), - qc: -FE::one(), - }, - l: *v, - r: result, - o: self.null_variable(), - hint: None, - }); - result - } -} - -#[cfg(test)] -mod tests { - use std::collections::HashMap; - - use super::*; - use lambdaworks_math::field::{ - element::FieldElement as FE, fields::u64_prime_field::U64PrimeField, - }; - - #[test] - fn test_linear_combination() { - let system = &mut ConstraintSystem::>::new(); - - let v1 = system.new_variable(); - let c1 = FE::from(15); - let v2 = system.new_variable(); - let c2 = -FE::from(7); - let b = FE::from(99); - let result = system.linear_combination(&v1, c1, &v2, c2, b, None); - - let x = FE::from(17); - let y = FE::from(29); - - let inputs = HashMap::from([(v1, x), (v2, y)]); - - let assignments = system.solve(inputs).unwrap(); - assert_eq!(assignments.get(&result).unwrap(), &(x * c1 + y * c2 + b)); - } - - #[test] - fn test_linear_function() { - let system = &mut ConstraintSystem::>::new(); - - let v = system.new_variable(); - let c = FE::from(8); - let b = FE::from(109); - let result = system.linear_function(&v, c, b, None); - - let x = FE::from(17); - - let inputs = HashMap::from([(v, x)]); - - let assignments = system.solve(inputs).unwrap(); - assert_eq!(assignments.get(&result).unwrap(), &(x * c + b)); - } - - #[test] - fn test_add() { - let system = &mut ConstraintSystem::>::new(); - - let input1 = system.new_variable(); - let input2 = system.new_variable(); - let result = system.add(&input1, &input2); - - let a = FE::from(3); - let b = FE::from(10); - - let inputs = HashMap::from([(input1, a), (input2, b)]); - - let assignments = system.solve(inputs).unwrap(); - assert_eq!(assignments.get(&result).unwrap(), &(a + b)); - } - - #[test] - fn test_mul() { - let system = &mut ConstraintSystem::>::new(); - - let input1 = system.new_variable(); - let input2 = system.new_variable(); - let result = system.mul(&input1, &input2); - - let a = FE::from(3); - let b = FE::from(11); - - let inputs = HashMap::from([(input1, a), (input2, b)]); - - let assignments = system.solve(inputs).unwrap(); - assert_eq!(assignments.get(&result).unwrap(), &(a * b)); - } - - #[test] - fn test_div() { - let system = &mut ConstraintSystem::>::new(); - - let input1 = system.new_variable(); - let input2 = system.new_variable(); - let result = system.div(&input1, &input2); - - let a = FE::from(3); - let b = FE::from(11); - - let inputs = HashMap::from([(input1, a), (input2, b)]); - - let assignments = system.solve(inputs).unwrap(); - assert_eq!(assignments.get(&result).unwrap(), &(a / b).unwrap()); - } - - #[test] - fn test_add_constant() { - let system = &mut ConstraintSystem::>::new(); - - let input1 = system.new_variable(); - let b = FE::from(11); - let result = system.add_constant(&input1, b); - - let a = FE::from(3); - - let inputs = HashMap::from([(input1, a)]); - - let assignments = system.solve(inputs).unwrap(); - assert_eq!(assignments.get(&result).unwrap(), &(a + b)); - } - - #[test] - fn test_not() { - let system = &mut ConstraintSystem::>::new(); - - let boolean = system.new_boolean(); - let result1 = system.not(&boolean); - let result2 = system.not(&result1); - - let inputs = HashMap::from([(boolean, FE::one())]); - - let assignments = system.solve(inputs).unwrap(); - assert_eq!(assignments.get(&result1).unwrap(), &FE::zero()); - assert_eq!(assignments.get(&result2).unwrap(), &FE::one()); - } - - #[test] - fn test_inv() { - let system = &mut ConstraintSystem::>::new(); - - let v = system.new_variable(); - let w = system.new_variable(); - let (v_is_zero, v_inverse) = system.inv(&v); - let (w_is_zero, w_inverse) = system.inv(&w); - - let inputs = HashMap::from([(v, FE::from(2)), (w, FE::from(0))]); - - let assignments = system.solve(inputs).unwrap(); - assert_eq!( - assignments.get(&v_inverse).unwrap(), - &FE::from(2).inv().unwrap() - ); - assert_eq!(assignments.get(&v_is_zero).unwrap(), &FE::zero()); - - assert_eq!(assignments.get(&w_inverse).unwrap(), &FE::from(0)); - assert_eq!(assignments.get(&w_is_zero).unwrap(), &FE::one()); - } -} diff --git a/crates/provers/plonk/src/constraint_system/solver.rs b/crates/provers/plonk/src/constraint_system/solver.rs deleted file mode 100644 index ee22bcffd..000000000 --- a/crates/provers/plonk/src/constraint_system/solver.rs +++ /dev/null @@ -1,909 +0,0 @@ -use std::collections::HashMap; - -use lambdaworks_math::field::{element::FieldElement as FE, traits::IsField}; - -use super::{errors::SolverError, Column, Constraint, ConstraintSystem, Variable}; - -/// Finds a solution to the system extending the `assignments` map. It uses the -/// simple strategy of going through all the constraints trying to determine an -/// unkwown value of a variable in terms of known values. It stops when it goes -/// through every constraint and there's nothing else to be solved this way. -/// It returns an error in case there is no such solution or in case this strategy -/// is not enough. -impl ConstraintSystem -where - F: IsField, -{ - pub fn solve( - &self, - mut assignments: HashMap>, - ) -> Result>, SolverError> { - loop { - let old_solved = assignments.keys().len(); - for constraint in self.constraints.iter() { - assignments = solve_hint(assignments, constraint); - assignments = solve_constraint(assignments, constraint); - } - if old_solved == assignments.keys().len() { - break; - } - } - - // Check the system is solved - for constraint in self.constraints.iter() { - let a = assignments.get(&constraint.l); - let b = assignments.get(&constraint.r); - let c = assignments.get(&constraint.o); - - match (a, b, c) { - (Some(a), Some(b), Some(c)) => { - let ct = &constraint.constraint_type; - let result = a * &ct.ql + b * &ct.qr + a * b * &ct.qm + c * &ct.qo + &ct.qc; - if result != FE::zero() { - return Err(SolverError::InconsistentSystem); - } - } - _ => return Err(SolverError::UnableToSolve), - } - } - Ok(assignments) - } -} - -fn solve_hint( - mut assignments: HashMap>, - constraint: &Constraint, -) -> HashMap> { - let column_to_variable = |column: &Column| match column { - Column::L => constraint.l, - Column::R => constraint.r, - Column::O => constraint.o, - }; - if let Some(hint) = &constraint.hint { - if !assignments.contains_key(&column_to_variable(&hint.output)) { - if let Some(input) = assignments.get(&column_to_variable(&hint.input)) { - assignments.insert(column_to_variable(&hint.output), (hint.function)(input)); - } - } - } - - assignments -} - -fn solve_constraint( - mut assignments: HashMap>, - constraint: &Constraint, -) -> HashMap> { - let ct = &constraint.constraint_type; - let a = assignments.get(&constraint.l); - let b = assignments.get(&constraint.r); - let c = assignments.get(&constraint.o); - let zero = FE::zero(); - - match ( - (a, b, c), - (ct.ql == zero, ct.qr == zero, ct.qm == zero, ct.qo == zero), - ) { - ((Some(a), Some(b), None), _) => { - if ct.qo != FE::zero() { - let c = -(a * &ct.ql + b * &ct.qr + a * b * &ct.qm + &ct.qc) * ct.qo.inv().unwrap(); - assignments.insert(constraint.o, c); - } - } - ((Some(a), None, Some(c)), _) => { - let denominator = &ct.qr + a * &ct.qm; - if denominator != FE::zero() { - let b = -(a * &ct.ql + c * &ct.qo + &ct.qc) * denominator.inv().unwrap(); - assignments.insert(constraint.r, b); - } - } - ((None, Some(b), Some(c)), _) => { - let denominator = &ct.ql + b * &ct.qm; - if denominator != FE::zero() { - let a = -(b * &ct.qr + c * &ct.qo + &ct.qc) * denominator.inv().unwrap(); - assignments.insert(constraint.l, a); - } - } - ((Some(a), None, None), _) => { - let b_coefficient = &ct.qr + a * &ct.qm; - if b_coefficient == FE::zero() && ct.qo != FE::zero() { - let c = -(a * &ct.ql + &ct.qc) * ct.qo.inv().unwrap(); - assignments.insert(constraint.o, c); - } else if b_coefficient != FE::zero() && ct.qo == FE::zero() { - let b = -(a * &ct.ql + &ct.qc) * b_coefficient.inv().unwrap(); - assignments.insert(constraint.r, b); - } - } - ((None, Some(b), None), _) => { - let a_coefficient = &ct.ql + b * &ct.qm; - if a_coefficient == FE::zero() && ct.qo != FE::zero() { - let c = -(b * &ct.qr + &ct.qc) * ct.qo.inv().unwrap(); - assignments.insert(constraint.o, c); - } else if a_coefficient != FE::zero() && ct.qo == FE::zero() { - let a = -(b * &ct.qr + &ct.qc) * a_coefficient.inv().unwrap(); - assignments.insert(constraint.l, a); - } - } - ((None, None, Some(c)), (false, true, true, _)) => { - let a = -(c * &ct.qo + &ct.qc) * ct.ql.inv().unwrap(); - assignments.insert(constraint.l, a); - } - ((None, None, Some(c)), (true, false, true, _)) => { - let b = -(c * &ct.qo + &ct.qc) * ct.qr.inv().unwrap(); - assignments.insert(constraint.r, b); - } - ((None, None, None), (true, true, true, false)) => { - let c = -&ct.qc * ct.qo.inv().unwrap(); - assignments.insert(constraint.o, c); - } - ((None, None, None), (true, false, true, true)) => { - let b = -&ct.qc * ct.qr.inv().unwrap(); - assignments.insert(constraint.r, b); - } - ((None, None, None), (false, true, true, true)) => { - let a = -&ct.qc * ct.ql.inv().unwrap(); - assignments.insert(constraint.l, a); - } - _ => {} - } - assignments -} - -#[cfg(test)] -mod tests { - use std::collections::HashMap; - - use crate::constraint_system::{ - errors::SolverError, Constraint, ConstraintSystem, ConstraintType, - }; - use lambdaworks_math::field::{ - element::FieldElement as FE, fields::u64_prime_field::U64PrimeField, - }; - - #[test] - fn test_case_all_values_are_known() { - let mut system = ConstraintSystem::>::new(); - let a = system.new_variable(); - let b = system.new_variable(); - let c = system.new_variable(); - let constraint = Constraint { - constraint_type: ConstraintType { - ql: FE::one(), - qr: FE::one(), - qm: FE::one(), - qo: -FE::one(), - qc: FE::one(), - }, - hint: None, - l: a, - r: b, - o: c, - }; - system.add_constraint(constraint); - let inputs = HashMap::from([(a, FE::from(2)), (b, FE::from(3)), (c, FE::from(12))]); - system.solve(inputs).unwrap(); - } - - #[test] - fn test_case_b_and_c_are_known() { - let mut system = ConstraintSystem::>::new(); - let a = system.new_variable(); - let b = system.new_variable(); - let c = system.new_variable(); - let constraint = Constraint { - constraint_type: ConstraintType { - ql: FE::one(), - qr: FE::one(), - qm: FE::one(), - qo: -FE::one(), - qc: FE::one(), - }, - hint: None, - l: a, - r: b, - o: c, - }; - system.add_constraint(constraint); - let inputs = HashMap::from([(b, FE::from(3)), (c, FE::from(12))]); - let assignments = system.solve(inputs).unwrap(); - assert_eq!(assignments.get(&a).unwrap(), &FE::from(2)); - } - - #[test] - fn test_case_b_and_c_are_known_but_as_coefficient_is_zero() { - let mut system = ConstraintSystem::>::new(); - let a = system.new_variable(); - let b = system.new_variable(); - let c = system.new_variable(); - let constraint = Constraint { - constraint_type: ConstraintType { - ql: FE::from(3), - qr: FE::one(), - qm: -FE::one(), - qo: -FE::one(), - qc: FE::one(), - }, - hint: None, - l: a, - r: b, - o: c, - }; - system.add_constraint(constraint); - let inputs = HashMap::from([(b, FE::from(3)), (c, FE::from(12))]); - assert_eq!( - system.solve(inputs).unwrap_err(), - SolverError::UnableToSolve - ); - } - - #[test] - fn test_case_a_and_c_are_known() { - let mut system = ConstraintSystem::>::new(); - let a = system.new_variable(); - let b = system.new_variable(); - let c = system.new_variable(); - let constraint = Constraint { - constraint_type: ConstraintType { - ql: FE::one(), - qr: FE::one(), - qm: FE::one(), - qo: -FE::one(), - qc: FE::one(), - }, - hint: None, - l: a, - r: b, - o: c, - }; - system.add_constraint(constraint); - let inputs = HashMap::from([(a, FE::from(2)), (c, FE::from(12))]); - let assignments = system.solve(inputs).unwrap(); - assert_eq!(assignments.get(&b).unwrap(), &FE::from(3)); - } - - #[test] - fn test_case_a_and_c_are_known_but_bs_coefficient_is_zero() { - let mut system = ConstraintSystem::>::new(); - let a = system.new_variable(); - let b = system.new_variable(); - let c = system.new_variable(); - let constraint = Constraint { - constraint_type: ConstraintType { - ql: FE::one(), - qr: FE::from(2), - qm: -FE::one(), - qo: FE::one(), - qc: FE::one(), - }, - hint: None, - l: a, - r: b, - o: c, - }; - system.add_constraint(constraint); - let inputs = HashMap::from([(a, FE::from(2)), (c, FE::from(12))]); - assert_eq!( - system.solve(inputs).unwrap_err(), - SolverError::UnableToSolve - ); - } - - #[test] - fn test_case_a_and_b_are_known() { - let mut system = ConstraintSystem::>::new(); - let a = system.new_variable(); - let b = system.new_variable(); - let c = system.new_variable(); - let constraint = Constraint { - constraint_type: ConstraintType { - ql: FE::one(), - qr: FE::one(), - qm: FE::one(), - qo: -FE::one(), - qc: FE::one(), - }, - hint: None, - l: a, - r: b, - o: c, - }; - system.add_constraint(constraint); - let inputs = HashMap::from([(a, FE::from(2)), (b, FE::from(3))]); - let assignments = system.solve(inputs).unwrap(); - assert_eq!(assignments.get(&c).unwrap(), &FE::from(12)); - } - - #[test] - fn test_case_a_and_b_are_known_but_cs_coefficient_is_zero() { - let mut system = ConstraintSystem::>::new(); - let a = system.new_variable(); - let b = system.new_variable(); - let c = system.new_variable(); - let constraint = Constraint { - constraint_type: ConstraintType { - ql: FE::one(), - qr: FE::one(), - qm: FE::one(), - qo: FE::zero(), - qc: FE::one(), - }, - hint: None, - l: a, - r: b, - o: c, - }; - system.add_constraint(constraint); - let inputs = HashMap::from([(a, FE::from(2)), (b, FE::from(3))]); - assert_eq!( - system.solve(inputs).unwrap_err(), - SolverError::UnableToSolve - ); - } - - #[test] - fn test_case_only_a_is_known_but_bs_coeffient_is_zero_and_cs_coefficient_is_nonzero() { - let mut system = ConstraintSystem::>::new(); - let a = system.new_variable(); - let b = system.new_variable(); - let c = system.new_variable(); - let constraint1 = Constraint { - constraint_type: ConstraintType { - ql: FE::one(), - qr: -FE::from(2), - qm: FE::one(), - qo: FE::one(), - qc: FE::one(), - }, - hint: None, - l: a, - r: b, - o: c, - }; - let constraint2 = Constraint { - constraint_type: ConstraintType { - ql: FE::one(), - qr: FE::one(), - qm: FE::zero(), - qo: FE::zero(), - qc: FE::zero(), - }, - hint: None, - l: b, - r: c, - o: system.null_variable(), - }; - system.add_constraint(constraint1); - system.add_constraint(constraint2); - let inputs = HashMap::from([(a, FE::from(2))]); - let assignments = system.solve(inputs).unwrap(); - assert_eq!(assignments.get(&b).unwrap(), &FE::from(3)); - assert_eq!(assignments.get(&c).unwrap(), &-FE::from(3)); - } - - #[test] - fn test_case_only_a_is_known_but_bs_coefficient_is_nonzero_and_cs_coeffient_is_zero() { - let mut system = ConstraintSystem::>::new(); - let a = system.new_variable(); - let b = system.new_variable(); - let c = system.new_variable(); - let constraint1 = Constraint { - constraint_type: ConstraintType { - ql: FE::one(), - qr: FE::one(), - qm: FE::one(), - qo: FE::zero(), - qc: -FE::from(5), - }, - hint: None, - l: a, - r: b, - o: c, - }; - let constraint2 = Constraint { - constraint_type: ConstraintType { - ql: FE::one(), - qr: FE::one(), - qm: FE::zero(), - qo: FE::zero(), - qc: FE::zero(), - }, - hint: None, - l: b, - r: c, - o: system.null_variable(), - }; - system.add_constraint(constraint1); - system.add_constraint(constraint2); - let inputs = HashMap::from([(a, FE::from(1))]); - let assignments = system.solve(inputs).unwrap(); - assert_eq!(assignments.get(&b).unwrap(), &FE::from(2)); - assert_eq!(assignments.get(&c).unwrap(), &-FE::from(2)); - } - - #[test] - fn test_case_only_a_is_known_but_bs_cofficient_is_zero_and_cs_coeffient_is_zero() { - let mut system = ConstraintSystem::>::new(); - let a = system.new_variable(); - let b = system.new_variable(); - let c = system.new_variable(); - let constraint1 = Constraint { - constraint_type: ConstraintType { - ql: FE::one(), - qr: FE::one(), - qm: -FE::one(), - qo: FE::zero(), - qc: -FE::from(5), - }, - hint: None, - l: a, - r: b, - o: c, - }; - let constraint2 = Constraint { - constraint_type: ConstraintType { - ql: FE::one(), - qr: FE::one(), - qm: FE::zero(), - qo: FE::zero(), - qc: FE::zero(), - }, - hint: None, - l: b, - r: c, - o: system.null_variable(), - }; - system.add_constraint(constraint1); - system.add_constraint(constraint2); - let inputs = HashMap::from([(a, FE::from(1))]); - assert_eq!( - system.solve(inputs).unwrap_err(), - SolverError::UnableToSolve - ); - } - - #[test] - // TODO: This system is actually solvable but not with our current solver logic - fn test_case_only_a_is_known_but_bs_cofficient_is_nonzero_and_cs_coeffient_is_nonzero() { - let mut system = ConstraintSystem::>::new(); - let a = system.new_variable(); - let b = system.new_variable(); - let c = system.new_variable(); - let constraint1 = Constraint { - constraint_type: ConstraintType { - ql: FE::one(), - qr: FE::one(), - qm: FE::one(), - qo: FE::one(), - qc: -FE::from(5), - }, - hint: None, - l: a, - r: b, - o: c, - }; - let constraint2 = Constraint { - constraint_type: ConstraintType { - ql: FE::one(), - qr: FE::one(), - qm: FE::zero(), - qo: FE::zero(), - qc: FE::zero(), - }, - hint: None, - l: b, - r: c, - o: system.null_variable(), - }; - system.add_constraint(constraint1); - system.add_constraint(constraint2); - let inputs = HashMap::from([(a, FE::from(1))]); - assert_eq!( - system.solve(inputs).unwrap_err(), - SolverError::UnableToSolve - ); - } - - #[test] - fn test_case_only_b_is_known_but_as_coeffient_is_zero_and_cs_coefficient_is_nonzero() { - let mut system = ConstraintSystem::>::new(); - let a = system.new_variable(); - let b = system.new_variable(); - let c = system.new_variable(); - let constraint1 = Constraint { - constraint_type: ConstraintType { - ql: -FE::from(3), - qr: FE::one(), - qm: FE::one(), - qo: FE::one(), - qc: FE::one(), - }, - hint: None, - l: a, - r: b, - o: c, - }; - let constraint2 = Constraint { - constraint_type: ConstraintType { - ql: FE::one(), - qr: FE::one(), - qm: FE::zero(), - qo: FE::zero(), - qc: FE::zero(), - }, - hint: None, - l: a, - r: c, - o: system.null_variable(), - }; - system.add_constraint(constraint1); - system.add_constraint(constraint2); - let inputs = HashMap::from([(b, FE::from(3))]); - let assignments = system.solve(inputs).unwrap(); - assert_eq!(assignments.get(&a).unwrap(), &FE::from(4)); - assert_eq!(assignments.get(&c).unwrap(), &-FE::from(4)); - } - - #[test] - fn test_case_only_b_is_known_but_as_coefficient_is_nonzero_and_cs_coeffient_is_zero() { - let mut system = ConstraintSystem::>::new(); - let a = system.new_variable(); - let b = system.new_variable(); - let c = system.new_variable(); - let constraint1 = Constraint { - constraint_type: ConstraintType { - ql: FE::one(), - qr: FE::one(), - qm: FE::one(), - qo: FE::zero(), - qc: -FE::from(5), - }, - hint: None, - l: a, - r: b, - o: system.null_variable(), - }; - let constraint2 = Constraint { - constraint_type: ConstraintType { - ql: FE::one(), - qr: FE::one(), - qm: FE::zero(), - qo: FE::zero(), - qc: FE::zero(), - }, - hint: None, - l: a, - r: c, - o: system.null_variable(), - }; - system.add_constraint(constraint1); - system.add_constraint(constraint2); - let inputs = HashMap::from([(b, FE::from(1))]); - let assignments = system.solve(inputs).unwrap(); - assert_eq!(assignments.get(&a).unwrap(), &FE::from(2)); - assert_eq!(assignments.get(&c).unwrap(), &-FE::from(2)); - } - - #[test] - fn test_case_only_b_is_known_but_as_coefficient_is_zero_and_cs_coeffient_is_zero() { - let mut system = ConstraintSystem::>::new(); - let a = system.new_variable(); - let b = system.new_variable(); - let c = system.new_variable(); - let constraint1 = Constraint { - constraint_type: ConstraintType { - ql: FE::one(), - qr: FE::one(), - qm: -FE::one(), - qo: FE::zero(), - qc: -FE::from(5), - }, - hint: None, - l: a, - r: b, - o: system.null_variable(), - }; - let constraint2 = Constraint { - constraint_type: ConstraintType { - ql: FE::one(), - qr: FE::one(), - qm: FE::zero(), - qo: FE::zero(), - qc: FE::zero(), - }, - hint: None, - l: a, - r: c, - o: system.null_variable(), - }; - system.add_constraint(constraint1); - system.add_constraint(constraint2); - let inputs = HashMap::from([(b, FE::from(1))]); - assert_eq!( - system.solve(inputs).unwrap_err(), - SolverError::UnableToSolve - ); - } - - #[test] - fn test_case_only_c_is_known_but_bs_coeffient_is_zero_and_qm_is_zero() { - let mut system = ConstraintSystem::>::new(); - let a = system.new_variable(); - let b = system.new_variable(); - let c = system.new_variable(); - let constraint1 = Constraint { - constraint_type: ConstraintType { - ql: FE::from(2), - qr: FE::zero(), - qm: FE::zero(), - qo: FE::one(), - qc: FE::zero(), - }, - hint: None, - l: a, - r: b, - o: c, - }; - let constraint2 = Constraint { - constraint_type: ConstraintType { - ql: FE::one(), - qr: FE::one(), - qm: FE::zero(), - qo: FE::zero(), - qc: FE::zero(), - }, - hint: None, - l: a, - r: b, - o: system.null_variable(), - }; - system.add_constraint(constraint1); - system.add_constraint(constraint2); - let inputs = HashMap::from([(c, FE::from(2))]); - let assignments = system.solve(inputs).unwrap(); - assert_eq!(assignments.get(&a).unwrap(), &-FE::from(1)); - assert_eq!(assignments.get(&b).unwrap(), &FE::from(1)); - } - - #[test] - fn test_case_only_c_is_known_and_bs_coeffient_is_zero_but_qm_is_nonzero() { - let mut system = ConstraintSystem::>::new(); - let a = system.new_variable(); - let b = system.new_variable(); - let c = system.new_variable(); - let constraint1 = Constraint { - constraint_type: ConstraintType { - ql: FE::from(2), - qr: FE::zero(), - qm: FE::one(), - qo: FE::one(), - qc: FE::zero(), - }, - hint: None, - l: a, - r: b, - o: c, - }; - let constraint2 = Constraint { - constraint_type: ConstraintType { - ql: FE::one(), - qr: FE::one(), - qm: FE::zero(), - qo: FE::zero(), - qc: FE::zero(), - }, - hint: None, - l: a, - r: b, - o: system.null_variable(), - }; - system.add_constraint(constraint1); - system.add_constraint(constraint2); - let inputs = HashMap::from([(c, FE::from(2))]); - assert_eq!( - system.solve(inputs).unwrap_err(), - SolverError::UnableToSolve - ); - } - - #[test] - fn test_case_only_c_is_known_but_as_coeffient_is_zero_and_qm_is_zero() { - let mut system = ConstraintSystem::>::new(); - let a = system.new_variable(); - let b = system.new_variable(); - let c = system.new_variable(); - let constraint1 = Constraint { - constraint_type: ConstraintType { - ql: FE::zero(), - qr: FE::from(2), - qm: FE::zero(), - qo: FE::one(), - qc: FE::zero(), - }, - hint: None, - l: a, - r: b, - o: c, - }; - let constraint2 = Constraint { - constraint_type: ConstraintType { - ql: FE::one(), - qr: FE::one(), - qm: FE::zero(), - qo: FE::zero(), - qc: FE::zero(), - }, - hint: None, - l: a, - r: b, - o: system.null_variable(), - }; - system.add_constraint(constraint1); - system.add_constraint(constraint2); - let inputs = HashMap::from([(c, FE::from(2))]); - let assignments = system.solve(inputs).unwrap(); - assert_eq!(assignments.get(&a).unwrap(), &FE::from(1)); - assert_eq!(assignments.get(&b).unwrap(), &-FE::from(1)); - } - - #[test] - fn test_case_only_c_is_known_but_as_coeffient_is_nonzero_and_qm_is_nonzero() { - let mut system = ConstraintSystem::>::new(); - let a = system.new_variable(); - let b = system.new_variable(); - let c = system.new_variable(); - let constraint1 = Constraint { - constraint_type: ConstraintType { - ql: FE::one(), - qr: FE::from(2), - qm: FE::one(), - qo: FE::one(), - qc: FE::zero(), - }, - hint: None, - l: a, - r: b, - o: c, - }; - let constraint2 = Constraint { - constraint_type: ConstraintType { - ql: FE::one(), - qr: FE::one(), - qm: FE::zero(), - qo: FE::zero(), - qc: FE::zero(), - }, - hint: None, - l: a, - r: b, - o: system.null_variable(), - }; - system.add_constraint(constraint1); - system.add_constraint(constraint2); - let inputs = HashMap::from([(c, FE::from(2))]); - assert_eq!( - system.solve(inputs).unwrap_err(), - SolverError::UnableToSolve - ); - } - - #[test] - fn test_case_all_values_are_unknown() { - let mut system = ConstraintSystem::>::new(); - let a = system.new_variable(); - let b = system.new_variable(); - let c = system.new_variable(); - let constraint1 = Constraint { - constraint_type: ConstraintType { - ql: FE::from(2), - qr: FE::zero(), - qm: FE::zero(), - qo: FE::zero(), - qc: -FE::from(2), - }, - hint: None, - l: a, - r: b, - o: c, - }; - let constraint2 = Constraint { - constraint_type: ConstraintType { - ql: FE::zero(), - qr: FE::from(2), - qm: FE::zero(), - qo: FE::zero(), - qc: -FE::from(4), - }, - hint: None, - l: a, - r: b, - o: c, - }; - let constraint3 = Constraint { - constraint_type: ConstraintType { - ql: FE::zero(), - qr: FE::zero(), - qm: FE::zero(), - qo: FE::from(2), - qc: -FE::from(6), - }, - hint: None, - l: a, - r: b, - o: c, - }; - system.add_constraint(constraint1); - system.add_constraint(constraint2); - system.add_constraint(constraint3); - let inputs = HashMap::from([]); - let assignments = system.solve(inputs).unwrap(); - assert_eq!(assignments.get(&a).unwrap(), &FE::from(1)); - assert_eq!(assignments.get(&b).unwrap(), &FE::from(2)); - assert_eq!(assignments.get(&c).unwrap(), &FE::from(3)); - } - - #[test] - fn test_inconsistent_system() { - let mut system = ConstraintSystem::>::new(); - let a = system.new_variable(); - let b = system.new_variable(); - let constraint1 = Constraint { - constraint_type: ConstraintType { - ql: FE::one(), - qr: FE::one(), - qm: FE::zero(), - qo: FE::zero(), - qc: FE::one(), - }, - hint: None, - l: a, - r: b, - o: system.null_variable(), - }; - system.add_constraint(constraint1); - let constraint2 = Constraint { - constraint_type: ConstraintType { - ql: -FE::one(), - qr: FE::one(), - qm: FE::zero(), - qo: FE::zero(), - qc: FE::one(), - }, - hint: None, - l: a, - r: b, - o: system.null_variable(), - }; - let inputs = HashMap::from([(a, FE::from(2))]); - system.add_constraint(constraint2); - assert_eq!( - system.solve(inputs).unwrap_err(), - SolverError::InconsistentSystem - ); - } - - #[test] - fn test_indeterminate_system() { - let mut system = ConstraintSystem::>::new(); - let a = system.new_variable(); - let b = system.new_variable(); - let constraint = Constraint { - constraint_type: ConstraintType { - ql: FE::one(), - qr: FE::one(), - qm: FE::zero(), - qo: FE::zero(), - qc: FE::one(), - }, - hint: None, - l: a, - r: b, - o: system.null_variable(), - }; - let inputs = HashMap::from([]); - system.add_constraint(constraint); - assert_eq!( - system.solve(inputs).unwrap_err(), - SolverError::UnableToSolve - ); - } -} diff --git a/crates/provers/plonk/src/constraint_system/types.rs b/crates/provers/plonk/src/constraint_system/types.rs deleted file mode 100644 index c1637919a..000000000 --- a/crates/provers/plonk/src/constraint_system/types.rs +++ /dev/null @@ -1,168 +0,0 @@ -use lambdaworks_math::field::{ - element::FieldElement as FE, - traits::{IsField, IsPrimeField}, -}; - -use super::{Column, Constraint, ConstraintSystem, ConstraintType, Hint, Variable}; - -impl ConstraintSystem -where - F: IsField, -{ - /// Returns a new variable `w` constrained to take the value `value`. - pub fn new_constant(&mut self, value: FE) -> Variable { - let constant = self.new_variable(); - self.add_constraint(Constraint { - constraint_type: ConstraintType { - ql: -FE::one(), - qr: FE::zero(), - qm: FE::zero(), - qo: FE::zero(), - qc: value, - }, - l: constant, - r: self.null_variable(), - o: self.null_variable(), - hint: None, - }); - constant - } - - /// Returns a new variable `w` constrained to take either `0` or `1` - /// values. - pub fn new_boolean(&mut self) -> Variable { - let boolean = self.new_variable(); - self.add_constraint(Constraint { - constraint_type: ConstraintType { - ql: -FE::one(), - qr: FE::zero(), - qm: FE::one(), - qo: FE::zero(), - qc: FE::zero(), - }, - l: boolean, - r: boolean, - o: self.null_variable(), - hint: None, - }); - boolean - } - - /// Returns 32 new variables `[b31, b30,..., b1, b0]` constrained to take either - /// `0` or `1` values and to represent the binary decomposition - /// of the representative of the value of `v`: - /// `v = b0 + b1 * 2 + b2 * 2^2 + ... + b31 * 2^31`. - pub fn new_u32(&mut self, v: &Variable) -> Vec - where - F: IsPrimeField, - { - let bits: Vec<_> = (0..32).map(|_| self.new_boolean()).collect(); - let mut aux_vars: Vec = Vec::new(); - let hint_function = |v: &FE| { - if v.representative() & 1.into() == 1.into() { - FE::one() - } else { - FE::zero() - } - }; - - let hint = Some(Hint { - function: hint_function, - input: Column::O, - output: Column::R, - }); - // t1 := 2 b_0 + b_1 - let t_0 = self.linear_combination( - &bits[0], - FE::from(2), - &bits[1], - FE::one(), - FE::zero(), - hint.clone(), - ); - aux_vars.push(t_0); - for bit in bits.iter().take(32).skip(2) { - // t_i := 2 t_{i-1} + b_i - let t_i = self.linear_combination( - aux_vars.last().unwrap(), - FE::from(2), - bit, - FE::one(), - FE::zero(), - hint.clone(), - ); - aux_vars.push(t_i); - } - self.assert_eq(v, aux_vars.last().unwrap()); - bits - } -} - -#[cfg(test)] -mod tests { - use std::collections::HashMap; - - use lambdaworks_math::field::{element::FieldElement, fields::u64_prime_field::U64PrimeField}; - - use crate::constraint_system::ConstraintSystem; - - #[test] - fn test_constant() { - let system = &mut ConstraintSystem::>::new(); - let constant = system.new_constant(FieldElement::from(17)); - let inputs = HashMap::new(); - let assignments = system.solve(inputs).unwrap(); - assert_eq!(assignments.get(&constant).unwrap(), &FieldElement::from(17)); - } - - #[test] - fn test_boolean_1() { - let system = &mut ConstraintSystem::>::new(); - - let boolean = system.new_boolean(); - let inputs = HashMap::from([(boolean, FieldElement::from(2))]); - // system is inconsistent - system.solve(inputs).unwrap_err(); - } - - #[test] - fn test_boolean_2() { - let system = &mut ConstraintSystem::>::new(); - - let boolean = system.new_boolean(); - let inputs = HashMap::from([(boolean, FieldElement::from(1))]); - // system is solvable - system.solve(inputs).unwrap(); - } - - #[test] - fn test_boolean_3() { - let system = &mut ConstraintSystem::>::new(); - - let boolean = system.new_boolean(); - let inputs = HashMap::from([(boolean, FieldElement::from(0))]); - // system is solvable - system.solve(inputs).unwrap(); - } - - #[test] - fn test_u32() { - let system = &mut ConstraintSystem::>::new(); - - let input = system.new_variable(); - let u32_var = system.new_u32(&input); - - let a = 59049; - let inputs = HashMap::from([(input, FieldElement::from(a))]); - - let assignments = system.solve(inputs).unwrap(); - - #[allow(clippy::needless_range_loop)] - for i in 0..32 { - assert_eq!( - assignments.get(&u32_var[i]).unwrap().representative(), - (a >> (31 - i)) & 1 - ); - } - } -} diff --git a/crates/provers/plonk/src/lib.rs b/crates/provers/plonk/src/lib.rs deleted file mode 100644 index a1124b47e..000000000 --- a/crates/provers/plonk/src/lib.rs +++ /dev/null @@ -1,5 +0,0 @@ -pub mod constraint_system; -pub mod prover; -pub mod setup; -pub mod test_utils; -pub mod verifier; diff --git a/crates/provers/plonk/src/prover.rs b/crates/provers/plonk/src/prover.rs deleted file mode 100644 index 04a2a1151..000000000 --- a/crates/provers/plonk/src/prover.rs +++ /dev/null @@ -1,934 +0,0 @@ -use lambdaworks_crypto::fiat_shamir::is_transcript::IsTranscript; -use lambdaworks_math::errors::DeserializationError; -use lambdaworks_math::field::traits::IsFFTField; -use lambdaworks_math::traits::{AsBytes, Deserializable, IsRandomFieldElementGenerator}; -use std::marker::PhantomData; -use std::mem::size_of; - -use crate::setup::{ - new_strong_fiat_shamir_transcript, CommonPreprocessedInput, VerificationKey, Witness, -}; -use lambdaworks_crypto::commitments::traits::IsCommitmentScheme; -use lambdaworks_math::{ - field::element::FieldElement, - polynomial::{self, Polynomial}, -}; -use lambdaworks_math::{ - field::traits::{HasDefaultTranscript, IsField}, - traits::ByteConversion, -}; - -#[derive(Debug)] -pub enum ProverError { - DivisionByZero, -} -/// Plonk proof. -/// The challenges are denoted -/// Round 2: β,γ, -/// Round 3: α, -/// Round 4: ζ, -/// Round 5: υ. -/// Here `Z_H` denotes the domain polynomial, `z` is the polynomial -/// that encodes the copy constraints, and `p` is the sum of `z` and -/// the polynomial that encodes the gates constraints. -/// The polynomial `t` is defined as `p / Z_H`. -/// `a`, `b`, and `c` are the wire assignment polynomials. -/// `S_σ1(ζ), S_σ2(ζ) and S_σ3(ζ)` are the copy permutation polynomials. -/// The polynomial `p` can be "linearized" and the result can be written as -/// `linearized_p = p_non_constant + p_constant`, where -/// `p_non_constant` is the sum of all the terms with a "non-constant" -/// polynomial factor, such as `b(ζ)Q_R(X)`, and `p_constant` is the -/// sum of all the rest (such as `PI(ζ)`). -pub struct Proof> { - // Round 1. - /// Commitment to the wire polynomial `a(x)` - pub a_1: CS::Commitment, - /// Commitment to the wire polynomial `b(x)` - pub b_1: CS::Commitment, - /// Commitment to the wire polynomial `c(x)` - pub c_1: CS::Commitment, - - // Round 2. - /// Commitment to the copy constraints polynomial `z(x)` - pub z_1: CS::Commitment, - - // Round 3. - /// Commitment to the low part of the quotient polynomial t(X) - pub t_lo_1: CS::Commitment, - /// Commitment to the middle part of the quotient polynomial t(X) - pub t_mid_1: CS::Commitment, - /// Commitment to the high part of the quotient polynomial t(X) - pub t_hi_1: CS::Commitment, - - // Round 4. - /// Value of `a(ζ)`. - pub a_zeta: FieldElement, - /// Value of `b(ζ)`. - pub b_zeta: FieldElement, - /// Value of `c(ζ)`. - pub c_zeta: FieldElement, - /// Value of `S_σ1(ζ)`. - pub s1_zeta: FieldElement, - /// Value of `S_σ2(ζ)`. - pub s2_zeta: FieldElement, - /// Value of `z(ζω)`. - pub z_zeta_omega: FieldElement, - - // Round 5 - /// Value of `p_non_constant(ζ)`. - pub p_non_constant_zeta: FieldElement, - /// Value of `t(ζ)`. - pub t_zeta: FieldElement, - /// Batch opening proof for all the evaluations at ζ - pub w_zeta_1: CS::Commitment, - /// Single opening proof for `z(ζω)`. - pub w_zeta_omega_1: CS::Commitment, -} - -impl AsBytes for Proof -where - F: IsField, - CS: IsCommitmentScheme, - FieldElement: ByteConversion, - CS::Commitment: AsBytes, -{ - fn as_bytes(&self) -> Vec { - let field_elements = [ - &self.a_zeta, - &self.b_zeta, - &self.c_zeta, - &self.s1_zeta, - &self.s2_zeta, - &self.z_zeta_omega, - &self.p_non_constant_zeta, - &self.t_zeta, - ]; - let commitments = [ - &self.a_1, - &self.b_1, - &self.c_1, - &self.z_1, - &self.t_lo_1, - &self.t_mid_1, - &self.t_hi_1, - &self.w_zeta_1, - &self.w_zeta_omega_1, - ]; - - let mut serialized_proof: Vec = Vec::new(); - - field_elements.iter().for_each(|element| { - let serialized_element = element.to_bytes_be(); - serialized_proof.extend_from_slice(&(serialized_element.len() as u32).to_be_bytes()); - serialized_proof.extend_from_slice(&serialized_element); - }); - - commitments.iter().for_each(|commitment| { - let serialized_commitment = commitment.as_bytes(); - serialized_proof.extend_from_slice(&(serialized_commitment.len() as u32).to_be_bytes()); - serialized_proof.extend_from_slice(&serialized_commitment); - }); - - serialized_proof - } -} - -// TODO: Remove this once FieldElements implement Serializable -fn deserialize_field_element( - bytes: &[u8], - offset: usize, -) -> Result<(usize, FieldElement), DeserializationError> -where - F: IsField, - FieldElement: ByteConversion, -{ - let mut offset = offset; - let element_size_bytes: [u8; size_of::()] = bytes - .get(offset..offset + size_of::()) - .ok_or(DeserializationError::InvalidAmountOfBytes)? - .try_into() - .map_err(|_| DeserializationError::InvalidAmountOfBytes)?; - let element_size = u32::from_be_bytes(element_size_bytes) as usize; - offset += size_of::(); - let field_element = FieldElement::from_bytes_be( - bytes - .get(offset..offset + element_size) - .ok_or(DeserializationError::InvalidAmountOfBytes)?, - )?; - offset += element_size; - Ok((offset, field_element)) -} - -fn deserialize_commitment( - bytes: &[u8], - offset: usize, -) -> Result<(usize, Commitment), DeserializationError> -where - Commitment: Deserializable, -{ - let mut offset = offset; - let element_size_bytes: [u8; size_of::()] = bytes - .get(offset..offset + size_of::()) - .ok_or(DeserializationError::InvalidAmountOfBytes)? - .try_into() - .map_err(|_| DeserializationError::InvalidAmountOfBytes)?; - let element_size = u32::from_be_bytes(element_size_bytes) as usize; - offset += size_of::(); - let commitment = Commitment::deserialize( - bytes - .get(offset..offset + element_size) - .ok_or(DeserializationError::InvalidAmountOfBytes)?, - )?; - offset += element_size; - Ok((offset, commitment)) -} - -impl Deserializable for Proof -where - F: IsField, - CS: IsCommitmentScheme, - FieldElement: ByteConversion, - CS::Commitment: Deserializable, -{ - fn deserialize(bytes: &[u8]) -> Result - where - Self: Sized, - { - let (offset, a_zeta) = deserialize_field_element(bytes, 0)?; - let (offset, b_zeta) = deserialize_field_element(bytes, offset)?; - let (offset, c_zeta) = deserialize_field_element(bytes, offset)?; - let (offset, s1_zeta) = deserialize_field_element(bytes, offset)?; - let (offset, s2_zeta) = deserialize_field_element(bytes, offset)?; - let (offset, z_zeta_omega) = deserialize_field_element(bytes, offset)?; - let (offset, p_non_constant_zeta) = deserialize_field_element(bytes, offset)?; - let (offset, t_zeta) = deserialize_field_element(bytes, offset)?; - - let (offset, a_1) = deserialize_commitment(bytes, offset)?; - let (offset, b_1) = deserialize_commitment(bytes, offset)?; - let (offset, c_1) = deserialize_commitment(bytes, offset)?; - let (offset, z_1) = deserialize_commitment(bytes, offset)?; - let (offset, t_lo_1) = deserialize_commitment(bytes, offset)?; - let (offset, t_mid_1) = deserialize_commitment(bytes, offset)?; - let (offset, t_hi_1) = deserialize_commitment(bytes, offset)?; - let (offset, w_zeta_1) = deserialize_commitment(bytes, offset)?; - let (_, w_zeta_omega_1) = deserialize_commitment(bytes, offset)?; - - Ok(Proof { - a_1, - b_1, - c_1, - z_1, - t_lo_1, - t_mid_1, - t_hi_1, - a_zeta, - b_zeta, - c_zeta, - s1_zeta, - s2_zeta, - z_zeta_omega, - p_non_constant_zeta, - t_zeta, - w_zeta_1, - w_zeta_omega_1, - }) - } -} - -pub struct Prover, R: IsRandomFieldElementGenerator> { - commitment_scheme: CS, - random_generator: R, - phantom: PhantomData, -} - -struct Round1Result { - a_1: Hiding, - b_1: Hiding, - c_1: Hiding, - p_a: Polynomial>, - p_b: Polynomial>, - p_c: Polynomial>, -} - -struct Round2Result { - z_1: Hiding, - p_z: Polynomial>, - beta: FieldElement, - gamma: FieldElement, -} - -struct Round3Result { - t_lo_1: Hiding, - t_mid_1: Hiding, - t_hi_1: Hiding, - p_t_lo: Polynomial>, - p_t_mid: Polynomial>, - p_t_hi: Polynomial>, - alpha: FieldElement, -} - -struct Round4Result { - a_zeta: FieldElement, - b_zeta: FieldElement, - c_zeta: FieldElement, - s1_zeta: FieldElement, - s2_zeta: FieldElement, - z_zeta_omega: FieldElement, - zeta: FieldElement, -} - -struct Round5Result { - w_zeta_1: Hiding, - w_zeta_omega_1: Hiding, - p_non_constant_zeta: FieldElement, - t_zeta: FieldElement, -} - -impl Prover -where - F: IsField + IsFFTField + HasDefaultTranscript, - CS: IsCommitmentScheme, - FieldElement: ByteConversion, - CS::Commitment: AsBytes, - R: IsRandomFieldElementGenerator, -{ - pub fn new(commitment_scheme: CS, random_generator: R) -> Self { - Self { - commitment_scheme, - random_generator, - phantom: PhantomData, - } - } - - fn blind_polynomial( - &self, - target: &Polynomial>, - blinder: &Polynomial>, - n: u64, - ) -> Polynomial> - where - F: IsField, - R: IsRandomFieldElementGenerator, - { - let bs: Vec> = (0..n).map(|_| self.random_generator.generate()).collect(); - let random_part = Polynomial::new(&bs); - target + blinder * random_part - } - - fn round_1( - &self, - witness: &Witness, - common_preprocessed_input: &CommonPreprocessedInput, - ) -> Round1Result { - let p_a = Polynomial::interpolate_fft::(&witness.a) - .expect("xs and ys have equal length and xs are unique"); - let p_b = Polynomial::interpolate_fft::(&witness.b) - .expect("xs and ys have equal length and xs are unique"); - let p_c = Polynomial::interpolate_fft::(&witness.c) - .expect("xs and ys have equal length and xs are unique"); - - let z_h = Polynomial::new_monomial(FieldElement::one(), common_preprocessed_input.n) - - FieldElement::::one(); - let p_a = self.blind_polynomial(&p_a, &z_h, 2); - let p_b = self.blind_polynomial(&p_b, &z_h, 2); - let p_c = self.blind_polynomial(&p_c, &z_h, 2); - - let a_1 = self.commitment_scheme.commit(&p_a); - let b_1 = self.commitment_scheme.commit(&p_b); - let c_1 = self.commitment_scheme.commit(&p_c); - - Round1Result { - a_1, - b_1, - c_1, - p_a, - p_b, - p_c, - } - } - - fn round_2( - &self, - witness: &Witness, - common_preprocessed_input: &CommonPreprocessedInput, - beta: FieldElement, - gamma: FieldElement, - ) -> Round2Result { - let cpi = common_preprocessed_input; - let mut coefficients: Vec> = vec![FieldElement::one()]; - let (s1, s2, s3) = (&cpi.s1_lagrange, &cpi.s2_lagrange, &cpi.s3_lagrange); - - let k2 = &cpi.k1 * &cpi.k1; - - let lp = |w: &FieldElement, eta: &FieldElement| w + &beta * eta + γ - - for i in 0..&cpi.n - 1 { - let (a_i, b_i, c_i) = (&witness.a[i], &witness.b[i], &witness.c[i]); - let num = lp(a_i, &cpi.domain[i]) - * lp(b_i, &(&cpi.domain[i] * &cpi.k1)) - * lp(c_i, &(&cpi.domain[i] * &k2)); - let den = lp(a_i, &s1[i]) * lp(b_i, &s2[i]) * lp(c_i, &s3[i]); - // We are using that den != 0 with high probability because beta and gamma are random elements. - let new_factor = (num / den) - .map_err(|_| ProverError::DivisionByZero) - .unwrap(); - - let new_term = coefficients.last().unwrap() * &new_factor; - coefficients.push(new_term); - } - - let p_z = Polynomial::interpolate_fft::(&coefficients) - .expect("xs and ys have equal length and xs are unique"); - let z_h = Polynomial::new_monomial(FieldElement::one(), common_preprocessed_input.n) - - FieldElement::::one(); - let p_z = self.blind_polynomial(&p_z, &z_h, 3); - let z_1 = self.commitment_scheme.commit(&p_z); - Round2Result { - z_1, - p_z, - beta, - gamma, - } - } - - fn round_3( - &self, - common_preprocessed_input: &CommonPreprocessedInput, - public_input: &[FieldElement], - Round1Result { p_a, p_b, p_c, .. }: &Round1Result, - Round2Result { - p_z, beta, gamma, .. - }: &Round2Result, - alpha: FieldElement, - ) -> Round3Result { - let cpi = common_preprocessed_input; - let k2 = &cpi.k1 * &cpi.k1; - - let one = Polynomial::new_monomial(FieldElement::one(), 0); - let p_x = &Polynomial::new_monomial(FieldElement::::one(), 1); - let zh = Polynomial::new_monomial(FieldElement::::one(), cpi.n) - &one; - - let z_x_omega_coefficients: Vec> = p_z - .coefficients() - .iter() - .enumerate() - .map(|(i, x)| x * &cpi.domain[i % cpi.n]) - .collect(); - let z_x_omega = Polynomial::new(&z_x_omega_coefficients); - let mut e1 = vec![FieldElement::::zero(); cpi.domain.len()]; - e1[0] = FieldElement::one(); - let l1 = Polynomial::interpolate_fft::(&e1) - .expect("xs and ys have equal length and xs are unique"); - let mut p_pi_y = public_input.to_vec(); - p_pi_y.append(&mut vec![FieldElement::zero(); cpi.n - public_input.len()]); - let p_pi = Polynomial::interpolate_fft::(&p_pi_y) - .expect("xs and ys have equal length and xs are unique"); - - // Compute p - // To leverage FFT we work with the evaluation form of every polynomial - // involved - // TODO: check a factor of 4 is a sensible upper bound - let degree = 4 * cpi.n; - let offset = &cpi.k1; - let p_a_eval = Polynomial::evaluate_offset_fft(p_a, 1, Some(degree), offset).unwrap(); - let p_b_eval = Polynomial::evaluate_offset_fft(p_b, 1, Some(degree), offset).unwrap(); - let p_c_eval = Polynomial::evaluate_offset_fft(p_c, 1, Some(degree), offset).unwrap(); - let ql_eval = Polynomial::evaluate_offset_fft(&cpi.ql, 1, Some(degree), offset).unwrap(); - let qr_eval = Polynomial::evaluate_offset_fft(&cpi.qr, 1, Some(degree), offset).unwrap(); - let qm_eval = Polynomial::evaluate_offset_fft(&cpi.qm, 1, Some(degree), offset).unwrap(); - let qo_eval = Polynomial::evaluate_offset_fft(&cpi.qo, 1, Some(degree), offset).unwrap(); - let qc_eval = Polynomial::evaluate_offset_fft(&cpi.qc, 1, Some(degree), offset).unwrap(); - let p_pi_eval = Polynomial::evaluate_offset_fft(&p_pi, 1, Some(degree), offset).unwrap(); - let p_x_eval = Polynomial::evaluate_offset_fft(p_x, 1, Some(degree), offset).unwrap(); - let p_z_eval = Polynomial::evaluate_offset_fft(p_z, 1, Some(degree), offset).unwrap(); - let p_z_x_omega_eval = - Polynomial::evaluate_offset_fft(&z_x_omega, 1, Some(degree), offset).unwrap(); - let p_s1_eval = Polynomial::evaluate_offset_fft(&cpi.s1, 1, Some(degree), offset).unwrap(); - let p_s2_eval = Polynomial::evaluate_offset_fft(&cpi.s2, 1, Some(degree), offset).unwrap(); - let p_s3_eval = Polynomial::evaluate_offset_fft(&cpi.s3, 1, Some(degree), offset).unwrap(); - let l1_eval = Polynomial::evaluate_offset_fft(&l1, 1, Some(degree), offset).unwrap(); - - let p_constraints_eval: Vec<_> = p_a_eval - .iter() - .zip(p_b_eval.iter()) - .zip(p_c_eval.iter()) - .zip(ql_eval.iter()) - .zip(qr_eval.iter()) - .zip(qm_eval.iter()) - .zip(qo_eval.iter()) - .zip(qc_eval.iter()) - .zip(p_pi_eval.iter()) - .map(|((((((((a, b), c), ql), qr), qm), qo), qc), pi)| { - a * b * qm + a * ql + b * qr + c * qo + qc + pi - }) - .collect(); - - let f_eval: Vec<_> = p_a_eval - .iter() - .zip(p_b_eval.iter()) - .zip(p_c_eval.iter()) - .zip(p_x_eval.iter()) - .map(|(((a, b), c), x)| { - (a + x * beta + gamma) - * (b + x * beta * &cpi.k1 + gamma) - * (c + x * beta * &k2 + gamma) - }) - .collect(); - - let g_eval: Vec<_> = p_a_eval - .iter() - .zip(p_b_eval.iter()) - .zip(p_c_eval.iter()) - .zip(p_s1_eval.iter()) - .zip(p_s2_eval.iter()) - .zip(p_s3_eval.iter()) - .map(|(((((a, b), c), s1), s2), s3)| { - (a + s1 * beta + gamma) * (b + s2 * beta + gamma) * (c + s3 * beta + gamma) - }) - .collect(); - - let p_permutation_1_eval: Vec<_> = g_eval - .iter() - .zip(f_eval.iter()) - .zip(p_z_eval.iter()) - .zip(p_z_x_omega_eval.iter()) - .map(|(((g, f), z), y)| g * y - f * z) - .collect(); - - let p_permutation_2_eval: Vec<_> = p_z_eval - .iter() - .zip(l1_eval.iter()) - .map(|(z, l)| (z - FieldElement::::one()) * l) - .collect(); - - let p_eval: Vec<_> = p_permutation_2_eval - .iter() - .zip(p_permutation_1_eval.iter()) - .zip(p_constraints_eval.iter()) - .map(|((p2, p1), co)| (p2 * &alpha + p1) * &alpha + co) - .collect(); - - let mut zh_eval = Polynomial::evaluate_offset_fft(&zh, 1, Some(degree), offset).unwrap(); - FieldElement::inplace_batch_inverse(&mut zh_eval).unwrap(); - let c: Vec<_> = p_eval - .iter() - .zip(zh_eval.iter()) - .map(|(a, b)| a * b) - .collect(); - let mut t = Polynomial::interpolate_offset_fft(&c, offset).unwrap(); - - polynomial::pad_with_zero_coefficients_to_length(&mut t, 3 * (&cpi.n + 2)); - let p_t_lo = Polynomial::new(&t.coefficients[..&cpi.n + 2]); - let p_t_mid = Polynomial::new(&t.coefficients[&cpi.n + 2..2 * (&cpi.n + 2)]); - let p_t_hi = Polynomial::new(&t.coefficients[2 * (&cpi.n + 2)..3 * (&cpi.n + 2)]); - - let b_0 = self.random_generator.generate(); - let b_1 = self.random_generator.generate(); - - let p_t_lo = &p_t_lo + &b_0 * Polynomial::new_monomial(FieldElement::one(), cpi.n + 2); - let p_t_mid = - &p_t_mid - b_0 + &b_1 * Polynomial::new_monomial(FieldElement::one(), cpi.n + 2); - let p_t_hi = &p_t_hi - b_1; - - let t_lo_1 = self.commitment_scheme.commit(&p_t_lo); - let t_mid_1 = self.commitment_scheme.commit(&p_t_mid); - let t_hi_1 = self.commitment_scheme.commit(&p_t_hi); - - Round3Result { - t_lo_1, - t_mid_1, - t_hi_1, - p_t_lo, - p_t_mid, - p_t_hi, - alpha, - } - } - - fn round_4( - &self, - CommonPreprocessedInput { s1, s2, omega, .. }: &CommonPreprocessedInput, - Round1Result { p_a, p_b, p_c, .. }: &Round1Result, - Round2Result { p_z, .. }: &Round2Result, - zeta: FieldElement, - ) -> Round4Result { - let a_zeta = p_a.evaluate(&zeta); - let b_zeta = p_b.evaluate(&zeta); - let c_zeta = p_c.evaluate(&zeta); - let s1_zeta = s1.evaluate(&zeta); - let s2_zeta = s2.evaluate(&zeta); - let z_zeta_omega = p_z.evaluate(&(&zeta * omega)); - Round4Result { - a_zeta, - b_zeta, - c_zeta, - s1_zeta, - s2_zeta, - z_zeta_omega, - zeta, - } - } - - fn round_5( - &self, - common_preprocessed_input: &CommonPreprocessedInput, - round_1: &Round1Result, - round_2: &Round2Result, - round_3: &Round3Result, - round_4: &Round4Result, - upsilon: FieldElement, - ) -> Round5Result { - let cpi = common_preprocessed_input; - let (r1, r2, r3, r4) = (round_1, round_2, round_3, round_4); - // Precompute variables - let k2 = &cpi.k1 * &cpi.k1; - let zeta_raised_n = Polynomial::new_monomial(r4.zeta.pow(cpi.n + 2), 0); // TODO: Paper says n and 2n, but Gnark uses n+2 and 2n+4 - let zeta_raised_2n = Polynomial::new_monomial(r4.zeta.pow(2 * cpi.n + 4), 0); - - // We are using that zeta != 0 because is sampled outside the set of roots of unity, - // and n != 0 because is the length of the trace. - let l1_zeta = ((&r4.zeta.pow(cpi.n as u64) - FieldElement::::one()) - / ((&r4.zeta - FieldElement::::one()) * FieldElement::::from(cpi.n as u64))) - .unwrap(); - - let mut p_non_constant = &cpi.qm * &r4.a_zeta * &r4.b_zeta - + &r4.a_zeta * &cpi.ql - + &r4.b_zeta * &cpi.qr - + &r4.c_zeta * &cpi.qo - + &cpi.qc; - - let r_2_1 = (&r4.a_zeta + &r2.beta * &r4.zeta + &r2.gamma) - * (&r4.b_zeta + &r2.beta * &cpi.k1 * &r4.zeta + &r2.gamma) - * (&r4.c_zeta + &r2.beta * &k2 * &r4.zeta + &r2.gamma) - * &r2.p_z; - let r_2_2 = (&r4.a_zeta + &r2.beta * &r4.s1_zeta + &r2.gamma) - * (&r4.b_zeta + &r2.beta * &r4.s2_zeta + &r2.gamma) - * &r2.beta - * &r4.z_zeta_omega - * &cpi.s3; - p_non_constant = p_non_constant + (r_2_2 - r_2_1) * &r3.alpha; - - let r_3 = &r2.p_z * l1_zeta; - p_non_constant = p_non_constant + (r_3 * &r3.alpha * &r3.alpha); - - let partial_t = &r3.p_t_lo + zeta_raised_n * &r3.p_t_mid + zeta_raised_2n * &r3.p_t_hi; - - // TODO: Refactor to remove clones. - let polynomials = vec![ - partial_t, - p_non_constant, - r1.p_a.clone(), - r1.p_b.clone(), - r1.p_c.clone(), - cpi.s1.clone(), - cpi.s2.clone(), - ]; - let ys: Vec> = polynomials.iter().map(|p| p.evaluate(&r4.zeta)).collect(); - let w_zeta_1 = self - .commitment_scheme - .open_batch(&r4.zeta, &ys, &polynomials, &upsilon); - - let w_zeta_omega_1 = - self.commitment_scheme - .open(&(&r4.zeta * &cpi.omega), &r4.z_zeta_omega, &r2.p_z); - - Round5Result { - w_zeta_1, - w_zeta_omega_1, - p_non_constant_zeta: ys[1].clone(), - t_zeta: ys[0].clone(), - } - } - - pub fn prove( - &self, - witness: &Witness, - public_input: &[FieldElement], - common_preprocessed_input: &CommonPreprocessedInput, - vk: &VerificationKey, - ) -> Proof { - let mut transcript = new_strong_fiat_shamir_transcript::(vk, public_input); - - // Round 1 - let round_1 = self.round_1(witness, common_preprocessed_input); - transcript.append_bytes(&round_1.a_1.as_bytes()); - transcript.append_bytes(&round_1.b_1.as_bytes()); - transcript.append_bytes(&round_1.c_1.as_bytes()); - - // Round 2 - // TODO: Handle error - let beta = transcript.sample_field_element(); - let gamma = transcript.sample_field_element(); - - let round_2 = self.round_2(witness, common_preprocessed_input, beta, gamma); - transcript.append_bytes(&round_2.z_1.as_bytes()); - - // Round 3 - let alpha = transcript.sample_field_element(); - let round_3 = self.round_3( - common_preprocessed_input, - public_input, - &round_1, - &round_2, - alpha, - ); - transcript.append_bytes(&round_3.t_lo_1.as_bytes()); - transcript.append_bytes(&round_3.t_mid_1.as_bytes()); - transcript.append_bytes(&round_3.t_hi_1.as_bytes()); - - // Round 4 - let zeta = transcript.sample_field_element(); - let round_4 = self.round_4(common_preprocessed_input, &round_1, &round_2, zeta); - - transcript.append_field_element(&round_4.a_zeta); - transcript.append_field_element(&round_4.b_zeta); - transcript.append_field_element(&round_4.c_zeta); - transcript.append_field_element(&round_4.s1_zeta); - transcript.append_field_element(&round_4.s2_zeta); - transcript.append_field_element(&round_4.z_zeta_omega); - - // Round 5 - let upsilon = transcript.sample_field_element(); - let round_5 = self.round_5( - common_preprocessed_input, - &round_1, - &round_2, - &round_3, - &round_4, - upsilon, - ); - - Proof { - a_1: round_1.a_1, - b_1: round_1.b_1, - c_1: round_1.c_1, - z_1: round_2.z_1, - t_lo_1: round_3.t_lo_1, - t_mid_1: round_3.t_mid_1, - t_hi_1: round_3.t_hi_1, - a_zeta: round_4.a_zeta, - b_zeta: round_4.b_zeta, - c_zeta: round_4.c_zeta, - s1_zeta: round_4.s1_zeta, - s2_zeta: round_4.s2_zeta, - z_zeta_omega: round_4.z_zeta_omega, - w_zeta_1: round_5.w_zeta_1, - w_zeta_omega_1: round_5.w_zeta_omega_1, - p_non_constant_zeta: round_5.p_non_constant_zeta, - t_zeta: round_5.t_zeta, - } - } -} - -#[cfg(test)] -mod tests { - use lambdaworks_math::{ - cyclic_group::IsGroup, - elliptic_curve::{ - short_weierstrass::{ - curves::bls12_381::{curve::BLS12381Curve, default_types::FrElement}, - point::ShortWeierstrassProjectivePoint, - }, - traits::IsEllipticCurve, - }, - }; - - use crate::{ - test_utils::circuit_1::{test_common_preprocessed_input_1, test_witness_1}, - test_utils::utils::{test_srs, FpElement, TestRandomFieldGenerator, KZG}, - }; - - use super::*; - - fn alpha() -> FrElement { - FrElement::from_hex_unchecked( - "583cfb0df2ef98f2131d717bc6aadd571c5302597c135cab7c00435817bf6e50", - ) - } - - fn beta() -> FrElement { - FrElement::from_hex_unchecked( - "bdda7414bdf5bf42b77cbb3af4a82f32ec7622dd6c71575bede021e6e4609d4", - ) - } - - fn gamma() -> FrElement { - FrElement::from_hex_unchecked( - "58f6690d9b36e62e4a0aef27612819288df2a3ff5bf01597cf06779503f51583", - ) - } - - fn zeta() -> FrElement { - FrElement::from_hex_unchecked( - "2a4040abb941ee5e2a42602a7a60d282a430a4cf099fa3bb0ba8f4da628ec59a", - ) - } - - fn upsilon() -> FrElement { - FrElement::from_hex_unchecked( - "2d15959489a2a8e44693221ca7cbdcab15253d6bae9fd7fe0664cff02fe4f1cf", - ) - } - - #[test] - fn test_round_1() { - let witness = test_witness_1(FrElement::from(2), FrElement::from(2)); - let common_preprocessed_input = test_common_preprocessed_input_1(); - let srs = test_srs(common_preprocessed_input.n); - let kzg = KZG::new(srs); - let random_generator = TestRandomFieldGenerator {}; - - let prover = Prover::new(kzg, random_generator); - let round_1 = prover.round_1(&witness, &common_preprocessed_input); - let a_1_expected = BLS12381Curve::create_point_from_affine( - FpElement::from_hex_unchecked("17f1d3a73197d7942695638c4fa9ac0fc3688c4f9774b905a14e3a3f171bac586c55e83ff97a1aeffb3af00adb22c6bb"), - FpElement::from_hex_unchecked("114d1d6855d545a8aa7d76c8cf2e21f267816aef1db507c96655b9d5caac42364e6f38ba0ecb751bad54dcd6b939c2ca"), - ).unwrap(); - let b_1_expected = BLS12381Curve::create_point_from_affine( - FpElement::from_hex_unchecked("44ed7c3ed015c6a39c350cd06d03b48d3e1f5eaf7a256c5b6203886e6e78cd9b76623d163da4dfb0f2491e7cc06408"), - FpElement::from_hex_unchecked("14c4464d2556fdfdc8e31068ef8d953608e511569a236c825f2ddab4fe04af03aba29e38b9b2b6221243124d235f4c67"), - ).unwrap(); - let c_1_expected = BLS12381Curve::create_point_from_affine( - FpElement::from_hex_unchecked("7726dc031bd26122395153ca428d5e6dea0a64c1f9b3b1bb2f2508a5eb6ea0ea0363294fad3160858bc87e46d3422fd"), - FpElement::from_hex_unchecked("8db0c15bfd77df7fe66284c3b04e6043eaba99ef6a845d4f7255fd0da95f2fb8e474df2e7f8e1a38829f7a9612a9b87"), - ).unwrap(); - assert_eq!(round_1.a_1, a_1_expected); - assert_eq!(round_1.b_1, b_1_expected); - assert_eq!(round_1.c_1, c_1_expected); - } - - #[test] - fn test_round_2() { - let witness = test_witness_1(FrElement::from(2), FrElement::from(2)); - let common_preprocessed_input = test_common_preprocessed_input_1(); - let srs = test_srs(common_preprocessed_input.n); - let kzg = KZG::new(srs); - let random_generator = TestRandomFieldGenerator {}; - let prover = Prover::new(kzg, random_generator); - - let result_2 = prover.round_2(&witness, &common_preprocessed_input, beta(), gamma()); - let z_1_expected = BLS12381Curve::create_point_from_affine( - FpElement::from_hex_unchecked("3e8322968c3496cf1b5786d4d71d158a646ec90c14edf04e758038e1f88dcdfe8443fcecbb75f3074a872a380391742"), - FpElement::from_hex_unchecked("11eac40d09796ff150004e7b858d83ddd9fe995dced0b3fbd7535d6e361729b25d488799da61fdf1d7b5022684053327"), - ).unwrap(); - assert_eq!(result_2.z_1, z_1_expected); - } - - #[test] - fn test_round_3() { - let witness = test_witness_1(FrElement::from(2), FrElement::from(2)); - let common_preprocessed_input = test_common_preprocessed_input_1(); - let srs = test_srs(common_preprocessed_input.n); - let kzg = KZG::new(srs); - let public_input = vec![FieldElement::from(2_u64), FieldElement::from(4)]; - let random_generator = TestRandomFieldGenerator {}; - let prover = Prover::new(kzg, random_generator); - let round_1 = prover.round_1(&witness, &common_preprocessed_input); - let round_2 = prover.round_2(&witness, &common_preprocessed_input, beta(), gamma()); - let round_3 = prover.round_3( - &common_preprocessed_input, - &public_input, - &round_1, - &round_2, - alpha(), - ); - - let t_lo_1_expected = BLS12381Curve::create_point_from_affine( - FpElement::from_hex_unchecked("9f511a769e77e87537b0749d65f467532fbf0f9dc1bcc912c333741be9d0a613f61e5fe595996964646ce30794701e5"), - FpElement::from_hex_unchecked("89fd6bb571323912210517237d6121144fc01ba2756f47c12c9cc94fc9197313867d68530f152dc8d447f10fcf75a6c"), - ).unwrap(); - let t_mid_1_expected = BLS12381Curve::create_point_from_affine( - FpElement::from_hex_unchecked("f96d8a93f3f5be2ab2819891f41c9f883cacea63da423e6ed1701765fcd659fc11e056a48c554f5df3a9c6603d48ca8"), - FpElement::from_hex_unchecked("14fa74fa049b7276007b739f3b8cfeac09e8cfabd4f858b6b99798c81124c34851960bebda90133cb03c981c08c8b6d3"), - ).unwrap(); - let t_hi_1_expected = ShortWeierstrassProjectivePoint::::neutral_element(); - - assert_eq!(round_3.t_lo_1, t_lo_1_expected); - assert_eq!(round_3.t_mid_1, t_mid_1_expected); - assert_eq!(round_3.t_hi_1, t_hi_1_expected); - } - - #[test] - fn test_round_4() { - let witness = test_witness_1(FrElement::from(2), FrElement::from(2)); - let common_preprocessed_input = test_common_preprocessed_input_1(); - let srs = test_srs(common_preprocessed_input.n); - let kzg = KZG::new(srs); - let random_generator = TestRandomFieldGenerator {}; - let prover = Prover::new(kzg, random_generator); - - let round_1 = prover.round_1(&witness, &common_preprocessed_input); - let round_2 = prover.round_2(&witness, &common_preprocessed_input, beta(), gamma()); - - let round_4 = prover.round_4(&common_preprocessed_input, &round_1, &round_2, zeta()); - let expected_a_value = FrElement::from_hex_unchecked( - "2c090a95b57f1f493b7b747bba34fef7772fd72f97d718ed69549641a823eb2e", - ); - let expected_b_value = FrElement::from_hex_unchecked( - "5975959d91369ba4e7a03c6ae94b7fe98e8b61b7bf9af63c8ae0759e17ac0c7e", - ); - let expected_c_value = FrElement::from_hex_unchecked( - "6bf31edeb4344b7d2df2cb1bd40b4d13e182d9cb09f89591fa043c1a34b4a93", - ); - let expected_z_value = FrElement::from_hex_unchecked( - "38e2ec8e7c3dab29e2b8e9c8ea152914b8fe4612e91f2902c80238efcf21f4ee", - ); - let expected_s1_value = FrElement::from_hex_unchecked( - "472f66db4fb6947d9ed9808241fe82324bc08aa2a54be93179db8e564e1137d4", - ); - let expected_s2_value = FrElement::from_hex_unchecked( - "5588f1239c24efe0538868d0f716984e69c6980e586864f615e4b0621fdc6f81", - ); - - assert_eq!(round_4.a_zeta, expected_a_value); - assert_eq!(round_4.b_zeta, expected_b_value); - assert_eq!(round_4.c_zeta, expected_c_value); - assert_eq!(round_4.z_zeta_omega, expected_z_value); - assert_eq!(round_4.s1_zeta, expected_s1_value); - assert_eq!(round_4.s2_zeta, expected_s2_value); - } - - #[test] - fn test_round_5() { - let witness = test_witness_1(FrElement::from(2), FrElement::from(2)); - let common_preprocessed_input = test_common_preprocessed_input_1(); - let srs = test_srs(common_preprocessed_input.n); - let kzg = KZG::new(srs); - let public_input = vec![FieldElement::from(2_u64), FieldElement::from(4)]; - let random_generator = TestRandomFieldGenerator {}; - let prover = Prover::new(kzg, random_generator); - - let round_1 = prover.round_1(&witness, &common_preprocessed_input); - let round_2 = prover.round_2(&witness, &common_preprocessed_input, beta(), gamma()); - - let round_3 = prover.round_3( - &common_preprocessed_input, - &public_input, - &round_1, - &round_2, - alpha(), - ); - - let round_4 = prover.round_4(&common_preprocessed_input, &round_1, &round_2, zeta()); - - let expected_w_zeta_1 = BLS12381Curve::create_point_from_affine( - FpElement::from_hex_unchecked("fa6250b80a418f0548b132ac264ff9915b2076c0c2548da9316ae19ffa35bbcf905d9f02f9274739608045ef83a4757"), - FpElement::from_hex_unchecked("17713ade2dbd66e923d4092a5d2da98202959dd65a15e9f7791fab3c0dd08788aa9b4a1cb21d04e0c43bd29225472145"), - ).unwrap(); - let expected_w_zeta_omega_1 = BLS12381Curve::create_point_from_affine( - FpElement::from_hex_unchecked("4484f08f8eaccf28bab8ee9539e6e7f4059cb1ce77b9b18e9e452f387163dc0b845f4874bf6445399e650d362799ff5"), - FpElement::from_hex_unchecked("1254347a0fa2ac856917825a5cff5f9583d39a52edbc2be5bb10fabd0c04d23019bcb963404345743120310fd734a61a"), - ).unwrap(); - - let round_5 = prover.round_5( - &common_preprocessed_input, - &round_1, - &round_2, - &round_3, - &round_4, - upsilon(), - ); - assert_eq!(round_5.w_zeta_1, expected_w_zeta_1); - assert_eq!(round_5.w_zeta_omega_1, expected_w_zeta_omega_1); - } -} diff --git a/crates/provers/plonk/src/setup.rs b/crates/provers/plonk/src/setup.rs deleted file mode 100644 index c02c63107..000000000 --- a/crates/provers/plonk/src/setup.rs +++ /dev/null @@ -1,219 +0,0 @@ -use std::collections::HashMap; - -use crate::constraint_system::{get_permutation, ConstraintSystem, Variable}; -use crate::test_utils::utils::{generate_domain, generate_permutation_coefficients}; -use lambdaworks_crypto::commitments::traits::IsCommitmentScheme; -use lambdaworks_crypto::fiat_shamir::{ - default_transcript::DefaultTranscript, is_transcript::IsTranscript, -}; -use lambdaworks_math::field::traits::{HasDefaultTranscript, IsFFTField}; -use lambdaworks_math::field::{element::FieldElement, traits::IsField}; -use lambdaworks_math::polynomial::Polynomial; -use lambdaworks_math::traits::{AsBytes, ByteConversion}; - -// TODO: implement getters -pub struct Witness { - pub a: Vec>, - pub b: Vec>, - pub c: Vec>, -} - -impl Witness { - pub fn new(values: HashMap>, system: &ConstraintSystem) -> Self { - let (lro, _) = system.to_matrices(); - let abc: Vec<_> = lro.iter().map(|v| values[v].clone()).collect(); - let n = lro.len() / 3; - - Self { - a: abc[..n].to_vec(), - b: abc[n..2 * n].to_vec(), - c: abc[2 * n..].to_vec(), - } - } -} - -// TODO: implement getters -#[derive(Clone)] -pub struct CommonPreprocessedInput { - pub n: usize, - /// Number of constraints - pub domain: Vec>, - pub omega: FieldElement, - pub k1: FieldElement, - - pub ql: Polynomial>, - pub qr: Polynomial>, - pub qo: Polynomial>, - pub qm: Polynomial>, - pub qc: Polynomial>, - - pub s1: Polynomial>, - pub s2: Polynomial>, - pub s3: Polynomial>, - - pub s1_lagrange: Vec>, - pub s2_lagrange: Vec>, - pub s3_lagrange: Vec>, -} - -impl CommonPreprocessedInput { - pub fn from_constraint_system( - system: &ConstraintSystem, - order_r_minus_1_root_unity: &FieldElement, - ) -> Self { - let (lro, q) = system.to_matrices(); - let n = lro.len() / 3; - let omega = F::get_primitive_root_of_unity(n.trailing_zeros() as u64).unwrap(); - let domain = generate_domain(&omega, n); - - let m = q.len() / 5; - let ql: Vec<_> = q[..m].to_vec(); - let qr: Vec<_> = q[m..2 * m].to_vec(); - let qm: Vec<_> = q[2 * m..3 * m].to_vec(); - let qo: Vec<_> = q[3 * m..4 * m].to_vec(); - let qc: Vec<_> = q[4 * m..].to_vec(); - - let permutation = get_permutation(&lro); - let permuted = - generate_permutation_coefficients(&omega, n, &permutation, order_r_minus_1_root_unity); - - let s1_lagrange: Vec<_> = permuted[..n].to_vec(); - let s2_lagrange: Vec<_> = permuted[n..2 * n].to_vec(); - let s3_lagrange: Vec<_> = permuted[2 * n..].to_vec(); - - Self { - domain, - n, - omega, - k1: order_r_minus_1_root_unity.clone(), - ql: Polynomial::interpolate_fft::(&ql).unwrap(), // TODO: Remove unwraps - qr: Polynomial::interpolate_fft::(&qr).unwrap(), - qo: Polynomial::interpolate_fft::(&qo).unwrap(), - qm: Polynomial::interpolate_fft::(&qm).unwrap(), - qc: Polynomial::interpolate_fft::(&qc).unwrap(), - s1: Polynomial::interpolate_fft::(&s1_lagrange).unwrap(), - s2: Polynomial::interpolate_fft::(&s2_lagrange).unwrap(), - s3: Polynomial::interpolate_fft::(&s3_lagrange).unwrap(), - s1_lagrange, - s2_lagrange, - s3_lagrange, - } - } -} - -pub struct VerificationKey { - pub qm_1: G1Point, - pub ql_1: G1Point, - pub qr_1: G1Point, - pub qo_1: G1Point, - pub qc_1: G1Point, - - pub s1_1: G1Point, - pub s2_1: G1Point, - pub s3_1: G1Point, -} - -pub fn setup>( - common_input: &CommonPreprocessedInput, - commitment_scheme: &CS, -) -> VerificationKey { - VerificationKey { - qm_1: commitment_scheme.commit(&common_input.qm), - ql_1: commitment_scheme.commit(&common_input.ql), - qr_1: commitment_scheme.commit(&common_input.qr), - qo_1: commitment_scheme.commit(&common_input.qo), - qc_1: commitment_scheme.commit(&common_input.qc), - - s1_1: commitment_scheme.commit(&common_input.s1), - s2_1: commitment_scheme.commit(&common_input.s2), - s3_1: commitment_scheme.commit(&common_input.s3), - } -} - -pub fn new_strong_fiat_shamir_transcript( - vk: &VerificationKey, - public_input: &[FieldElement], -) -> DefaultTranscript -where - F: HasDefaultTranscript, - FieldElement: ByteConversion, - CS: IsCommitmentScheme, - CS::Commitment: AsBytes, -{ - let mut transcript = DefaultTranscript::default(); - - transcript.append_bytes(&vk.s1_1.as_bytes()); - transcript.append_bytes(&vk.s2_1.as_bytes()); - transcript.append_bytes(&vk.s3_1.as_bytes()); - transcript.append_bytes(&vk.ql_1.as_bytes()); - transcript.append_bytes(&vk.qr_1.as_bytes()); - transcript.append_bytes(&vk.qm_1.as_bytes()); - transcript.append_bytes(&vk.qo_1.as_bytes()); - transcript.append_bytes(&vk.qc_1.as_bytes()); - - for value in public_input.iter() { - transcript.append_field_element(value); - } - - transcript -} - -#[cfg(test)] -mod tests { - use lambdaworks_math::elliptic_curve::short_weierstrass::curves::bls12_381::default_types::FrField; - use lambdaworks_math::elliptic_curve::{ - short_weierstrass::curves::bls12_381::curve::BLS12381Curve, traits::IsEllipticCurve, - }; - - use super::*; - use crate::test_utils::circuit_1::test_common_preprocessed_input_1; - use crate::test_utils::utils::{test_srs, FpElement, KZG}; - - #[test] - fn setup_works_for_simple_circuit() { - let common_input = test_common_preprocessed_input_1(); - let srs = test_srs(common_input.n); - let kzg = KZG::new(srs); - - let vk = setup::(&common_input, &kzg); - - let expected_ql = BLS12381Curve::create_point_from_affine( - FpElement::from_hex_unchecked("1492341357755e31a6306abf3237f84f707ded7cb526b8ffd40901746234ef27f12bc91ef638e4977563db208b765f12"), - FpElement::from_hex_unchecked("ec3ff8288ea339010658334f494a614f7470c19a08d53a9cf5718e0613bb65d2cdbc1df374057d9b45c35cf1f1b5b72"), - ).unwrap(); - let expected_qr = BLS12381Curve::create_point_from_affine( - FpElement::from_hex_unchecked("107ab09b6b8c6fc55087aeb8045e17a6d016bdacbc64476264328e71f3e85a4eacaee34ee963e9c9249b6b1bc9653674"), - FpElement::from_hex_unchecked("f98e3fe5a53545b67a51da7e7a6cedc51af467abdefd644113fb97edf339aeaa5e2f6a5713725ec76754510b76a10be"), - ).unwrap(); - let expected_qo = BLS12381Curve::create_point_from_affine( - FpElement::from_hex_unchecked("9fd00baa112a0064ce5c3c2d243e657b25df8a2f237b91eec27e83157f6ca896a2401d07ec7d7d097d2f2a344e2018f"), - FpElement::from_hex_unchecked("15922cfa65972d80823c6bb9aeb0637c864b636267bfee2818413e9cdc5f7948575c4ce097bb8b9db8087c4ed5056592"), - ).unwrap(); - let expected_qm = BLS12381Curve::create_point_from_affine( - FpElement::from_hex_unchecked("9fd00baa112a0064ce5c3c2d243e657b25df8a2f237b91eec27e83157f6ca896a2401d07ec7d7d097d2f2a344e2018f"), - FpElement::from_hex_unchecked("46ee4efd3e8b919c8df3bfc949b495ade2be8228bc524974eef94041a517cdbc74fb31e1998746201f683b12afa4519"), - ).unwrap(); - - let expected_s1 = BLS12381Curve::create_point_from_affine( - FpElement::from_hex_unchecked("187ee12de08728650d18912aa9fe54863922a9eeb37e34ff43041f1d039f00028ad2cdf23705e6f6ab7ea9406535c1b0"), - FpElement::from_hex_unchecked("4f29051990de0d12b38493992845d9abcb48ef18239eca8b8228618c78ec371d39917bc0d45cf6dc4f79bd64baa9ae2") - ).unwrap(); - let expected_s2 = BLS12381Curve::create_point_from_affine( - FpElement::from_hex_unchecked("167c0384025887c01ea704234e813842a4acef7d765c3a94a5442ca685b4fc1d1b425ba7786a7413bd4a7d6a1eb5a35a"), - FpElement::from_hex_unchecked("12b644100c5d00af27c121806c4779f88e840ff3fdac44124b8175a303d586c4d910486f909b37dda1505c485f053da1") - ).unwrap(); - let expected_s3 = BLS12381Curve::create_point_from_affine( - FpElement::from_hex_unchecked("188fb6dba3cf5af8a7f6a44d935bb3dd2083a5beb4c020f581739ebc40659c824a4ca8279cf7d852decfbca572e4fa0e"), - FpElement::from_hex_unchecked("d84d52582fd95bfa7672f7cef9dd4d0b1b4a54d33f244fdb97df71c7d45fd5c5329296b633c9ed23b8475ee47b9d99") - ).unwrap(); - - assert_eq!(vk.ql_1, expected_ql); - assert_eq!(vk.qr_1, expected_qr); - assert_eq!(vk.qo_1, expected_qo); - assert_eq!(vk.qm_1, expected_qm); - - assert_eq!(vk.s1_1, expected_s1); - assert_eq!(vk.s2_1, expected_s2); - assert_eq!(vk.s3_1, expected_s3); - } -} diff --git a/crates/provers/plonk/src/test_utils/circuit_1.rs b/crates/provers/plonk/src/test_utils/circuit_1.rs deleted file mode 100644 index 929f73583..000000000 --- a/crates/provers/plonk/src/test_utils/circuit_1.rs +++ /dev/null @@ -1,115 +0,0 @@ -use super::utils::{ - generate_domain, generate_permutation_coefficients, ORDER_R_MINUS_1_ROOT_UNITY, -}; -use crate::setup::{CommonPreprocessedInput, Witness}; -use lambdaworks_math::{ - elliptic_curve::short_weierstrass::curves::bls12_381::default_types::{FrElement, FrField}, - field::{element::FieldElement, traits::IsFFTField}, - polynomial::Polynomial, -}; - -pub const ORDER_4_ROOT_UNITY: FrElement = - FrElement::from_hex_unchecked("8d51ccce760304d0ec030002760300000001000000000000"); // order 4 - -/* Test circuit for the program: - public input x - public input y - private input e - z = x * e - assert y == z -*/ -pub fn test_common_preprocessed_input_1() -> CommonPreprocessedInput { - let n = 4; - let omega = FrField::get_primitive_root_of_unity(2).unwrap(); - let domain = generate_domain(&omega, n); - let permuted = generate_permutation_coefficients( - &omega, - n, - &[11, 3, 0, 1, 2, 4, 6, 10, 5, 8, 7, 9], - &ORDER_R_MINUS_1_ROOT_UNITY, - ); - - let s1_lagrange: Vec = permuted[..4].to_vec(); - let s2_lagrange: Vec = permuted[4..8].to_vec(); - let s3_lagrange: Vec = permuted[8..].to_vec(); - - CommonPreprocessedInput { - n, - omega, - domain, - k1: ORDER_R_MINUS_1_ROOT_UNITY, - // domain: domain.clone(), - ql: Polynomial::interpolate_fft::(&[ - -FieldElement::one(), - -FieldElement::one(), - FieldElement::zero(), - FieldElement::one(), - ]) - .unwrap(), - - qr: Polynomial::interpolate_fft::(&[ - FieldElement::zero(), - FieldElement::zero(), - FieldElement::zero(), - -FieldElement::one(), - ]) - .unwrap(), - - qo: Polynomial::interpolate_fft::(&[ - FieldElement::zero(), - FieldElement::zero(), - -FieldElement::one(), - FieldElement::zero(), - ]) - .unwrap(), - - qm: Polynomial::interpolate_fft::(&[ - FieldElement::zero(), - FieldElement::zero(), - FieldElement::one(), - FieldElement::zero(), - ]) - .unwrap(), - - qc: Polynomial::interpolate_fft::(&[ - FieldElement::from(0_u64), - FieldElement::from(0_u64), - FieldElement::zero(), - FieldElement::zero(), - ]) - .unwrap(), - - s1: Polynomial::interpolate_fft::(&s1_lagrange).unwrap(), - s2: Polynomial::interpolate_fft::(&s2_lagrange).unwrap(), - s3: Polynomial::interpolate_fft::(&s3_lagrange).unwrap(), - - s1_lagrange, - s2_lagrange, - s3_lagrange, - } -} - -pub fn test_witness_1(x: FrElement, e: FrElement) -> Witness { - let y = &x * &e; - let empty = x.clone(); - Witness { - a: vec![ - x.clone(), // Public input - y.clone(), // Public input - x.clone(), // LHS for multiplication - y, // LHS for == - ], - b: vec![ - empty.clone(), - empty.clone(), - e.clone(), // RHS for multiplication - &x * &e, // RHS for == - ], - c: vec![ - empty.clone(), - empty.clone(), - &x * &e, // Output of multiplication - empty, - ], - } -} diff --git a/crates/provers/plonk/src/test_utils/circuit_2.rs b/crates/provers/plonk/src/test_utils/circuit_2.rs deleted file mode 100644 index b47eafbef..000000000 --- a/crates/provers/plonk/src/test_utils/circuit_2.rs +++ /dev/null @@ -1,157 +0,0 @@ -use super::utils::{ - generate_domain, generate_permutation_coefficients, ORDER_R_MINUS_1_ROOT_UNITY, -}; -use crate::setup::{CommonPreprocessedInput, Witness}; -use lambdaworks_math::{ - elliptic_curve::short_weierstrass::curves::bls12_381::default_types::{FrElement, FrField}, - field::{element::FieldElement, traits::IsFFTField}, - polynomial::Polynomial, -}; - -pub const ORDER_8_ROOT_UNITY: FrElement = FrElement::from_hex_unchecked( - "345766f603fa66e78c0625cd70d77ce2b38b21c28713b7007228fd3397743f7a", -); // order 8 - -/* Test circuit for the program: - public input x - public input y - private input e - z1 = x * e - z2 = z1 + 5 - assert y == z2 -*/ -pub fn test_common_preprocessed_input_2() -> CommonPreprocessedInput { - let n: usize = 8; - let omega = FrField::get_primitive_root_of_unity(3).unwrap(); - let domain = generate_domain(&omega, n); - let permutation = &[ - 23, 4, 0, 18, 1, 2, 5, 6, 7, 8, 10, 9, 19, 11, 13, 14, 15, 16, 3, 12, 17, 20, 21, 22, - ]; - let permuted = - generate_permutation_coefficients(&omega, n, permutation, &ORDER_R_MINUS_1_ROOT_UNITY); - - let s1_lagrange: Vec = permuted[..8].to_vec(); - let s2_lagrange: Vec = permuted[8..16].to_vec(); - let s3_lagrange: Vec = permuted[16..].to_vec(); - - CommonPreprocessedInput { - n, - omega, - k1: ORDER_R_MINUS_1_ROOT_UNITY, - domain: domain.clone(), - - ql: Polynomial::interpolate( - &domain, - &[ - -FieldElement::one(), - -FieldElement::one(), - FieldElement::zero(), - FieldElement::one(), - FieldElement::one(), - FieldElement::zero(), - FieldElement::zero(), - FieldElement::zero(), - ], - ) - .unwrap(), - qr: Polynomial::interpolate( - &domain, - &[ - FieldElement::zero(), - FieldElement::zero(), - FieldElement::zero(), - FieldElement::zero(), - -FieldElement::one(), - FieldElement::zero(), - FieldElement::zero(), - FieldElement::zero(), - ], - ) - .unwrap(), - qo: Polynomial::interpolate( - &domain, - &[ - FieldElement::zero(), - FieldElement::zero(), - -FieldElement::one(), - -FieldElement::one(), - FieldElement::zero(), - FieldElement::zero(), - FieldElement::zero(), - FieldElement::zero(), - ], - ) - .unwrap(), - qm: Polynomial::interpolate( - &domain, - &[ - FieldElement::zero(), - FieldElement::zero(), - FieldElement::one(), - FieldElement::zero(), - FieldElement::zero(), - FieldElement::zero(), - FieldElement::zero(), - FieldElement::zero(), - ], - ) - .unwrap(), - qc: Polynomial::interpolate( - &domain, - &[ - FieldElement::zero(), - FieldElement::zero(), - FieldElement::zero(), - FieldElement::from(5_u64), - FieldElement::zero(), - FieldElement::zero(), - FieldElement::zero(), - FieldElement::zero(), - ], - ) - .unwrap(), - - s1: Polynomial::interpolate(&domain, &s1_lagrange).unwrap(), - s2: Polynomial::interpolate(&domain, &s2_lagrange).unwrap(), - s3: Polynomial::interpolate(&domain, &s3_lagrange).unwrap(), - - s1_lagrange, - s2_lagrange, - s3_lagrange, - } -} - -pub fn test_witness_2(x: FrElement, e: FrElement) -> Witness { - Witness { - a: vec![ - x.clone(), - &x * &e + FieldElement::from(5_u64), - x.clone(), - &x * &e, - &x * &e + FieldElement::from(5_u64), - x.clone(), - x.clone(), - x.clone(), - ], - b: vec![ - x.clone(), - x.clone(), - e.clone(), - x.clone(), - &x * &e + FieldElement::from(5_u64), - x.clone(), - x.clone(), - x.clone(), - ], - c: vec![ - x.clone(), - x.clone(), - &x * &e, - &x * &e + FieldElement::from(5_u64), - x.clone(), - x.clone(), - x.clone(), - x, - ], - } -} diff --git a/crates/provers/plonk/src/test_utils/circuit_json.rs b/crates/provers/plonk/src/test_utils/circuit_json.rs deleted file mode 100644 index 43913c844..000000000 --- a/crates/provers/plonk/src/test_utils/circuit_json.rs +++ /dev/null @@ -1,204 +0,0 @@ -use super::utils::{ - generate_domain, generate_permutation_coefficients, ORDER_R_MINUS_1_ROOT_UNITY, -}; -use crate::setup::{CommonPreprocessedInput, Witness}; -use lambdaworks_math::field::traits::IsFFTField; -use lambdaworks_math::{ - elliptic_curve::short_weierstrass::curves::bls12_381::default_types::{FrElement, FrField}, - polynomial::Polynomial, -}; -use serde::{Deserialize, Serialize}; - -// The json exported in go comes with Uppercase in the first letter. -#[allow(non_snake_case)] -#[derive(Serialize, Deserialize)] -struct JsonPlonkCircuit { - N: usize, - N_Padded: usize, - Input: Vec, - Ql: Vec, - Qr: Vec, - Qm: Vec, - Qo: Vec, - Qc: Vec, - A: Vec, - B: Vec, - C: Vec, - Permutation: Vec, -} - -pub fn common_preprocessed_input_from_json( - json_string: &str, -) -> ( - Witness, - CommonPreprocessedInput, - Vec, -) { - let json_input: JsonPlonkCircuit = serde_json::from_str(json_string).unwrap(); - let n = json_input.N_Padded; - let omega = FrField::get_primitive_root_of_unity(n.trailing_zeros() as u64).unwrap(); - let domain = generate_domain(&omega, n); - let permuted = generate_permutation_coefficients( - &omega, - n, - &json_input.Permutation, - &ORDER_R_MINUS_1_ROOT_UNITY, - ); - - let pad = FrElement::from_hex_unchecked(&json_input.Input[0]); - - let s1_lagrange: Vec = permuted[..n].to_vec(); - let s2_lagrange: Vec = permuted[n..2 * n].to_vec(); - let s3_lagrange: Vec = permuted[2 * n..].to_vec(); - ( - Witness { - a: process_vector(json_input.A, &pad, n), - b: process_vector(json_input.B, &pad, n), - c: process_vector(json_input.C, &pad, n), - }, - CommonPreprocessedInput { - n, - domain, - omega, - k1: ORDER_R_MINUS_1_ROOT_UNITY, - ql: Polynomial::interpolate_fft::(&process_vector( - json_input.Ql, - &FrElement::zero(), - n, - )) - .unwrap(), - qr: Polynomial::interpolate_fft::(&process_vector( - json_input.Qr, - &FrElement::zero(), - n, - )) - .unwrap(), - qo: Polynomial::interpolate_fft::(&process_vector( - json_input.Qo, - &FrElement::zero(), - n, - )) - .unwrap(), - qm: Polynomial::interpolate_fft::(&process_vector( - json_input.Qm, - &FrElement::zero(), - n, - )) - .unwrap(), - qc: Polynomial::interpolate_fft::(&process_vector( - json_input.Qc, - &FrElement::zero(), - n, - )) - .unwrap(), - s1: Polynomial::interpolate_fft::(&s1_lagrange).unwrap(), - s2: Polynomial::interpolate_fft::(&s2_lagrange).unwrap(), - s3: Polynomial::interpolate_fft::(&s3_lagrange).unwrap(), - s1_lagrange, - s2_lagrange, - s3_lagrange, - }, - convert_str_vec_to_frelement_vec(json_input.Input), - ) -} - -pub fn pad_vector<'a>( - v: &'a mut Vec, - p: &FrElement, - target_size: usize, -) -> &'a mut Vec { - v.append(&mut vec![p.clone(); target_size - v.len()]); - v -} - -fn convert_str_vec_to_frelement_vec(ss: Vec) -> Vec { - ss.iter() - .map(|s| FrElement::from_hex_unchecked(s)) - .collect() -} - -fn process_vector(vector: Vec, pad: &FrElement, n: usize) -> Vec { - pad_vector(&mut convert_str_vec_to_frelement_vec(vector), pad, n).to_owned() -} - -#[cfg(test)] -mod tests { - use super::common_preprocessed_input_from_json; - - #[test] - fn test_import_gnark_circuit_from_json() { - common_preprocessed_input_from_json( - r#"{ - "N": 4, - "N_Padded": 4, - "Omega": "8d51ccce760304d0ec030002760300000001000000000000", - "Input": [ - "2", - "4" - ], - "Ql": [ - "73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000000", - "73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000000", - "0", - "1" - ], - "Qr": [ - "0", - "0", - "0", - "73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000000" - ], - "Qm": [ - "0", - "0", - "1", - "0" - ], - "Qo": [ - "0", - "0", - "73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000000", - "0" - ], - "Qc": [ - "0", - "0", - "0", - "0" - ], - "A": [ - "2", - "4", - "2", - "4" - ], - "B": [ - "2", - "2", - "2", - "4" - ], - "C": [ - "2", - "2", - "4", - "2" - ], - "Permutation": [ - 11, - 3, - 2, - 1, - 0, - 4, - 5, - 10, - 6, - 8, - 7, - 9 - ] -}"#, - ); - } -} diff --git a/crates/provers/plonk/src/test_utils/mod.rs b/crates/provers/plonk/src/test_utils/mod.rs deleted file mode 100644 index 891980d8e..000000000 --- a/crates/provers/plonk/src/test_utils/mod.rs +++ /dev/null @@ -1,8 +0,0 @@ -/// A test circuit -pub mod circuit_1; -/// A test circuit -pub mod circuit_2; -/// Deserialize json to generate test circuits -pub mod circuit_json; -/// Useful tools to test plonk over different circuits -pub mod utils; diff --git a/crates/provers/plonk/src/test_utils/utils.rs b/crates/provers/plonk/src/test_utils/utils.rs deleted file mode 100644 index cecd0f1b0..000000000 --- a/crates/provers/plonk/src/test_utils/utils.rs +++ /dev/null @@ -1,93 +0,0 @@ -use lambdaworks_crypto::commitments::kzg::KateZaveruchaGoldberg; -use lambdaworks_crypto::commitments::kzg::StructuredReferenceString; -use lambdaworks_math::elliptic_curve::short_weierstrass::curves::bls12_381::default_types::FrElement; -use lambdaworks_math::elliptic_curve::short_weierstrass::curves::bls12_381::default_types::FrField; -use lambdaworks_math::elliptic_curve::short_weierstrass::curves::bls12_381::pairing::BLS12381AtePairing; -use lambdaworks_math::field::traits::IsField; -use lambdaworks_math::{ - cyclic_group::IsGroup, - elliptic_curve::{ - short_weierstrass::curves::bls12_381::{ - curve::BLS12381Curve, field_extension::BLS12381PrimeField, twist::BLS12381TwistCurve, - }, - traits::IsEllipticCurve, - }, - field::element::FieldElement, - traits::IsRandomFieldElementGenerator, -}; - -pub type Curve = BLS12381Curve; -pub type TwistedCurve = BLS12381TwistCurve; -pub type FpField = BLS12381PrimeField; -pub type FpElement = FieldElement; -pub type Pairing = BLS12381AtePairing; -pub type KZG = KateZaveruchaGoldberg; -pub const ORDER_R_MINUS_1_ROOT_UNITY: FrElement = FrElement::from_hex_unchecked("7"); - -pub type G1Point = ::PointRepresentation; -pub type G2Point = ::PointRepresentation; - -/// Generates a test SRS for the BLS12381 curve -/// n is the number of constraints in the system. -pub fn test_srs(n: usize) -> StructuredReferenceString { - let s = FrElement::from(2); - let g1 = ::generator(); - let g2 = ::generator(); - - let powers_main_group: Vec = (0..n + 3) - .map(|exp| g1.operate_with_self(s.pow(exp as u64).representative())) - .collect(); - let powers_secondary_group = [g2.clone(), g2.operate_with_self(s.representative())]; - - StructuredReferenceString::new(&powers_main_group, &powers_secondary_group) -} - -/// Generates a domain to interpolate: 1, omega, omega², ..., omega^size -pub fn generate_domain(omega: &FieldElement, size: usize) -> Vec> { - (1..size).fold(vec![FieldElement::one()], |mut acc, _| { - acc.push(acc.last().unwrap() * omega); - acc - }) -} - -/// Generates the permutation coefficients for the copy constraints. -/// polynomials S1, S2, S3. -pub fn generate_permutation_coefficients( - omega: &FieldElement, - n: usize, - permutation: &[usize], - order_r_minus_1_root_unity: &FieldElement, -) -> Vec> { - let identity = identity_permutation(omega, n, order_r_minus_1_root_unity); - let permuted: Vec> = (0..n * 3) - .map(|i| identity[permutation[i]].clone()) - .collect(); - permuted -} - -/// The identity permutation, auxiliary function to generate the copy constraints. -fn identity_permutation( - w: &FieldElement, - n: usize, - order_r_minus_1_root_unity: &FieldElement, -) -> Vec> { - let u = order_r_minus_1_root_unity; - let mut result: Vec> = vec![]; - for index_column in 0..=2 { - for index_row in 0..n { - result.push(w.pow(index_row) * u.pow(index_column as u64)); - } - } - result -} - -/// A mock of a random number generator, to have deterministic tests. -/// When set to zero, there is no zero knowledge applied, because it is used -/// to get random numbers to blind polynomials. -#[derive(Clone)] -pub struct TestRandomFieldGenerator; -impl IsRandomFieldElementGenerator for TestRandomFieldGenerator { - fn generate(&self) -> FrElement { - FrElement::zero() - } -} diff --git a/crates/provers/plonk/src/verifier.rs b/crates/provers/plonk/src/verifier.rs deleted file mode 100644 index cdf9c26ba..000000000 --- a/crates/provers/plonk/src/verifier.rs +++ /dev/null @@ -1,426 +0,0 @@ -use lambdaworks_crypto::commitments::traits::IsCommitmentScheme; -use lambdaworks_crypto::fiat_shamir::is_transcript::IsTranscript; -use lambdaworks_math::cyclic_group::IsGroup; -use lambdaworks_math::field::element::FieldElement; -use lambdaworks_math::field::traits::{HasDefaultTranscript, IsFFTField, IsField, IsPrimeField}; -use lambdaworks_math::traits::{AsBytes, ByteConversion}; -use std::marker::PhantomData; - -use crate::prover::Proof; -use crate::setup::{new_strong_fiat_shamir_transcript, CommonPreprocessedInput, VerificationKey}; - -pub struct Verifier> { - commitment_scheme: CS, - phantom: PhantomData, -} - -impl> Verifier { - pub fn new(commitment_scheme: CS) -> Self { - Self { - commitment_scheme, - phantom: PhantomData, - } - } - - fn compute_challenges( - &self, - p: &Proof, - vk: &VerificationKey, - public_input: &[FieldElement], - ) -> [FieldElement; 5] - where - F: IsField, - CS: IsCommitmentScheme, - CS::Commitment: AsBytes, - FieldElement: ByteConversion, - { - let mut transcript = new_strong_fiat_shamir_transcript::(vk, public_input); - - transcript.append_bytes(&p.a_1.as_bytes()); - transcript.append_bytes(&p.b_1.as_bytes()); - transcript.append_bytes(&p.c_1.as_bytes()); - let beta = transcript.sample_field_element(); - let gamma = transcript.sample_field_element(); - - transcript.append_bytes(&p.z_1.as_bytes()); - let alpha = transcript.sample_field_element(); - - transcript.append_bytes(&p.t_lo_1.as_bytes()); - transcript.append_bytes(&p.t_mid_1.as_bytes()); - transcript.append_bytes(&p.t_hi_1.as_bytes()); - let zeta = transcript.sample_field_element(); - - transcript.append_field_element(&p.a_zeta); - transcript.append_field_element(&p.b_zeta); - transcript.append_field_element(&p.c_zeta); - transcript.append_field_element(&p.s1_zeta); - transcript.append_field_element(&p.s2_zeta); - transcript.append_field_element(&p.z_zeta_omega); - let upsilon = transcript.sample_field_element(); - - [beta, gamma, alpha, zeta, upsilon] - } - - pub fn verify( - &self, - p: &Proof, - public_input: &[FieldElement], - input: &CommonPreprocessedInput, - vk: &VerificationKey, - ) -> bool - where - F: IsPrimeField + IsFFTField, - CS: IsCommitmentScheme, - CS::Commitment: AsBytes + IsGroup, - FieldElement: ByteConversion, - { - // TODO: First three steps are validations: belonging to main subgroup, belonging to prime field. - let [beta, gamma, alpha, zeta, upsilon] = self.compute_challenges(p, vk, public_input); - let zh_zeta = zeta.pow(input.n) - FieldElement::::one(); - - let k1 = &input.k1; - let k2 = k1 * k1; - - // We are using that zeta != 0 because is sampled outside the set of roots of unity, - // and n != 0 because is the length of the trace. - let l1_zeta = ((zeta.pow(input.n as u64) - FieldElement::::one()) - / ((&zeta - FieldElement::::one()) * FieldElement::from(input.n as u64))) - .unwrap(); - - // Use the following equality to compute PI(ζ) - // without interpolating: - // Lᵢ₊₁ = ω Lᵢ (X − ωⁱ) / (X − ωⁱ⁺¹) - // Here Lᵢ is the i-th polynomial of the Lagrange basis. - let p_pi_zeta = if public_input.is_empty() { - FieldElement::zero() - } else { - let mut p_pi_zeta = &l1_zeta * &public_input[0]; - let mut li_zeta = l1_zeta.clone(); - for (i, value) in public_input.iter().enumerate().skip(1) { - li_zeta = &input.omega - * &li_zeta - // We are using that zeta is sampled outside the domain. - * ((&zeta - &input.domain[i - 1]) / (&zeta - &input.domain[i])).unwrap(); - p_pi_zeta = &p_pi_zeta + value * &li_zeta; - } - p_pi_zeta - }; - - let mut p_constant_zeta = &alpha - * &p.z_zeta_omega - * (&p.c_zeta + &gamma) - * (&p.a_zeta + &beta * &p.s1_zeta + &gamma) - * (&p.b_zeta + &beta * &p.s2_zeta + &gamma); - p_constant_zeta = p_constant_zeta - &l1_zeta * &alpha * α - p_constant_zeta += p_pi_zeta; - - let p_zeta = p_constant_zeta + &p.p_non_constant_zeta; - - let constraints_check = p_zeta - (&zh_zeta * &p.t_zeta) == FieldElement::zero(); - - // Compute commitment of partial evaluation of t (p = zh * t) - let partial_t_1 = p - .t_lo_1 - .operate_with( - &p.t_mid_1 - .operate_with_self(zeta.pow(input.n + 2).representative()), - ) - .operate_with( - &p.t_hi_1 - .operate_with_self(zeta.pow(2 * input.n + 4).representative()), - ); - - // Compute commitment of the non constant part of the linearization of p - // The first term corresponds to the gates constraints - let mut first_term = vk - .qm_1 - .operate_with_self((&p.a_zeta * &p.b_zeta).representative()); - first_term = first_term.operate_with(&vk.ql_1.operate_with_self(p.a_zeta.representative())); - first_term = first_term.operate_with(&vk.qr_1.operate_with_self(p.b_zeta.representative())); - first_term = first_term.operate_with(&vk.qo_1.operate_with_self(p.c_zeta.representative())); - first_term = first_term.operate_with(&vk.qc_1); - - // Second and third terms correspond to copy constraints - // + α*((l(ζ)+β*s1(ζ)+γ)*(r(ζ)+β*s2(ζ)+γ)*Z(μζ)*β*s3(X) - Z(X)*(l(ζ)+β*id1(ζ)+γ)*(r(ζ)+β*id2(ζ)+γ)*(o(ζ)+β*id3(ζ)+γ)) - let z_coefficient = -(&p.a_zeta + &beta * &zeta + &gamma) - * (&p.b_zeta + &beta * k1 * &zeta + &gamma) - * (&p.c_zeta + &beta * k2 * &zeta + &gamma); - let s3_coefficient = (&p.a_zeta + &beta * &p.s1_zeta + &gamma) - * (&p.b_zeta + &beta * &p.s2_zeta + &gamma) - * beta - * &p.z_zeta_omega; - let second_term = p - .z_1 - .operate_with_self(z_coefficient.representative()) - .operate_with(&vk.s3_1.operate_with_self(s3_coefficient.representative())) - .operate_with_self(alpha.representative()); - // α²*L₁(ζ)*Z(X) - let third_term = p - .z_1 - .operate_with_self((&alpha * &alpha * l1_zeta).representative()); - - let p_non_constant_1 = first_term - .operate_with(&second_term) - .operate_with(&third_term); - - let ys = [ - p.t_zeta.clone(), - p.p_non_constant_zeta.clone(), - p.a_zeta.clone(), - p.b_zeta.clone(), - p.c_zeta.clone(), - p.s1_zeta.clone(), - p.s2_zeta.clone(), - ]; - let commitments = [ - partial_t_1, - p_non_constant_1, - p.a_1.clone(), - p.b_1.clone(), - p.c_1.clone(), - vk.s1_1.clone(), - vk.s2_1.clone(), - ]; - let batch_openings_check = - self.commitment_scheme - .verify_batch(&zeta, &ys, &commitments, &p.w_zeta_1, &upsilon); - - let single_opening_check = self.commitment_scheme.verify( - &(zeta * &input.omega), - &p.z_zeta_omega, - &p.z_1, - &p.w_zeta_omega_1, - ); - - constraints_check && batch_openings_check && single_opening_check - } -} - -#[cfg(test)] -mod tests { - use lambdaworks_math::traits::Deserializable; - - use super::*; - - use crate::{ - prover::Prover, - setup::setup, - test_utils::circuit_1::{test_common_preprocessed_input_1, test_witness_1}, - test_utils::circuit_2::{test_common_preprocessed_input_2, test_witness_2}, - test_utils::circuit_json::common_preprocessed_input_from_json, - test_utils::utils::{test_srs, TestRandomFieldGenerator, KZG}, - }; - - #[test] - fn test_happy_path_for_circuit_1() { - // This is the circuit for x * e == y - let common_preprocessed_input = test_common_preprocessed_input_1(); - let srs = test_srs(common_preprocessed_input.n); - - // Public input - let x = FieldElement::from(4_u64); - let y = FieldElement::from(12_u64); - - // Private variable - let e = FieldElement::from(3_u64); - - let public_input = vec![x.clone(), y]; - let witness = test_witness_1(x, e); - - let kzg = KZG::new(srs); - let verifying_key = setup(&common_preprocessed_input, &kzg); - let random_generator = TestRandomFieldGenerator {}; - - let prover = Prover::new(kzg.clone(), random_generator); - let proof = prover.prove( - &witness, - &public_input, - &common_preprocessed_input, - &verifying_key, - ); - - let verifier = Verifier::new(kzg); - assert!(verifier.verify( - &proof, - &public_input, - &common_preprocessed_input, - &verifying_key - )); - } - - #[test] - fn test_happy_path_for_circuit_2() { - // This is the circuit for x * e + 5 == y - let common_preprocessed_input = test_common_preprocessed_input_2(); - let srs = test_srs(common_preprocessed_input.n); - - // Public input - let x = FieldElement::from(2_u64); - let y = FieldElement::from(11_u64); - - // Private variable - let e = FieldElement::from(3_u64); - - let public_input = vec![x.clone(), y]; - let witness = test_witness_2(x, e); - - let kzg = KZG::new(srs); - let verifying_key = setup(&common_preprocessed_input, &kzg); - let random_generator = TestRandomFieldGenerator {}; - - let prover = Prover::new(kzg.clone(), random_generator); - let proof = prover.prove( - &witness, - &public_input, - &common_preprocessed_input, - &verifying_key, - ); - - let verifier = Verifier::new(kzg); - assert!(verifier.verify( - &proof, - &public_input, - &common_preprocessed_input, - &verifying_key - )); - } - - #[test] - fn test_happy_path_from_json() { - let (witness, common_preprocessed_input, public_input) = - common_preprocessed_input_from_json( - r#"{ - "N": 4, - "N_Padded": 4, - "Omega": "8d51ccce760304d0ec030002760300000001000000000000", - "Input": [ - "2", - "4" - ], - "Ql": [ - "73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000000", - "73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000000", - "0", - "1" - ], - "Qr": [ - "0", - "0", - "0", - "73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000000" - ], - "Qm": [ - "0", - "0", - "1", - "0" - ], - "Qo": [ - "0", - "0", - "73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000000", - "0" - ], - "Qc": [ - "0", - "0", - "0", - "0" - ], - "A": [ - "2", - "4", - "2", - "4" - ], - "B": [ - "2", - "2", - "2", - "4" - ], - "C": [ - "2", - "2", - "4", - "2" - ], - "Permutation": [ - 11, - 3, - 2, - 1, - 0, - 4, - 5, - 10, - 6, - 8, - 7, - 9 - ] - }"#, - ); - let srs = test_srs(common_preprocessed_input.n); - - let kzg = KZG::new(srs); - let verifying_key = setup(&common_preprocessed_input, &kzg); - let random_generator = TestRandomFieldGenerator {}; - - let prover = Prover::new(kzg.clone(), random_generator); - let proof = prover.prove( - &witness, - &public_input, - &common_preprocessed_input, - &verifying_key, - ); - - let verifier = Verifier::new(kzg); - assert!(verifier.verify( - &proof, - &public_input, - &common_preprocessed_input, - &verifying_key - )); - } - - #[test] - fn test_serialize_proof() { - // This is the circuit for x * e == y - let common_preprocessed_input = test_common_preprocessed_input_1(); - let srs = test_srs(common_preprocessed_input.n); - - // Public input - let x = FieldElement::from(4_u64); - let y = FieldElement::from(12_u64); - - // Private variable - let e = FieldElement::from(3_u64); - - let public_input = vec![x.clone(), y]; - let witness = test_witness_1(x, e); - - let kzg = KZG::new(srs); - let verifying_key = setup(&common_preprocessed_input, &kzg); - let random_generator = TestRandomFieldGenerator {}; - - let prover = Prover::new(kzg.clone(), random_generator); - let proof = prover.prove( - &witness, - &public_input, - &common_preprocessed_input, - &verifying_key, - ); - - let serialized_proof = proof.as_bytes(); - let deserialized_proof = Proof::deserialize(&serialized_proof).unwrap(); - - let verifier = Verifier::new(kzg); - assert!(verifier.verify( - &deserialized_proof, - &public_input, - &common_preprocessed_input, - &verifying_key - )); - } -} diff --git a/crates/provers/stark/Cargo.toml b/crates/provers/stark/Cargo.toml deleted file mode 100644 index e84fffeb3..000000000 --- a/crates/provers/stark/Cargo.toml +++ /dev/null @@ -1,98 +0,0 @@ -[package] -name = "stark-platinum-prover" -rust-version = "1.66" -version.workspace = true -edition.workspace = true -license.workspace = true - -[lib] -crate-type = ["cdylib", "rlib"] - -[dependencies] -lambdaworks-math = { workspace = true, features = [ - "std", - "lambdaworks-serde-binary", -] } -lambdaworks-crypto = { workspace = true, features = ["std", "serde"] } - -miden-core = { git = "https://github.com/lambdaclass/miden-vm", optional = true } - -rand = "0.8.5" -thiserror = "1.0.38" -log = "0.4.17" -bincode = { version = "2.0.1", features = ["serde"] } -sha3 = "0.10.6" -serde = { version = "1.0", features = ["derive"] } -serde_json = "1.0" -num-integer = "0.1.45" -itertools = "0.11.0" - -# Parallelization crates -rayon = { version = "1.8.0", optional = true } - -# wasm -wasm-bindgen = { version = "0.2", optional = true } -serde-wasm-bindgen = { version = "0.5", optional = true } -web-sys = { version = "0.3.64", features = ['console'], optional = true } -serde_cbor = { version = "0.11.1" } - -[dev-dependencies] -hex = "0.4.3" -criterion = { version = "0.4", default-features = false } -env_logger = "*" -test-log = { version = "0.2.11", features = ["log"] } -assert_matches = "1.5.0" -rstest = "0.17.0" -rand = "0.8.5" -wasm-bindgen-test = "0.3.0" - -[features] -test_fiat_shamir = [] -instruments = [] # This enables timing prints in prover and verifier -parallel = ["dep:rayon", "lambdaworks-crypto/parallel"] -wasm = ["dep:wasm-bindgen", "dep:serde-wasm-bindgen", "dep:web-sys"] -winter_compatibility = ["miden-core"] - -[target.'cfg(not(all(target_arch = "wasm32", target_os = "unknown")))'.dev-dependencies] -proptest = "1.2.0" - -[package.metadata.wasm-pack.profile.dev] -# Should `wasm-opt` be used to further optimize the wasm binary generated after -# the Rust compiler has finished? Using `wasm-opt` can often further decrease -# binary size or do clever tricks that haven't made their way into LLVM yet. -# -# Configuration is set to `false` by default for the dev profile, but it can -# be set to an array of strings which are explicit arguments to pass to -# `wasm-opt`. For example `['-Os']` would optimize for size while `['-O4']` -# would execute very expensive optimizations passes -wasm-opt = ['-O'] - -[package.metadata.wasm-pack.profile.dev.wasm-bindgen] -# Should we enable wasm-bindgen's debug assertions in its generated JS glue? -debug-js-glue = true -# Should wasm-bindgen demangle the symbols in the "name" custom section -demangle-name-section = true -# Should we emit the DWARF debug info custom sections -dwarf-debug-info = false -# Should we omit the default import path -omit-default-module-path = false - -[package.metadata.wasm-pack.profile.profiling] -wasm-opt = ['-O'] - -[package.metadata.wasm-pack.profile.profiling.wasm-bindgen] -debug-js-glue = false -demangle-name-section = true -dwarf-debug-info = false -omit-default-module-path = false - -# `wasm-opt` is on by default in for the release profile, but it can be -# disabled by setting it to `false` -[package.metadata.wasm-pack.profile.release] -wasm-opt = false - -[package.metadata.wasm-pack.profile.release.wasm-bindgen] -debug-js-glue = false -demangle-name-section = true -dwarf-debug-info = false -omit-default-module-path = false diff --git a/crates/provers/stark/README.md b/crates/provers/stark/README.md deleted file mode 100644 index 6de0564a2..000000000 --- a/crates/provers/stark/README.md +++ /dev/null @@ -1,391 +0,0 @@ -

- -# 🌟 Lambdaworks Stark Platinum Prover 🌟 - -drawing - -## An open-source STARK prover, drop-in replacement for Winterfell. - -
- -[![Telegram Chat][tg-badge]][tg-url] - -[tg-badge]: https://img.shields.io/static/v1?color=green&logo=telegram&label=chat&style=flat&message=join -[tg-url]: https://t.me/+98Whlzql7Hs0MDZh - -## ⚠️ Disclaimer - -This prover is still in development and may contain bugs. It is not intended to be used in production yet. - -## Description - -This is a [STARK prover and verifier](https://eprint.iacr.org/2018/046), which is a transparent (no trusted setup) and post-quantum secure argument of knowledge. The main ingredients are: -- [Hash functions](../../crypto/src/hash/README.md) -- [Fiat-Shamir transformation](../../crypto/src/fiat_shamir/README.md) -- [Finite fields](../../math/src/field/README.md) -- [Univariate polynomials](../../math/src/polynomial/README.md) -- [Reed-Solomon codes](https://en.wikipedia.org/wiki/Reed%E2%80%93Solomon_error_correction) - -The security of STARKs depends on collision-resistant hash functions. The security level depends on the number of queries and the size of the underlying field. The prover works either with: -- Finite fields of prime order, where the size of the field should be at least 128 bits. -- Field extensions, where the size of the extension should be at least 128 bits. - -The field (or base field $\mathbb{F}_p$ in case of extensions $\mathbb{F}_{p^k}$) has to implement the trait `IsFFTField`, ensuring we can use the [FFT algorithm](../../math/src/fft/README.md) (which is crucial for efficiency). Some fields implementing this trait are: -- [STARK-252](../../math/src/field/fields/fft_friendly/stark_252_prime_field.rs) -- [Baby-Bear](../../math/src/field/fields/fft_friendly/babybear_u32.rs) with its [quartic degree extension](../../math/src/field/fields/fft_friendly/quartic_babybear_u32.rs) - -To prove a statement, we will need a description of it, in the form of an Algebraic Intermediate Representation (AIR). This consists of: -- One or more tables (trace and auxiliary trace) -- A set of polynomial equations that have to be enforced on the trace (constraints) - -## [Documentation](https://lambdaclass.github.io/lambdaworks/starks/cairo.html) - -## Examples - -You can take a look at the examples for [read-only memory](https://blog.lambdaclass.com/continuous-read-only-memory-constraints-an-implementation-using-lambdaworks/) and [logUp](https://blog.lambdaclass.com/logup-lookup-argument-and-its-implementation-using-lambdaworks-for-continuous-read-only-memory/). - -The examples are [here](./src/examples/) and you can take a look at [integration tests](./src/tests/integration_tests.rs). - -The following code summarizes the procedure to generate and verify a STARK proof that attests to the validity of the computation of the 8th [Fibonacci number](https://en.wikipedia.org/wiki/Fibonacci_sequence). -```rust - let mut trace = simple_fibonacci::fibonacci_trace([Felt252::from(1), Felt252::from(1)], 8); - - let proof_options = ProofOptions::default_test_options(); - - let pub_inputs = FibonacciPublicInputs { - a0: Felt252::one(), - a1: Felt252::one(), - }; - - let proof = Prover::>::prove( - &mut trace, - &pub_inputs, - &proof_options, - StoneProverTranscript::new(&[]), - ) - .unwrap(); - assert!(Verifier::>::verify( - &proof, - &pub_inputs, - &proof_options, - StoneProverTranscript::new(&[]), - )); -``` -Here we want to compute the following program: -$a_0 = 1$ -$a_1 = 1$ -$a_{n + 2} = a_{n + 1} + a_n$ for $n \in \{ 0, 1, ... , 6 \}$ -We are going to organize the numbers in one single column. The program is valid if and only if: -- $a_0 - 1 = 0$ -- $a_1 - 1 = 0$ -- $a_{n + 2} - a_{n + 1} - a_n = 0$ - -The first step -```rust -let mut trace = simple_fibonacci::fibonacci_trace([Felt252::from(1), Felt252::from(1)], 8); -``` -generates the table containing the first 8 Fibonacci numbers. This is what the function does under the hood: -```rust -pub fn fibonacci_trace( - initial_values: [FieldElement; 2], - trace_length: usize, -) -> TraceTable { - let mut ret: Vec> = vec![]; - - ret.push(initial_values[0].clone()); - ret.push(initial_values[1].clone()); - - for i in 2..(trace_length) { - ret.push(ret[i - 1].clone() + ret[i - 2].clone()); - } - - TraceTable::from_columns_main(vec![ret], 1) -} -``` -Notice the field `F` implements the trait `IsFFTField`. Then we set the proof options, -```rust -let proof_options = ProofOptions::default_test_options(); -``` -Here we set the options for the test to the default. This determine the blow up factor (i.e. the inverse of the rate of the code) number of queries, whether the prover needs to solve a proof of work before sampling queries (grinding) and the coset offset (a field element which should not be in the domain used to interpolate). Since this is a test, the security level is too low, but the user can use other options that ensure the right security level (for example, 128 bits). Except for the offset, the rest influence the speed of the prover, the verification time and the proof size. - -We now create the public inputs for the program. -```rust -let pub_inputs = FibonacciPublicInputs { - a0: Felt252::one(), - a1: Felt252::one(), - }; -``` -These values are known by prover and verifier. They have to be enforced on the trace and are incorporated into the transcript (otherwise you have a weak version of Fiat-Shamir that can be exploited). So far, we have a table (trace) containing all the intermediate steps of the program and the public inputs (here we only care about the initial values, but we could also include the output of the program). Values in the trace which are not public inputs are witness values. Our program will prove that we computed correctly the 8th Fibonacci number, without revealing it (if we wanted to show the number, it must be included among the public input). - -To generate the proof, we simply call -```rust - let proof = Prover::>::prove( - &mut trace, - &pub_inputs, - &proof_options, - StoneProverTranscript::new(&[]), - ) - .unwrap(); -``` -The prover has `FibonacciAIR` which defines the AIR over the STARK-252 finite field. A closer look shows that -```rust -pub struct FibonacciAIR -where - F: IsFFTField, -{ - context: AirContext, - trace_length: usize, - pub_inputs: FibonacciPublicInputs, - constraints: Vec>>, -} -``` -The AIR contains an `AirContext`, the length of the table (if we wanted to prove that we calculated correctly the 16th Fibonacci number, this should change), the public input and a vector of constraints `constraints: Vec>>`. The public input is -```rust -pub struct FibonacciPublicInputs -where - F: IsFFTField, -{ - pub a0: FieldElement, - pub a1: FieldElement, -} -``` -The following code implements the AIR trait for `FibonacciAIR` -```rust -impl AIR for FibonacciAIR -where - F: IsFFTField + Send + Sync + 'static, -{ - type Field = F; - type FieldExtension = F; - type PublicInputs = FibonacciPublicInputs; - - const STEP_SIZE: usize = 1; - - fn new( - trace_length: usize, - pub_inputs: &Self::PublicInputs, - proof_options: &ProofOptions, - ) -> Self { - let constraints: Vec>> = - vec![Box::new(FibConstraint::new())]; - - let context = AirContext { - proof_options: proof_options.clone(), - trace_columns: 1, - transition_offsets: vec![0, 1, 2], - num_transition_constraints: constraints.len(), - }; - - Self { - pub_inputs: pub_inputs.clone(), - context, - trace_length, - constraints, - } - } - - fn composition_poly_degree_bound(&self) -> usize { - self.trace_length() - } - - fn transition_constraints(&self) -> &Vec>> { - &self.constraints - } - - fn boundary_constraints( - &self, - _rap_challenges: &[FieldElement], - ) -> BoundaryConstraints { - let a0 = BoundaryConstraint::new_simple_main(0, self.pub_inputs.a0.clone()); - let a1 = BoundaryConstraint::new_simple_main(1, self.pub_inputs.a1.clone()); - - BoundaryConstraints::from_constraints(vec![a0, a1]) - } - - fn context(&self) -> &AirContext { - &self.context - } - - fn trace_length(&self) -> usize { - self.trace_length - } - - fn trace_layout(&self) -> (usize, usize) { - (1, 0) - } - - fn pub_inputs(&self) -> &Self::PublicInputs { - &self.pub_inputs - } -} -``` -When defining the AIR, we need to define constraints (either consistency, type or transition constraints) and boundary constraints (these are defined separately, which enforce that a particular public value holds in a specific place of the table). The context contains useful data related to proof options, number of transition constraints, number of columns in the trace and offsets (these are needed when we want to evaluate constraints). - -We also have some auxiliary methods that give the bound of the composition polynomial (`composition_poly_degree_bound`), trace layout (`trace_layout`), public input (`pub_inputs`), trace length (`trace_length`) and AIR context (`context`). Below we show the definition of the transition constraint for Fibonacci, which encodes $a_{n + 2} - a_{n + 1} - a_n = 0$ -```rust -#[derive(Clone)] -struct FibConstraint { - phantom: PhantomData, -} - -impl FibConstraint { - pub fn new() -> Self { - Self { - phantom: PhantomData, - } - } -} - -impl TransitionConstraint for FibConstraint -where - F: IsFFTField + Send + Sync, -{ - fn degree(&self) -> usize { - 1 - } - - fn constraint_idx(&self) -> usize { - 0 - } - - fn end_exemptions(&self) -> usize { - 2 - } - - fn evaluate( - &self, - evaluation_context: &TransitionEvaluationContext, - transition_evaluations: &mut [FieldElement], - ) { - let (frame, _periodic_values, _rap_challenges) = match evaluation_context { - TransitionEvaluationContext::Prover { - frame, - periodic_values, - rap_challenges, - } - | TransitionEvaluationContext::Verifier { - frame, - periodic_values, - rap_challenges, - } => (frame, periodic_values, rap_challenges), - }; - - let first_step = frame.get_evaluation_step(0); - let second_step = frame.get_evaluation_step(1); - let third_step = frame.get_evaluation_step(2); - - let a0 = first_step.get_main_evaluation_element(0, 0); - let a1 = second_step.get_main_evaluation_element(0, 0); - let a2 = third_step.get_main_evaluation_element(0, 0); - - let res = a2 - a1 - a0; - - transition_evaluations[self.constraint_idx()] = res; - } -} -``` -The most important method is `evaluate`: -```rust - let res = a2 - a1 - a0; - transition_evaluations[self.constraint_idx()] = res; -``` -The function will take three consecutive values of the (low-degree extension of) trace and compute the relation defining the Fibonacci sequence. These values will be later used to reconstruct a polynomial enforcing the constraints. Some important parameters of transition constraints are: -- `degree`: indicates the highest degree in the algebraic expression defining the constraint. For Fibonacci ($a_2 - a_1 - a_0$) is linear, so degree is 1, but if the constraint were $a_2*a_1^2 - a_0^2$, it would have been 3. -- `idx`: only relevant when there are multiple constraints, it assigns a number allowing to identify the constraint more easily. -- `end_exemptions`: indicates where the constraint is enforced. If `0`, then it is valid over the whole length of the trace; `1` means that it is valid over all the trace except the last value, `2` that is valid everywhere except the last two values and so on. This is relevant to compute the zerofiers/vanishing polynomials efficiently. - -The boundary constraints are defined as follows: -```rust -fn boundary_constraints( - &self, - _rap_challenges: &[FieldElement], - ) -> BoundaryConstraints { - let a0 = BoundaryConstraint::new_simple_main(0, self.pub_inputs.a0.clone()); - let a1 = BoundaryConstraint::new_simple_main(1, self.pub_inputs.a1.clone()); - - BoundaryConstraints::from_constraints(vec![a0, a1]) - } -``` -To understand how this works, we will look at -```rust -let a1 = BoundaryConstraint::new_simple_main(1, self.pub_inputs.a1.clone()); -``` -This creates a new boundary constraint that is applied to the second row (row with index 1) and takes the value `self.pub_inputs.a1`. With all this information, we can run the prover and generate the proof. - -Once we have the proof, we can check it using the verifier: -```rust -let result = Verifier::>::verify( - &proof, - &pub_inputs, - &proof_options, - StoneProverTranscript::new(&[]), - ); -``` - -## To test compatibility with stone prover - -Fetch the submodule with the Stone fork compatibility demo with: - -```git submodule update --init --recursive``` - -You can then cd to the downloaded Stone Prover, and follow the README instructions to make a proof with Platinum and verify it with Stone - -```cd ../stone-demo``` - -## To be added - -- Winterfell api compatibility -- Add more parallelization -- Optimizations - - Skip layers - - Stop FRI - - Others -- Optimized backend for mini goldilocks -- Pick hash configuration with ProofOptions -- Support FFTx for CUDA -- Tracing tools -- Virtual columns - -## Requirements - -- Cargo 1.69+ - -## 📚 References - -The following links, repos and projects have been important in the development of this library and we want to thank and acknowledge them. - -- [Starkware](https://starkware.co/) -- [Winterfell](https://github.com/facebook/winterfell) -- [Anatomy of a Stark](https://aszepieniec.github.io/stark-anatomy/overview) -- [Giza](https://github.com/maxgillett/giza) -- [Ministark](https://github.com/andrewmilson/ministark) -- [Sandstorm](https://github.com/andrewmilson/sandstorm) -- [STARK-101](https://starkware.co/stark-101/) -- [Risc0](https://github.com/risc0/risc0) -- [Neptune](https://github.com/Neptune-Crypto) -- [Summary on FRI low degree test](https://eprint.iacr.org/2022/1216) -- [STARKs paper](https://eprint.iacr.org/2018/046) -- [DEEP FRI](https://eprint.iacr.org/2019/336) -- [BrainSTARK](https://aszepieniec.github.io/stark-brainfuck/) -- [Plonky2](https://github.com/mir-protocol/plonky2) -- [Aztec](https://github.com/AztecProtocol) -- [Arkworks](https://github.com/arkworks-rs) -- [Thank goodness it's FRIday](https://vitalik.ca/general/2017/11/22/starks_part_2.html) -- [Diving DEEP FRI](https://blog.lambdaclass.com/diving-deep-fri/) -- [Periodic constraints](https://blog.lambdaclass.com/periodic-constraints-and-recursion-in-zk-starks/) -- [Chiplets Miden VM](https://wiki.polygon.technology/docs/miden/design/chiplets/main/) -- [Valida](https://github.com/valida-xyz/valida/tree/main) -- [Solidity Verifier](https://github.com/starkware-libs/starkex-contracts/tree/master/evm-verifier/solidity/contracts/cpu) -- [CAIRO verifier](https://github.com/starkware-libs/cairo-lang/tree/master/src/starkware/cairo/stark_verifier) -- [EthSTARK](https://github.com/starkware-libs/ethSTARK/tree/master) -- [CAIRO whitepaper](https://eprint.iacr.org/2021/1063.pdf) -- [Gnark](https://github.com/Consensys/gnark) - -## 🌞 Related Projects - -- [CAIRO VM - Rust](https://github.com/lambdaclass/cairo-vm) -- [CAIRO VM - Go](https://github.com/lambdaclass/cairo_vm.go) -- [Lambdaworks](https://github.com/lambdaclass/lambdaworks) -- [CAIRO native](https://github.com/lambdaclass/cairo_native/) -- [StarkNet in Rust](https://github.com/lambdaclass/starknet_in_rust) -- [StarkNet Stack](https://github.com/lambdaclass/starknet_stack) diff --git a/crates/provers/stark/src/config.rs b/crates/provers/stark/src/config.rs deleted file mode 100644 index 67cb43989..000000000 --- a/crates/provers/stark/src/config.rs +++ /dev/null @@ -1,20 +0,0 @@ -use lambdaworks_crypto::merkle_tree::{ - backends::types::{BatchKeccak256Backend, Keccak256Backend}, - merkle::MerkleTree, -}; - -// Merkle Trees configuration - -// Security of both hashes should match - -pub type FriMerkleTreeBackend = Keccak256Backend; -pub type FriMerkleTree = MerkleTree>; - -// If using hashes with 256-bit security, commitment size should be 32 -// If using hashes with 512-bit security, commitment size should be 64 -// TODO: Commitment type should be obtained from MerkleTrees -pub const COMMITMENT_SIZE: usize = 32; -pub type Commitment = [u8; COMMITMENT_SIZE]; - -pub type BatchedMerkleTreeBackend = BatchKeccak256Backend; -pub type BatchedMerkleTree = MerkleTree>; diff --git a/crates/provers/stark/src/constraints/boundary.rs b/crates/provers/stark/src/constraints/boundary.rs deleted file mode 100644 index 50a4bbb98..000000000 --- a/crates/provers/stark/src/constraints/boundary.rs +++ /dev/null @@ -1,200 +0,0 @@ -use itertools::Itertools; -use lambdaworks_math::{ - field::{element::FieldElement, traits::IsField}, - polynomial::Polynomial, -}; - -#[derive(Debug)] -/// Represents a boundary constraint that must hold in an execution -/// trace: -/// * col: The column of the trace where the constraint must hold -/// * step: The step (or row) of the trace where the constraint must hold -/// * value: The value the constraint must have in that column and step -pub struct BoundaryConstraint { - pub col: usize, - pub step: usize, - pub value: FieldElement, - pub is_aux: bool, -} - -impl BoundaryConstraint { - pub fn new_main(col: usize, step: usize, value: FieldElement) -> Self { - Self { - col, - step, - value, - is_aux: false, - } - } - - pub fn new_aux(col: usize, step: usize, value: FieldElement) -> Self { - Self { - col, - step, - value, - is_aux: true, - } - } - - /// Used for creating boundary constraints for a trace with only one column - pub fn new_simple_main(step: usize, value: FieldElement) -> Self { - Self { - col: 0, - step, - value, - is_aux: false, - } - } - - /// Used for creating boundary constraints for a trace with only one column - pub fn new_simple_aux(step: usize, value: FieldElement) -> Self { - Self { - col: 0, - step, - value, - is_aux: true, - } - } -} - -/// Data structure that stores all the boundary constraints that must -/// hold for the execution trace -#[derive(Default, Debug)] -pub struct BoundaryConstraints { - pub constraints: Vec>, -} - -impl BoundaryConstraints { - #[allow(dead_code)] - pub fn new() -> Self { - Self { - constraints: Vec::>::new(), - } - } - - /// To instantiate from a vector of BoundaryConstraint elements - pub fn from_constraints(constraints: Vec>) -> Self { - Self { constraints } - } - - /// Returns all the steps where boundary conditions exist for the given column - pub fn steps(&self, col: usize) -> Vec { - self.constraints - .iter() - .filter(|v| v.col == col) - .map(|c| c.step) - .collect() - } - - pub fn steps_for_boundary(&self) -> Vec { - self.constraints - .iter() - .unique_by(|elem| elem.step) - .map(|v| v.step) - .collect() - } - - pub fn cols_for_boundary(&self) -> Vec { - self.constraints - .iter() - .unique_by(|elem| elem.col) - .map(|v| v.col) - .collect() - } - - /// Given the primitive root of some domain, returns the domain values corresponding - /// to the steps where the boundary conditions hold. This is useful when interpolating - /// the boundary conditions, since we must know the x values - pub fn generate_roots_of_unity( - &self, - primitive_root: &FieldElement, - cols_trace: &[usize], - ) -> Vec>> { - cols_trace - .iter() - .map(|i| { - self.steps(*i) - .into_iter() - .map(|s| primitive_root.pow(s)) - .collect::>>() - }) - .collect::>>>() - } - - /// For every trace column, give all the values the trace must be equal to in - /// the steps where the boundary constraints hold - pub fn values(&self, cols_trace: &[usize]) -> Vec>> { - cols_trace - .iter() - .map(|i| { - self.constraints - .iter() - .filter(|c| c.col == *i) - .map(|c| c.value.clone()) - .collect() - }) - .collect() - } - - /// Computes the zerofier of the boundary quotient. The result is the - /// multiplication of each binomial that evaluates to zero in the domain - /// values where the boundary constraints must hold. - /// - /// Example: If there are boundary conditions in the third and fifth steps, - /// then the zerofier will be (x - w^3) * (x - w^5) - pub fn compute_zerofier( - &self, - primitive_root: &FieldElement, - col: usize, - ) -> Polynomial> { - self.steps(col).into_iter().fold( - Polynomial::new_monomial(FieldElement::::one(), 0), - |zerofier, step| { - let binomial = - Polynomial::new(&[-primitive_root.pow(step), FieldElement::::one()]); - // TODO: Implement the MulAssign trait for Polynomials? - zerofier * binomial - }, - ) - } -} - -#[cfg(test)] -mod test { - use lambdaworks_math::field::{ - fields::fft_friendly::stark_252_prime_field::Stark252PrimeField, traits::IsFFTField, - }; - type PrimeField = Stark252PrimeField; - - use super::*; - - #[test] - fn zerofier_is_the_correct_one() { - let one = FieldElement::::one(); - - // Fibonacci constraints: - // * a0 = 1 - // * a1 = 1 - // * a7 = 32 - let a0 = BoundaryConstraint::new_simple_main(0, one); - let a1 = BoundaryConstraint::new_simple_main(1, one); - let result = BoundaryConstraint::new_simple_main(7, FieldElement::::from(32)); - - let constraints = BoundaryConstraints::from_constraints(vec![a0, a1, result]); - - let primitive_root = PrimeField::get_primitive_root_of_unity(3).unwrap(); - - // P_0(x) = (x - 1) - let a0_zerofier = Polynomial::new(&[-one, one]); - // P_1(x) = (x - w^1) - let a1_zerofier = Polynomial::new(&[-primitive_root.pow(1u32), one]); - // P_res(x) = (x - w^7) - let res_zerofier = Polynomial::new(&[-primitive_root.pow(7u32), one]); - - let expected_zerofier = a0_zerofier * a1_zerofier * res_zerofier; - - let zerofier = constraints.compute_zerofier(&primitive_root, 0); - - assert_eq!(expected_zerofier, zerofier); - } -} diff --git a/crates/provers/stark/src/constraints/evaluator.rs b/crates/provers/stark/src/constraints/evaluator.rs deleted file mode 100644 index 8a4423187..000000000 --- a/crates/provers/stark/src/constraints/evaluator.rs +++ /dev/null @@ -1,226 +0,0 @@ -use super::boundary::BoundaryConstraints; -#[cfg(all(debug_assertions, not(feature = "parallel")))] -use crate::debug::check_boundary_polys_divisibility; -use crate::domain::Domain; -use crate::trace::LDETraceTable; -use crate::traits::{TransitionEvaluationContext, AIR}; -use crate::{frame::Frame, prover::evaluate_polynomial_on_lde_domain}; -use itertools::Itertools; -#[cfg(not(feature = "parallel"))] -use lambdaworks_math::polynomial::Polynomial; -use lambdaworks_math::{fft::errors::FFTError, field::element::FieldElement, traits::AsBytes}; -#[cfg(feature = "parallel")] -use rayon::{ - iter::IndexedParallelIterator, - prelude::{IntoParallelIterator, ParallelIterator}, -}; - -#[cfg(feature = "instruments")] -use std::time::Instant; - -pub struct ConstraintEvaluator { - boundary_constraints: BoundaryConstraints, -} -impl ConstraintEvaluator { - pub fn new(air: &A, rap_challenges: &[FieldElement]) -> Self { - let boundary_constraints = air.boundary_constraints(rap_challenges); - - Self { - boundary_constraints, - } - } - - pub(crate) fn evaluate( - &self, - air: &A, - lde_trace: &LDETraceTable, - domain: &Domain, - transition_coefficients: &[FieldElement], - boundary_coefficients: &[FieldElement], - rap_challenges: &[FieldElement], - ) -> Vec> - where - FieldElement: AsBytes + Send + Sync, - FieldElement: AsBytes + Send + Sync, - A: Send + Sync, - { - let boundary_constraints = &self.boundary_constraints; - let number_of_b_constraints = boundary_constraints.constraints.len(); - let boundary_zerofiers_inverse_evaluations: Vec>> = - boundary_constraints - .constraints - .iter() - .map(|bc| { - let point = &domain.trace_primitive_root.pow(bc.step as u64); - let mut evals = domain - .lde_roots_of_unity_coset - .iter() - .map(|v| v.clone() - point) - .collect::>>(); - FieldElement::inplace_batch_inverse(&mut evals).unwrap(); - evals - }) - .collect::>>>(); - - #[cfg(all(debug_assertions, not(feature = "parallel")))] - let boundary_polys: Vec>> = Vec::new(); - - #[cfg(feature = "instruments")] - let timer = Instant::now(); - - let lde_periodic_columns = air - .get_periodic_column_polynomials() - .iter() - .map(|poly| { - evaluate_polynomial_on_lde_domain( - poly, - domain.blowup_factor, - domain.interpolation_domain_size, - &domain.coset_offset, - ) - }) - .collect::>>, FFTError>>() - .unwrap(); - - #[cfg(feature = "instruments")] - println!( - " Evaluating periodic columns on lde: {:#?}", - timer.elapsed() - ); - - #[cfg(feature = "instruments")] - let timer = Instant::now(); - - let boundary_polys_evaluations = boundary_constraints - .constraints - .iter() - .map(|constraint| { - if constraint.is_aux { - (0..lde_trace.num_rows()) - .map(|row| { - let v = lde_trace.get_aux(row, constraint.col); - v - &constraint.value - }) - .collect_vec() - } else { - (0..lde_trace.num_rows()) - .map(|row| { - let v = lde_trace.get_main(row, constraint.col); - v - &constraint.value - }) - .collect_vec() - } - }) - .collect_vec(); - - #[cfg(feature = "instruments")] - println!(" Created boundary polynomials: {:#?}", timer.elapsed()); - #[cfg(feature = "instruments")] - let timer = Instant::now(); - - #[cfg(feature = "parallel")] - let boundary_eval_iter = (0..domain.lde_roots_of_unity_coset.len()).into_par_iter(); - #[cfg(not(feature = "parallel"))] - let boundary_eval_iter = 0..domain.lde_roots_of_unity_coset.len(); - - let boundary_evaluation: Vec<_> = boundary_eval_iter - .map(|domain_index| { - (0..number_of_b_constraints) - .zip(boundary_coefficients) - .fold(FieldElement::zero(), |acc, (constraint_index, beta)| { - acc + &boundary_zerofiers_inverse_evaluations[constraint_index] - [domain_index] - * beta - * &boundary_polys_evaluations[constraint_index][domain_index] - }) - }) - .collect(); - - #[cfg(feature = "instruments")] - println!( - " Evaluated boundary polynomials on LDE: {:#?}", - timer.elapsed() - ); - - #[cfg(all(debug_assertions, not(feature = "parallel")))] - let boundary_zerofiers = Vec::new(); - - #[cfg(all(debug_assertions, not(feature = "parallel")))] - check_boundary_polys_divisibility(boundary_polys, boundary_zerofiers); - - #[cfg(all(debug_assertions, not(feature = "parallel")))] - let mut transition_evaluations = Vec::new(); - - #[cfg(feature = "instruments")] - let timer = Instant::now(); - let zerofiers_evals = air.transition_zerofier_evaluations(domain); - #[cfg(feature = "instruments")] - println!( - " Evaluated transition zerofiers: {:#?}", - timer.elapsed() - ); - - // Iterate over all LDE domain and compute the part of the composition polynomial - // related to the transition constraints and add it to the already computed part of the - // boundary constraints. - - #[cfg(feature = "instruments")] - let timer = Instant::now(); - let evaluations_t_iter = 0..domain.lde_roots_of_unity_coset.len(); - - #[cfg(feature = "parallel")] - let boundary_evaluation = boundary_evaluation.into_par_iter(); - #[cfg(feature = "parallel")] - let evaluations_t_iter = evaluations_t_iter.into_par_iter(); - - let evaluations_t = evaluations_t_iter - .zip(boundary_evaluation) - .map(|(i, boundary)| { - let frame = Frame::read_from_lde(lde_trace, i, &air.context().transition_offsets); - - let periodic_values: Vec<_> = lde_periodic_columns - .iter() - .map(|col| col[i].clone()) - .collect(); - - // Compute all the transition constraints at this point of the LDE domain. - let transition_evaluation_context = TransitionEvaluationContext::new_prover( - &frame, - &periodic_values, - rap_challenges, - ); - let evaluations_transition = air.compute_transition(&transition_evaluation_context); - - #[cfg(all(debug_assertions, not(feature = "parallel")))] - transition_evaluations.push(evaluations_transition.clone()); - - // Add each term of the transition constraints to the composition polynomial, including the zerofier, - // the challenge and the exemption polynomial if it is necessary. - let acc_transition = itertools::izip!( - evaluations_transition, - &zerofiers_evals, - transition_coefficients - ) - .fold(FieldElement::zero(), |acc, (eval, zerof_eval, beta)| { - // Zerofier evaluations are cyclical, so we only calculate one cycle. - // This means that here we have to wrap around - // Ex: Suppose the full zerofier vector is Z = [1,2,3,1,2,3] - // we will instead have calculated Z' = [1,2,3] - // Now if you need Z[4] this is equal to Z'[1] - let wrapped_idx = i % zerof_eval.len(); - acc + &zerof_eval[wrapped_idx] * eval * beta - }); - - acc_transition + boundary - }) - .collect(); - - #[cfg(feature = "instruments")] - println!( - " Evaluated transitions and accumulated results: {:#?}", - timer.elapsed() - ); - - evaluations_t - } -} diff --git a/crates/provers/stark/src/constraints/mod.rs b/crates/provers/stark/src/constraints/mod.rs deleted file mode 100644 index 3811523b5..000000000 --- a/crates/provers/stark/src/constraints/mod.rs +++ /dev/null @@ -1,3 +0,0 @@ -pub mod boundary; -pub mod evaluator; -pub mod transition; diff --git a/crates/provers/stark/src/constraints/transition.rs b/crates/provers/stark/src/constraints/transition.rs deleted file mode 100644 index de0c9ade3..000000000 --- a/crates/provers/stark/src/constraints/transition.rs +++ /dev/null @@ -1,247 +0,0 @@ -use std::ops::Div; - -use crate::domain::Domain; -use crate::prover::evaluate_polynomial_on_lde_domain; -use crate::traits::TransitionEvaluationContext; -use itertools::Itertools; -use lambdaworks_math::field::element::FieldElement; -use lambdaworks_math::field::traits::{IsFFTField, IsField, IsSubFieldOf}; -use lambdaworks_math::polynomial::Polynomial; - -/// TransitionConstraint represents the behaviour that a transition constraint -/// over the computation that wants to be proven must comply with. -pub trait TransitionConstraint: Send + Sync -where - F: IsSubFieldOf + IsFFTField + Send + Sync, - E: IsField + Send + Sync, -{ - /// The degree of the constraint interpreting it as a multivariate polynomial. - fn degree(&self) -> usize; - - /// The index of the constraint. - /// Each transition constraint should have one index in the range [0, N), - /// where N is the total number of transition constraints. - fn constraint_idx(&self) -> usize; - - /// The function representing the evaluation of the constraint over elements - /// of the trace table. - /// - /// Elements of the trace table are found in the `frame` input, and depending on the - /// constraint, elements of `periodic_values` and `rap_challenges` may be used in - /// the evaluation. - /// Once computed, the evaluation should be inserted in the `transition_evaluations` - /// vector, in the index corresponding to the constraint as given by `constraint_idx()`. - fn evaluate( - &self, - evaluation_context: &TransitionEvaluationContext, - transition_evaluations: &mut [FieldElement], - ); - - /// The periodicity the constraint is applied over the trace. - /// - /// Default value is 1, meaning that the constraint is applied to every - /// step of the trace. - fn period(&self) -> usize { - 1 - } - - /// The offset with respect to the first trace row, where the constraint - /// is applied. - /// For example, if the constraint has periodicity 2 and offset 1, this means - /// the constraint will be applied over trace rows of index 1, 3, 5, etc. - /// - /// Default value is 0, meaning that the constraint is applied from the first - /// element of the trace on. - fn offset(&self) -> usize { - 0 - } - - /// For a more fine-grained description of where the constraint should apply, - /// an exemptions period can be defined. - /// This specifies the periodicity of the row indexes where the constraint should - /// NOT apply, within the row indexes where the constraint applies, as specified by - /// `period()` and `offset()`. - /// - /// Default value is None. - fn exemptions_period(&self) -> Option { - None - } - - /// The offset value for periodic exemptions. Check documentation of `period()`, - /// `offset()` and `exemptions_period` for a better understanding. - fn periodic_exemptions_offset(&self) -> Option { - None - } - - /// The number of exemptions at the end of the trace. - /// - /// This method's output defines what trace elements should not be considered for - /// the constraint evaluation at the end of the trace. For example, for a fibonacci - /// computation that has to use the result 2 following steps, this method is defined - /// to return the value 2. - fn end_exemptions(&self) -> usize; - - /// Method for calculating the end exemptions polynomial. - /// - /// This polynomial is used to compute zerofiers of the constraint, and the default - /// implementation should normally not be changed. - fn end_exemptions_poly( - &self, - trace_primitive_root: &FieldElement, - trace_length: usize, - ) -> Polynomial> { - let one_poly = Polynomial::new_monomial(FieldElement::::one(), 0); - if self.end_exemptions() == 0 { - return one_poly; - } - let period = self.period(); - // FIXME: CHECK IF WE NEED TO CHANGE THE NEW MONOMIAL'S ARGUMENTS TO trace_root^(offset * trace_length / period) INSTEAD OF ONE!!!! - (1..=self.end_exemptions()) - .map(|exemption| trace_primitive_root.pow(trace_length - exemption * period)) - .fold(one_poly, |acc, offset| { - acc * (Polynomial::new_monomial(FieldElement::::one(), 1) - offset) - }) - } - - /// Compute evaluations of the constraints zerofier over a LDE domain. - #[allow(unstable_name_collisions)] - fn zerofier_evaluations_on_extended_domain(&self, domain: &Domain) -> Vec> { - let blowup_factor = domain.blowup_factor; - let trace_length = domain.trace_roots_of_unity.len(); - let trace_primitive_root = &domain.trace_primitive_root; - let coset_offset = &domain.coset_offset; - let lde_root_order = u64::from((blowup_factor * trace_length).trailing_zeros()); - let lde_root = F::get_primitive_root_of_unity(lde_root_order).unwrap(); - - let end_exemptions_poly = self.end_exemptions_poly(trace_primitive_root, trace_length); - - // If there is an exemptions period defined for this constraint, the evaluations are calculated directly - // by computing P_exemptions(x) / Zerofier(x) - #[expect(clippy::incompatible_msrv)] - if let Some(exemptions_period) = self.exemptions_period() { - // FIXME: Rather than making this assertions here, it would be better to handle these - // errors or make these checks when the AIR is initialized. - - debug_assert!(exemptions_period.is_multiple_of(self.period())); - - debug_assert!(self.periodic_exemptions_offset().is_some()); - - // The elements of the domain have order `trace_length * blowup_factor`, so the zerofier evaluations - // without the end exemptions, repeat their values after `blowup_factor * exemptions_period` iterations, - // so we only need to compute those. - let last_exponent = blowup_factor * exemptions_period; - - let evaluations: Vec<_> = (0..last_exponent) - .map(|exponent| { - let x = lde_root.pow(exponent); - let offset_times_x = coset_offset * &x; - let offset_exponent = trace_length * self.periodic_exemptions_offset().unwrap() - / exemptions_period; - - let numerator = offset_times_x.pow(trace_length / exemptions_period) - - trace_primitive_root.pow(offset_exponent); - let denominator = offset_times_x.pow(trace_length / self.period()) - - trace_primitive_root.pow(self.offset() * trace_length / self.period()); - - // The denominator is guaranteed to be non-zero because the sets of powers of `offset_times_x` - // and `trace_primitive_root` are disjoint, provided that the offset is neither an element of the - // interpolation domain nor part of a subgroup with order less than n. - unsafe { numerator.div(denominator).unwrap_unchecked() } - }) - .collect(); - - // FIXME: Instead of computing this evaluations for each constraint, they can be computed - // once for every constraint with the same end exemptions (combination of end_exemptions() - // and period). - let end_exemption_evaluations = evaluate_polynomial_on_lde_domain( - &end_exemptions_poly, - blowup_factor, - domain.interpolation_domain_size, - coset_offset, - ) - .unwrap(); - - let cycled_evaluations = evaluations - .iter() - .cycle() - .take(end_exemption_evaluations.len()); - - std::iter::zip(cycled_evaluations, end_exemption_evaluations) - .map(|(eval, exemption_eval)| eval * exemption_eval) - .collect() - - // In this else branch, the zerofiers are computed as the numerator, then inverted - // using batch inverse and then multiplied by P_exemptions(x). This way we don't do - // useless divisions. - } else { - let last_exponent = blowup_factor * self.period(); - - let mut evaluations = (0..last_exponent) - .map(|exponent| { - let x = lde_root.pow(exponent); - (coset_offset * &x).pow(trace_length / self.period()) - - trace_primitive_root.pow(self.offset() * trace_length / self.period()) - }) - .collect_vec(); - - FieldElement::inplace_batch_inverse(&mut evaluations).unwrap(); - - // FIXME: Instead of computing this evaluations for each constraint, they can be computed - // once for every constraint with the same end exemptions (combination of end_exemptions() - // and period). - let end_exemption_evaluations = evaluate_polynomial_on_lde_domain( - &end_exemptions_poly, - blowup_factor, - domain.interpolation_domain_size, - coset_offset, - ) - .unwrap(); - - let cycled_evaluations = evaluations - .iter() - .cycle() - .take(end_exemption_evaluations.len()); - - std::iter::zip(cycled_evaluations, end_exemption_evaluations) - .map(|(eval, exemption_eval)| eval * exemption_eval) - .collect() - } - } - - /// Returns the evaluation of the zerofier corresponding to this constraint in some point - /// `z`, which could be in a field extension. - #[allow(unstable_name_collisions)] - fn evaluate_zerofier( - &self, - z: &FieldElement, - trace_primitive_root: &FieldElement, - trace_length: usize, - ) -> FieldElement { - let end_exemptions_poly = self.end_exemptions_poly(trace_primitive_root, trace_length); - - #[expect(clippy::incompatible_msrv)] - if let Some(exemptions_period) = self.exemptions_period() { - debug_assert!(exemptions_period.is_multiple_of(self.period())); - - debug_assert!(self.periodic_exemptions_offset().is_some()); - - let periodic_exemptions_offset = self.periodic_exemptions_offset().unwrap(); - let offset_exponent = trace_length * periodic_exemptions_offset / exemptions_period; - - let numerator = -trace_primitive_root.pow(offset_exponent) - + z.pow(trace_length / exemptions_period); - let denominator = -trace_primitive_root - .pow(self.offset() * trace_length / self.period()) - + z.pow(trace_length / self.period()); - // The denominator isn't zero because z is sampled outside the set of primitive roots. - return unsafe { numerator.div(denominator).unwrap_unchecked() } - * end_exemptions_poly.evaluate(z); - } - - (-trace_primitive_root.pow(self.offset() * trace_length / self.period()) - + z.pow(trace_length / self.period())) - .inv() - .unwrap() - * end_exemptions_poly.evaluate(z) - } -} diff --git a/crates/provers/stark/src/context.rs b/crates/provers/stark/src/context.rs deleted file mode 100644 index e7f164d27..000000000 --- a/crates/provers/stark/src/context.rs +++ /dev/null @@ -1,21 +0,0 @@ -use super::proof::options::ProofOptions; - -#[derive(Clone, Debug)] -pub struct AirContext { - pub proof_options: ProofOptions, - pub trace_columns: usize, - - /// This is a vector with the indices of all the rows that constitute - /// an evaluation frame. Note that, because of how we write all constraints - /// in one method (`compute_transitions`), this vector needs to include the - /// offsets that are needed to compute EVERY transition constraint, even if some - /// constraints don't use all of the indexes in said offsets. - pub transition_offsets: Vec, - pub num_transition_constraints: usize, -} - -impl AirContext { - pub fn num_transition_constraints(&self) -> usize { - self.num_transition_constraints - } -} diff --git a/crates/provers/stark/src/debug.rs b/crates/provers/stark/src/debug.rs deleted file mode 100644 index 968de1351..000000000 --- a/crates/provers/stark/src/debug.rs +++ /dev/null @@ -1,142 +0,0 @@ -use super::domain::Domain; -use super::traits::{TransitionEvaluationContext, AIR}; -use crate::{frame::Frame, trace::LDETraceTable}; -use lambdaworks_math::{ - field::{ - element::FieldElement, - traits::{IsFFTField, IsField}, - }, - polynomial::Polynomial, -}; -use log::{error, info}; - -/// Validates that the trace is valid with respect to the supplied AIR constraints -pub fn validate_trace( - air: &A, - main_trace_polys: &[Polynomial>], - aux_trace_polys: &[Polynomial>], - domain: &Domain, - rap_challenges: &[FieldElement], -) -> bool { - info!("Starting constraints validation over trace..."); - let mut ret = true; - - let main_trace_columns: Vec<_> = main_trace_polys - .iter() - .map(|poly| { - Polynomial::>::evaluate_fft::( - poly, - 1, - Some(domain.interpolation_domain_size), - ) - .unwrap() - }) - .collect(); - - let aux_trace_columns: Vec<_> = aux_trace_polys - .iter() - .map(|poly| { - Polynomial::evaluate_fft::(poly, 1, Some(domain.interpolation_domain_size)) - .unwrap() - }) - .collect(); - - let lde_trace = - LDETraceTable::from_columns(main_trace_columns, aux_trace_columns, A::STEP_SIZE, 1); - - let periodic_columns: Vec<_> = air - .get_periodic_column_polynomials() - .iter() - .map(|poly| { - Polynomial::>::evaluate_fft::( - poly, - 1, - Some(domain.interpolation_domain_size), - ) - .unwrap() - }) - .collect(); - - // --------- VALIDATE BOUNDARY CONSTRAINTS ------------ - air.boundary_constraints(rap_challenges) - .constraints - .iter() - .for_each(|constraint| { - let col = constraint.col; - let step = constraint.step; - let boundary_value = constraint.value.clone(); - - let trace_value = if !constraint.is_aux { - lde_trace.get_main(step, col).clone().to_extension() - } else { - lde_trace.get_aux(step, col).clone() - }; - - if boundary_value.clone().to_extension() != trace_value { - ret = false; - error!("Boundary constraint inconsistency - Expected value {boundary_value:?} in step {step} and column {col}, found: {trace_value:?}"); - } - }); - - // --------- VALIDATE TRANSITION CONSTRAINTS ----------- - let n_transition_constraints = air.context().num_transition_constraints(); - let exemption_steps: Vec = std::iter::repeat(lde_trace.num_steps()) - .take(n_transition_constraints) - .zip(air.transition_constraints()) - .map(|(trace_steps, constraint)| trace_steps - constraint.end_exemptions()) - .collect(); - - // Iterate over trace and compute transitions - for step in 0..lde_trace.num_steps() { - let frame = Frame::read_step_from_lde(&lde_trace, step, &air.context().transition_offsets); - let periodic_values: Vec<_> = periodic_columns - .iter() - .map(|col| col[step].clone()) - .collect(); - let transition_evaluation_context = - TransitionEvaluationContext::new_prover(&frame, &periodic_values, rap_challenges); - let evaluations = air.compute_transition(&transition_evaluation_context); - - // Iterate over each transition evaluation. When the evaluated step is not from - // the exemption steps corresponding to the transition, it should have zero as a - // result - evaluations.iter().enumerate().for_each(|(i, eval)| { - // Check that all the transition constraint evaluations of the trace are zero. - // We don't take into account the transition exemptions. - if step < exemption_steps[i] && eval != &FieldElement::zero() { - ret = false; - error!( - "Inconsistent evaluation of transition {i} in step {step} - expected 0, got {eval:?}" - ); - } - }) - } - info!("Constraints validation check ended"); - ret -} - -pub fn check_boundary_polys_divisibility( - boundary_polys: Vec>>, - boundary_zerofiers: Vec>>, -) { - for (i, (poly, z)) in boundary_polys - .iter() - .zip(boundary_zerofiers.iter()) - .enumerate() - { - let (_, b) = poly.clone().long_division_with_remainder(z); - if b != Polynomial::zero() { - error!("Boundary poly {i} is not divisible by its zerofier"); - } - } -} - -/// Validates that the one-dimensional array `data` can be interpreted as two-dimensional -/// array, returning a true when valid and false when not. -pub fn validate_2d_structure(data: &[FieldElement], width: usize) -> bool -where - F: IsField, -{ - let rows: Vec>> = data.chunks(width).map(|c| c.to_vec()).collect(); - rows.iter().all(|r| r.len() == rows[0].len()) -} diff --git a/crates/provers/stark/src/domain.rs b/crates/provers/stark/src/domain.rs deleted file mode 100644 index 4744a50d2..000000000 --- a/crates/provers/stark/src/domain.rs +++ /dev/null @@ -1,55 +0,0 @@ -use lambdaworks_math::{ - fft::cpu::roots_of_unity::get_powers_of_primitive_root_coset, - field::{element::FieldElement, traits::IsFFTField}, -}; - -use super::traits::AIR; - -pub struct Domain { - pub(crate) root_order: u32, - pub(crate) lde_roots_of_unity_coset: Vec>, - pub(crate) trace_primitive_root: FieldElement, - pub(crate) trace_roots_of_unity: Vec>, - pub(crate) coset_offset: FieldElement, - pub(crate) blowup_factor: usize, - pub(crate) interpolation_domain_size: usize, -} - -impl Domain { - pub fn new(air: &A) -> Self - where - A: AIR, - { - // Initial definitions - let blowup_factor = air.options().blowup_factor as usize; - let coset_offset = FieldElement::from(air.options().coset_offset); - let interpolation_domain_size = air.trace_length(); - let root_order = air.trace_length().trailing_zeros(); - // * Generate Coset - let trace_primitive_root = F::get_primitive_root_of_unity(root_order as u64).unwrap(); - let trace_roots_of_unity = get_powers_of_primitive_root_coset( - root_order as u64, - interpolation_domain_size, - &FieldElement::one(), - ) - .unwrap(); - - let lde_root_order = (air.trace_length() * blowup_factor).trailing_zeros(); - let lde_roots_of_unity_coset = get_powers_of_primitive_root_coset( - lde_root_order as u64, - air.trace_length() * blowup_factor, - &coset_offset, - ) - .unwrap(); - - Self { - root_order, - lde_roots_of_unity_coset, - trace_primitive_root, - trace_roots_of_unity, - blowup_factor, - coset_offset, - interpolation_domain_size, - } - } -} diff --git a/crates/provers/stark/src/examples/bit_flags.rs b/crates/provers/stark/src/examples/bit_flags.rs deleted file mode 100644 index cd6d9f14c..000000000 --- a/crates/provers/stark/src/examples/bit_flags.rs +++ /dev/null @@ -1,219 +0,0 @@ -use crate::{ - constraints::{boundary::BoundaryConstraints, transition::TransitionConstraint}, - context::AirContext, - proof::options::ProofOptions, - trace::TraceTable, - traits::{TransitionEvaluationContext, AIR}, - Felt252, -}; -use lambdaworks_math::field::{ - element::FieldElement, fields::fft_friendly::stark_252_prime_field::Stark252PrimeField, -}; -use std::iter; - -type StarkField = Stark252PrimeField; - -#[derive(Clone)] -pub struct BitConstraint; -impl BitConstraint { - fn new() -> Self { - Self - } -} - -impl TransitionConstraint for BitConstraint { - fn degree(&self) -> usize { - 2 - } - - fn constraint_idx(&self) -> usize { - 0 - } - - fn exemptions_period(&self) -> Option { - Some(16) - } - - fn periodic_exemptions_offset(&self) -> Option { - Some(15) - } - - fn end_exemptions(&self) -> usize { - 0 - } - - fn evaluate( - &self, - evaluation_context: &TransitionEvaluationContext, - transition_evaluations: &mut [FieldElement], - ) { - let (frame, _periodic_values, _rap_challenges) = match evaluation_context { - TransitionEvaluationContext::Prover { - frame, - periodic_values, - rap_challenges, - } - | TransitionEvaluationContext::Verifier { - frame, - periodic_values, - rap_challenges, - } => (frame, periodic_values, rap_challenges), - }; - - let step = frame.get_evaluation_step(0); - - let prefix_flag = step.get_main_evaluation_element(0, 0); - let next_prefix_flag = step.get_main_evaluation_element(1, 0); - - let two = Felt252::from(2); - let one = Felt252::one(); - let bit_flag = prefix_flag - two * next_prefix_flag; - - let bit_constraint = bit_flag * (bit_flag - one); - - transition_evaluations[self.constraint_idx()] = bit_constraint; - } -} - -#[derive(Clone)] -pub struct ZeroFlagConstraint; -impl ZeroFlagConstraint { - fn new() -> Self { - Self - } -} - -impl TransitionConstraint for ZeroFlagConstraint { - fn degree(&self) -> usize { - 1 - } - - fn constraint_idx(&self) -> usize { - 1 - } - - fn end_exemptions(&self) -> usize { - 0 - } - - fn period(&self) -> usize { - 16 - } - - fn evaluate( - &self, - evaluation_context: &TransitionEvaluationContext, - transition_evaluations: &mut [FieldElement], - ) { - let (frame, _periodic_values, _rap_challenges) = match evaluation_context { - TransitionEvaluationContext::Prover { - frame, - periodic_values, - rap_challenges, - } - | TransitionEvaluationContext::Verifier { - frame, - periodic_values, - rap_challenges, - } => (frame, periodic_values, rap_challenges), - }; - - let step = frame.get_evaluation_step(0); - let zero_flag = step.get_main_evaluation_element(15, 0); - - transition_evaluations[self.constraint_idx()] = *zero_flag; - } -} - -pub struct BitFlagsAIR { - context: AirContext, - constraints: Vec>>, - trace_length: usize, -} - -impl AIR for BitFlagsAIR { - type Field = StarkField; - type FieldExtension = StarkField; - type PublicInputs = (); - - const STEP_SIZE: usize = 16; - - fn new( - trace_length: usize, - _pub_inputs: &Self::PublicInputs, - proof_options: &ProofOptions, - ) -> Self { - let bit_constraint = Box::new(BitConstraint::new()); - let flag_constraint = Box::new(ZeroFlagConstraint::new()); - let constraints: Vec>> = - vec![bit_constraint, flag_constraint]; - - let num_transition_constraints = constraints.len(); - - let context = AirContext { - proof_options: proof_options.clone(), - trace_columns: 2, - transition_offsets: vec![0], - num_transition_constraints, - }; - - Self { - context, - trace_length, - constraints, - } - } - - fn transition_constraints( - &self, - ) -> &Vec>> { - &self.constraints - } - - fn boundary_constraints( - &self, - _rap_challenges: &[FieldElement], - ) -> BoundaryConstraints { - BoundaryConstraints::from_constraints(vec![]) - } - - fn context(&self) -> &AirContext { - &self.context - } - - fn composition_poly_degree_bound(&self) -> usize { - self.trace_length * 2 - } - - fn trace_length(&self) -> usize { - self.trace_length - } - - fn trace_layout(&self) -> (usize, usize) { - (1, 0) - } - - fn pub_inputs(&self) -> &Self::PublicInputs { - &() - } -} - -pub fn bit_prefix_flag_trace(num_steps: usize) -> TraceTable { - debug_assert!(num_steps.is_power_of_two()); - let step: Vec = [ - 1031u64, 515, 257, 128, 64, 32, 16, 8, 4, 2, 1, 0, 0, 0, 0, 0, - ] - .iter() - .map(|t| Felt252::from(*t)) - .collect(); - - let mut data: Vec = iter::repeat(step).take(num_steps).flatten().collect(); - data[0] = Felt252::from(1030); - - let mut dummy_column = (0..16).map(Felt252::from).collect(); - dummy_column = iter::repeat(dummy_column) - .take(num_steps) - .flatten() - .collect(); - TraceTable::from_columns_main(vec![data, dummy_column], 16) -} diff --git a/crates/provers/stark/src/examples/dummy_air.rs b/crates/provers/stark/src/examples/dummy_air.rs deleted file mode 100644 index 8501eb866..000000000 --- a/crates/provers/stark/src/examples/dummy_air.rs +++ /dev/null @@ -1,226 +0,0 @@ -use std::marker::PhantomData; - -use crate::{ - constraints::{ - boundary::{BoundaryConstraint, BoundaryConstraints}, - transition::TransitionConstraint, - }, - context::AirContext, - proof::options::ProofOptions, - trace::TraceTable, - traits::{TransitionEvaluationContext, AIR}, -}; -use lambdaworks_math::field::{ - element::FieldElement, fields::fft_friendly::stark_252_prime_field::Stark252PrimeField, - traits::IsFFTField, -}; - -type StarkField = Stark252PrimeField; - -#[derive(Clone)] -struct FibConstraint { - phantom: PhantomData, -} -impl FibConstraint { - pub fn new() -> Self { - Self { - phantom: PhantomData, - } - } -} - -impl TransitionConstraint for FibConstraint -where - F: IsFFTField + Send + Sync, -{ - fn degree(&self) -> usize { - 1 - } - - fn constraint_idx(&self) -> usize { - 0 - } - - fn end_exemptions(&self) -> usize { - 2 - } - - fn evaluate( - &self, - evaluation_context: &TransitionEvaluationContext, - transition_evaluations: &mut [FieldElement], - ) { - let (frame, _periodic_values, _rap_challenges) = match evaluation_context { - TransitionEvaluationContext::Prover { - frame, - periodic_values, - rap_challenges, - } - | TransitionEvaluationContext::Verifier { - frame, - periodic_values, - rap_challenges, - } => (frame, periodic_values, rap_challenges), - }; - - let first_step = frame.get_evaluation_step(0); - let second_step = frame.get_evaluation_step(1); - let third_step = frame.get_evaluation_step(2); - - let a0 = first_step.get_main_evaluation_element(0, 1); - let a1 = second_step.get_main_evaluation_element(0, 1); - let a2 = third_step.get_main_evaluation_element(0, 1); - - let res = a2 - a1 - a0; - - transition_evaluations[self.constraint_idx()] = res; - } -} - -#[derive(Clone)] -struct BitConstraint { - phantom: PhantomData, -} -impl BitConstraint { - pub fn new() -> Self { - Self { - phantom: PhantomData, - } - } -} - -impl TransitionConstraint for BitConstraint -where - F: IsFFTField + Send + Sync, -{ - fn degree(&self) -> usize { - 2 - } - - fn constraint_idx(&self) -> usize { - 1 - } - - fn end_exemptions(&self) -> usize { - 0 - } - - fn evaluate( - &self, - evaluation_context: &TransitionEvaluationContext, - transition_evaluations: &mut [FieldElement], - ) { - let (frame, _periodic_values, _rap_challenges) = match evaluation_context { - TransitionEvaluationContext::Prover { - frame, - periodic_values, - rap_challenges, - } - | TransitionEvaluationContext::Verifier { - frame, - periodic_values, - rap_challenges, - } => (frame, periodic_values, rap_challenges), - }; - - let first_step = frame.get_evaluation_step(0); - - let bit = first_step.get_main_evaluation_element(0, 0); - - let res = bit * (bit - FieldElement::::one()); - - transition_evaluations[self.constraint_idx()] = res; - } -} - -pub struct DummyAIR { - context: AirContext, - trace_length: usize, - transition_constraints: Vec>>, -} - -impl AIR for DummyAIR { - type Field = StarkField; - type FieldExtension = StarkField; - type PublicInputs = (); - - const STEP_SIZE: usize = 1; - - fn new( - trace_length: usize, - _pub_inputs: &Self::PublicInputs, - proof_options: &ProofOptions, - ) -> Self { - let transition_constraints: Vec< - Box>, - > = vec![ - Box::new(FibConstraint::new()), - Box::new(BitConstraint::new()), - ]; - - let context = AirContext { - proof_options: proof_options.clone(), - trace_columns: 2, - transition_offsets: vec![0, 1, 2], - num_transition_constraints: 2, - }; - - Self { - context, - trace_length, - transition_constraints, - } - } - - fn boundary_constraints( - &self, - _rap_challenges: &[FieldElement], - ) -> BoundaryConstraints { - let a0 = BoundaryConstraint::new_main(1, 0, FieldElement::::one()); - let a1 = BoundaryConstraint::new_main(1, 1, FieldElement::::one()); - - BoundaryConstraints::from_constraints(vec![a0, a1]) - } - - fn transition_constraints( - &self, - ) -> &Vec>> { - &self.transition_constraints - } - - fn context(&self) -> &AirContext { - &self.context - } - - fn composition_poly_degree_bound(&self) -> usize { - self.trace_length * 2 - } - - fn trace_layout(&self) -> (usize, usize) { - (2, 0) - } - - fn trace_length(&self) -> usize { - self.trace_length - } - - fn pub_inputs(&self) -> &Self::PublicInputs { - &() - } -} - -pub fn dummy_trace(trace_length: usize) -> TraceTable { - let mut ret: Vec> = vec![]; - - let a0 = FieldElement::one(); - let a1 = FieldElement::one(); - - ret.push(a0); - ret.push(a1); - - for i in 2..(trace_length) { - ret.push(ret[i - 1].clone() + ret[i - 2].clone()); - } - - TraceTable::from_columns_main(vec![vec![FieldElement::::one(); trace_length], ret], 1) -} diff --git a/crates/provers/stark/src/examples/fibonacci_2_cols_shifted.rs b/crates/provers/stark/src/examples/fibonacci_2_cols_shifted.rs deleted file mode 100644 index e4fc03b10..000000000 --- a/crates/provers/stark/src/examples/fibonacci_2_cols_shifted.rs +++ /dev/null @@ -1,283 +0,0 @@ -use crate::{ - constraints::{ - boundary::{BoundaryConstraint, BoundaryConstraints}, - transition::TransitionConstraint, - }, - context::AirContext, - proof::options::ProofOptions, - trace::TraceTable, - traits::{TransitionEvaluationContext, AIR}, -}; -use lambdaworks_math::{ - field::{element::FieldElement, traits::IsFFTField}, - traits::AsBytes, -}; -use std::marker::PhantomData; - -#[derive(Clone)] -struct ShiftedFibTransition1 { - phantom: PhantomData, -} - -impl ShiftedFibTransition1 { - pub fn new() -> Self { - Self { - phantom: PhantomData, - } - } -} - -impl TransitionConstraint for ShiftedFibTransition1 -where - F: IsFFTField + Send + Sync, -{ - fn degree(&self) -> usize { - 1 - } - - fn constraint_idx(&self) -> usize { - 0 - } - - fn end_exemptions(&self) -> usize { - 1 - } - - fn evaluate( - &self, - evaluation_context: &TransitionEvaluationContext, - transition_evaluations: &mut [FieldElement], - ) { - let (frame, _periodic_values, _rap_challenges) = match evaluation_context { - TransitionEvaluationContext::Prover { - frame, - periodic_values, - rap_challenges, - } - | TransitionEvaluationContext::Verifier { - frame, - periodic_values, - rap_challenges, - } => (frame, periodic_values, rap_challenges), - }; - - let first_row = frame.get_evaluation_step(0); - let second_row = frame.get_evaluation_step(1); - - let a0_1 = first_row.get_main_evaluation_element(0, 1); - let a1_0 = second_row.get_main_evaluation_element(0, 0); - - let res = a1_0 - a0_1; - - transition_evaluations[self.constraint_idx()] = res; - } -} - -#[derive(Clone)] -struct ShiftedFibTransition2 { - phantom: PhantomData, -} - -impl ShiftedFibTransition2 { - pub fn new() -> Self { - Self { - phantom: PhantomData, - } - } -} - -impl TransitionConstraint for ShiftedFibTransition2 -where - F: IsFFTField + Send + Sync, -{ - fn degree(&self) -> usize { - 1 - } - - fn constraint_idx(&self) -> usize { - 1 - } - - fn end_exemptions(&self) -> usize { - 1 - } - - fn evaluate( - &self, - evaluation_context: &TransitionEvaluationContext, - transition_evaluations: &mut [FieldElement], - ) { - let (frame, _periodic_values, _rap_challenges) = match evaluation_context { - TransitionEvaluationContext::Prover { - frame, - periodic_values, - rap_challenges, - } - | TransitionEvaluationContext::Verifier { - frame, - periodic_values, - rap_challenges, - } => (frame, periodic_values, rap_challenges), - }; - - let first_row = frame.get_evaluation_step(0); - let second_row = frame.get_evaluation_step(1); - - let a0_0 = first_row.get_main_evaluation_element(0, 0); - let a0_1 = first_row.get_main_evaluation_element(0, 1); - let a1_1 = second_row.get_main_evaluation_element(0, 1); - - let res = a1_1 - a0_0 - a0_1; - - transition_evaluations[self.constraint_idx()] = res; - } -} - -#[derive(Clone, Debug)] -pub struct PublicInputs -where - F: IsFFTField, -{ - pub claimed_value: FieldElement, - pub claimed_index: usize, -} - -impl AsBytes for PublicInputs -where - F: IsFFTField, - FieldElement: AsBytes, -{ - fn as_bytes(&self) -> Vec { - let mut transcript_init_seed = self.claimed_index.to_be_bytes().to_vec(); - transcript_init_seed.extend_from_slice(&self.claimed_value.as_bytes()); - transcript_init_seed - } -} - -pub struct Fibonacci2ColsShifted -where - F: IsFFTField, -{ - context: AirContext, - trace_length: usize, - pub_inputs: PublicInputs, - transition_constraints: Vec>>, -} - -/// The AIR for to a 2 column trace, where each column is a Fibonacci sequence and the -/// second column is constrained to be the shift of the first one. That is, if `Col0_i` -/// and `Col1_i` denote the i-th entry of each column, then `Col0_{i+1}` equals `Col1_{i}` -/// for all `i`. Also, `Col0_0` is constrained to be `1`. -impl AIR for Fibonacci2ColsShifted -where - F: IsFFTField + Send + Sync + 'static, -{ - type Field = F; - type FieldExtension = F; - type PublicInputs = PublicInputs; - - const STEP_SIZE: usize = 1; - - fn new( - trace_length: usize, - pub_inputs: &Self::PublicInputs, - proof_options: &ProofOptions, - ) -> Self { - let transition_constraints: Vec< - Box>, - > = vec![ - Box::new(ShiftedFibTransition1::new()), - Box::new(ShiftedFibTransition2::new()), - ]; - - let context = AirContext { - proof_options: proof_options.clone(), - transition_offsets: vec![0, 1], - num_transition_constraints: 2, - trace_columns: 2, - }; - - Self { - trace_length, - context, - pub_inputs: pub_inputs.clone(), - transition_constraints, - } - } - - fn boundary_constraints( - &self, - _rap_challenges: &[FieldElement], - ) -> BoundaryConstraints { - let initial_condition = BoundaryConstraint::new_main(0, 0, FieldElement::one()); - let claimed_value_constraint = BoundaryConstraint::new_main( - 0, - self.pub_inputs.claimed_index, - self.pub_inputs.claimed_value.clone(), - ); - - BoundaryConstraints::from_constraints(vec![initial_condition, claimed_value_constraint]) - } - - fn transition_constraints( - &self, - ) -> &Vec>> { - &self.transition_constraints - } - - fn context(&self) -> &AirContext { - &self.context - } - - fn composition_poly_degree_bound(&self) -> usize { - self.trace_length() - } - - fn trace_length(&self) -> usize { - self.trace_length - } - - fn trace_layout(&self) -> (usize, usize) { - (2, 0) - } - - fn pub_inputs(&self) -> &Self::PublicInputs { - &self.pub_inputs - } -} - -pub fn compute_trace( - initial_value: FieldElement, - trace_length: usize, -) -> TraceTable { - let mut x = FieldElement::one(); - let mut y = initial_value; - let mut col0 = vec![x.clone()]; - let mut col1 = vec![y.clone()]; - - for _ in 1..trace_length { - (x, y) = (y.clone(), &x + &y); - col0.push(x.clone()); - col1.push(y.clone()); - } - - TraceTable::from_columns_main(vec![col0, col1], 1) -} - -#[cfg(test)] -mod tests { - use lambdaworks_math::field::{ - element::FieldElement, fields::fft_friendly::stark_252_prime_field::Stark252PrimeField, - }; - - use super::compute_trace; - - #[test] - fn trace_has_expected_rows() { - let trace = compute_trace(FieldElement::::one(), 8); - assert_eq!(trace.num_rows(), 8); - - let trace = compute_trace(FieldElement::::one(), 64); - assert_eq!(trace.num_rows(), 64); - } -} diff --git a/crates/provers/stark/src/examples/fibonacci_2_columns.rs b/crates/provers/stark/src/examples/fibonacci_2_columns.rs deleted file mode 100644 index 9e375756a..000000000 --- a/crates/provers/stark/src/examples/fibonacci_2_columns.rs +++ /dev/null @@ -1,237 +0,0 @@ -use std::marker::PhantomData; - -use super::simple_fibonacci::FibonacciPublicInputs; -use crate::{ - constraints::{ - boundary::{BoundaryConstraint, BoundaryConstraints}, - transition::TransitionConstraint, - }, - context::AirContext, - proof::options::ProofOptions, - trace::TraceTable, - traits::{TransitionEvaluationContext, AIR}, -}; -use lambdaworks_math::field::{element::FieldElement, traits::IsFFTField}; - -#[derive(Clone)] -struct FibTransition1 { - phantom: PhantomData, -} - -impl FibTransition1 { - pub fn new() -> Self { - Self { - phantom: PhantomData, - } - } -} - -impl TransitionConstraint for FibTransition1 -where - F: IsFFTField + Send + Sync, -{ - fn degree(&self) -> usize { - 1 - } - - fn constraint_idx(&self) -> usize { - 0 - } - - fn end_exemptions(&self) -> usize { - 1 - } - - fn evaluate( - &self, - evaluation_context: &TransitionEvaluationContext, - transition_evaluations: &mut [FieldElement], - ) { - let (frame, _periodic_values, _rap_challenges) = match evaluation_context { - TransitionEvaluationContext::Prover { - frame, - periodic_values, - rap_challenges, - } - | TransitionEvaluationContext::Verifier { - frame, - periodic_values, - rap_challenges, - } => (frame, periodic_values, rap_challenges), - }; - - let first_step = frame.get_evaluation_step(0); - let second_step = frame.get_evaluation_step(1); - - // s_{0, i+1} = s_{0, i} + s_{1, i} - let s0_0 = first_step.get_main_evaluation_element(0, 0); - let s0_1 = first_step.get_main_evaluation_element(0, 1); - let s1_0 = second_step.get_main_evaluation_element(0, 0); - - let res = s1_0 - s0_0 - s0_1; - - transition_evaluations[self.constraint_idx()] = res; - } -} - -#[derive(Clone)] -struct FibTransition2 { - phantom: PhantomData, -} - -impl FibTransition2 { - pub fn new() -> Self { - Self { - phantom: PhantomData, - } - } -} - -impl TransitionConstraint for FibTransition2 -where - F: IsFFTField + Send + Sync, -{ - fn degree(&self) -> usize { - 1 - } - - fn constraint_idx(&self) -> usize { - 1 - } - - fn end_exemptions(&self) -> usize { - 1 - } - - fn evaluate( - &self, - evaluation_context: &TransitionEvaluationContext, - transition_evaluations: &mut [FieldElement], - ) { - let (frame, _periodic_values, _rap_challenges) = match evaluation_context { - TransitionEvaluationContext::Prover { - frame, - periodic_values, - rap_challenges, - } - | TransitionEvaluationContext::Verifier { - frame, - periodic_values, - rap_challenges, - } => (frame, periodic_values, rap_challenges), - }; - - let first_step = frame.get_evaluation_step(0); - let second_step = frame.get_evaluation_step(1); - - // s_{1, i+1} = s_{1, i} + s_{0, i+1} - let s0_1 = first_step.get_main_evaluation_element(0, 1); - let s1_0 = second_step.get_main_evaluation_element(0, 0); - let s1_1 = second_step.get_main_evaluation_element(0, 1); - - let res = s1_1 - s0_1 - s1_0; - - transition_evaluations[self.constraint_idx()] = res; - } -} - -pub struct Fibonacci2ColsAIR -where - F: IsFFTField, -{ - context: AirContext, - trace_length: usize, - pub_inputs: FibonacciPublicInputs, - constraints: Vec>>, -} - -/// The AIR for to a 2 column trace, where the columns form a Fibonacci sequence when -/// stacked in row-major order. -impl AIR for Fibonacci2ColsAIR -where - F: IsFFTField + Send + Sync + 'static, -{ - type Field = F; - type FieldExtension = F; - type PublicInputs = FibonacciPublicInputs; - - const STEP_SIZE: usize = 1; - - fn new( - trace_length: usize, - pub_inputs: &Self::PublicInputs, - proof_options: &ProofOptions, - ) -> Self { - let constraints: Vec>> = vec![ - Box::new(FibTransition1::new()), - Box::new(FibTransition2::new()), - ]; - - let context = AirContext { - proof_options: proof_options.clone(), - transition_offsets: vec![0, 1], - num_transition_constraints: constraints.len(), - trace_columns: 2, - }; - - Self { - trace_length, - context, - constraints, - pub_inputs: pub_inputs.clone(), - } - } - - fn boundary_constraints( - &self, - _rap_challenges: &[FieldElement], - ) -> BoundaryConstraints { - let a0 = BoundaryConstraint::new_main(0, 0, self.pub_inputs.a0.clone()); - let a1 = BoundaryConstraint::new_main(1, 0, self.pub_inputs.a1.clone()); - - BoundaryConstraints::from_constraints(vec![a0, a1]) - } - - fn transition_constraints(&self) -> &Vec>> { - &self.constraints - } - - fn context(&self) -> &AirContext { - &self.context - } - - fn composition_poly_degree_bound(&self) -> usize { - self.trace_length() - } - - fn trace_length(&self) -> usize { - self.trace_length - } - - fn trace_layout(&self) -> (usize, usize) { - (2, 0) - } - - fn pub_inputs(&self) -> &Self::PublicInputs { - &self.pub_inputs - } -} - -pub fn compute_trace( - initial_values: [FieldElement; 2], - trace_length: usize, -) -> TraceTable { - let mut ret1: Vec> = vec![]; - let mut ret2: Vec> = vec![]; - - ret1.push(initial_values[0].clone()); - ret2.push(initial_values[1].clone()); - - for i in 1..(trace_length) { - let new_val = ret1[i - 1].clone() + ret2[i - 1].clone(); - ret1.push(new_val.clone()); - ret2.push(new_val + ret2[i - 1].clone()); - } - - TraceTable::from_columns_main(vec![ret1, ret2], 1) -} diff --git a/crates/provers/stark/src/examples/fibonacci_rap.rs b/crates/provers/stark/src/examples/fibonacci_rap.rs deleted file mode 100644 index 90581232c..000000000 --- a/crates/provers/stark/src/examples/fibonacci_rap.rs +++ /dev/null @@ -1,387 +0,0 @@ -use std::{marker::PhantomData, ops::Div}; - -use crate::{ - constraints::{ - boundary::{BoundaryConstraint, BoundaryConstraints}, - transition::TransitionConstraint, - }, - context::AirContext, - proof::options::ProofOptions, - trace::TraceTable, - traits::{TransitionEvaluationContext, AIR}, -}; -use lambdaworks_crypto::fiat_shamir::is_transcript::IsTranscript; -use lambdaworks_math::{ - field::{element::FieldElement, traits::IsFFTField}, - helpers::resize_to_next_power_of_two, - traits::ByteConversion, -}; - -#[derive(Clone)] -struct FibConstraint { - phantom: PhantomData, -} - -impl FibConstraint { - pub fn new() -> Self { - Self { - phantom: PhantomData, - } - } -} - -impl TransitionConstraint for FibConstraint -where - F: IsFFTField + Send + Sync, -{ - fn degree(&self) -> usize { - 1 - } - - fn constraint_idx(&self) -> usize { - 0 - } - - fn end_exemptions(&self) -> usize { - // NOTE: This is hard-coded for the example of steps = 16 in the integration tests. - // If that number changes in the test, this should be changed too or the test will fail. - 3 + 32 - 16 - 1 - } - - fn evaluate( - &self, - evaluation_context: &TransitionEvaluationContext, - transition_evaluations: &mut [FieldElement], - ) { - let (frame, _periodic_values, _rap_challenges) = match evaluation_context { - TransitionEvaluationContext::Prover { - frame, - periodic_values, - rap_challenges, - } - | TransitionEvaluationContext::Verifier { - frame, - periodic_values, - rap_challenges, - } => (frame, periodic_values, rap_challenges), - }; - - let first_step = frame.get_evaluation_step(0); - let second_step = frame.get_evaluation_step(1); - let third_step = frame.get_evaluation_step(2); - - let a0 = first_step.get_main_evaluation_element(0, 0); - let a1 = second_step.get_main_evaluation_element(0, 0); - let a2 = third_step.get_main_evaluation_element(0, 0); - - let res = a2 - a1 - a0; - - transition_evaluations[self.constraint_idx()] = res; - } -} - -#[derive(Clone)] -struct PermutationConstraint { - phantom: PhantomData, -} - -impl PermutationConstraint { - pub fn new() -> Self { - Self { - phantom: PhantomData, - } - } -} - -impl TransitionConstraint for PermutationConstraint -where - F: IsFFTField + Send + Sync, -{ - fn degree(&self) -> usize { - 2 - } - - fn constraint_idx(&self) -> usize { - 1 - } - - fn end_exemptions(&self) -> usize { - 1 - } - - fn evaluate( - &self, - evaluation_context: &TransitionEvaluationContext, - transition_evaluations: &mut [FieldElement], - ) { - let (frame, _periodic_values, rap_challenges) = match evaluation_context { - TransitionEvaluationContext::Prover { - frame, - periodic_values, - rap_challenges, - } - | TransitionEvaluationContext::Verifier { - frame, - periodic_values, - rap_challenges, - } => (frame, periodic_values, rap_challenges), - }; - - let first_step = frame.get_evaluation_step(0); - let second_step = frame.get_evaluation_step(1); - - // Auxiliary constraints - let z_i = first_step.get_aux_evaluation_element(0, 0); - let z_i_plus_one = second_step.get_aux_evaluation_element(0, 0); - let gamma = &rap_challenges[0]; - - let a_i = first_step.get_main_evaluation_element(0, 0); - let b_i = first_step.get_main_evaluation_element(0, 1); - - let res = z_i_plus_one * (b_i + gamma) - z_i * (a_i + gamma); - - transition_evaluations[self.constraint_idx()] = res; - } -} - -pub struct FibonacciRAP -where - F: IsFFTField, -{ - context: AirContext, - trace_length: usize, - pub_inputs: FibonacciRAPPublicInputs, - transition_constraints: Vec>>, -} - -#[derive(Clone, Debug)] -pub struct FibonacciRAPPublicInputs -where - F: IsFFTField, -{ - pub steps: usize, - pub a0: FieldElement, - pub a1: FieldElement, -} - -impl AIR for FibonacciRAP -where - F: IsFFTField + Send + Sync + 'static, - FieldElement: ByteConversion, -{ - type Field = F; - type FieldExtension = F; - type PublicInputs = FibonacciRAPPublicInputs; - - const STEP_SIZE: usize = 1; - - fn new( - trace_length: usize, - pub_inputs: &Self::PublicInputs, - proof_options: &ProofOptions, - ) -> Self { - let transition_constraints: Vec< - Box>, - > = vec![ - Box::new(FibConstraint::new()), - Box::new(PermutationConstraint::new()), - ]; - - let context = AirContext { - proof_options: proof_options.clone(), - trace_columns: 3, - transition_offsets: vec![0, 1, 2], - num_transition_constraints: transition_constraints.len(), - }; - - Self { - context, - trace_length, - pub_inputs: pub_inputs.clone(), - transition_constraints, - } - } - - fn build_auxiliary_trace( - &self, - trace: &mut TraceTable, - challenges: &[FieldElement], - ) { - let main_segment_cols = trace.columns_main(); - let not_perm = &main_segment_cols[0]; - let perm = &main_segment_cols[1]; - let gamma = &challenges[0]; - - let trace_len = trace.num_rows(); - - let mut aux_col = Vec::new(); - for i in 0..trace_len { - if i == 0 { - aux_col.push(FieldElement::::one()); - } else { - let z_i = &aux_col[i - 1]; - let n_p_term = not_perm[i - 1].clone() + gamma; - let p_term = &perm[i - 1] + gamma; - - // We are using that with high probability p_term != 0 because gamma is a random element. - aux_col.push(z_i * n_p_term.div(p_term).unwrap()); - } - } - - for (i, aux_elem) in aux_col.iter().enumerate().take(trace.num_rows()) { - trace.set_aux(i, 0, aux_elem.clone()) - } - } - - fn build_rap_challenges( - &self, - transcript: &mut impl IsTranscript, - ) -> Vec> { - vec![transcript.sample_field_element()] - } - - fn trace_layout(&self) -> (usize, usize) { - (2, 1) - } - - fn boundary_constraints( - &self, - _rap_challenges: &[FieldElement], - ) -> BoundaryConstraints { - // Main boundary constraints - let a0 = - BoundaryConstraint::new_simple_main(0, FieldElement::::one()); - let a1 = - BoundaryConstraint::new_simple_main(1, FieldElement::::one()); - - // Auxiliary boundary constraints - let a0_aux = BoundaryConstraint::new_aux(0, 0, FieldElement::::one()); - - BoundaryConstraints::from_constraints(vec![a0, a1, a0_aux]) - } - - fn transition_constraints( - &self, - ) -> &Vec>> { - &self.transition_constraints - } - - fn context(&self) -> &AirContext { - &self.context - } - - fn composition_poly_degree_bound(&self) -> usize { - self.trace_length() - } - - fn trace_length(&self) -> usize { - self.trace_length - } - - fn pub_inputs(&self) -> &Self::PublicInputs { - &self.pub_inputs - } -} - -pub fn fibonacci_rap_trace( - initial_values: [FieldElement; 2], - trace_length: usize, -) -> TraceTable { - let mut fib_seq: Vec> = vec![]; - fib_seq.push(initial_values[0].clone()); - fib_seq.push(initial_values[1].clone()); - - for i in 2..(trace_length) { - fib_seq.push(fib_seq[i - 1].clone() + fib_seq[i - 2].clone()); - } - - let last_value = fib_seq[trace_length - 1].clone(); - let mut fib_permuted = fib_seq.clone(); - fib_permuted[0] = last_value; - fib_permuted[trace_length - 1] = initial_values[0].clone(); - - fib_seq.push(FieldElement::::zero()); - fib_permuted.push(FieldElement::::zero()); - let mut trace_cols = vec![fib_seq, fib_permuted]; - resize_to_next_power_of_two(&mut trace_cols); - - let mut trace = TraceTable::allocate_with_zeros(trace_cols[0].len(), 2, 1, 1); - for i in 0..trace.num_rows() { - trace.set_main(i, 0, trace_cols[0][i].clone()); - trace.set_main(i, 1, trace_cols[1][i].clone()); - } - - trace -} - -#[cfg(test)] -mod test { - use super::*; - use lambdaworks_math::field::fields::u64_prime_field::FE17; - - #[test] - fn test_build_fibonacci_rap_trace() { - // The fibonacci RAP trace should have two columns: - // * The usual fibonacci sequence column - // * The permuted fibonacci sequence column. The first and last elements are permuted. - // Also, a 0 is appended at the end of both columns. The reason for this can be read in - // https://hackmd.io/@aztec-network/plonk-arithmetiization-air#RAPs---PAIRs-with-interjected-verifier-randomness - - let trace = fibonacci_rap_trace([FE17::from(1), FE17::from(1)], 8); - let mut expected_trace = vec![ - vec![ - FE17::one(), - FE17::one(), - FE17::from(2), - FE17::from(3), - FE17::from(5), - FE17::from(8), - FE17::from(13), - FE17::from(21), - FE17::zero(), - ], - vec![ - FE17::from(21), - FE17::one(), - FE17::from(2), - FE17::from(3), - FE17::from(5), - FE17::from(8), - FE17::from(13), - FE17::one(), - FE17::zero(), - ], - ]; - resize_to_next_power_of_two(&mut expected_trace); - - assert_eq!(trace.columns_main(), expected_trace); - } - - #[test] - fn aux_col() { - let trace = fibonacci_rap_trace([FE17::from(1), FE17::from(1)], 64); - let trace_cols = trace.columns_main(); - - let not_perm = trace_cols[0].clone(); - let perm = trace_cols[1].clone(); - let gamma = FE17::from(10); - - assert_eq!(perm.len(), not_perm.len()); - let trace_len = not_perm.len(); - - let mut aux_col = Vec::new(); - for i in 0..trace_len { - if i == 0 { - aux_col.push(FE17::one()); - } else { - let z_i = aux_col[i - 1]; - let n_p_term = not_perm[i - 1] + gamma; - let p_term = perm[i - 1] + gamma; - - aux_col.push(z_i * n_p_term.div(p_term).unwrap()); - } - } - - assert_eq!(aux_col.last().unwrap(), &FE17::one()); - } -} diff --git a/crates/provers/stark/src/examples/mod.rs b/crates/provers/stark/src/examples/mod.rs deleted file mode 100644 index f21b62568..000000000 --- a/crates/provers/stark/src/examples/mod.rs +++ /dev/null @@ -1,10 +0,0 @@ -pub mod bit_flags; -pub mod dummy_air; -pub mod fibonacci_2_cols_shifted; -pub mod fibonacci_2_columns; -pub mod fibonacci_rap; -pub mod quadratic_air; -pub mod read_only_memory; -pub mod read_only_memory_logup; -pub mod simple_fibonacci; -pub mod simple_periodic_cols; diff --git a/crates/provers/stark/src/examples/quadratic_air.rs b/crates/provers/stark/src/examples/quadratic_air.rs deleted file mode 100644 index b9d0d8f99..000000000 --- a/crates/provers/stark/src/examples/quadratic_air.rs +++ /dev/null @@ -1,174 +0,0 @@ -use std::marker::PhantomData; - -use crate::{ - constraints::{ - boundary::{BoundaryConstraint, BoundaryConstraints}, - transition::TransitionConstraint, - }, - context::AirContext, - proof::options::ProofOptions, - trace::TraceTable, - traits::{TransitionEvaluationContext, AIR}, -}; -use lambdaworks_math::field::{element::FieldElement, traits::IsFFTField}; - -#[derive(Clone)] -struct QuadraticConstraint { - phantom: PhantomData, -} - -impl QuadraticConstraint { - pub fn new() -> Self { - Self { - phantom: PhantomData, - } - } -} - -impl TransitionConstraint for QuadraticConstraint -where - F: IsFFTField + Send + Sync, -{ - fn degree(&self) -> usize { - 2 - } - - fn constraint_idx(&self) -> usize { - 0 - } - - fn end_exemptions(&self) -> usize { - 1 - } - - fn evaluate( - &self, - evaluation_context: &TransitionEvaluationContext, - transition_evaluations: &mut [FieldElement], - ) { - let (frame, _periodic_values, _rap_challenges) = match evaluation_context { - TransitionEvaluationContext::Prover { - frame, - periodic_values, - rap_challenges, - } - | TransitionEvaluationContext::Verifier { - frame, - periodic_values, - rap_challenges, - } => (frame, periodic_values, rap_challenges), - }; - - let first_step = frame.get_evaluation_step(0); - let second_step = frame.get_evaluation_step(1); - - let x = first_step.get_main_evaluation_element(0, 0); - let x_squared = second_step.get_main_evaluation_element(0, 0); - - let res = x_squared - x * x; - - transition_evaluations[self.constraint_idx()] = res; - } -} - -pub struct QuadraticAIR -where - F: IsFFTField, -{ - context: AirContext, - trace_length: usize, - pub_inputs: QuadraticPublicInputs, - constraints: Vec>>, -} - -#[derive(Clone, Debug)] -pub struct QuadraticPublicInputs -where - F: IsFFTField, -{ - pub a0: FieldElement, -} - -impl AIR for QuadraticAIR -where - F: IsFFTField + Send + Sync + 'static, -{ - type Field = F; - type FieldExtension = F; - type PublicInputs = QuadraticPublicInputs; - - const STEP_SIZE: usize = 1; - - fn new( - trace_length: usize, - pub_inputs: &Self::PublicInputs, - proof_options: &ProofOptions, - ) -> Self { - let constraints: Vec>> = - vec![Box::new(QuadraticConstraint::new())]; - - let context = AirContext { - proof_options: proof_options.clone(), - trace_columns: 1, - transition_offsets: vec![0, 1], - num_transition_constraints: constraints.len(), - }; - - Self { - trace_length, - context, - pub_inputs: pub_inputs.clone(), - constraints, - } - } - - fn boundary_constraints( - &self, - _rap_challenges: &[FieldElement], - ) -> BoundaryConstraints { - let a0 = BoundaryConstraint::new_simple_main(0, self.pub_inputs.a0.clone()); - - BoundaryConstraints::from_constraints(vec![a0]) - } - - fn transition_constraints( - &self, - ) -> &Vec>> { - &self.constraints - } - - fn context(&self) -> &AirContext { - &self.context - } - - fn composition_poly_degree_bound(&self) -> usize { - 2 * self.trace_length() - } - - fn trace_layout(&self) -> (usize, usize) { - (1, 0) - } - - fn trace_length(&self) -> usize { - self.trace_length - } - - fn pub_inputs(&self) -> &Self::PublicInputs { - &self.pub_inputs - } -} - -pub fn quadratic_trace( - initial_value: FieldElement, - trace_length: usize, -) -> TraceTable { - let mut ret: Vec> = vec![]; - - ret.push(initial_value); - - for i in 1..(trace_length) { - ret.push(ret[i - 1].clone() * ret[i - 1].clone()); - } - - TraceTable::from_columns_main(vec![ret], 1) -} diff --git a/crates/provers/stark/src/examples/read_only_memory.rs b/crates/provers/stark/src/examples/read_only_memory.rs deleted file mode 100644 index cb1db9162..000000000 --- a/crates/provers/stark/src/examples/read_only_memory.rs +++ /dev/null @@ -1,458 +0,0 @@ -use std::marker::PhantomData; - -use crate::{ - constraints::{ - boundary::{BoundaryConstraint, BoundaryConstraints}, - transition::TransitionConstraint, - }, - context::AirContext, - proof::options::ProofOptions, - trace::TraceTable, - traits::{TransitionEvaluationContext, AIR}, -}; -use lambdaworks_crypto::fiat_shamir::is_transcript::IsTranscript; -use lambdaworks_math::field::traits::IsPrimeField; -use lambdaworks_math::{ - field::{element::FieldElement, traits::IsFFTField}, - traits::ByteConversion, -}; - -/// This condition ensures the continuity in a read-only memory structure, preserving strict ordering. -/// Equation based on Cairo Whitepaper section 9.7.2 -#[derive(Clone)] -struct ContinuityConstraint { - phantom: PhantomData, -} - -impl ContinuityConstraint { - pub fn new() -> Self { - Self { - phantom: PhantomData, - } - } -} - -impl TransitionConstraint for ContinuityConstraint -where - F: IsFFTField + Send + Sync, -{ - fn degree(&self) -> usize { - 2 - } - - fn constraint_idx(&self) -> usize { - 0 - } - - fn end_exemptions(&self) -> usize { - // NOTE: We are assuming that the trace has as length a power of 2. - 1 - } - - fn evaluate( - &self, - evaluation_context: &TransitionEvaluationContext, - transition_evaluations: &mut [FieldElement], - ) { - let (frame, _periodic_values, _rap_challenges) = match evaluation_context { - TransitionEvaluationContext::Prover { - frame, - periodic_values, - rap_challenges, - } - | TransitionEvaluationContext::Verifier { - frame, - periodic_values, - rap_challenges, - } => (frame, periodic_values, rap_challenges), - }; - - let first_step = frame.get_evaluation_step(0); - let second_step = frame.get_evaluation_step(1); - - let a_sorted_0 = first_step.get_main_evaluation_element(0, 2); - let a_sorted_1 = second_step.get_main_evaluation_element(0, 2); - // (a'_{i+1} - a'_i)(a'_{i+1} - a'_i - 1) = 0 where a' is the sorted address - let res = (a_sorted_1 - a_sorted_0) * (a_sorted_1 - a_sorted_0 - FieldElement::::one()); - - // The eval always exists, except if the constraint idx were incorrectly defined. - if let Some(eval) = transition_evaluations.get_mut(self.constraint_idx()) { - *eval = res; - } - } -} -/// Transition constraint that ensures that same addresses have same values, making the memory read-only. -/// Equation based on Cairo Whitepaper section 9.7.2 -#[derive(Clone)] -struct SingleValueConstraint { - phantom: PhantomData, -} - -impl SingleValueConstraint { - pub fn new() -> Self { - Self { - phantom: PhantomData, - } - } -} - -impl TransitionConstraint for SingleValueConstraint -where - F: IsFFTField + Send + Sync, -{ - fn degree(&self) -> usize { - 2 - } - - fn constraint_idx(&self) -> usize { - 1 - } - - fn end_exemptions(&self) -> usize { - // NOTE: We are assuming that the trace has as length a power of 2. - 1 - } - - fn evaluate( - &self, - evaluation_context: &TransitionEvaluationContext, - transition_evaluations: &mut [FieldElement], - ) { - let (frame, _periodic_values, _rap_challenges) = match evaluation_context { - TransitionEvaluationContext::Prover { - frame, - periodic_values, - rap_challenges, - } - | TransitionEvaluationContext::Verifier { - frame, - periodic_values, - rap_challenges, - } => (frame, periodic_values, rap_challenges), - }; - - let first_step = frame.get_evaluation_step(0); - let second_step = frame.get_evaluation_step(1); - - let a_sorted0 = first_step.get_main_evaluation_element(0, 2); - let a_sorted1 = second_step.get_main_evaluation_element(0, 2); - let v_sorted0 = first_step.get_main_evaluation_element(0, 3); - let v_sorted1 = second_step.get_main_evaluation_element(0, 3); - // (v'_{i+1} - v'_i) * (a'_{i+1} - a'_i - 1) = 0 - let res = (v_sorted1 - v_sorted0) * (a_sorted1 - a_sorted0 - FieldElement::::one()); - - // The eval always exists, except if the constraint idx were incorrectly defined. - if let Some(eval) = transition_evaluations.get_mut(self.constraint_idx()) { - *eval = res; - } - } -} -/// Permutation constraint ensures that the values are permuted in the memory. -/// Equation based on Cairo Whitepaper section 9.7.2 -#[derive(Clone)] -struct PermutationConstraint { - phantom: PhantomData, -} - -impl PermutationConstraint { - pub fn new() -> Self { - Self { - phantom: PhantomData, - } - } -} - -impl TransitionConstraint for PermutationConstraint -where - F: IsFFTField + Send + Sync, -{ - fn degree(&self) -> usize { - 2 - } - - fn constraint_idx(&self) -> usize { - 2 - } - - fn end_exemptions(&self) -> usize { - 1 - } - - fn evaluate( - &self, - evaluation_context: &TransitionEvaluationContext, - transition_evaluations: &mut [FieldElement], - ) { - let (frame, _periodic_values, rap_challenges) = match evaluation_context { - TransitionEvaluationContext::Prover { - frame, - periodic_values, - rap_challenges, - } - | TransitionEvaluationContext::Verifier { - frame, - periodic_values, - rap_challenges, - } => (frame, periodic_values, rap_challenges), - }; - - let first_step = frame.get_evaluation_step(0); - let second_step = frame.get_evaluation_step(1); - - // Auxiliary constraints - let p0 = first_step.get_aux_evaluation_element(0, 0); - let p1 = second_step.get_aux_evaluation_element(0, 0); - let z = &rap_challenges[0]; - let alpha = &rap_challenges[1]; - let a1 = second_step.get_main_evaluation_element(0, 0); - let v1 = second_step.get_main_evaluation_element(0, 1); - let a_sorted_1 = second_step.get_main_evaluation_element(0, 2); - let v_sorted_1 = second_step.get_main_evaluation_element(0, 3); - // (z - (a'_{i+1} + α * v'_{i+1})) * p_{i+1} = (z - (a_{i+1} + α * v_{i+1})) * p_i - let res = (z - (a_sorted_1 + alpha * v_sorted_1)) * p1 - (z - (a1 + alpha * v1)) * p0; - - // The eval always exists, except if the constraint idx were incorrectly defined. - if let Some(eval) = transition_evaluations.get_mut(self.constraint_idx()) { - *eval = res; - } - } -} - -pub struct ReadOnlyRAP -where - F: IsFFTField, -{ - context: AirContext, - trace_length: usize, - pub_inputs: ReadOnlyPublicInputs, - transition_constraints: Vec>>, -} - -#[derive(Clone, Debug)] -pub struct ReadOnlyPublicInputs -where - F: IsFFTField, -{ - pub a0: FieldElement, - pub v0: FieldElement, - pub a_sorted0: FieldElement, - pub v_sorted0: FieldElement, -} - -impl AIR for ReadOnlyRAP -where - F: IsFFTField + Send + Sync + 'static, - FieldElement: ByteConversion, -{ - type Field = F; - type FieldExtension = F; - type PublicInputs = ReadOnlyPublicInputs; - - const STEP_SIZE: usize = 1; - - fn new( - trace_length: usize, - pub_inputs: &Self::PublicInputs, - proof_options: &ProofOptions, - ) -> Self { - let transition_constraints: Vec< - Box>, - > = vec![ - Box::new(ContinuityConstraint::new()), - Box::new(SingleValueConstraint::new()), - Box::new(PermutationConstraint::new()), - ]; - - let context = AirContext { - proof_options: proof_options.clone(), - trace_columns: 5, - transition_offsets: vec![0, 1], - num_transition_constraints: transition_constraints.len(), - }; - - Self { - context, - trace_length, - pub_inputs: pub_inputs.clone(), - transition_constraints, - } - } - - fn build_auxiliary_trace( - &self, - trace: &mut TraceTable, - challenges: &[FieldElement], - ) { - let main_segment_cols = trace.columns_main(); - let a = &main_segment_cols[0]; - let v = &main_segment_cols[1]; - let a_sorted = &main_segment_cols[2]; - let v_sorted = &main_segment_cols[3]; - let z = &challenges[0]; - let alpha = &challenges[1]; - - let trace_len = trace.num_rows(); - - let mut aux_col = Vec::new(); - let num = z - (&a[0] + alpha * &v[0]); - let den = z - (&a_sorted[0] + alpha * &v_sorted[0]); - // We are using that den != 0 with high probability because alpha is a random element. - aux_col.push((num / den).unwrap()); - // Apply the same equation given in the permutation case to the rest of the trace - for i in 0..trace_len - 1 { - let num = (z - (&a[i + 1] + alpha * &v[i + 1])) * &aux_col[i]; - let den = z - (&a_sorted[i + 1] + alpha * &v_sorted[i + 1]); - // We are using that den != 0 with high probability because alpha is a random element. - aux_col.push((num / den).unwrap()); - } - - for (i, aux_elem) in aux_col.iter().enumerate().take(trace.num_rows()) { - trace.set_aux(i, 0, aux_elem.clone()) - } - } - - fn build_rap_challenges( - &self, - transcript: &mut impl IsTranscript, - ) -> Vec> { - vec![ - transcript.sample_field_element(), - transcript.sample_field_element(), - ] - } - - fn trace_layout(&self) -> (usize, usize) { - (4, 1) - } - - fn boundary_constraints( - &self, - rap_challenges: &[FieldElement], - ) -> BoundaryConstraints { - let a0 = &self.pub_inputs.a0; - let v0 = &self.pub_inputs.v0; - let a_sorted0 = &self.pub_inputs.a_sorted0; - let v_sorted0 = &self.pub_inputs.v_sorted0; - let z = &rap_challenges[0]; - let alpha = &rap_challenges[1]; - - // Main boundary constraints - let c1 = BoundaryConstraint::new_main(0, 0, a0.clone()); - let c2 = BoundaryConstraint::new_main(1, 0, v0.clone()); - let c3 = BoundaryConstraint::new_main(2, 0, a_sorted0.clone()); - let c4 = BoundaryConstraint::new_main(3, 0, v_sorted0.clone()); - - // Auxiliary boundary constraints - let num = z - (a0 + alpha * v0); - let den = z - (a_sorted0 + alpha * v_sorted0); - let p0_value = num / den; - - let c_aux1 = BoundaryConstraint::new_aux(0, 0, p0_value.unwrap()); - let c_aux2 = BoundaryConstraint::new_aux( - 0, - self.trace_length - 1, - FieldElement::::one(), - ); - - BoundaryConstraints::from_constraints(vec![c1, c2, c3, c4, c_aux1, c_aux2]) - } - - fn transition_constraints( - &self, - ) -> &Vec>> { - &self.transition_constraints - } - - fn context(&self) -> &AirContext { - &self.context - } - - fn composition_poly_degree_bound(&self) -> usize { - self.trace_length() - } - - fn trace_length(&self) -> usize { - self.trace_length - } - - fn pub_inputs(&self) -> &Self::PublicInputs { - &self.pub_inputs - } -} - -/// Given the adress and value columns, it returns the trace table with 5 columns, which are: -/// Addres, Value, Adress Sorted, Value Sorted and a Column of Zeroes (where we'll insert the auxiliary colunn). -pub fn sort_rap_trace( - address: Vec>, - value: Vec>, -) -> TraceTable { - let mut address_value_pairs: Vec<_> = address.iter().zip(value.iter()).collect(); - - address_value_pairs.sort_by_key(|(addr, _)| addr.representative()); - - let (sorted_address, sorted_value): (Vec>, Vec>) = - address_value_pairs - .into_iter() - .map(|(addr, val)| (addr.clone(), val.clone())) - .unzip(); - let main_columns = vec![address.clone(), value.clone(), sorted_address, sorted_value]; - // create a vector with zeros of the same length as the main columns - let zero_vec = vec![FieldElement::::zero(); main_columns[0].len()]; - TraceTable::from_columns(main_columns, vec![zero_vec], 1) -} - -#[cfg(test)] -mod test { - use super::*; - use lambdaworks_math::field::fields::u64_prime_field::FE17; - - #[test] - fn test_sort_rap_trace() { - let address_col = vec![ - FE17::from(5), - FE17::from(2), - FE17::from(3), - FE17::from(4), - FE17::from(1), - FE17::from(6), - FE17::from(7), - FE17::from(8), - ]; - let value_col = vec![ - FE17::from(50), - FE17::from(20), - FE17::from(30), - FE17::from(40), - FE17::from(10), - FE17::from(60), - FE17::from(70), - FE17::from(80), - ]; - - let sorted_trace = sort_rap_trace(address_col.clone(), value_col.clone()); - - let expected_sorted_addresses = vec![ - FE17::from(1), - FE17::from(2), - FE17::from(3), - FE17::from(4), - FE17::from(5), - FE17::from(6), - FE17::from(7), - FE17::from(8), - ]; - let expected_sorted_values = vec![ - FE17::from(10), - FE17::from(20), - FE17::from(30), - FE17::from(40), - FE17::from(50), - FE17::from(60), - FE17::from(70), - FE17::from(80), - ]; - - assert_eq!(sorted_trace.columns_main()[2], expected_sorted_addresses); - assert_eq!(sorted_trace.columns_main()[3], expected_sorted_values); - } -} diff --git a/crates/provers/stark/src/examples/read_only_memory_logup.rs b/crates/provers/stark/src/examples/read_only_memory_logup.rs deleted file mode 100644 index 6a45b20be..000000000 --- a/crates/provers/stark/src/examples/read_only_memory_logup.rs +++ /dev/null @@ -1,704 +0,0 @@ -//! Implementation of a LogUp Lookup Argument example. -//! See our blog post for detailed explanation. -//! - -use std::marker::PhantomData; - -use crate::{ - constraints::{ - boundary::{BoundaryConstraint, BoundaryConstraints}, - transition::TransitionConstraint, - }, - context::AirContext, - proof::options::ProofOptions, - trace::TraceTable, - traits::{TransitionEvaluationContext, AIR}, -}; -use itertools::Itertools; -use lambdaworks_crypto::fiat_shamir::is_transcript::IsTranscript; -use lambdaworks_math::{ - field::{ - element::FieldElement, - traits::{IsFFTField, IsField, IsPrimeField, IsSubFieldOf}, - }, - traits::ByteConversion, -}; - -/// Transition Constraint that ensures the continuity of the sorted address column of a memory. -#[derive(Clone)] -struct ContinuityConstraint + IsFFTField + Send + Sync, E: IsField + Send + Sync> -{ - phantom_f: PhantomData, - phantom_e: PhantomData, -} - -impl ContinuityConstraint -where - F: IsSubFieldOf + IsFFTField + Send + Sync, - E: IsField + Send + Sync, -{ - pub fn new() -> Self { - Self { - phantom_f: PhantomData::, - phantom_e: PhantomData::, - } - } -} - -impl TransitionConstraint for ContinuityConstraint -where - F: IsFFTField + IsSubFieldOf + Send + Sync, - E: IsField + Send + Sync, -{ - fn degree(&self) -> usize { - 2 - } - - fn constraint_idx(&self) -> usize { - 0 - } - - fn end_exemptions(&self) -> usize { - // NOTE: We are assuming that the trace has as length a power of 2. - 1 - } - - fn evaluate( - &self, - evaluation_context: &TransitionEvaluationContext, - transition_evaluations: &mut [FieldElement], - ) { - // In both evaluation contexts, Prover and Verfier will evaluate the transition polynomial in the same way. - // The only difference is that the Prover's Frame has base field and field extension elements, - // while the Verfier's Frame has only field extension elements. - match evaluation_context { - TransitionEvaluationContext::Prover { - frame, - periodic_values: _periodic_values, - rap_challenges: _rap_challenges, - } => { - let first_step = frame.get_evaluation_step(0); - let second_step = frame.get_evaluation_step(1); - - let a_sorted_0 = first_step.get_main_evaluation_element(0, 2); - let a_sorted_1 = second_step.get_main_evaluation_element(0, 2); - // (a'_{i+1} - a'_i)(a'_{i+1} - a'_i - 1) = 0 where a' is the sorted address - let res = (a_sorted_1 - a_sorted_0) - * (a_sorted_1 - a_sorted_0 - FieldElement::::one()); - - // The eval always exists, except if the constraint idx were incorrectly defined. - if let Some(eval) = transition_evaluations.get_mut(self.constraint_idx()) { - *eval = res.to_extension(); - } - } - - TransitionEvaluationContext::Verifier { - frame, - periodic_values: _periodic_values, - rap_challenges: _rap_challenges, - } => { - let first_step = frame.get_evaluation_step(0); - let second_step = frame.get_evaluation_step(1); - - let a_sorted_0 = first_step.get_main_evaluation_element(0, 2); - let a_sorted_1 = second_step.get_main_evaluation_element(0, 2); - // (a'_{i+1} - a'_i)(a'_{i+1} - a'_i - 1) = 0 where a' is the sorted address - let res = (a_sorted_1 - a_sorted_0) - * (a_sorted_1 - a_sorted_0 - FieldElement::::one()); - - // The eval always exists, except if the constraint idx were incorrectly defined. - if let Some(eval) = transition_evaluations.get_mut(self.constraint_idx()) { - *eval = res; - } - } - } - } -} -/// Transition constraint that ensures that same addresses have same values, making the sorted memory read-only. -#[derive(Clone)] -struct SingleValueConstraint< - F: IsSubFieldOf + IsFFTField + Send + Sync, - E: IsField + Send + Sync, -> { - phantom_f: PhantomData, - phantom_e: PhantomData, -} - -impl SingleValueConstraint -where - F: IsSubFieldOf + IsFFTField + Send + Sync, - E: IsField + Send + Sync, -{ - pub fn new() -> Self { - Self { - phantom_f: PhantomData::, - phantom_e: PhantomData::, - } - } -} - -impl TransitionConstraint for SingleValueConstraint -where - F: IsFFTField + IsSubFieldOf + Send + Sync, - E: IsField + Send + Sync, -{ - fn degree(&self) -> usize { - 2 - } - - fn constraint_idx(&self) -> usize { - 1 - } - - fn end_exemptions(&self) -> usize { - // NOTE: We are assuming that the trace has as length a power of 2. - 1 - } - - fn evaluate( - &self, - evaluation_context: &TransitionEvaluationContext, - transition_evaluations: &mut [FieldElement], - ) { - // In both evaluation contexts, Prover and Verfier will evaluate the transition polynomial in the same way. - // The only difference is that the Prover's Frame has base field and field extension elements, - // while the Verfier's Frame has only field extension elements. - match evaluation_context { - TransitionEvaluationContext::Prover { - frame, - periodic_values: _periodic_values, - rap_challenges: _rap_challenges, - } => { - let first_step = frame.get_evaluation_step(0); - let second_step = frame.get_evaluation_step(1); - - let a_sorted_0 = first_step.get_main_evaluation_element(0, 2); - let a_sorted_1 = second_step.get_main_evaluation_element(0, 2); - let v_sorted_0 = first_step.get_main_evaluation_element(0, 3); - let v_sorted_1 = second_step.get_main_evaluation_element(0, 3); - // (v'_{i+1} - v'_i) * (a'_{i+1} - a'_i - 1) = 0 - let res = (v_sorted_1 - v_sorted_0) - * (a_sorted_1 - a_sorted_0 - FieldElement::::one()); - - // The eval always exists, except if the constraint idx were incorrectly defined. - if let Some(eval) = transition_evaluations.get_mut(self.constraint_idx()) { - *eval = res.to_extension(); - } - } - - TransitionEvaluationContext::Verifier { - frame, - periodic_values: _periodic_values, - rap_challenges: _rap_challenges, - } => { - let first_step = frame.get_evaluation_step(0); - let second_step = frame.get_evaluation_step(1); - - let a_sorted_0 = first_step.get_main_evaluation_element(0, 2); - let a_sorted_1 = second_step.get_main_evaluation_element(0, 2); - let v_sorted_0 = first_step.get_main_evaluation_element(0, 3); - let v_sorted_1 = second_step.get_main_evaluation_element(0, 3); - // (v'_{i+1} - v'_i) * (a'_{i+1} - a'_i - 1) = 0 - let res = (v_sorted_1 - v_sorted_0) - * (a_sorted_1 - a_sorted_0 - FieldElement::::one()); - - // The eval always exists, except if the constraint idx were incorrectly defined. - if let Some(eval) = transition_evaluations.get_mut(self.constraint_idx()) { - *eval = res; - } - } - } - } -} -/// Transition constraint that ensures that the sorted columns are a permutation of the original ones. -/// We are using the LogUp construction described in: -/// . -/// See also our post of LogUp argument in blog.lambdaclass.com. -#[derive(Clone)] -struct PermutationConstraint< - F: IsSubFieldOf + IsFFTField + Send + Sync, - E: IsField + Send + Sync, -> { - phantom_f: PhantomData, - phantom_e: PhantomData, -} - -impl PermutationConstraint -where - F: IsSubFieldOf + IsFFTField + Send + Sync, - E: IsField + Send + Sync, -{ - pub fn new() -> Self { - Self { - phantom_f: PhantomData::, - phantom_e: PhantomData::, - } - } -} - -impl TransitionConstraint for PermutationConstraint -where - F: IsSubFieldOf + IsFFTField + Send + Sync, - E: IsField + Send + Sync, -{ - fn degree(&self) -> usize { - 3 - } - - fn constraint_idx(&self) -> usize { - 2 - } - - fn end_exemptions(&self) -> usize { - 1 - } - - fn evaluate( - &self, - evaluation_context: &TransitionEvaluationContext, - transition_evaluations: &mut [FieldElement], - ) { - // In both evaluation contexts, Prover and Verfier will evaluate the transition polynomial in the same way. - // The only difference is that the Prover's Frame has base field and field extension elements, - // while the Verfier's Frame has only field extension elements. - match evaluation_context { - TransitionEvaluationContext::Prover { - frame, - periodic_values: _periodic_values, - rap_challenges, - } => { - let first_step = frame.get_evaluation_step(0); - let second_step = frame.get_evaluation_step(1); - - // Auxiliary frame elements - let s0 = first_step.get_aux_evaluation_element(0, 0); - let s1 = second_step.get_aux_evaluation_element(0, 0); - - // Challenges - let z = &rap_challenges[0]; - let alpha = &rap_challenges[1]; - - // Main frame elements - let a1 = second_step.get_main_evaluation_element(0, 0); - let v1 = second_step.get_main_evaluation_element(0, 1); - let a_sorted_1 = second_step.get_main_evaluation_element(0, 2); - let v_sorted_1 = second_step.get_main_evaluation_element(0, 3); - let m = second_step.get_main_evaluation_element(0, 4); - - let unsorted_term = -(a1 + v1 * alpha) + z; - let sorted_term = -(a_sorted_1 + v_sorted_1 * alpha) + z; - - // We are using the following LogUp equation: - // s1 = s0 + m / sorted_term - 1/unsorted_term. - // Since constraints must be expressed without division, we multiply each term by sorted_term * unsorted_term: - let res = s0 * &unsorted_term * &sorted_term + m * &unsorted_term - - &sorted_term - - s1 * unsorted_term * sorted_term; - - // The eval always exists, except if the constraint idx were incorrectly defined. - if let Some(eval) = transition_evaluations.get_mut(self.constraint_idx()) { - *eval = res; - } - } - - TransitionEvaluationContext::Verifier { - frame, - periodic_values: _periodic_values, - rap_challenges, - } => { - let first_step = frame.get_evaluation_step(0); - let second_step = frame.get_evaluation_step(1); - - // Auxiliary frame elements - let s0 = first_step.get_aux_evaluation_element(0, 0); - let s1 = second_step.get_aux_evaluation_element(0, 0); - - // Challenges - let z = &rap_challenges[0]; - let alpha = &rap_challenges[1]; - - // Main frame elements - let a1 = second_step.get_main_evaluation_element(0, 0); - let v1 = second_step.get_main_evaluation_element(0, 1); - let a_sorted_1 = second_step.get_main_evaluation_element(0, 2); - let v_sorted_1 = second_step.get_main_evaluation_element(0, 3); - let m = second_step.get_main_evaluation_element(0, 4); - - let unsorted_term = z - (a1 + alpha * v1); - let sorted_term = z - (a_sorted_1 + alpha * v_sorted_1); - - // We are using the following LogUp equation: - // s1 = s0 + m / sorted_term - 1/unsorted_term. - // Since constraints must be expressed without division, we multiply each term by sorted_term * unsorted_term: - let res = s0 * &unsorted_term * &sorted_term + m * &unsorted_term - - &sorted_term - - s1 * unsorted_term * sorted_term; - - // The eval always exists, except if the constraint idx were incorrectly defined. - if let Some(eval) = transition_evaluations.get_mut(self.constraint_idx()) { - *eval = res; - } - } - } - } -} - -/// AIR for a continuous read-only memory using the LogUp Lookup Argument. -/// To accompany the understanding of this code you can see corresponding post in blog.lambdaclass.com. -pub struct LogReadOnlyRAP -where - F: IsFFTField + IsSubFieldOf + Send + Sync, - E: IsField + Send + Sync, -{ - context: AirContext, - trace_length: usize, - pub_inputs: LogReadOnlyPublicInputs, - transition_constraints: Vec>>, -} - -#[derive(Clone, Debug)] -pub struct LogReadOnlyPublicInputs -where - F: IsFFTField + Send + Sync, -{ - pub a0: FieldElement, - pub v0: FieldElement, - pub a_sorted_0: FieldElement, - pub v_sorted_0: FieldElement, - // The multiplicity of (a_sorted_0, v_sorted_0) - pub m0: FieldElement, -} - -impl AIR for LogReadOnlyRAP -where - F: IsFFTField + IsSubFieldOf + Send + Sync + 'static, - E: IsField + Send + Sync + 'static, - FieldElement: ByteConversion, -{ - type Field = F; - type FieldExtension = E; - type PublicInputs = LogReadOnlyPublicInputs; - - const STEP_SIZE: usize = 1; - - fn new( - trace_length: usize, - pub_inputs: &Self::PublicInputs, - proof_options: &ProofOptions, - ) -> Self { - let transition_constraints: Vec< - Box>, - > = vec![ - Box::new(ContinuityConstraint::new()), - Box::new(SingleValueConstraint::new()), - Box::new(PermutationConstraint::new()), - ]; - - let context = AirContext { - proof_options: proof_options.clone(), - trace_columns: 6, - transition_offsets: vec![0, 1], - num_transition_constraints: transition_constraints.len(), - }; - - Self { - context, - trace_length, - pub_inputs: pub_inputs.clone(), - transition_constraints, - } - } - - fn build_auxiliary_trace( - &self, - trace: &mut TraceTable, - challenges: &[FieldElement], - ) where - Self::FieldExtension: IsFFTField, - { - // Main table - let main_segment_cols = trace.columns_main(); - let a = &main_segment_cols[0]; - let v = &main_segment_cols[1]; - let a_sorted = &main_segment_cols[2]; - let v_sorted = &main_segment_cols[3]; - let m = &main_segment_cols[4]; - - // Challenges - let z = &challenges[0]; - let alpha = &challenges[1]; - - let trace_len = trace.num_rows(); - let mut aux_col = Vec::new(); - - // s_0 = m_0/(z - (a'_0 + α * v'_0) - 1/(z - (a_0 + α * v_0) - let unsorted_term = (-(&a[0] + &v[0] * alpha) + z).inv().unwrap(); - let sorted_term = (-(&a_sorted[0] + &v_sorted[0] * alpha) + z).inv().unwrap(); - aux_col.push(&m[0] * sorted_term - unsorted_term); - - // Apply the same equation given in the permutation transition contraint to the rest of the trace. - // s_{i+1} = s_i + m_{i+1}/(z - (a'_{i+1} + α * v'_{i+1}) - 1/(z - (a_{i+1} + α * v_{i+1}) - for i in 0..trace_len - 1 { - let unsorted_term = (-(&a[i + 1] + &v[i + 1] * alpha) + z).inv().unwrap(); - let sorted_term = (-(&a_sorted[i + 1] + &v_sorted[i + 1] * alpha) + z) - .inv() - .unwrap(); - aux_col.push(&aux_col[i] + &m[i + 1] * sorted_term - unsorted_term); - } - - for (i, aux_elem) in aux_col.iter().enumerate().take(trace.num_rows()) { - trace.set_aux(i, 0, aux_elem.clone()) - } - } - - fn build_rap_challenges( - &self, - transcript: &mut impl IsTranscript, - ) -> Vec> { - vec![ - transcript.sample_field_element(), - transcript.sample_field_element(), - ] - } - - fn trace_layout(&self) -> (usize, usize) { - (5, 1) - } - - fn boundary_constraints( - &self, - rap_challenges: &[FieldElement], - ) -> BoundaryConstraints { - let a0 = &self.pub_inputs.a0; - let v0 = &self.pub_inputs.v0; - let a_sorted_0 = &self.pub_inputs.a_sorted_0; - let v_sorted_0 = &self.pub_inputs.v_sorted_0; - let m0 = &self.pub_inputs.m0; - let z = &rap_challenges[0]; - let alpha = &rap_challenges[1]; - - // Main boundary constraints - let c1 = BoundaryConstraint::new_main(0, 0, a0.clone().to_extension()); - let c2 = BoundaryConstraint::new_main(1, 0, v0.clone().to_extension()); - let c3 = BoundaryConstraint::new_main(2, 0, a_sorted_0.clone().to_extension()); - let c4 = BoundaryConstraint::new_main(3, 0, v_sorted_0.clone().to_extension()); - let c5 = BoundaryConstraint::new_main(4, 0, m0.clone().to_extension()); - - // Auxiliary boundary constraints - let unsorted_term = (-(a0 + v0 * alpha) + z).inv().unwrap(); - let sorted_term = (-(a_sorted_0 + v_sorted_0 * alpha) + z).inv().unwrap(); - let p0_value = m0 * sorted_term - unsorted_term; - - let c_aux1 = BoundaryConstraint::new_aux(0, 0, p0_value); - let c_aux2 = BoundaryConstraint::new_aux( - 0, - self.trace_length - 1, - FieldElement::::zero(), - ); - - BoundaryConstraints::from_constraints(vec![c1, c2, c3, c4, c5, c_aux1, c_aux2]) - } - - fn transition_constraints( - &self, - ) -> &Vec>> { - &self.transition_constraints - } - - fn context(&self) -> &AirContext { - &self.context - } - - // The prover use this function to define the number of parts of the composition polynomial. - // The number of parts will be: composition_poly_degree_bound() / trace_length(). - // Since we have a transition constraint of degree 3, we need the bound to be two times the trace length. - fn composition_poly_degree_bound(&self) -> usize { - self.trace_length() * 2 - } - - fn trace_length(&self) -> usize { - self.trace_length - } - - fn pub_inputs(&self) -> &Self::PublicInputs { - &self.pub_inputs - } -} - -/// Return a trace table with an auxiliary column full of zeros (that will be then replaced -/// with the correct values by the air) and the following five main columns: -/// The original addresses and values, the sorted addresses and values without duplicates, and -/// the multiplicities of each sorted address and value in the original ones (i.e. how many times -/// they appear in the original address an value columns). -pub fn read_only_logup_trace< - F: IsPrimeField + IsFFTField + IsSubFieldOf + Send + Sync, - E: IsField + Send + Sync, ->( - addresses: Vec>, - values: Vec>, -) -> TraceTable { - let mut address_value_pairs: Vec<_> = addresses.iter().zip(values.iter()).collect(); - address_value_pairs.sort_by_key(|(addr, _)| addr.representative()); - - let mut multiplicities = Vec::new(); - let mut sorted_addresses = Vec::new(); - let mut sorted_values = Vec::new(); - - for (key, group) in &address_value_pairs.into_iter().group_by(|&(a, v)| (a, v)) { - let group_vec: Vec<_> = group.collect(); - multiplicities.push(FieldElement::::from(group_vec.len() as u64)); - sorted_addresses.push(key.0.clone()); - sorted_values.push(key.1.clone()); - } - - // We resize the sorted addresses and values with the last value of each one so they have the - // same number of rows as the original addresses and values. However, their multiplicity should be zero. - sorted_addresses.resize(addresses.len(), sorted_addresses.last().unwrap().clone()); - sorted_values.resize(addresses.len(), sorted_values.last().unwrap().clone()); - multiplicities.resize(addresses.len(), FieldElement::::zero()); - - let main_columns = vec![ - addresses.clone(), - values.clone(), - sorted_addresses, - sorted_values, - multiplicities, - ]; - - // create a vector with zeros of the same length as the main columns - let zero_vec = vec![FieldElement::::zero(); main_columns[0].len()]; - TraceTable::from_columns(main_columns, vec![zero_vec], 1) -} - -#[cfg(test)] -mod test { - use super::*; - use lambdaworks_math::field::fields::{ - fft_friendly::{ - babybear::Babybear31PrimeField, quartic_babybear::Degree4BabyBearExtensionField, - }, - u64_prime_field::{F17, FE17}, - }; - - #[test] - fn tes_logup_trace_construction() { - let address_col = vec![ - FE17::from(3), - FE17::from(7), - FE17::from(2), - FE17::from(8), - FE17::from(4), - FE17::from(5), - FE17::from(1), - FE17::from(6), - ]; - let value_col = vec![ - FE17::from(30), - FE17::from(70), - FE17::from(20), - FE17::from(80), - FE17::from(40), - FE17::from(50), - FE17::from(10), - FE17::from(60), - ]; - - let logup_trace: TraceTable = read_only_logup_trace(address_col, value_col); - - let expected_sorted_addresses = vec![ - FE17::from(1), - FE17::from(2), - FE17::from(3), - FE17::from(4), - FE17::from(5), - FE17::from(6), - FE17::from(7), - FE17::from(8), - ]; - let expected_sorted_values = vec![ - FE17::from(10), - FE17::from(20), - FE17::from(30), - FE17::from(40), - FE17::from(50), - FE17::from(60), - FE17::from(70), - FE17::from(80), - ]; - let expected_multiplicities = vec![ - FE17::one(), - FE17::one(), - FE17::one(), - FE17::one(), - FE17::one(), - FE17::one(), - FE17::one(), - FE17::one(), - ]; - assert_eq!(logup_trace.columns_main()[2], expected_sorted_addresses); - assert_eq!(logup_trace.columns_main()[3], expected_sorted_values); - assert_eq!(logup_trace.columns_main()[4], expected_multiplicities); - } - - #[test] - fn test_logup_trace_construction_2() { - let address_col = vec![ - FieldElement::::from(3), // a0 - FieldElement::::from(2), // a1 - FieldElement::::from(2), // a2 - FieldElement::::from(3), // a3 - FieldElement::::from(4), // a4 - FieldElement::::from(5), // a5 - FieldElement::::from(1), // a6 - FieldElement::::from(3), // a7 - ]; - let value_col = vec![ - FieldElement::::from(30), // v0 - FieldElement::::from(20), // v1 - FieldElement::::from(20), // v2 - FieldElement::::from(30), // v3 - FieldElement::::from(40), // v4 - FieldElement::::from(50), // v5 - FieldElement::::from(10), // v6 - FieldElement::::from(30), // v7 - ]; - - let sorted_address_col = vec![ - FieldElement::::from(1), // a0 - FieldElement::::from(2), // a1 - FieldElement::::from(3), // a2 - FieldElement::::from(4), // a3 - FieldElement::::from(5), // a4 - FieldElement::::from(5), // a5 - FieldElement::::from(5), // a6 - FieldElement::::from(5), // a7 - ]; - let sorted_value_col = vec![ - FieldElement::::from(10), // v0 - FieldElement::::from(20), // v1 - FieldElement::::from(30), // v2 - FieldElement::::from(40), // v3 - FieldElement::::from(50), // v4 - FieldElement::::from(50), // v5 - FieldElement::::from(50), // v6 - FieldElement::::from(50), // v7 - ]; - - let multiplicity_col = vec![ - FieldElement::::from(1), // v0 - FieldElement::::from(2), // v1 - FieldElement::::from(3), // v2 - FieldElement::::from(1), // v3 - FieldElement::::from(1), // v4 - FieldElement::::from(0), // v5 - FieldElement::::from(0), // v6 - FieldElement::::from(0), // v7 - ]; - let logup_trace: TraceTable = - read_only_logup_trace(address_col, value_col); - - assert_eq!(logup_trace.columns_main()[2], sorted_address_col); - assert_eq!(logup_trace.columns_main()[3], sorted_value_col); - assert_eq!(logup_trace.columns_main()[4], multiplicity_col); - } -} diff --git a/crates/provers/stark/src/examples/simple_fibonacci.rs b/crates/provers/stark/src/examples/simple_fibonacci.rs deleted file mode 100644 index 123b32d53..000000000 --- a/crates/provers/stark/src/examples/simple_fibonacci.rs +++ /dev/null @@ -1,176 +0,0 @@ -use crate::{ - constraints::{ - boundary::{BoundaryConstraint, BoundaryConstraints}, - transition::TransitionConstraint, - }, - context::AirContext, - proof::options::ProofOptions, - trace::TraceTable, - traits::{TransitionEvaluationContext, AIR}, -}; -use lambdaworks_math::field::{element::FieldElement, traits::IsFFTField}; -use std::marker::PhantomData; - -#[derive(Clone)] -struct FibConstraint { - phantom: PhantomData, -} - -impl FibConstraint { - pub fn new() -> Self { - Self { - phantom: PhantomData, - } - } -} - -impl TransitionConstraint for FibConstraint -where - F: IsFFTField + Send + Sync, -{ - fn degree(&self) -> usize { - 1 - } - - fn constraint_idx(&self) -> usize { - 0 - } - - fn end_exemptions(&self) -> usize { - 2 - } - - fn evaluate( - &self, - evaluation_context: &TransitionEvaluationContext, - transition_evaluations: &mut [FieldElement], - ) { - let (frame, _periodic_values, _rap_challenges) = match evaluation_context { - TransitionEvaluationContext::Prover { - frame, - periodic_values, - rap_challenges, - } - | TransitionEvaluationContext::Verifier { - frame, - periodic_values, - rap_challenges, - } => (frame, periodic_values, rap_challenges), - }; - - let first_step = frame.get_evaluation_step(0); - let second_step = frame.get_evaluation_step(1); - let third_step = frame.get_evaluation_step(2); - - let a0 = first_step.get_main_evaluation_element(0, 0); - let a1 = second_step.get_main_evaluation_element(0, 0); - let a2 = third_step.get_main_evaluation_element(0, 0); - - let res = a2 - a1 - a0; - - transition_evaluations[self.constraint_idx()] = res; - } -} - -pub struct FibonacciAIR -where - F: IsFFTField, -{ - context: AirContext, - trace_length: usize, - pub_inputs: FibonacciPublicInputs, - constraints: Vec>>, -} - -#[derive(Clone, Debug)] -pub struct FibonacciPublicInputs -where - F: IsFFTField, -{ - pub a0: FieldElement, - pub a1: FieldElement, -} - -impl AIR for FibonacciAIR -where - F: IsFFTField + Send + Sync + 'static, -{ - type Field = F; - type FieldExtension = F; - type PublicInputs = FibonacciPublicInputs; - - const STEP_SIZE: usize = 1; - - fn new( - trace_length: usize, - pub_inputs: &Self::PublicInputs, - proof_options: &ProofOptions, - ) -> Self { - let constraints: Vec>> = - vec![Box::new(FibConstraint::new())]; - - let context = AirContext { - proof_options: proof_options.clone(), - trace_columns: 1, - transition_offsets: vec![0, 1, 2], - num_transition_constraints: constraints.len(), - }; - - Self { - pub_inputs: pub_inputs.clone(), - context, - trace_length, - constraints, - } - } - - fn composition_poly_degree_bound(&self) -> usize { - self.trace_length() - } - - fn transition_constraints(&self) -> &Vec>> { - &self.constraints - } - - fn boundary_constraints( - &self, - _rap_challenges: &[FieldElement], - ) -> BoundaryConstraints { - let a0 = BoundaryConstraint::new_simple_main(0, self.pub_inputs.a0.clone()); - let a1 = BoundaryConstraint::new_simple_main(1, self.pub_inputs.a1.clone()); - - BoundaryConstraints::from_constraints(vec![a0, a1]) - } - - fn context(&self) -> &AirContext { - &self.context - } - - fn trace_length(&self) -> usize { - self.trace_length - } - - fn trace_layout(&self) -> (usize, usize) { - (1, 0) - } - - fn pub_inputs(&self) -> &Self::PublicInputs { - &self.pub_inputs - } -} - -pub fn fibonacci_trace( - initial_values: [FieldElement; 2], - trace_length: usize, -) -> TraceTable { - let mut ret: Vec> = vec![]; - - ret.push(initial_values[0].clone()); - ret.push(initial_values[1].clone()); - - for i in 2..(trace_length) { - ret.push(ret[i - 1].clone() + ret[i - 2].clone()); - } - - TraceTable::from_columns_main(vec![ret], 1) -} diff --git a/crates/provers/stark/src/examples/simple_periodic_cols.rs b/crates/provers/stark/src/examples/simple_periodic_cols.rs deleted file mode 100644 index 132667d13..000000000 --- a/crates/provers/stark/src/examples/simple_periodic_cols.rs +++ /dev/null @@ -1,206 +0,0 @@ -use std::marker::PhantomData; - -use crate::{ - constraints::{ - boundary::{BoundaryConstraint, BoundaryConstraints}, - transition::TransitionConstraint, - }, - context::AirContext, - proof::options::ProofOptions, - trace::TraceTable, - traits::{TransitionEvaluationContext, AIR}, -}; -use lambdaworks_math::field::{element::FieldElement, traits::IsFFTField}; - -pub struct PeriodicConstraint { - phantom: PhantomData, -} -impl PeriodicConstraint { - pub fn new() -> Self { - Self { - phantom: PhantomData, - } - } -} -impl Default for PeriodicConstraint { - fn default() -> Self { - Self::new() - } -} - -impl TransitionConstraint for PeriodicConstraint -where - F: IsFFTField + Send + Sync, -{ - fn degree(&self) -> usize { - 1 - } - - fn constraint_idx(&self) -> usize { - 0 - } - - fn end_exemptions(&self) -> usize { - 2 - } - - fn evaluate( - &self, - evaluation_context: &TransitionEvaluationContext, - transition_evaluations: &mut [FieldElement], - ) { - let (frame, periodic_values, _rap_challenges) = match evaluation_context { - TransitionEvaluationContext::Prover { - frame, - periodic_values, - rap_challenges, - } - | TransitionEvaluationContext::Verifier { - frame, - periodic_values, - rap_challenges, - } => (frame, periodic_values, rap_challenges), - }; - - let first_step = frame.get_evaluation_step(0); - let second_step = frame.get_evaluation_step(1); - let third_step = frame.get_evaluation_step(2); - - let a0 = first_step.get_main_evaluation_element(0, 0); - let a1 = second_step.get_main_evaluation_element(0, 0); - let a2 = third_step.get_main_evaluation_element(0, 0); - - let s = &periodic_values[0]; - - transition_evaluations[self.constraint_idx()] = s * (a2 - a1 - a0); - } -} - -/// A sequence that uses periodic columns. It has two columns -/// - C1: at each step adds the last two values or does -/// nothing depending on C2. -/// - C2: it is a binary column that cycles around [0, 1] -/// -/// C1 | C2 -/// 1 | 0 Boundary col1 = 1 -/// 1 | 1 Boundary col1 = 1 -/// 1 | 0 Does nothing -/// 2 | 1 Adds 1 + 1 -/// 2 | 0 Does nothing -/// 4 | 1 Adds 2 + 2 -/// 4 | 0 ... -/// 8 | 1 -pub struct SimplePeriodicAIR -where - F: IsFFTField, -{ - context: AirContext, - trace_length: usize, - pub_inputs: SimplePeriodicPublicInputs, - transition_constraints: Vec>>, -} - -#[derive(Clone, Debug)] -pub struct SimplePeriodicPublicInputs -where - F: IsFFTField, -{ - pub a0: FieldElement, - pub a1: FieldElement, -} - -impl AIR for SimplePeriodicAIR -where - F: IsFFTField + Send + Sync + 'static, -{ - type Field = F; - type FieldExtension = F; - type PublicInputs = SimplePeriodicPublicInputs; - - const STEP_SIZE: usize = 1; - - fn new( - trace_length: usize, - pub_inputs: &Self::PublicInputs, - proof_options: &ProofOptions, - ) -> Self { - let transition_constraints: Vec< - Box>, - > = vec![Box::new(PeriodicConstraint::new())]; - - let context = AirContext { - proof_options: proof_options.clone(), - trace_columns: 1, - transition_offsets: vec![0, 1, 2], - num_transition_constraints: transition_constraints.len(), - }; - - Self { - pub_inputs: pub_inputs.clone(), - context, - trace_length, - transition_constraints, - } - } - - fn composition_poly_degree_bound(&self) -> usize { - self.trace_length() - } - - fn boundary_constraints( - &self, - _rap_challenges: &[FieldElement], - ) -> BoundaryConstraints { - let a0 = BoundaryConstraint::new_simple_main(0, self.pub_inputs.a0.clone()); - let a1 = BoundaryConstraint::new_simple_main( - self.trace_length() - 1, - self.pub_inputs.a1.clone(), - ); - - BoundaryConstraints::from_constraints(vec![a0, a1]) - } - - fn transition_constraints( - &self, - ) -> &Vec>> { - &self.transition_constraints - } - - fn get_periodic_column_values(&self) -> Vec>> { - vec![vec![FieldElement::zero(), FieldElement::one()]] - } - - fn context(&self) -> &AirContext { - &self.context - } - - fn trace_length(&self) -> usize { - self.trace_length - } - - fn trace_layout(&self) -> (usize, usize) { - (1, 0) - } - - fn pub_inputs(&self) -> &Self::PublicInputs { - &self.pub_inputs - } -} - -pub fn simple_periodic_trace(trace_length: usize) -> TraceTable { - let mut ret: Vec> = vec![]; - - ret.push(FieldElement::one()); - ret.push(FieldElement::one()); - ret.push(FieldElement::one()); - - let mut accum = FieldElement::from(2); - while ret.len() < trace_length - 1 { - ret.push(accum.clone()); - ret.push(accum.clone()); - accum = &accum + &accum; - } - ret.push(accum); - - TraceTable::from_columns_main(vec![ret], 1) -} diff --git a/crates/provers/stark/src/frame.rs b/crates/provers/stark/src/frame.rs deleted file mode 100644 index 0c70dda31..000000000 --- a/crates/provers/stark/src/frame.rs +++ /dev/null @@ -1,88 +0,0 @@ -use crate::{table::TableView, trace::LDETraceTable}; -use itertools::Itertools; -use lambdaworks_math::field::traits::{IsField, IsSubFieldOf}; - -/// A frame represents a collection of trace steps. -/// The collected steps are all the necessary steps for -/// all transition costraints over a trace to be evaluated. -#[derive(Clone, Debug, PartialEq)] -pub struct Frame<'t, F: IsSubFieldOf, E: IsField> -where - E: IsField, - F: IsSubFieldOf, -{ - steps: Vec>, -} - -impl<'t, F: IsSubFieldOf, E: IsField> Frame<'t, F, E> { - pub fn new(steps: Vec>) -> Self { - Self { steps } - } - - pub fn get_evaluation_step(&self, step: usize) -> &TableView<'t, F, E> { - &self.steps[step] - } - - pub fn read_from_lde( - lde_trace: &'t LDETraceTable, - row: usize, - offsets: &[usize], - ) -> Self { - let blowup_factor = lde_trace.blowup_factor; - let num_rows = lde_trace.num_rows(); - let step_size = lde_trace.lde_step_size; - - let lde_steps = offsets - .iter() - .map(|offset| { - let initial_step_row = row + offset * step_size; - let end_step_row = initial_step_row + step_size; - let (table_view_main_data, table_view_aux_data) = (initial_step_row..end_step_row) - .step_by(blowup_factor) - .map(|step_row| { - let step_row_idx = step_row % num_rows; - let main_row = lde_trace.get_main_row(step_row_idx); - let aux_row = lde_trace.get_aux_row(step_row_idx); - (main_row, aux_row) - }) - .unzip(); - - TableView::new(table_view_main_data, table_view_aux_data) - }) - .collect_vec(); - - Frame::new(lde_steps) - } - - pub fn read_step_from_lde( - lde_trace: &'t LDETraceTable, - step: usize, - offsets: &[usize], - ) -> Self { - let blowup_factor = lde_trace.blowup_factor; - let num_rows = lde_trace.num_rows(); - let step_size = lde_trace.lde_step_size; - let row = lde_trace.step_to_row(step); - - let lde_steps = offsets - .iter() - .map(|offset| { - let initial_step_row = row + offset * step_size; - let end_step_row = initial_step_row + step_size; - let (table_view_main_data, table_view_aux_data) = (initial_step_row..end_step_row) - .step_by(blowup_factor) - .map(|step_row| { - let step_row_idx = step_row % num_rows; - let main_row = lde_trace.get_main_row(step_row_idx); - let aux_row = lde_trace.get_aux_row(step_row_idx); - (main_row, aux_row) - }) - .unzip(); - - TableView::new(table_view_main_data, table_view_aux_data) - }) - .collect_vec(); - - Frame::new(lde_steps) - } -} diff --git a/crates/provers/stark/src/fri/fri_commitment.rs b/crates/provers/stark/src/fri/fri_commitment.rs deleted file mode 100644 index 551ead0c3..000000000 --- a/crates/provers/stark/src/fri/fri_commitment.rs +++ /dev/null @@ -1,39 +0,0 @@ -use lambdaworks_crypto::merkle_tree::{merkle::MerkleTree, traits::IsMerkleTreeBackend}; -use lambdaworks_math::{ - field::{element::FieldElement, traits::IsField}, - traits::AsBytes, -}; - -#[derive(Clone)] -pub struct FriLayer -where - F: IsField, - FieldElement: AsBytes, - B: IsMerkleTreeBackend, -{ - pub evaluation: Vec>, - pub merkle_tree: MerkleTree, - pub coset_offset: FieldElement, - pub domain_size: usize, -} - -impl FriLayer -where - F: IsField, - FieldElement: AsBytes, - B: IsMerkleTreeBackend, -{ - pub fn new( - evaluation: &[FieldElement], - merkle_tree: MerkleTree, - coset_offset: FieldElement, - domain_size: usize, - ) -> Self { - Self { - evaluation: evaluation.to_vec(), - merkle_tree, - coset_offset, - domain_size, - } - } -} diff --git a/crates/provers/stark/src/fri/fri_decommit.rs b/crates/provers/stark/src/fri/fri_decommit.rs deleted file mode 100644 index cfdf5bf2f..000000000 --- a/crates/provers/stark/src/fri/fri_decommit.rs +++ /dev/null @@ -1,11 +0,0 @@ -use lambdaworks_crypto::merkle_tree::proof::Proof; -use lambdaworks_math::field::element::FieldElement; -use lambdaworks_math::field::traits::IsField; - -use crate::config::Commitment; - -#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] -pub struct FriDecommitment { - pub layers_auth_paths: Vec>, - pub layers_evaluations_sym: Vec>, -} diff --git a/crates/provers/stark/src/fri/fri_functions.rs b/crates/provers/stark/src/fri/fri_functions.rs deleted file mode 100644 index 33e8a8b43..000000000 --- a/crates/provers/stark/src/fri/fri_functions.rs +++ /dev/null @@ -1,67 +0,0 @@ -use super::Polynomial; -use lambdaworks_math::{ - field::{element::FieldElement, traits::IsField}, - polynomial, -}; - -pub fn fold_polynomial( - poly: &Polynomial>, - beta: &FieldElement, -) -> Polynomial> -where - F: IsField, -{ - let coef = poly.coefficients(); - let even_coef: Vec> = coef.iter().step_by(2).cloned().collect(); - - // odd coeficients of poly are multiplied by beta - let odd_coef_mul_beta: Vec> = coef - .iter() - .skip(1) - .step_by(2) - .map(|v| (v.clone()) * beta) - .collect(); - - let (even_poly, odd_poly) = polynomial::pad_with_zero_coefficients( - &Polynomial::new(&even_coef), - &Polynomial::new(&odd_coef_mul_beta), - ); - even_poly + odd_poly -} - -#[cfg(test)] -mod tests { - use super::fold_polynomial; - use lambdaworks_math::field::element::FieldElement; - use lambdaworks_math::field::fields::u64_prime_field::U64PrimeField; - const MODULUS: u64 = 293; - type FE = FieldElement>; - use lambdaworks_math::polynomial::Polynomial; - - #[test] - fn test_fold() { - let p0 = Polynomial::new(&[ - FE::new(3), - FE::new(1), - FE::new(2), - FE::new(7), - FE::new(3), - FE::new(5), - ]); - let beta = FE::new(4); - let p1 = fold_polynomial(&p0, &beta); - assert_eq!( - p1, - Polynomial::new(&[FE::new(7), FE::new(30), FE::new(23),]) - ); - - let gamma = FE::new(3); - let p2 = fold_polynomial(&p1, &gamma); - assert_eq!(p2, Polynomial::new(&[FE::new(97), FE::new(23),])); - - let delta = FE::new(2); - let p3 = fold_polynomial(&p2, &delta); - assert_eq!(p3, Polynomial::new(&[FE::new(143)])); - assert_eq!(p3.degree(), 0); - } -} diff --git a/crates/provers/stark/src/fri/mod.rs b/crates/provers/stark/src/fri/mod.rs deleted file mode 100644 index b0b102319..000000000 --- a/crates/provers/stark/src/fri/mod.rs +++ /dev/null @@ -1,142 +0,0 @@ -pub mod fri_commitment; -pub mod fri_decommit; -mod fri_functions; - -use lambdaworks_crypto::fiat_shamir::is_transcript::IsTranscript; -use lambdaworks_math::field::traits::{IsFFTField, IsField}; -use lambdaworks_math::traits::AsBytes; -use lambdaworks_math::{ - fft::cpu::bit_reversing::in_place_bit_reverse_permute, field::traits::IsSubFieldOf, -}; -pub use lambdaworks_math::{ - field::{element::FieldElement, fields::u64_prime_field::U64PrimeField}, - polynomial::Polynomial, -}; - -use crate::config::{BatchedMerkleTree, BatchedMerkleTreeBackend}; - -use self::fri_commitment::FriLayer; -use self::fri_decommit::FriDecommitment; -use self::fri_functions::fold_polynomial; - -pub fn commit_phase, E: IsField>( - number_layers: usize, - p_0: Polynomial>, - transcript: &mut impl IsTranscript, - coset_offset: &FieldElement, - domain_size: usize, -) -> ( - FieldElement, - Vec>>, -) -where - FieldElement: AsBytes + Sync + Send, - FieldElement: AsBytes + Sync + Send, -{ - let mut domain_size = domain_size; - - let mut fri_layer_list = Vec::with_capacity(number_layers); - let mut current_layer: FriLayer>; - let mut current_poly = p_0; - - let mut coset_offset = coset_offset.clone(); - - for _ in 1..number_layers { - // <<<< Receive challenge 𝜁ₖ₋₁ - let zeta = transcript.sample_field_element(); - coset_offset = coset_offset.square(); - domain_size /= 2; - - // Compute layer polynomial and domain - current_poly = FieldElement::::from(2) * fold_polynomial(¤t_poly, &zeta); - current_layer = new_fri_layer(¤t_poly, &coset_offset, domain_size); - let new_data = ¤t_layer.merkle_tree.root; - fri_layer_list.push(current_layer.clone()); // TODO: remove this clone - - // >>>> Send commitment: [pₖ] - transcript.append_bytes(new_data); - } - - // <<<< Receive challenge: 𝜁ₙ₋₁ - let zeta = transcript.sample_field_element(); - - let last_poly = FieldElement::::from(2) * fold_polynomial(¤t_poly, &zeta); - - let last_value = last_poly - .coefficients() - .first() - .unwrap_or(&FieldElement::zero()) - .clone(); - - // >>>> Send value: pₙ - transcript.append_field_element(&last_value); - - (last_value, fri_layer_list) -} - -pub fn query_phase( - fri_layers: &Vec>>, - iotas: &[usize], -) -> Vec> -where - FieldElement: AsBytes + Sync + Send, -{ - if !fri_layers.is_empty() { - let query_list = iotas - .iter() - .map(|iota_s| { - let mut layers_evaluations_sym = Vec::new(); - let mut layers_auth_paths_sym = Vec::new(); - - let mut index = *iota_s; - for layer in fri_layers { - // symmetric element - let evaluation_sym = layer.evaluation[index ^ 1].clone(); - let auth_path_sym = layer.merkle_tree.get_proof_by_pos(index >> 1).unwrap(); - layers_evaluations_sym.push(evaluation_sym); - layers_auth_paths_sym.push(auth_path_sym); - - index >>= 1; - } - - FriDecommitment { - layers_auth_paths: layers_auth_paths_sym, - layers_evaluations_sym, - } - }) - .collect(); - - query_list - } else { - vec![] - } -} - -pub fn new_fri_layer, E: IsField>( - poly: &Polynomial>, - coset_offset: &FieldElement, - domain_size: usize, -) -> crate::fri::fri_commitment::FriLayer> -where - FieldElement: AsBytes + Sync + Send, - FieldElement: AsBytes + Sync + Send, -{ - let mut evaluation = - Polynomial::evaluate_offset_fft(poly, 1, Some(domain_size), coset_offset).unwrap(); // TODO: return error - - in_place_bit_reverse_permute(&mut evaluation); - - let mut to_commit = Vec::new(); - for chunk in evaluation.chunks(2) { - to_commit.push(vec![chunk[0].clone(), chunk[1].clone()]); - } - - let merkle_tree = BatchedMerkleTree::build(&to_commit).unwrap(); - - FriLayer::new( - &evaluation, - merkle_tree, - coset_offset.clone().to_extension(), - domain_size, - ) -} diff --git a/crates/provers/stark/src/grinding.rs b/crates/provers/stark/src/grinding.rs deleted file mode 100644 index bd8d16645..000000000 --- a/crates/provers/stark/src/grinding.rs +++ /dev/null @@ -1,169 +0,0 @@ -#[cfg(feature = "parallel")] -use rayon::prelude::{IntoParallelIterator, ParallelIterator}; -use sha3::{Digest, Keccak256}; - -const PREFIX: [u8; 8] = [0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xed]; - -/// Checks if the bit-string `Hash(Hash(prefix || seed || grinding_factor) || nonce)` -/// has at least `grinding_factor` zeros to the left. -/// `prefix` is the bit-string `0x123456789abcded` -/// -/// # Parameters -/// -/// * `seed`: the input seed, -/// * `nonce`: the value to be tested, -/// * `grinding_factor`: the number of leading zeros needed. -/// -/// # Returns -/// -/// `true` if the number of leading zeros is at least `grinding_factor`, and `false` otherwise. -pub fn is_valid_nonce(seed: &[u8; 32], nonce: u64, grinding_factor: u8) -> bool { - let inner_hash = get_inner_hash(seed, grinding_factor); - let limit = 1 << (64 - grinding_factor); - is_valid_nonce_for_inner_hash(&inner_hash, nonce, limit) -} - -/// Performs grinding, returning a new nonce for the proof. -/// The nonce generated is such that: -/// Hash(Hash(prefix || seed || grinding_factor) || nonce) has at least `grinding_factor` zeros -/// to the left. -/// `prefix` is the bit-string `0x123456789abcded` -/// -/// # Parameters -/// -/// * `seed`: the input seed, -/// * `grinding_factor`: the number of leading zeros needed. -/// -/// # Returns -/// -/// A `nonce` satisfying the required condition. -pub fn generate_nonce(seed: &[u8; 32], grinding_factor: u8) -> Option { - let inner_hash = get_inner_hash(seed, grinding_factor); - let limit = 1 << (64 - grinding_factor); - - #[cfg(not(feature = "parallel"))] - return (0..u64::MAX).find(|&candidate_nonce| { - is_valid_nonce_for_inner_hash(&inner_hash, candidate_nonce, limit) - }); - - #[cfg(feature = "parallel")] - return (0..u64::MAX).into_par_iter().find_any(|&candidate_nonce| { - is_valid_nonce_for_inner_hash(&inner_hash, candidate_nonce, limit) - }); -} - -/// Checks if the leftmost 8 bytes of `Hash(inner_hash || candidate_nonce)` are less than `limit` -/// when interpreted as `u64`. -#[inline(always)] -fn is_valid_nonce_for_inner_hash(inner_hash: &[u8; 32], candidate_nonce: u64, limit: u64) -> bool { - let mut data = [0; 40]; - data[..32].copy_from_slice(inner_hash); - data[32..].copy_from_slice(&candidate_nonce.to_be_bytes()); - - let digest = Keccak256::digest(data); - - let seed_head = u64::from_be_bytes(digest[..8].try_into().unwrap()); - seed_head < limit -} - -/// Returns the bit-string constructed as -/// Hash(prefix || seed || grinding_factor) -/// `prefix` is the bit-string `0x123456789abcded` -fn get_inner_hash(seed: &[u8; 32], grinding_factor: u8) -> [u8; 32] { - let mut inner_data = [0u8; 41]; - inner_data[0..8].copy_from_slice(&PREFIX); - inner_data[8..40].copy_from_slice(seed); - inner_data[40] = grinding_factor; - - let digest = Keccak256::digest(inner_data); - digest[..32].try_into().unwrap() -} - -#[cfg(test)] -mod test { - use crate::grinding::is_valid_nonce; - - #[test] - fn test_invalid_nonce_grinding_factor_6() { - // This setting produces a hash with 5 leading zeros, therefore not enough for grinding - // factor 6. - let seed = [ - 174, 187, 26, 134, 6, 43, 222, 151, 140, 48, 52, 67, 69, 181, 177, 165, 111, 222, 148, - 92, 130, 241, 171, 2, 62, 34, 95, 159, 37, 116, 155, 217, - ]; - let nonce = 4; - let grinding_factor = 6; - assert!(!is_valid_nonce(&seed, nonce, grinding_factor)); - } - - #[test] - fn test_invalid_nonce_grinding_factor_9() { - // This setting produces a hash with 8 leading zeros, therefore not enough for grinding - // factor 9. - let seed = [ - 174, 187, 26, 134, 6, 43, 222, 151, 140, 48, 52, 67, 69, 181, 177, 165, 111, 222, 148, - 92, 130, 241, 171, 2, 62, 34, 95, 159, 37, 116, 155, 217, - ]; - let nonce = 287; - let grinding_factor = 9; - assert!(!is_valid_nonce(&seed, nonce, grinding_factor)); - } - - #[test] - fn test_is_valid_nonce_grinding_factor_10() { - let seed = [ - 37, 68, 26, 150, 139, 142, 66, 175, 33, 47, 199, 160, 9, 109, 79, 234, 135, 254, 39, - 11, 225, 219, 206, 108, 224, 165, 25, 72, 189, 96, 218, 95, - ]; - let nonce = 0x5ba; - let grinding_factor = 10; - assert!(is_valid_nonce(&seed, nonce, grinding_factor)); - } - - #[test] - fn test_is_valid_nonce_grinding_factor_20() { - let seed = [ - 37, 68, 26, 150, 139, 142, 66, 175, 33, 47, 199, 160, 9, 109, 79, 234, 135, 254, 39, - 11, 225, 219, 206, 108, 224, 165, 25, 72, 189, 96, 218, 95, - ]; - let nonce = 0x2c5db8; - let grinding_factor = 20; - assert!(is_valid_nonce(&seed, nonce, grinding_factor)); - } - - #[test] - fn test_invalid_nonce_grinding_factor_19() { - // This setting would pass for grinding factor 20 instead of 19. The nonce is invalid - // here because the grinding factor is part of the inner hash, changing the outer hash - // and the resulting number of leading zeros. - let seed = [ - 37, 68, 26, 150, 139, 142, 66, 175, 33, 47, 199, 160, 9, 109, 79, 234, 135, 254, 39, - 11, 225, 219, 206, 108, 224, 165, 25, 72, 189, 96, 218, 95, - ]; - let nonce = 0x2c5db8; - let grinding_factor = 19; - assert!(!is_valid_nonce(&seed, nonce, grinding_factor)); - } - - #[test] - fn test_is_valid_nonce_grinding_factor_30() { - let seed = [ - 37, 68, 26, 150, 139, 142, 66, 175, 33, 47, 199, 160, 9, 109, 79, 234, 135, 254, 39, - 11, 225, 219, 206, 108, 224, 165, 25, 72, 189, 96, 218, 95, - ]; - let nonce = 0x1ae839e1; - let grinding_factor = 30; - assert!(is_valid_nonce(&seed, nonce, grinding_factor)); - } - - #[test] - fn test_is_valid_nonce_grinding_factor_33() { - let seed = [ - 37, 68, 26, 150, 139, 142, 66, 175, 33, 47, 199, 160, 9, 109, 79, 234, 135, 254, 39, - 11, 225, 219, 206, 108, 224, 165, 25, 72, 189, 96, 218, 95, - ]; - let nonce = 0x4cc3123f; - let grinding_factor = 33; - assert!(is_valid_nonce(&seed, nonce, grinding_factor)); - } -} diff --git a/crates/provers/stark/src/lib.rs b/crates/provers/stark/src/lib.rs deleted file mode 100644 index 6ea2f366a..000000000 --- a/crates/provers/stark/src/lib.rs +++ /dev/null @@ -1,29 +0,0 @@ -use lambdaworks_math::field::{ - element::FieldElement, fields::fft_friendly::stark_252_prime_field::Stark252PrimeField, -}; - -pub mod constraints; -pub mod context; -pub mod debug; -pub mod domain; -pub mod examples; -pub mod frame; -pub mod fri; -pub mod grinding; -pub mod proof; -pub mod prover; -pub mod table; -pub mod trace; -pub mod traits; -pub mod transcript; -pub mod utils; -pub mod verifier; - -#[cfg(test)] -pub mod tests; - -/// Configurations of the Prover available in compile time -pub mod config; - -pub type PrimeField = Stark252PrimeField; -pub type Felt252 = FieldElement; diff --git a/crates/provers/stark/src/proof/errors.rs b/crates/provers/stark/src/proof/errors.rs deleted file mode 100644 index c0e875425..000000000 --- a/crates/provers/stark/src/proof/errors.rs +++ /dev/null @@ -1,7 +0,0 @@ -#[derive(Debug)] -pub enum InsecureOptionError { - /// Field Size is not big enough - FieldSize, - /// Number of security bits is not enough - LowSecurityBits, -} diff --git a/crates/provers/stark/src/proof/mod.rs b/crates/provers/stark/src/proof/mod.rs deleted file mode 100644 index 15165edc7..000000000 --- a/crates/provers/stark/src/proof/mod.rs +++ /dev/null @@ -1,3 +0,0 @@ -pub mod errors; -pub mod options; -pub mod stark; diff --git a/crates/provers/stark/src/proof/options.rs b/crates/provers/stark/src/proof/options.rs deleted file mode 100644 index 105e442e2..000000000 --- a/crates/provers/stark/src/proof/options.rs +++ /dev/null @@ -1,269 +0,0 @@ -use super::errors::InsecureOptionError; -use lambdaworks_math::field::traits::IsPrimeField; - -#[cfg(feature = "wasm")] -use wasm_bindgen::prelude::wasm_bindgen; - -pub enum SecurityLevel { - Conjecturable80Bits, - Conjecturable100Bits, - Conjecturable128Bits, - Provable80Bits, - Provable100Bits, - Provable128Bits, -} - -/// The options for the proof -/// -/// - `blowup_factor`: the blowup factor for the trace -/// - `fri_number_of_queries`: the number of queries for the FRI layer -/// - `coset_offset`: the offset for the coset -/// - `grinding_factor`: the number of leading zeros that we want for the Hash(hash || nonce) -#[cfg_attr(feature = "wasm", wasm_bindgen)] -#[derive(Clone, Debug)] -pub struct ProofOptions { - pub blowup_factor: u8, - pub fri_number_of_queries: usize, - pub coset_offset: u64, - pub grinding_factor: u8, -} - -impl ProofOptions { - // TODO: Make it work for extended fields - const EXTENSION_DEGREE: usize = 1; - // Estimated maximum domain size. 2^40 = 1 TB - const NUM_BITS_MAX_DOMAIN_SIZE: usize = 40; - - /// See section 5.10.1 of https://eprint.iacr.org/2021/582.pdf - pub fn new_secure(security_level: SecurityLevel, coset_offset: u64) -> Self { - match security_level { - SecurityLevel::Conjecturable80Bits => ProofOptions { - blowup_factor: 4, - fri_number_of_queries: 31, - coset_offset, - grinding_factor: 20, - }, - SecurityLevel::Conjecturable100Bits => ProofOptions { - blowup_factor: 4, - fri_number_of_queries: 41, - coset_offset, - grinding_factor: 20, - }, - SecurityLevel::Conjecturable128Bits => ProofOptions { - blowup_factor: 4, - fri_number_of_queries: 55, - coset_offset, - grinding_factor: 20, - }, - SecurityLevel::Provable80Bits => ProofOptions { - blowup_factor: 4, - fri_number_of_queries: 80, - coset_offset, - grinding_factor: 20, - }, - SecurityLevel::Provable100Bits => ProofOptions { - blowup_factor: 4, - fri_number_of_queries: 104, - coset_offset, - grinding_factor: 20, - }, - SecurityLevel::Provable128Bits => ProofOptions { - blowup_factor: 4, - fri_number_of_queries: 140, - coset_offset, - grinding_factor: 20, - }, - } - } - - /// Checks security of proof options given 128 bits of security - pub fn new_with_checked_security( - blowup_factor: u8, - fri_number_of_queries: usize, - coset_offset: u64, - grinding_factor: u8, - security_target: u8, - ) -> Result { - Self::check_field_security::(security_target)?; - - let num_bits_blowup_factor = blowup_factor.trailing_zeros() as usize; - - if security_target as usize - >= grinding_factor as usize + num_bits_blowup_factor * fri_number_of_queries - 1 - { - return Err(InsecureOptionError::LowSecurityBits); - } - - Ok(ProofOptions { - blowup_factor, - fri_number_of_queries, - coset_offset, - grinding_factor, - }) - } - - /// Checks provable security of proof options given 128 bits of security - /// This is an approximation. It's stricter than the formula in the paper. - /// See https://eprint.iacr.org/2021/582.pdf - pub fn new_with_checked_provable_security( - blowup_factor: u8, - fri_number_of_queries: usize, - coset_offset: u64, - grinding_factor: u8, - security_target: u8, - ) -> Result { - Self::check_field_security::(security_target)?; - - let num_bits_blowup_factor = blowup_factor.leading_zeros() as usize; - - if (security_target as usize) - < grinding_factor as usize + num_bits_blowup_factor * fri_number_of_queries / 2 - { - return Err(InsecureOptionError::LowSecurityBits); - } - - Ok(ProofOptions { - blowup_factor, - fri_number_of_queries, - coset_offset, - grinding_factor, - }) - } - - fn check_field_security( - security_target: u8, - ) -> Result<(), InsecureOptionError> { - if F::field_bit_size() * Self::EXTENSION_DEGREE - <= security_target as usize + Self::NUM_BITS_MAX_DOMAIN_SIZE - { - return Err(InsecureOptionError::FieldSize); - } - - Ok(()) - } - - /// Default proof options used for testing purposes. - /// These options should never be used in production. - pub fn default_test_options() -> Self { - Self { - blowup_factor: 4, - fri_number_of_queries: 3, - coset_offset: 3, - grinding_factor: 1, - } - } -} - -#[cfg(test)] -mod tests { - use lambdaworks_math::field::fields::{ - fft_friendly::stark_252_prime_field::Stark252PrimeField, u64_prime_field::F17, - }; - - use crate::proof::{errors::InsecureOptionError, options::SecurityLevel}; - - use super::ProofOptions; - - #[test] - fn u64_prime_field_is_not_large_enough_to_be_secure() { - let ProofOptions { - blowup_factor, - fri_number_of_queries, - coset_offset, - grinding_factor, - } = ProofOptions::new_secure(SecurityLevel::Conjecturable128Bits, 1); - - let u64_options = ProofOptions::new_with_checked_security::( - blowup_factor, - fri_number_of_queries, - coset_offset, - grinding_factor, - 128, - ); - - assert!(matches!(u64_options, Err(InsecureOptionError::FieldSize))); - } - - #[test] - fn generated_stark_proof_options_for_128_bits_are_secure() { - let ProofOptions { - blowup_factor, - fri_number_of_queries, - coset_offset, - grinding_factor, - } = ProofOptions::new_secure(SecurityLevel::Conjecturable128Bits, 1); - - let secure_options = ProofOptions::new_with_checked_security::( - blowup_factor, - fri_number_of_queries, - coset_offset, - grinding_factor, - 128, - ); - - assert!(secure_options.is_ok()); - } - - #[test] - fn generated_proof_options_for_128_bits_with_one_fri_query_less_are_insecure() { - let ProofOptions { - blowup_factor, - fri_number_of_queries, - coset_offset, - grinding_factor, - } = ProofOptions::new_secure(SecurityLevel::Conjecturable128Bits, 1); - - let insecure_options = ProofOptions::new_with_checked_security::( - blowup_factor, - fri_number_of_queries - 1, - coset_offset, - grinding_factor, - 128, - ); - - assert!(matches!( - insecure_options, - Err(InsecureOptionError::LowSecurityBits) - )); - } - - #[test] - fn generated_stark_proof_options_for_100_bits_are_secure_for_100_target_bits() { - let ProofOptions { - blowup_factor, - fri_number_of_queries, - coset_offset, - grinding_factor, - } = ProofOptions::new_secure(SecurityLevel::Conjecturable100Bits, 1); - - let secure_options = ProofOptions::new_with_checked_security::( - blowup_factor, - fri_number_of_queries, - coset_offset, - grinding_factor, - 100, - ); - - assert!(secure_options.is_ok()); - } - - #[test] - fn generated_stark_proof_options_for_80_bits_are_secure_for_80_target_bits() { - let ProofOptions { - blowup_factor, - fri_number_of_queries, - coset_offset, - grinding_factor, - } = ProofOptions::new_secure(SecurityLevel::Conjecturable80Bits, 1); - - let secure_options = ProofOptions::new_with_checked_security::( - blowup_factor, - fri_number_of_queries, - coset_offset, - grinding_factor, - 80, - ); - - assert!(secure_options.is_ok()); - } -} diff --git a/crates/provers/stark/src/proof/stark.rs b/crates/provers/stark/src/proof/stark.rs deleted file mode 100644 index f7ddb9335..000000000 --- a/crates/provers/stark/src/proof/stark.rs +++ /dev/null @@ -1,465 +0,0 @@ -use std::collections::{BTreeSet, HashMap, HashSet}; - -use lambdaworks_crypto::merkle_tree::proof::Proof; -use lambdaworks_math::{ - field::{ - element::FieldElement, - fields::fft_friendly::stark_252_prime_field::Stark252PrimeField, - traits::{IsField, IsSubFieldOf}, - }, - traits::AsBytes, -}; - -use crate::{ - config::Commitment, - domain::Domain, - fri::fri_decommit::FriDecommitment, - table::Table, - traits::AIR, - transcript::StoneProverTranscript, - verifier::{IsStarkVerifier, Verifier}, -}; - -use super::options::ProofOptions; - -#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] -pub struct PolynomialOpenings { - pub proof: Proof, - pub proof_sym: Proof, - pub evaluations: Vec>, - pub evaluations_sym: Vec>, -} - -#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] -pub struct DeepPolynomialOpening, E: IsField> { - pub composition_poly: PolynomialOpenings, - pub main_trace_polys: PolynomialOpenings, - pub aux_trace_polys: Option>, -} - -pub type DeepPolynomialOpenings = Vec>; - -#[derive(Debug, serde::Serialize, serde::Deserialize)] -pub struct StarkProof, E: IsField> { - // Length of the execution trace - pub trace_length: usize, - // Commitments of the trace columns - // [tⱼ] - pub lde_trace_main_merkle_root: Commitment, - // Commitments of auxiliary trace columns - // [tⱼ] - pub lde_trace_aux_merkle_root: Option, - // tⱼ(zgᵏ) - pub trace_ood_evaluations: Table, - // Commitments to Hᵢ - pub composition_poly_root: Commitment, - // Hᵢ(z^N) - pub composition_poly_parts_ood_evaluation: Vec>, - // [pₖ] - pub fri_layers_merkle_roots: Vec, - // pₙ - pub fri_last_value: FieldElement, - // Open(pₖ(Dₖ), −𝜐ₛ^(2ᵏ)) - pub query_list: Vec>, - // Open(H₁(D_LDE, 𝜐ᵢ), Open(H₂(D_LDE, 𝜐ᵢ), Open(tⱼ(D_LDE), 𝜐ᵢ) - // Open(H₁(D_LDE, -𝜐ᵢ), Open(H₂(D_LDE, -𝜐ᵢ), Open(tⱼ(D_LDE), -𝜐ᵢ) - pub deep_poly_openings: DeepPolynomialOpenings, - // nonce obtained from grinding - pub nonce: Option, -} - -/// Serializer compatible with Stone prover -/// (https://github.com/starkware-libs/stone-prover/) -pub struct StoneCompatibleSerializer; - -impl StoneCompatibleSerializer { - pub fn serialize_proof( - proof: &StarkProof, - public_inputs: &A::PublicInputs, - options: &ProofOptions, - ) -> Vec - where - A: AIR, - A::PublicInputs: AsBytes, - { - let mut output = Vec::new(); - - Self::append_trace_commitment(proof, &mut output); - Self::append_composition_polynomial_commitment(proof, &mut output); - Self::append_out_of_domain_evaluations(proof, &mut output); - Self::append_fri_commit_phase_commitments(proof, &mut output); - Self::append_proof_of_work_nonce(proof, &mut output); - - let fri_query_indexes = Self::get_fri_query_indexes::(proof, public_inputs, options); - Self::append_fri_query_phase_first_layer(proof, &fri_query_indexes, &mut output); - Self::append_fri_query_phase_inner_layers(proof, &fri_query_indexes, &mut output); - - output - } - - /// Appends the root bytes of the Merkle tree for the main trace, and if there is a RAP round, - /// it also appends the root bytes of the Merkle tree for the extended columns. - fn append_trace_commitment( - proof: &StarkProof, - output: &mut Vec, - ) { - output.extend_from_slice(&proof.lde_trace_main_merkle_root); - - if let Some(lde_trace_aux_merkle_root) = proof.lde_trace_aux_merkle_root { - output.extend_from_slice(&lde_trace_aux_merkle_root); - } - } - - /// Appends the root bytes of the Merkle tree for the composition polynomial. - fn append_composition_polynomial_commitment( - proof: &StarkProof, - output: &mut Vec, - ) { - output.extend_from_slice(&proof.composition_poly_root); - } - - /// Appends the bytes of the evaluations of the trace `t_1, ..., t_m` and composition polynomial parts - /// `H_1, ..., H_s` at the out of domain challenge `z`, its shifts `g^i z` and its power `z^s`, respectively. - /// These are sorted as follows: first the evaluations of the trace in increasing order of - /// trace column and shift number. Then all the evaluations of the parts of the composition - /// polynomial. That is: - /// - /// t_1(z), ..., t_1(g^K z), t_2(z), ..., t_2(g^K z), ..., t_m(g z), ..., t_m(g^K z), H_1(z^s), ..., H_s(z^s). - /// - /// Here, K is the length of the frame size. - fn append_out_of_domain_evaluations( - proof: &StarkProof, - output: &mut Vec, - ) { - for i in 0..proof.trace_ood_evaluations.width { - for j in 0..proof.trace_ood_evaluations.height { - output.extend_from_slice(&proof.trace_ood_evaluations.get_row(j)[i].as_bytes()); - } - } - - for elem in proof.composition_poly_parts_ood_evaluation.iter() { - output.extend_from_slice(&elem.as_bytes()); - } - } - - /// Appends the commitments to the inner layers of FRI followed by the element of the last layer. - fn append_fri_commit_phase_commitments( - proof: &StarkProof, - output: &mut Vec, - ) { - output.extend_from_slice( - &proof - .fri_layers_merkle_roots - .iter() - .flatten() - .cloned() - .collect::>(), - ); - - output.extend_from_slice(&proof.fri_last_value.as_bytes()); - } - - /// Appends the proof of work nonce in case there is one. There could be none if the `grinding_factor` - /// was set to 0 during proof generation. In that case nothing is appended. - fn append_proof_of_work_nonce( - proof: &StarkProof, - output: &mut Vec, - ) { - if let Some(nonce_value) = proof.nonce { - output.extend_from_slice(&nonce_value.to_be_bytes()); - } - } - - /// Appends the values and authentication paths of the trace and composition polynomial parts - /// needed for the first layer of FRI. Next we describe the order in which these are appended. - /// - /// Each FRI query index `i` determines a pair of elements `d_i` and `-d_i` on the domain of the - /// first layer. - /// Let BT_i be the concatenation of the bytes of the following values - /// t_1(d_i), t_2(d_i), ..., t_m(d_i), t_1(-d_i), t_2(-d_i), ..., t_m(-d_i), - /// where m is the total number of columns, including RAP extended ones. - /// Similarly, let BH_i be the concatenation of the bytes of the following elements - /// H_1(d_i), ..., H_s(d_i), H_1(-d_i), ..., H_s(-d_i), - /// where s is the number of parts into which the composition polynomial was broken. - /// - /// If i_1, ..., i_k are all the FRI query indexes sorted in increasing order and without repeated - /// values, then this method appends the following to the output: - /// - /// BT_{i_1} | BT_{i_2} | ... | BT_{i_k} | TraceMergedPaths | BH_{i_1} | BH_{i_2} | ... | B_{i_k} | CompositionMergedPaths. - /// - /// Where TraceMergedPaths is the merged authentication paths of the trace Merkle tree for all queries - /// and similarly, CompositionMergedPaths is the merged authentication paths of the composition polynomial - /// Merkle tree for all queries (see the `merge_authentication_paths` method). - /// - /// Example: - /// If there are 6 queries [3, 1, 5, 2, 1, 3], then this method appends the - /// following to the output: - /// `BT_1 | BT_2 | BT_3 | BT_5 | TraceMergedPaths | BH_1 | BH_2 | BH_3 | BH_5 | CompositionMergedPaths` - fn append_fri_query_phase_first_layer( - proof: &StarkProof, - fri_query_indexes: &[usize], - output: &mut Vec, - ) { - let mut fri_first_layer_openings: Vec<_> = proof - .deep_poly_openings - .iter() - .zip(fri_query_indexes.iter()) - .collect(); - // Remove repeated values - let mut seen = HashSet::new(); - fri_first_layer_openings.retain(|&(_, index)| seen.insert(index)); - // Sort by increasing value of query - fri_first_layer_openings.sort_by(|a, b| a.1.cmp(b.1)); - - // Append BT_{i_1} | BT_{i_2} | ... | BT_{i_k} - for (opening, _) in fri_first_layer_openings.iter() { - for elem in opening.main_trace_polys.evaluations.iter() { - output.extend_from_slice(&elem.as_bytes()); - } - if let Some(aux) = &opening.aux_trace_polys { - for elem in aux.evaluations.iter() { - output.extend_from_slice(&elem.as_bytes()); - } - } - - for elem in opening.main_trace_polys.evaluations_sym.iter() { - output.extend_from_slice(&elem.as_bytes()); - } - if let Some(aux) = &opening.aux_trace_polys { - for elem in aux.evaluations_sym.iter() { - output.extend_from_slice(&elem.as_bytes()); - } - } - } - - let fri_trace_query_indexes: Vec<_> = fri_query_indexes - .iter() - .flat_map(|query| vec![query * 2, query * 2 + 1]) - .collect(); - - // Append TraceMergedPaths - // Main trace - let fri_trace_paths: Vec<_> = proof - .deep_poly_openings - .iter() - .flat_map(|opening| { - vec![ - &opening.main_trace_polys.proof, - &opening.main_trace_polys.proof_sym, - ] - }) - .collect(); - let nodes = Self::merge_authentication_paths(&fri_trace_paths, &fri_trace_query_indexes); - for node in nodes.iter() { - output.extend_from_slice(node); - } - - // Aux trace - let mut all_openings_aux_trace_polys_are_some = true; - let mut fri_trace_paths: Vec<&Proof> = Vec::new(); - for opening in proof.deep_poly_openings.iter() { - if let Some(aux_trace_polys) = &opening.aux_trace_polys { - fri_trace_paths.push(&aux_trace_polys.proof); - fri_trace_paths.push(&aux_trace_polys.proof_sym); - } else { - all_openings_aux_trace_polys_are_some = false; - } - } - if all_openings_aux_trace_polys_are_some { - let nodes = - Self::merge_authentication_paths(&fri_trace_paths, &fri_trace_query_indexes); - for node in nodes.iter() { - output.extend_from_slice(node); - } - } - - // Append BH_{i_1} | BH_{i_2} | ... | B_{i_k} - for (opening, _) in fri_first_layer_openings.iter() { - for elem in opening.composition_poly.evaluations.iter() { - output.extend_from_slice(&elem.as_bytes()); - } - for elem in opening.composition_poly.evaluations_sym.iter() { - output.extend_from_slice(&elem.as_bytes()); - } - } - - // Append CompositionMergedPaths - let fri_composition_paths: Vec<_> = proof - .deep_poly_openings - .iter() - .map(|opening| &opening.composition_poly.proof) - .collect(); - let nodes = Self::merge_authentication_paths(&fri_composition_paths, fri_query_indexes); - for node in nodes.iter() { - output.extend_from_slice(node); - } - } - - /// Appends the values and authentication paths needed for the inner layers of FRI. - /// Just as in the append_fri_query_phase_first_layer, for each layer, the authentication - /// paths are merged and the redundant field elements are not sent, in order to optimize - /// the size of the proof. When having multiple queries we can have repeated field elements - /// for two reasons: either we are sending two times the same field element because of - /// a repeated query, or we are sending a field element that the verifier could simply - /// derive from values from previous layers. - /// - /// For each layer i there are: - /// - X_i = { p_i(-d_j), p_i(d_j) for all queries j }, the elements the verifier needs. - /// - Y_i = { p_i( d_j) for all queries j }, the elements that the verifier computes from - /// previous layers. - /// - Z_i = X_i - Y_i, the elements that the verifier needs but cannot compute from previous layers. - /// sorted by increasing value of query. - /// - MergedPathsLayer_i: the merged authentication paths for all p_i(-d_j) and p_i(d_j). - /// - /// This method appends: - /// - /// Z_1 | MergedPathsLayer_1 | Z_2 | MergedPathsLayer_2 | ... | Z_n | MergedPathsLayer_n, - /// - /// where n is the total number of FRI layers. - fn append_fri_query_phase_inner_layers( - proof: &StarkProof, - fri_query_indexes: &[usize], - output: &mut Vec, - ) { - let mut fri_layers_evaluations: HashMap<(u64, usize, usize), FieldElement<_>> = - HashMap::new(); - for (decommitment, query_index) in proof.query_list.iter().zip(fri_query_indexes.iter()) { - let mut query_layer_index = *query_index; - for (i, element) in decommitment.layers_evaluations_sym.iter().enumerate() { - fri_layers_evaluations.insert( - ( - i as u64, - query_layer_index >> 1, - (query_layer_index + 1) % 2, - ), - *element, - ); - query_layer_index >>= 1; - } - } - - let mut indexes_previous_layer = fri_query_indexes.to_owned(); - for i in 0..proof.query_list[0].layers_evaluations_sym.len() { - // Compute set Y_i - let reconstructed_row_col: BTreeSet<_> = indexes_previous_layer - .iter() - .map(|index| (index >> 1, index % 2)) - .collect(); - - // Compute set X_i - let reconstructed_row_col_sym: BTreeSet<_> = reconstructed_row_col - .iter() - .map(|(x, y)| (*x, 1 - y)) - .collect(); - - // Compute set Z_i - let row_col_to_send: Vec<_> = reconstructed_row_col_sym - .difference(&reconstructed_row_col) - .collect(); - - // Append Z_i - for element in row_col_to_send - .iter() - .map(|(row, col)| &fri_layers_evaluations[&(i as u64, *row, *col)]) - { - output.extend_from_slice(&element.as_bytes()); - } - - indexes_previous_layer = indexes_previous_layer - .iter() - .map(|index| index >> 1) - .collect(); - - let layer_auth_paths: Vec<_> = proof - .query_list - .iter() - .map(|decommitment| &decommitment.layers_auth_paths[i]) - .collect(); - - // Append MergedPathsLayer_i - let nodes = - Self::merge_authentication_paths(&layer_auth_paths, &indexes_previous_layer); - for node in nodes.iter() { - output.extend_from_slice(node); - } - } - } - - /// Merges `n` authentication paths for `n` leaves into a list of the minimal number of nodes - /// needed to reach the Merkle root for all of them. The nodes of the merged authentication - /// paths are sorted from level 0 to the hightest level of the Merkle tree, and nodes at the - /// same level are sorted from left to right. - /// - /// Let's consider some examples. Suppose the Merkle tree is as follows: - /// - /// Root ABCD - /// / \ - /// Level 1 AB CD - /// / \ / \ - /// Level 0 A B C D - /// Leaf index 0 1 2 3 - /// - /// All authentication paths are `pA = [B, CD]`, `pB = [A, CD]`, `pC = [D, AB]` and `pD = [C, AB]`. - /// - /// (1) `merge_authentication_paths([pA, pB], [0, 1]) = [CD]`. - /// (2) `merge_authentication_paths([pC, pA], [2, 0]) = [B, D]`. - /// (3) `merge_authentication_paths([pA, pD], [0, 3]) = [B, C]`. - /// (4) `merge_authentication_paths([pA, pD, pB], [0, 3, 1]) = [C]`. - /// (5) `merge_authentication_paths([pA, pB, pC, pD], [0, 1, 2, 3]) = []`. - /// - /// Input: - /// `authentication_paths`: The authentication paths to be merged. - /// `leaf_indexes`: The leaf indexes corresponding to the authentication paths. - /// - /// Output: - /// The merged authentication paths - fn merge_authentication_paths( - authentication_paths: &[&Proof], - leaf_indexes: &[usize], - ) -> Vec { - debug_assert_eq!(leaf_indexes.len(), authentication_paths.len()); - let mut merkle_tree: HashMap<(usize, usize), Commitment> = HashMap::new(); - for (index_previous_layer, path) in leaf_indexes.iter().zip(authentication_paths.iter()) { - let mut node_index = *index_previous_layer; - for (tree_level, node) in path.merkle_path.iter().enumerate() { - merkle_tree.insert((tree_level, node_index ^ 1), *node); - node_index >>= 1; - } - } - - let mut result = Vec::new(); - let mut level_indexes: BTreeSet = leaf_indexes.iter().copied().collect(); - let merkle_tree_height = authentication_paths[0].merkle_path.len(); - for tree_level in 0..merkle_tree_height { - for node_index in level_indexes.iter() { - let sibling_index = node_index ^ 1; - if !level_indexes.contains(&sibling_index) { - let node = &merkle_tree[&(tree_level, sibling_index)]; - result.push(*node); - } - } - level_indexes = level_indexes.iter().map(|index| *index >> 1).collect(); - } - result - } - fn get_fri_query_indexes( - proof: &StarkProof, - public_inputs: &A::PublicInputs, - proof_options: &ProofOptions, - ) -> Vec - where - A: AIR, - A::PublicInputs: AsBytes, - { - let mut transcript = StoneProverTranscript::new(&public_inputs.as_bytes()); - let air = A::new(proof.trace_length, public_inputs, proof_options); - let domain = Domain::::new(&air); - let challenges = Verifier::step_1_replay_rounds_and_recover_challenges( - &air, - proof, - &domain, - &mut transcript, - ); - challenges.iotas - } -} diff --git a/crates/provers/stark/src/prover.rs b/crates/provers/stark/src/prover.rs deleted file mode 100644 index 6d6fcd680..000000000 --- a/crates/provers/stark/src/prover.rs +++ /dev/null @@ -1,1696 +0,0 @@ -use std::marker::PhantomData; -#[cfg(feature = "instruments")] -use std::time::Instant; - -use lambdaworks_crypto::fiat_shamir::is_transcript::IsTranscript; -use lambdaworks_math::fft::cpu::bit_reversing::{in_place_bit_reverse_permute, reverse_index}; -use lambdaworks_math::fft::errors::FFTError; - -use lambdaworks_math::field::traits::{IsField, IsSubFieldOf}; -use lambdaworks_math::traits::AsBytes; -use lambdaworks_math::{ - field::{element::FieldElement, traits::IsFFTField}, - polynomial::Polynomial, -}; -use log::info; - -#[cfg(feature = "parallel")] -use rayon::prelude::{IndexedParallelIterator, IntoParallelRefIterator, ParallelIterator}; - -#[cfg(debug_assertions)] -use crate::debug::validate_trace; -use crate::fri; -use crate::proof::stark::{DeepPolynomialOpenings, PolynomialOpenings}; -use crate::table::Table; -use crate::trace::{columns2rows, LDETraceTable}; - -use super::config::{BatchedMerkleTree, Commitment}; -use super::constraints::evaluator::ConstraintEvaluator; -use super::domain::Domain; -use super::fri::fri_decommit::FriDecommitment; -use super::grinding; -use super::proof::options::ProofOptions; -use super::proof::stark::{DeepPolynomialOpening, StarkProof}; -use super::trace::TraceTable; -use super::traits::AIR; - -/// A default STARK prover implementing `IsStarkProver`. -pub struct Prover { - phantom: PhantomData, -} - -impl IsStarkProver for Prover {} - -#[derive(Debug)] -pub enum ProvingError { - WrongParameter(String), - EmptyCommitment, -} - -/// A container for the intermediate results of the commitments to a trace table, main or auxiliary in case of RAP, -/// in the first round of the STARK Prove protocol. -pub struct Round1CommitmentData -where - F: IsField, - FieldElement: AsBytes + Send + Sync, -{ - /// The result of the interpolation of the columns of the trace table. - pub(crate) trace_polys: Vec>>, - /// The Merkle trees constructed to obtain the commitment of the entire trace table. - pub(crate) lde_trace_merkle_tree: BatchedMerkleTree, - /// The root of the Merkle tree in `lde_trace_merkle_tree`. - pub(crate) lde_trace_merkle_root: Commitment, -} - -/// A container for the results of the first round of the STARK Prove protocol. -pub struct Round1 -where - A: AIR, - FieldElement: AsBytes + Sync + Send, - FieldElement: AsBytes + Sync + Send, -{ - /// The table of evaluations over the LDE of the main and auxiliary trace tables. - pub(crate) lde_trace: LDETraceTable, - /// The intermediate results of the commitment to the main trace table. - pub(crate) main: Round1CommitmentData, - /// The intermediate results of the commitment to the auxiliary trace table in case of RAP. - pub(crate) aux: Option>, - /// The challenges of the RAP round. - pub(crate) rap_challenges: Vec>, -} - -impl Round1 -where - A: AIR, - FieldElement: AsBytes + Sync + Send, - FieldElement: AsBytes + Sync + Send, -{ - /// Returns the full list of the polynomials interpolating the trace. It includes both - /// main and auxiliary trace polynomials. The main trace polynomials are casted to - /// polynomials with coefficients over `Self::FieldExtension`. - fn all_trace_polys(&self) -> Vec>> { - let mut trace_polys: Vec<_> = self - .main - .trace_polys - .clone() - .into_iter() - .map(|poly| poly.to_extension()) - .collect(); - - if let Some(aux) = &self.aux { - trace_polys.extend_from_slice(&aux.trace_polys.to_owned()) - } - trace_polys - } -} - -/// A container for the results of the second round of the STARK Prove protocol. -pub struct Round2 -where - F: IsField, - FieldElement: AsBytes + Sync + Send, -{ - /// The list of polynomials `H₀, ..., Hₙ` such that `H = ∑ᵢXⁱH(Xⁿ)`, where H is the composition polynomial. - pub(crate) composition_poly_parts: Vec>>, - /// Evaluations of the composition polynomial parts over the LDE domain. - pub(crate) lde_composition_poly_evaluations: Vec>>, - /// The Merkle tree built to compute the commitment to the composition polynomial parts. - pub(crate) composition_poly_merkle_tree: BatchedMerkleTree, - /// The commitment to the composition polynomial parts. - pub(crate) composition_poly_root: Commitment, -} - -/// A container for the results of the third round of the STARK Prove protocol. -pub struct Round3 { - /// Evaluations of the trace polynomials, main ans auxiliary, at the out-of-domain challenge. - trace_ood_evaluations: Table, - /// Evaluations of the composition polynomial parts at the out-of-domain challenge. - composition_poly_parts_ood_evaluation: Vec>, -} - -/// A container for the results of the fourth round of the STARK Prove protocol. -pub struct Round4, E: IsField> { - /// The final value resulting from folding the Deep composition polynomial all the way down to a constant value. - fri_last_value: FieldElement, - /// The commitments to the fold polynomials of the inner layers of FRI. - fri_layers_merkle_roots: Vec, - /// The values and proofs of validity of the evaluations of the trace polynomials and the composition polynomials - /// parts at the domain values corresponding to the FRI query challenges and their symmetric counterparts. - deep_poly_openings: DeepPolynomialOpenings, - /// The values and proofs of validity of the evaluations of the fold polynomials of the inner - /// layers of FRI at the values corresponding to the symmetrics of the FRI query challenges. - query_list: Vec>, - /// The proof of work nonce. - nonce: Option, -} - -/// Returns the evaluations of the polynomial `p` over the lde domain defined by the given -/// `blowup_factor`, `domain_size` and `offset`. The number of evaluations returned is `domain_size -/// * blowup_factor`. The domain generator used is the one given by the implementation of `F` as `IsFFTField`. -pub fn evaluate_polynomial_on_lde_domain( - p: &Polynomial>, - blowup_factor: usize, - domain_size: usize, - offset: &FieldElement, -) -> Result>, FFTError> -where - F: IsFFTField + IsSubFieldOf, - E: IsField, -{ - let evaluations = Polynomial::evaluate_offset_fft(p, blowup_factor, Some(domain_size), offset)?; - let step = evaluations.len() / (domain_size * blowup_factor); - match step { - 1 => Ok(evaluations), - _ => Ok(evaluations.into_iter().step_by(step).collect()), - } -} - -/// The functionality of a STARK prover providing methods to run the STARK Prove protocol -/// https://lambdaclass.github.io/lambdaworks/starks/protocol.html -/// The default implementation is complete and is compatible with Stone prover -/// https://github.com/starkware-libs/stone-prover -pub trait IsStarkProver { - /// Returns the Merkle tree and the commitment to the vectors `vectors`. - fn batch_commit_main( - vectors: &[Vec>], - ) -> Option<(BatchedMerkleTree, Commitment)> - where - FieldElement: AsBytes + Sync + Send, - { - let tree = BatchedMerkleTree::build(vectors)?; - - let commitment = tree.root; - Some((tree, commitment)) - } - - /// Returns the Merkle tree and the commitment to the vectors `vectors`. - fn batch_commit_extension( - vectors: &[Vec>], - ) -> Option<(BatchedMerkleTree, Commitment)> - where - FieldElement: AsBytes + Sync + Send, - FieldElement: AsBytes + Sync + Send, - { - let tree = BatchedMerkleTree::build(vectors)?; - - let commitment = tree.root; - Some((tree, commitment)) - } - - /// Given a `TraceTable`, this method interpolates its columns, computes the commitment to the - /// table and appends it to the transcript. - /// Output: a touple of length 4 with the following: - /// • The polynomials interpolating the columns of `trace`. - /// • The evaluations of the above polynomials over the domain `domain`. - /// • The Merkle tree of evaluations of the above polynomials over the domain `domain`. - /// • The roots of the above Merkle trees. - #[allow(clippy::type_complexity)] - fn interpolate_and_commit_main( - trace: &TraceTable, - domain: &Domain, - transcript: &mut impl IsTranscript, - ) -> Option<( - Vec>>, - Vec>>, - BatchedMerkleTree, - Commitment, - )> - where - FieldElement: AsBytes + Send + Sync, - // FieldElement: AsBytes + Send + Sync, - FieldElement: AsBytes + Send + Sync, - A::Field: IsSubFieldOf, - { - // Interpolate columns of `trace`. - let trace_polys = trace.compute_trace_polys_main::(); - - // Evaluate those polynomials t_j on the large domain D_LDE. - let lde_trace_evaluations = - Self::compute_lde_trace_evaluations::(&trace_polys, domain); - - let mut lde_trace_permuted = lde_trace_evaluations.clone(); - for col in lde_trace_permuted.iter_mut() { - in_place_bit_reverse_permute(col); - } - - // Compute commitment. - let lde_trace_permuted_rows = columns2rows(lde_trace_permuted); - - let (lde_trace_merkle_tree, lde_trace_merkle_root) = - Self::batch_commit_main(&lde_trace_permuted_rows)?; - - // >>>> Send commitment. - transcript.append_bytes(&lde_trace_merkle_root); - - Some(( - trace_polys, - lde_trace_evaluations, - lde_trace_merkle_tree, - lde_trace_merkle_root, - )) - } - - /// Given a `TraceTable`, this method interpolates its columns, computes the commitment to the - /// table and appends it to the transcript. - /// Output: a touple of length 4 with the following: - /// • The polynomials interpolating the columns of `trace`. - /// • The evaluations of the above polynomials over the domain `domain`. - /// • The Merkle tree of evaluations of the above polynomials over the domain `domain`. - /// • The roots of the above Merkle trees. - #[allow(clippy::type_complexity)] - fn interpolate_and_commit_aux( - trace: &TraceTable, - domain: &Domain, - transcript: &mut impl IsTranscript, - ) -> Option<( - Vec>>, - Vec>>, - BatchedMerkleTree, - Commitment, - )> - where - FieldElement: AsBytes + Send + Sync, - FieldElement: AsBytes + Send + Sync, - A::Field: IsSubFieldOf + IsFFTField, - { - // Interpolate columns of `trace`. - let trace_polys = trace.compute_trace_polys_aux::(); - - // Evaluate those polynomials t_j on the large domain D_LDE. - let lde_trace_evaluations = Self::compute_lde_trace_evaluations(&trace_polys, domain); - - let mut lde_trace_permuted = lde_trace_evaluations.clone(); - for col in lde_trace_permuted.iter_mut() { - in_place_bit_reverse_permute(col); - } - - // Compute commitment. - let lde_trace_permuted_rows = columns2rows(lde_trace_permuted); - - let (lde_trace_merkle_tree, lde_trace_merkle_root) = - Self::batch_commit_extension(&lde_trace_permuted_rows)?; - - // >>>> Send commitment. - transcript.append_bytes(&lde_trace_merkle_root); - - Some(( - trace_polys, - lde_trace_evaluations, - lde_trace_merkle_tree, - lde_trace_merkle_root, - )) - } - - /// Evaluate polynomials `trace_polys` over the domain `domain`. - /// The i-th entry of the returned vector contains the evaluations of the i-th polynomial in `trace_polys`. - fn compute_lde_trace_evaluations( - trace_polys: &[Polynomial>], - domain: &Domain, - ) -> Vec>> - where - FieldElement: Send + Sync, - FieldElement: Send + Sync, - E: IsSubFieldOf, - A::Field: IsSubFieldOf, - { - #[cfg(not(feature = "parallel"))] - let trace_polys_iter = trace_polys.iter(); - #[cfg(feature = "parallel")] - let trace_polys_iter = trace_polys.par_iter(); - - trace_polys_iter - .map(|poly| { - evaluate_polynomial_on_lde_domain( - poly, - domain.blowup_factor, - domain.interpolation_domain_size, - &domain.coset_offset, - ) - }) - .collect::>>, FFTError>>() - .unwrap() - } - - /// Returns the result of the first round of the STARK Prove protocol. - fn round_1_randomized_air_with_preprocessing( - air: &A, - trace: &mut TraceTable, - domain: &Domain, - transcript: &mut impl IsTranscript, - ) -> Result, ProvingError> - where - FieldElement: AsBytes + Send + Sync, - A::FieldExtension: IsFFTField, - FieldElement: AsBytes + Send + Sync, - { - let Some((trace_polys, evaluations, main_merkle_tree, main_merkle_root)) = - Self::interpolate_and_commit_main(trace, domain, transcript) - else { - return Err(ProvingError::EmptyCommitment); - }; - - let main = Round1CommitmentData:: { - trace_polys, - lde_trace_merkle_tree: main_merkle_tree, - lde_trace_merkle_root: main_merkle_root, - }; - - let rap_challenges = air.build_rap_challenges(transcript); - let (aux, aux_evaluations) = if air.has_trace_interaction() { - air.build_auxiliary_trace(trace, &rap_challenges); - let Some(( - aux_trace_polys, - aux_trace_polys_evaluations, - aux_merkle_tree, - aux_merkle_root, - )) = Self::interpolate_and_commit_aux(trace, domain, transcript) - else { - return Err(ProvingError::EmptyCommitment); - }; - let aux_evaluations = aux_trace_polys_evaluations; - let aux = Some(Round1CommitmentData:: { - trace_polys: aux_trace_polys, - lde_trace_merkle_tree: aux_merkle_tree, - lde_trace_merkle_root: aux_merkle_root, - }); - (aux, aux_evaluations) - } else { - (None, Vec::new()) - }; - - let lde_trace = LDETraceTable::from_columns( - evaluations, - aux_evaluations, - A::STEP_SIZE, - domain.blowup_factor, - ); - - Ok(Round1 { - lde_trace, - main, - aux, - rap_challenges, - }) - } - - /// Returns the Merkle tree and the commitment to the evaluations of the parts of the - /// composition polynomial. - fn commit_composition_polynomial( - lde_composition_poly_parts_evaluations: &[Vec>], - ) -> Option<(BatchedMerkleTree, Commitment)> - where - FieldElement: AsBytes + Sync + Send, - FieldElement: AsBytes + Sync + Send, - { - // TODO: Remove clones - let mut lde_composition_poly_evaluations = Vec::new(); - for i in 0..lde_composition_poly_parts_evaluations[0].len() { - let mut row = Vec::new(); - for evaluation in lde_composition_poly_parts_evaluations.iter() { - row.push(evaluation[i].clone()); - } - lde_composition_poly_evaluations.push(row); - } - - in_place_bit_reverse_permute(&mut lde_composition_poly_evaluations); - - let mut lde_composition_poly_evaluations_merged = Vec::new(); - for chunk in lde_composition_poly_evaluations.chunks(2) { - let (mut chunk0, chunk1) = (chunk[0].clone(), &chunk[1]); - chunk0.extend_from_slice(chunk1); - lde_composition_poly_evaluations_merged.push(chunk0); - } - - Self::batch_commit_extension(&lde_composition_poly_evaluations_merged) - } - - /// Returns the result of the second round of the STARK Prove protocol. - fn round_2_compute_composition_polynomial( - air: &A, - domain: &Domain, - round_1_result: &Round1, - transition_coefficients: &[FieldElement], - boundary_coefficients: &[FieldElement], - ) -> Result, ProvingError> - where - A: Send + Sync, - FieldElement: AsBytes + Send + Sync, - FieldElement: AsBytes + Send + Sync, - { - // Compute the evaluations of the composition polynomial on the LDE domain. - let evaluator = ConstraintEvaluator::new(air, &round_1_result.rap_challenges); - let constraint_evaluations = evaluator.evaluate( - air, - &round_1_result.lde_trace, - domain, - transition_coefficients, - boundary_coefficients, - &round_1_result.rap_challenges, - ); - - // Get coefficients of the composition poly H - let composition_poly = - Polynomial::interpolate_offset_fft(&constraint_evaluations, &domain.coset_offset) - .unwrap(); - - let number_of_parts = air.composition_poly_degree_bound() / air.trace_length(); - let composition_poly_parts = composition_poly.break_in_parts(number_of_parts); - - let lde_composition_poly_parts_evaluations: Vec<_> = composition_poly_parts - .iter() - .map(|part| { - evaluate_polynomial_on_lde_domain( - part, - domain.blowup_factor, - domain.interpolation_domain_size, - &domain.coset_offset, - ) - .unwrap() - }) - .collect(); - - let Some((composition_poly_merkle_tree, composition_poly_root)) = - Self::commit_composition_polynomial(&lde_composition_poly_parts_evaluations) - else { - return Err(ProvingError::EmptyCommitment); - }; - - Ok(Round2 { - lde_composition_poly_evaluations: lde_composition_poly_parts_evaluations, - composition_poly_parts, - composition_poly_merkle_tree, - composition_poly_root, - }) - } - - /// Returns the result of the third round of the STARK Prove protocol. - fn round_3_evaluate_polynomials_in_out_of_domain_element( - air: &A, - domain: &Domain, - round_1_result: &Round1, - round_2_result: &Round2, - z: &FieldElement, - ) -> Round3 - where - FieldElement: AsBytes + Sync + Send, - FieldElement: AsBytes + Sync + Send, - { - let z_power = z.pow(round_2_result.composition_poly_parts.len()); - - // Evaluate H_i in z^N for all i, where N is the number of parts the composition poly was - // broken into. - let composition_poly_parts_ood_evaluation: Vec<_> = round_2_result - .composition_poly_parts - .iter() - .map(|part| part.evaluate(&z_power)) - .collect(); - - // Returns the Out of Domain Frame for the given trace polynomials, out of domain evaluation point (called `z` in the literature), - // frame offsets given by the AIR and primitive root used for interpolating the trace polynomials. - // An out of domain frame is nothing more than the evaluation of the trace polynomials in the points required by the - // verifier to check the consistency between the trace and the composition polynomial. - // - // In the fibonacci example, the ood frame is simply the evaluations `[t(z), t(z * g), t(z * g^2)]`, where `t` is the trace - // polynomial and `g` is the primitive root of unity used when interpolating `t`. - let trace_ood_evaluations = - crate::trace::get_trace_evaluations::( - &round_1_result.main.trace_polys, - round_1_result - .aux - .as_ref() - .map(|aux| &aux.trace_polys) - .unwrap_or(&vec![]), - z, - &air.context().transition_offsets, - &domain.trace_primitive_root, - A::STEP_SIZE, - ); - - Round3 { - trace_ood_evaluations, - composition_poly_parts_ood_evaluation, - } - } - - /// Returns the result of the fourth round of the STARK Prove protocol. - fn round_4_compute_and_run_fri_on_the_deep_composition_polynomial( - air: &A, - domain: &Domain, - round_1_result: &Round1, - round_2_result: &Round2, - round_3_result: &Round3, - z: &FieldElement, - transcript: &mut impl IsTranscript, - ) -> Round4 - where - FieldElement: AsBytes + Send + Sync, - FieldElement: AsBytes + Send + Sync, - { - let coset_offset_u64 = air.context().proof_options.coset_offset; - let coset_offset = FieldElement::::from(coset_offset_u64); - - let gamma = transcript.sample_field_element(); - - let n_terms_composition_poly = round_2_result.lde_composition_poly_evaluations.len(); - let num_terms_trace = - air.context().transition_offsets.len() * A::STEP_SIZE * air.context().trace_columns; - - // <<<< Receive challenges: 𝛾, 𝛾' - let mut deep_composition_coefficients: Vec<_> = - core::iter::successors(Some(FieldElement::one()), |x| Some(x * &gamma)) - .take(n_terms_composition_poly + num_terms_trace) - .collect(); - - let trace_term_coeffs: Vec<_> = deep_composition_coefficients - .drain(..num_terms_trace) - .collect::>() - .chunks(air.context().transition_offsets.len() * A::STEP_SIZE) - .map(|chunk| chunk.to_vec()) - .collect(); - - // <<<< Receive challenges: 𝛾ⱼ, 𝛾ⱼ' - let gammas = deep_composition_coefficients; - - // Compute p₀ (deep composition polynomial) - let deep_composition_poly = Self::compute_deep_composition_poly( - &round_1_result.all_trace_polys(), - round_2_result, - round_3_result, - z, - &domain.trace_primitive_root, - &gammas, - &trace_term_coeffs, - ); - - let domain_size = domain.lde_roots_of_unity_coset.len(); - - // FRI commit and query phases - let (fri_last_value, fri_layers) = fri::commit_phase::( - domain.root_order as usize, - deep_composition_poly, - transcript, - &coset_offset, - domain_size, - ); - - // grinding: generate nonce and append it to the transcript - let security_bits = air.context().proof_options.grinding_factor; - let mut nonce = None; - if security_bits > 0 { - let nonce_value = grinding::generate_nonce(&transcript.state(), security_bits) - .expect("nonce not found"); - transcript.append_bytes(&nonce_value.to_be_bytes()); - nonce = Some(nonce_value); - } - - let number_of_queries = air.options().fri_number_of_queries; - let iotas = Self::sample_query_indexes(number_of_queries, domain, transcript); - - let query_list = fri::query_phase(&fri_layers, &iotas); - - let fri_layers_merkle_roots: Vec<_> = fri_layers - .iter() - .map(|layer| layer.merkle_tree.root) - .collect(); - - let deep_poly_openings = - Self::open_deep_composition_poly(domain, round_1_result, round_2_result, &iotas); - - Round4 { - fri_last_value, - fri_layers_merkle_roots, - deep_poly_openings, - query_list, - nonce, - } - } - - fn sample_query_indexes( - number_of_queries: usize, - domain: &Domain, - transcript: &mut impl IsTranscript, - ) -> Vec { - let domain_size = domain.lde_roots_of_unity_coset.len() as u64; - (0..number_of_queries) - .map(|_| (transcript.sample_u64(domain_size >> 1)) as usize) - .collect::>() - } - - /// Returns the DEEP composition polynomial that the prover then commits to using - /// FRI. This polynomial is a linear combination of the trace polynomial and the - /// composition polynomial, with coefficients sampled by the verifier (i.e. using Fiat-Shamir). - #[allow(clippy::too_many_arguments)] - fn compute_deep_composition_poly( - trace_polys: &[Polynomial>], - round_2_result: &Round2, - round_3_result: &Round3, - z: &FieldElement, - primitive_root: &FieldElement, - composition_poly_gammas: &[FieldElement], - trace_terms_gammas: &[Vec>], - ) -> Polynomial> - where - FieldElement: AsBytes + Send + Sync, - FieldElement: AsBytes + Send + Sync, - { - let z_power = z.pow(round_2_result.composition_poly_parts.len()); - - // ∑ᵢ 𝛾ᵢ ( Hᵢ − Hᵢ(z^N) ) / ( X − z^N ) - let mut h_terms = Polynomial::zero(); - for (i, part) in round_2_result.composition_poly_parts.iter().enumerate() { - // h_i_eval is the evaluation of the i-th part of the composition polynomial at z^N, - // where N is the number of parts of the composition polynomial. - let h_i_eval = &round_3_result.composition_poly_parts_ood_evaluation[i]; - let h_i_term = &composition_poly_gammas[i] * (part - h_i_eval); - h_terms = h_terms + h_i_term; - } - assert_eq!(h_terms.evaluate(&z_power), FieldElement::zero()); - h_terms.ruffini_division_inplace(&z_power); - - // Get trace evaluations needed for the trace terms of the deep composition polynomial - let trace_frame_evaluations = &round_3_result.trace_ood_evaluations; - - // Compute the sum of all the trace terms of the deep composition polynomial. - // There is one term for every trace polynomial and for every row in the frame. - // ∑ ⱼₖ [ 𝛾ₖ ( tⱼ − tⱼ(z) ) / ( X − zgᵏ )] - - let trace_evaluations_columns = &trace_frame_evaluations.columns(); - - #[cfg(feature = "parallel")] - let trace_terms = trace_polys - .par_iter() - .enumerate() - .fold(Polynomial::zero, |trace_terms, (i, t_j)| { - let gammas_i = &trace_terms_gammas[i]; - let trace_evaluations_i = &trace_evaluations_columns[i]; - Self::compute_trace_term( - &trace_terms, - t_j, - gammas_i, - trace_evaluations_i, - (z, primitive_root), - ) - }) - .reduce(Polynomial::zero, |a, b| a + b); - - #[cfg(not(feature = "parallel"))] - let trace_terms = - trace_polys - .iter() - .enumerate() - .fold(Polynomial::zero(), |trace_terms, (i, t_j)| { - let gammas_i = &trace_terms_gammas[i]; - let trace_evaluations_i = &trace_evaluations_columns[i]; - Self::compute_trace_term( - &trace_terms, - t_j, - gammas_i, - trace_evaluations_i, - (z, primitive_root), - ) - }); - - h_terms + trace_terms - } - - // FIXME: FIX THIS DOCS! - /// Adds to `accumulator` the term corresponding to the trace polynomial `t_j` of the Deep - /// composition polynomial. That is, returns `accumulator + \sum_i \gamma_i \frac{ t_j - t_j(zg^i) }{ X - zg^i }`, - /// where `i` ranges from `T * j` to `T * j + T - 1`, where `T` is the number of offsets in every frame. - fn compute_trace_term( - accumulator: &Polynomial>, - trace_term_poly: &Polynomial>, - trace_terms_gammas: &[FieldElement], - trace_frame_evaluations: &[FieldElement], - (z, primitive_root): (&FieldElement, &FieldElement), - ) -> Polynomial> - where - FieldElement: AsBytes + Send + Sync, - FieldElement: AsBytes + Send + Sync, - { - let trace_int = trace_frame_evaluations - .iter() - .enumerate() - .zip(trace_terms_gammas) - .fold( - Polynomial::zero(), - |trace_agg, ((offset, trace_term_poly_evaluation), trace_gamma)| { - // @@@ this can be pre-computed - let z_shifted = primitive_root.pow(offset) * z; - let mut poly = trace_term_poly - trace_term_poly_evaluation; - poly.ruffini_division_inplace(&z_shifted); - trace_agg + poly * trace_gamma - }, - ); - - accumulator + trace_int - } - - /// Computes values and validity proofs of the evaluations of the composition polynomial parts - /// at the domain value corresponding to the FRI query challenge `index` and its symmetric - /// element. - fn open_composition_poly( - composition_poly_merkle_tree: &BatchedMerkleTree, - lde_composition_poly_evaluations: &[Vec>], - index: usize, - ) -> PolynomialOpenings - where - FieldElement: AsBytes + Sync + Send, - FieldElement: AsBytes + Sync + Send, - { - let proof = composition_poly_merkle_tree - .get_proof_by_pos(index) - .unwrap(); - - let lde_composition_poly_parts_evaluation: Vec<_> = lde_composition_poly_evaluations - .iter() - .flat_map(|part| { - vec![ - part[reverse_index(index * 2, part.len() as u64)].clone(), - part[reverse_index(index * 2 + 1, part.len() as u64)].clone(), - ] - }) - .collect(); - - PolynomialOpenings { - proof: proof.clone(), - proof_sym: proof, - evaluations: lde_composition_poly_parts_evaluation - .clone() - .into_iter() - .step_by(2) - .collect(), - evaluations_sym: lde_composition_poly_parts_evaluation - .into_iter() - .skip(1) - .step_by(2) - .collect(), - } - } - - /// Computes values and validity proofs of the evaluations of the trace polynomials - /// at the domain value corresponding to the FRI query challenge `index` and its symmetric - /// element. - fn open_trace_polys( - domain: &Domain, - tree: &BatchedMerkleTree, - lde_trace: &Table, - challenge: usize, - ) -> PolynomialOpenings - where - FieldElement: AsBytes + Sync + Send, - FieldElement: AsBytes + Sync + Send, - A::Field: IsSubFieldOf, - E: IsField, - { - let domain_size = domain.lde_roots_of_unity_coset.len(); - - let index = challenge * 2; - let index_sym = challenge * 2 + 1; - PolynomialOpenings { - proof: tree.get_proof_by_pos(index).unwrap(), - proof_sym: tree.get_proof_by_pos(index_sym).unwrap(), - evaluations: lde_trace - .get_row(reverse_index(index, domain_size as u64)) - .to_vec(), - evaluations_sym: lde_trace - .get_row(reverse_index(index_sym, domain_size as u64)) - .to_vec(), - } - } - - /// Open the deep composition polynomial on a list of indexes and their symmetric elements. - fn open_deep_composition_poly( - domain: &Domain, - round_1_result: &Round1, - round_2_result: &Round2, - indexes_to_open: &[usize], - ) -> DeepPolynomialOpenings - where - FieldElement: AsBytes + Send + Sync, - FieldElement: AsBytes + Send + Sync, - { - let mut openings = Vec::new(); - - for index in indexes_to_open.iter() { - let main_trace_opening = Self::open_trace_polys::( - domain, - &round_1_result.main.lde_trace_merkle_tree, - &round_1_result.lde_trace.main_table, - *index, - ); - - let composition_openings = Self::open_composition_poly( - &round_2_result.composition_poly_merkle_tree, - &round_2_result.lde_composition_poly_evaluations, - *index, - ); - - let aux_trace_polys = round_1_result.aux.as_ref().map(|aux| { - Self::open_trace_polys::( - domain, - &aux.lde_trace_merkle_tree, - &round_1_result.lde_trace.aux_table, - *index, - ) - }); - - openings.push(DeepPolynomialOpening { - composition_poly: composition_openings, - main_trace_polys: main_trace_opening, - aux_trace_polys, - }); - } - - openings - } - - // FIXME remove unwrap() calls and return errors - /// Generates a STARK proof for the trace `main_trace` with public inputs `pub_inputs`. - /// Warning: the transcript must be safely initializated before passing it to this method. - fn prove( - trace: &mut TraceTable, - pub_inputs: &A::PublicInputs, - proof_options: &ProofOptions, - mut transcript: impl IsTranscript, - ) -> Result, ProvingError> - where - A: Send + Sync, - FieldElement: AsBytes + Send + Sync, - A::FieldExtension: IsFFTField, - FieldElement: AsBytes + Send + Sync, - { - info!("Started proof generation..."); - #[cfg(feature = "instruments")] - println!("- Started round 0: Air Initialization"); - #[cfg(feature = "instruments")] - let timer0 = Instant::now(); - - let air = A::new(trace.num_rows(), pub_inputs, proof_options); - let domain = Domain::new(&air); - - #[cfg(feature = "instruments")] - let elapsed0 = timer0.elapsed(); - #[cfg(feature = "instruments")] - println!(" Time spent: {:?}", elapsed0); - - // =================================== - // ==========| Round 1 |========== - // =================================== - - #[cfg(feature = "instruments")] - println!("- Started round 1: RAP"); - #[cfg(feature = "instruments")] - let timer1 = Instant::now(); - - let round_1_result = - Self::round_1_randomized_air_with_preprocessing(&air, trace, &domain, &mut transcript)?; - - #[cfg(debug_assertions)] - validate_trace( - &air, - &round_1_result.main.trace_polys, - round_1_result - .aux - .as_ref() - .map(|a| &a.trace_polys) - .unwrap_or(&vec![]), - &domain, - &round_1_result.rap_challenges, - ); - - #[cfg(feature = "instruments")] - let elapsed1 = timer1.elapsed(); - #[cfg(feature = "instruments")] - println!(" Time spent: {:?}", elapsed1); - - // =================================== - // ==========| Round 2 |========== - // =================================== - - #[cfg(feature = "instruments")] - println!("- Started round 2: Compute composition polynomial"); - #[cfg(feature = "instruments")] - let timer2 = Instant::now(); - - // <<<< Receive challenge: 𝛽 - let beta = transcript.sample_field_element(); - let num_boundary_constraints = air - .boundary_constraints(&round_1_result.rap_challenges) - .constraints - .len(); - - let num_transition_constraints = air.context().num_transition_constraints; - - let mut coefficients: Vec<_> = - core::iter::successors(Some(FieldElement::one()), |x| Some(x * &beta)) - .take(num_boundary_constraints + num_transition_constraints) - .collect(); - - let transition_coefficients: Vec<_> = - coefficients.drain(..num_transition_constraints).collect(); - let boundary_coefficients = coefficients; - - let round_2_result = Self::round_2_compute_composition_polynomial( - &air, - &domain, - &round_1_result, - &transition_coefficients, - &boundary_coefficients, - )?; - - // >>>> Send commitments: [H₁], [H₂] - transcript.append_bytes(&round_2_result.composition_poly_root); - - #[cfg(feature = "instruments")] - let elapsed2 = timer2.elapsed(); - #[cfg(feature = "instruments")] - println!(" Time spent: {:?}", elapsed2); - - // =================================== - // ==========| Round 3 |========== - // =================================== - - #[cfg(feature = "instruments")] - println!("- Started round 3: Evaluate polynomial in out of domain elements"); - #[cfg(feature = "instruments")] - let timer3 = Instant::now(); - - // <<<< Receive challenge: z - let z = transcript.sample_z_ood( - &domain.lde_roots_of_unity_coset, - &domain.trace_roots_of_unity, - ); - - let round_3_result = Self::round_3_evaluate_polynomials_in_out_of_domain_element( - &air, - &domain, - &round_1_result, - &round_2_result, - &z, - ); - - // >>>> Send values: tⱼ(zgᵏ) - let trace_ood_evaluations_columns = round_3_result.trace_ood_evaluations.columns(); - for col in trace_ood_evaluations_columns.iter() { - for elem in col.iter() { - transcript.append_field_element(elem); - } - } - - // >>>> Send values: Hᵢ(z^N) - for element in round_3_result.composition_poly_parts_ood_evaluation.iter() { - transcript.append_field_element(element); - } - - #[cfg(feature = "instruments")] - let elapsed3 = timer3.elapsed(); - #[cfg(feature = "instruments")] - println!(" Time spent: {:?}", elapsed3); - - // =================================== - // ==========| Round 4 |========== - // =================================== - - #[cfg(feature = "instruments")] - println!("- Started round 4: FRI"); - #[cfg(feature = "instruments")] - let timer4 = Instant::now(); - - // Part of this round is running FRI, which is an interactive - // protocol on its own. Therefore we pass it the transcript - // to simulate the interactions with the verifier. - let round_4_result = Self::round_4_compute_and_run_fri_on_the_deep_composition_polynomial( - &air, - &domain, - &round_1_result, - &round_2_result, - &round_3_result, - &z, - &mut transcript, - ); - - #[cfg(feature = "instruments")] - let elapsed4 = timer4.elapsed(); - #[cfg(feature = "instruments")] - println!(" Time spent: {:?}", elapsed4); - - #[cfg(feature = "instruments")] - { - let total_time = elapsed1 + elapsed2 + elapsed3 + elapsed4; - println!( - " Fraction of proving time per round: {:.4} {:.4} {:.4} {:.4} {:.4}", - elapsed0.as_nanos() as f64 / total_time.as_nanos() as f64, - elapsed1.as_nanos() as f64 / total_time.as_nanos() as f64, - elapsed2.as_nanos() as f64 / total_time.as_nanos() as f64, - elapsed3.as_nanos() as f64 / total_time.as_nanos() as f64, - elapsed4.as_nanos() as f64 / total_time.as_nanos() as f64 - ); - } - - info!("End proof generation"); - - Ok(StarkProof:: { - // [t] - lde_trace_main_merkle_root: round_1_result.main.lde_trace_merkle_root, - // [t] - lde_trace_aux_merkle_root: round_1_result.aux.map(|x| x.lde_trace_merkle_root), - // tⱼ(zgᵏ) - trace_ood_evaluations: round_3_result.trace_ood_evaluations, - // [H₁] and [H₂] - composition_poly_root: round_2_result.composition_poly_root, - // Hᵢ(z^N) - composition_poly_parts_ood_evaluation: round_3_result - .composition_poly_parts_ood_evaluation, - // [pₖ] - fri_layers_merkle_roots: round_4_result.fri_layers_merkle_roots, - // pₙ - fri_last_value: round_4_result.fri_last_value, - // Open(p₀(D₀), 𝜐ₛ), Open(pₖ(Dₖ), −𝜐ₛ^(2ᵏ)) - query_list: round_4_result.query_list, - // Open(H₁(D_LDE, 𝜐₀), Open(H₂(D_LDE, 𝜐₀), Open(tⱼ(D_LDE), 𝜐₀) - // Open(H₁(D_LDE, -𝜐ᵢ), Open(H₂(D_LDE, -𝜐ᵢ), Open(tⱼ(D_LDE), -𝜐ᵢ) - deep_poly_openings: round_4_result.deep_poly_openings, - // nonce obtained from grinding - nonce: round_4_result.nonce, - - trace_length: air.trace_length(), - }) - } -} - -#[cfg(test)] -mod tests { - use std::num::ParseIntError; - - fn decode_hex(s: &str) -> Result, ParseIntError> { - (0..s.len()) - .step_by(2) - .map(|i| u8::from_str_radix(&s[i..i + 2], 16)) - .collect() - } - - use crate::{ - examples::{ - fibonacci_2_cols_shifted::{self, Fibonacci2ColsShifted}, - simple_fibonacci::{self, FibonacciPublicInputs}, - }, - proof::options::ProofOptions, - transcript::StoneProverTranscript, - verifier::{Challenges, IsStarkVerifier, Verifier}, - Felt252, - }; - - use super::*; - use lambdaworks_math::{ - field::{ - element::FieldElement, fields::fft_friendly::stark_252_prime_field::Stark252PrimeField, - traits::IsFFTField, - }, - polynomial::Polynomial, - }; - - #[test] - fn test_domain_constructor() { - let pub_inputs = FibonacciPublicInputs { - a0: Felt252::one(), - a1: Felt252::one(), - }; - let trace = simple_fibonacci::fibonacci_trace([Felt252::from(1), Felt252::from(1)], 8); - let trace_length = trace.num_rows(); - let coset_offset = 3; - let blowup_factor: usize = 2; - let grinding_factor = 20; - - let proof_options = ProofOptions { - blowup_factor: blowup_factor as u8, - fri_number_of_queries: 1, - coset_offset, - grinding_factor, - }; - - let domain = Domain::new(&simple_fibonacci::FibonacciAIR::new( - trace_length, - &pub_inputs, - &proof_options, - )); - assert_eq!(domain.blowup_factor, 2); - assert_eq!(domain.interpolation_domain_size, trace_length); - assert_eq!(domain.root_order, trace_length.trailing_zeros()); - assert_eq!(domain.coset_offset, FieldElement::from(coset_offset)); - - let primitive_root = Stark252PrimeField::get_primitive_root_of_unity( - (trace_length * blowup_factor).trailing_zeros() as u64, - ) - .unwrap(); - - assert_eq!( - domain.trace_primitive_root, - primitive_root.pow(blowup_factor) - ); - for i in 0..(trace_length * blowup_factor) { - assert_eq!( - domain.lde_roots_of_unity_coset[i], - primitive_root.pow(i) * FieldElement::from(coset_offset) - ); - } - } - - #[test] - fn test_evaluate_polynomial_on_lde_domain_on_trace_polys() { - let trace = simple_fibonacci::fibonacci_trace([Felt252::from(1), Felt252::from(1)], 8); - - let trace_length = trace.num_rows(); - - let trace_polys = trace.compute_trace_polys_main::(); - let coset_offset = Felt252::from(3); - let blowup_factor: usize = 2; - let domain_size = 8; - - let primitive_root = Stark252PrimeField::get_primitive_root_of_unity( - (trace_length * blowup_factor).trailing_zeros() as u64, - ) - .unwrap(); - - for poly in trace_polys.iter() { - let lde_evaluation = - evaluate_polynomial_on_lde_domain(poly, blowup_factor, domain_size, &coset_offset) - .unwrap(); - assert_eq!(lde_evaluation.len(), trace_length * blowup_factor); - for (i, evaluation) in lde_evaluation.iter().enumerate() { - assert_eq!( - *evaluation, - poly.evaluate(&(coset_offset * primitive_root.pow(i))) - ); - } - } - } - - #[test] - fn test_evaluate_polynomial_on_lde_domain_edge_case() { - let poly = Polynomial::new_monomial(Felt252::one(), 8); - let blowup_factor: usize = 4; - let domain_size: usize = 8; - let offset = Felt252::from(3); - let evaluations = - evaluate_polynomial_on_lde_domain(&poly, blowup_factor, domain_size, &offset).unwrap(); - assert_eq!(evaluations.len(), domain_size * blowup_factor); - - let primitive_root: Felt252 = Stark252PrimeField::get_primitive_root_of_unity( - (domain_size * blowup_factor).trailing_zeros() as u64, - ) - .unwrap(); - for (i, eval) in evaluations.iter().enumerate() { - assert_eq!(*eval, poly.evaluate(&(offset * primitive_root.pow(i)))); - } - } - - fn proof_parts_stone_compatibility_case_1() -> ( - StarkProof, - fibonacci_2_cols_shifted::PublicInputs, - ProofOptions, - [u8; 4], - ) { - let mut trace = fibonacci_2_cols_shifted::compute_trace(FieldElement::one(), 4); - - let claimed_index = 3; - let col = 0; - let claimed_value = *trace.get_main(claimed_index, col); - let mut proof_options = ProofOptions::default_test_options(); - proof_options.blowup_factor = 4; - proof_options.coset_offset = 3; - proof_options.grinding_factor = 0; - proof_options.fri_number_of_queries = 1; - - let pub_inputs = fibonacci_2_cols_shifted::PublicInputs { - claimed_value, - claimed_index, - }; - - let transcript_init_seed = [0xca, 0xfe, 0xca, 0xfe]; - - let proof = Prover::>::prove( - &mut trace, - &pub_inputs, - &proof_options, - StoneProverTranscript::new(&transcript_init_seed), - ) - .unwrap(); - (proof, pub_inputs, proof_options, transcript_init_seed) - } - - fn stone_compatibility_case_1_proof() -> StarkProof { - let (proof, _, _, _) = proof_parts_stone_compatibility_case_1(); - proof - } - - fn stone_compatibility_case_1_challenges( - ) -> Challenges> { - let (proof, public_inputs, options, seed) = proof_parts_stone_compatibility_case_1(); - - let air = Fibonacci2ColsShifted::new(proof.trace_length, &public_inputs, &options); - let domain = Domain::new(&air); - Verifier::step_1_replay_rounds_and_recover_challenges( - &air, - &proof, - &domain, - &mut StoneProverTranscript::new(&seed), - ) - } - - #[test] - fn stone_compatibility_case_1_proof_is_valid() { - let (proof, public_inputs, options, seed) = proof_parts_stone_compatibility_case_1(); - assert!(Verifier::>::verify( - &proof, - &public_inputs, - &options, - StoneProverTranscript::new(&seed) - )); - } - - #[test] - fn stone_compatibility_case_1_trace_commitment() { - let proof = stone_compatibility_case_1_proof(); - - assert_eq!( - proof.lde_trace_main_merkle_root.to_vec(), - decode_hex("0eb9dcc0fb1854572a01236753ce05139d392aa3aeafe72abff150fe21175594").unwrap() - ); - } - - #[test] - fn stone_compatibility_case_1_composition_poly_challenges() { - let challenges = stone_compatibility_case_1_challenges(); - - assert_eq!(challenges.transition_coeffs[0], FieldElement::one()); - let beta = challenges.transition_coeffs[1]; - assert_eq!( - beta, - FieldElement::from_hex_unchecked( - "86105fff7b04ed4068ecccb8dbf1ed223bd45cd26c3532d6c80a818dbd4fa7" - ), - ); - - assert_eq!(challenges.boundary_coeffs[0], beta.pow(2u64)); - assert_eq!(challenges.boundary_coeffs[1], beta.pow(3u64)); - } - - #[test] - fn stone_compatibility_case_1_composition_poly_commitment() { - let proof = stone_compatibility_case_1_proof(); - // Composition polynomial commitment - assert_eq!( - proof.composition_poly_root.to_vec(), - decode_hex("7cdd8d5fe3bd62254a417e2e260e0fed4fccdb6c9005e828446f645879394f38").unwrap() - ); - } - - #[test] - fn stone_compatibility_case_1_out_of_domain_challenge() { - let challenges = stone_compatibility_case_1_challenges(); - assert_eq!( - challenges.z, - FieldElement::from_hex_unchecked( - "317629e783794b52cd27ac3a5e418c057fec9dd42f2b537cdb3f24c95b3e550" - ) - ); - } - - #[test] - fn stone_compatibility_case_1_out_of_domain_trace_evaluation() { - let proof = stone_compatibility_case_1_proof(); - - assert_eq!( - proof.trace_ood_evaluations.get_row(0)[0], - FieldElement::from_hex_unchecked( - "70d8181785336cc7e0a0a1078a79ee6541ca0803ed3ff716de5a13c41684037", - ) - ); - assert_eq!( - proof.trace_ood_evaluations.get_row(1)[0], - FieldElement::from_hex_unchecked( - "29808fc8b7480a69295e4b61600480ae574ca55f8d118100940501b789c1630", - ) - ); - assert_eq!( - proof.trace_ood_evaluations.get_row(0)[1], - FieldElement::from_hex_unchecked( - "7d8110f21d1543324cc5e472ab82037eaad785707f8cae3d64c5b9034f0abd2", - ) - ); - assert_eq!( - proof.trace_ood_evaluations.get_row(1)[1], - FieldElement::from_hex_unchecked( - "1b58470130218c122f71399bf1e04cf75a6e8556c4751629d5ce8c02cc4e62d", - ) - ); - } - - #[test] - fn stone_compatibility_case_1_out_of_domain_composition_poly_evaluation() { - let proof = stone_compatibility_case_1_proof(); - - assert_eq!( - proof.composition_poly_parts_ood_evaluation[0], - FieldElement::from_hex_unchecked( - "1c0b7c2275e36d62dfb48c791be122169dcc00c616c63f8efb2c2a504687e85", - ) - ); - } - - #[test] - fn stone_compatibility_case_1_deep_composition_poly_challenges() { - let challenges = stone_compatibility_case_1_challenges(); - - // Trace terms coefficients - assert_eq!(challenges.trace_term_coeffs[0][0], FieldElement::one()); - let gamma = challenges.trace_term_coeffs[0][1]; - assert_eq!( - &gamma, - &FieldElement::from_hex_unchecked( - "a0c79c1c77ded19520873d9c2440451974d23302e451d13e8124cf82fc15dd" - ) - ); - assert_eq!(&challenges.trace_term_coeffs[1][0], &gamma.pow(2_u64)); - assert_eq!(&challenges.trace_term_coeffs[1][1], &gamma.pow(3_u64)); - - // Composition polynomial parts terms coefficient - assert_eq!(&challenges.gammas[0], &gamma.pow(4_u64)); - } - - #[test] - fn stone_compatibility_case_1_fri_commit_phase_challenge_0() { - let challenges = stone_compatibility_case_1_challenges(); - - // Challenge to fold FRI polynomial - assert_eq!( - challenges.zetas[0], - FieldElement::from_hex_unchecked( - "5c6b5a66c9fda19f583f0b10edbaade98d0e458288e62c2fa40e3da2b293cef" - ) - ); - } - - #[test] - fn stone_compatibility_case_1_fri_commit_phase_layer_1_commitment() { - let proof = stone_compatibility_case_1_proof(); - - // Commitment of first layer of FRI - assert_eq!( - proof.fri_layers_merkle_roots[0].to_vec(), - decode_hex("327d47da86f5961ee012b2b0e412de16023ffba97c82bfe85102f00daabd49fb").unwrap() - ); - } - - #[test] - fn stone_compatibility_case_1_fri_commit_phase_challenge_1() { - let challenges = stone_compatibility_case_1_challenges(); - assert_eq!( - challenges.zetas[1], - FieldElement::from_hex_unchecked( - "13c337c9dc727bea9eef1f82cab86739f17acdcef562f9e5151708f12891295" - ) - ); - } - - #[test] - fn stone_compatibility_case_1_fri_commit_phase_last_value() { - let proof = stone_compatibility_case_1_proof(); - - assert_eq!( - proof.fri_last_value, - FieldElement::from_hex_unchecked( - "43fedf9f9e3d1469309862065c7d7ca0e7e9ce451906e9c01553056f695aec9" - ) - ); - } - - #[test] - fn stone_compatibility_case_1_fri_query_iota_challenge() { - let challenges = stone_compatibility_case_1_challenges(); - assert_eq!(challenges.iotas[0], 1); - } - - #[test] - fn stone_compatibility_case_1_fri_query_phase_trace_openings() { - let proof = stone_compatibility_case_1_proof(); - - // Trace Col 0 - assert_eq!( - proof.deep_poly_openings[0].main_trace_polys.evaluations[0], - FieldElement::from_hex_unchecked( - "4de0d56f9cf97dff326c26592fbd4ae9ee756080b12c51cfe4864e9b8734f43" - ) - ); - - // Trace Col 1 - assert_eq!( - proof.deep_poly_openings[0].main_trace_polys.evaluations[1], - FieldElement::from_hex_unchecked( - "1bc1aadf39f2faee64d84cb25f7a95d3dceac1016258a39fc90c9d370e69e8e" - ) - ); - - // Trace Col 0 symmetric - assert_eq!( - proof.deep_poly_openings[0].main_trace_polys.evaluations_sym[0], - FieldElement::from_hex_unchecked( - "321f2a9063068310cd93d9a6d042b516118a9f7f4ed3ae301b79b16478cb0c6" - ) - ); - - // Trace Col 1 symmetric - assert_eq!( - proof.deep_poly_openings[0].main_trace_polys.evaluations_sym[1], - FieldElement::from_hex_unchecked( - "643e5520c60d06219b27b34da0856a2c23153efe9da75c6036f362c8f196186" - ) - ); - } - - #[test] - fn stone_compatibility_case_1_fri_query_phase_trace_terms_authentication_path() { - let proof = stone_compatibility_case_1_proof(); - - // Trace poly auth path level 1 - assert_eq!( - proof.deep_poly_openings[0] - .main_trace_polys - .proof - .merkle_path[1] - .to_vec(), - decode_hex("91b0c0b24b9d00067b0efab50832b76cf97192091624d42b86740666c5d369e6").unwrap() - ); - - // Trace poly auth path level 2 - assert_eq!( - proof.deep_poly_openings[0] - .main_trace_polys - .proof - .merkle_path[2] - .to_vec(), - decode_hex("993b044db22444c0c0ebf1095b9a51faeb001c9b4dea36abe905f7162620dbbd").unwrap() - ); - - // Trace poly auth path level 3 - assert_eq!( - proof.deep_poly_openings[0] - .main_trace_polys - .proof - .merkle_path[3] - .to_vec(), - decode_hex("5017abeca33fa82576b5c5c2c61792693b48c9d4414a407eef66b6029dae07ea").unwrap() - ); - } - - #[test] - fn stone_compatibility_case_1_fri_query_phase_composition_poly_openings() { - let proof = stone_compatibility_case_1_proof(); - - // Composition poly - assert_eq!( - proof.deep_poly_openings[0].composition_poly.evaluations[0], - FieldElement::from_hex_unchecked( - "2b54852557db698e97253e9d110d60e9bf09f1d358b4c1a96f9f3cf9d2e8755" - ) - ); - // Composition poly sym - assert_eq!( - proof.deep_poly_openings[0].composition_poly.evaluations_sym[0], - FieldElement::from_hex_unchecked( - "190f1b0acb7858bd3f5285b68befcf32b436a5f1e3a280e1f42565c1f35c2c3" - ) - ); - } - - #[test] - fn stone_compatibility_case_1_fri_query_phase_composition_poly_authentication_path() { - let proof = stone_compatibility_case_1_proof(); - - // Composition poly auth path level 0 - assert_eq!( - proof.deep_poly_openings[0] - .composition_poly - .proof - .merkle_path[0] - .to_vec(), - decode_hex("403b75a122eaf90a298e5d3db2cc7ca096db478078122379a6e3616e72da7546").unwrap() - ); - - // Composition poly auth path level 1 - assert_eq!( - proof.deep_poly_openings[0] - .composition_poly - .proof - .merkle_path[1] - .to_vec(), - decode_hex("07950888c0355c204a1e83ecbee77a0a6a89f93d41cc2be6b39ddd1e727cc965").unwrap() - ); - - // Composition poly auth path level 2 - assert_eq!( - proof.deep_poly_openings[0] - .composition_poly - .proof - .merkle_path[2] - .to_vec(), - decode_hex("58befe2c5de74cc5a002aa82ea219c5b242e761b45fd266eb95521e9f53f44eb").unwrap() - ); - } - - #[test] - fn stone_compatibility_case_1_fri_query_phase_query_lengths() { - let proof = stone_compatibility_case_1_proof(); - - assert_eq!(proof.query_list.len(), 1); - - assert_eq!(proof.query_list[0].layers_evaluations_sym.len(), 1); - - assert_eq!( - proof.query_list[0].layers_auth_paths[0].merkle_path.len(), - 2 - ); - } - - #[test] - fn stone_compatibility_case_1_fri_query_phase_layer_1_evaluation_symmetric() { - let proof = stone_compatibility_case_1_proof(); - - assert_eq!( - proof.query_list[0].layers_evaluations_sym[0], - FieldElement::from_hex_unchecked( - "0684991e76e5c08db17f33ea7840596be876d92c143f863e77cad10548289fd0" - ) - ); - } - - #[test] - fn stone_compatibility_case_1_fri_query_phase_layer_1_authentication_path() { - let proof = stone_compatibility_case_1_proof(); - - // FRI layer 1 auth path level 0 - assert_eq!( - proof.query_list[0].layers_auth_paths[0].merkle_path[0].to_vec(), - decode_hex("0683622478e9e93cc2d18754872f043619f030b494d7ec8e003b1cbafe83b67b").unwrap() - ); - - // FRI layer 1 auth path level 1 - assert_eq!( - proof.query_list[0].layers_auth_paths[0].merkle_path[1].to_vec(), - decode_hex("7985d945abe659a7502698051ec739508ed6bab594984c7f25e095a0a57a2e55").unwrap() - ); - } - - fn proof_parts_stone_compatibility_case_2() -> ( - StarkProof, - fibonacci_2_cols_shifted::PublicInputs, - ProofOptions, - [u8; 4], - ) { - let mut trace = fibonacci_2_cols_shifted::compute_trace(FieldElement::from(12345), 512); - - let claimed_index = 420; - let col = 0; - let claimed_value = *trace.get_main(claimed_index, col); - let mut proof_options = ProofOptions::default_test_options(); - proof_options.blowup_factor = 1 << 6; - proof_options.coset_offset = 3; - proof_options.grinding_factor = 0; - proof_options.fri_number_of_queries = 1; - - let pub_inputs = fibonacci_2_cols_shifted::PublicInputs { - claimed_value, - claimed_index, - }; - - let transcript_init_seed = [0xfa, 0xfa, 0xfa, 0xee]; - - let proof = Prover::>::prove( - &mut trace, - &pub_inputs, - &proof_options, - StoneProverTranscript::new(&transcript_init_seed), - ) - .unwrap(); - (proof, pub_inputs, proof_options, transcript_init_seed) - } - - fn stone_compatibility_case_2_proof() -> StarkProof { - let (proof, _, _, _) = proof_parts_stone_compatibility_case_2(); - proof - } - - fn stone_compatibility_case_2_challenges( - ) -> Challenges> { - let (proof, public_inputs, options, seed) = proof_parts_stone_compatibility_case_2(); - - let air = Fibonacci2ColsShifted::new(proof.trace_length, &public_inputs, &options); - let domain = Domain::new(&air); - Verifier::step_1_replay_rounds_and_recover_challenges( - &air, - &proof, - &domain, - &mut StoneProverTranscript::new(&seed), - ) - } - - #[test] - fn stone_compatibility_case_2_trace_commitment() { - let proof = stone_compatibility_case_2_proof(); - - assert_eq!( - proof.lde_trace_main_merkle_root.to_vec(), - decode_hex("6d31dd00038974bde5fe0c5e3a765f8ddc822a5df3254fca85a1950ae0208cbe").unwrap() - ); - } - - #[test] - fn stone_compatibility_case_2_fri_query_iota_challenge() { - let challenges = stone_compatibility_case_2_challenges(); - assert_eq!(challenges.iotas[0], 4239); - } - - #[test] - fn stone_compatibility_case_2_fri_query_phase_layer_7_evaluation_symmetric() { - let proof = stone_compatibility_case_2_proof(); - - assert_eq!( - proof.query_list[0].layers_evaluations_sym[7], - FieldElement::from_hex_unchecked( - "7aa40c5a4e30b44fee5bcc47c54072a435aa35c1a31b805cad8126118cc6860" - ) - ); - } - - #[test] - fn stone_compatibility_case_2_fri_query_phase_layer_8_authentication_path() { - let proof = stone_compatibility_case_2_proof(); - - // FRI layer 7 auth path level 5 - assert_eq!( - proof.query_list[0].layers_auth_paths[7].merkle_path[5].to_vec(), - decode_hex("f12f159b548ca2c571a270870d43e7ec2ead78b3e93b635738c31eb9bcda3dda").unwrap() - ); - } -} diff --git a/crates/provers/stark/src/table.rs b/crates/provers/stark/src/table.rs deleted file mode 100644 index 561482730..000000000 --- a/crates/provers/stark/src/table.rs +++ /dev/null @@ -1,170 +0,0 @@ -use crate::frame::Frame; -use lambdaworks_math::field::{ - element::FieldElement, - traits::{IsField, IsSubFieldOf}, -}; - -/// A two-dimensional Table holding field elements, arranged in a row-major order. -/// This is the basic underlying data structure used for any two-dimensional component in the -/// the STARK protocol implementation, such as the `TraceTable` and the `EvaluationFrame`. -/// Since this struct is a representation of a two-dimensional table, all rows should have the same -/// length. -#[derive(Clone, Default, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)] -pub struct Table { - pub data: Vec>, - pub width: usize, - pub height: usize, -} - -impl<'t, F: IsField> Table { - /// Crates a new Table instance from a one-dimensional array in row major order - /// and the intended width of the table. - pub fn new(data: Vec>, width: usize) -> Self { - // Check if the intented width is 0, used for creating an empty table. - if width == 0 { - return Self { - data: Vec::new(), - width, - height: 0, - }; - } - - // Check that the one-dimensional data makes sense to be interpreted as a 2D one. - debug_assert!(crate::debug::validate_2d_structure(&data, width)); - let height = data.len() / width; - - Self { - data, - width, - height, - } - } - - /// Creates a Table instance from a vector of the intended columns. - pub fn from_columns(columns: Vec>>) -> Self { - if columns.is_empty() { - return Self::new(Vec::new(), 0); - } - let height = columns[0].len(); - - // Check that all columns have the same length for integrity - debug_assert!(columns.iter().all(|c| c.len() == height)); - - let width = columns.len(); - let mut data = Vec::with_capacity(width * height); - - for row_idx in 0..height { - for column in columns.iter() { - data.push(column[row_idx].clone()); - } - } - - Self::new(data, width) - } - - /// Returns a vector of vectors of field elements representing the table rows - pub fn rows(&self) -> Vec>> { - self.data.chunks(self.width).map(|r| r.to_vec()).collect() - } - - /// Given a row index, returns a reference to that row as a slice of field elements. - pub fn get_row(&self, row_idx: usize) -> &[FieldElement] { - let row_offset = row_idx * self.width; - &self.data[row_offset..row_offset + self.width] - } - - /// Given a slice of field elements representing a row, appends it to - /// the end of the table. - pub fn append_row(&mut self, row: &[FieldElement]) { - debug_assert_eq!(row.len(), self.width); - self.data.extend_from_slice(row); - self.height += 1 - } - - /// Returns a reference to the last row of the table - pub fn last_row(&self) -> &[FieldElement] { - self.get_row(self.height - 1) - } - - /// Returns a vector of vectors of field elements representing the table - /// columns - pub fn columns(&self) -> Vec>> { - (0..self.width) - .map(|col_idx| { - (0..self.height) - .map(|row_idx| self.data[row_idx * self.width + col_idx].clone()) - .collect() - }) - .collect() - } - - pub fn get_column(&self, col_idx: usize) -> Vec> { - (0..self.height) - .map(|row_idx| self.data[row_idx * self.width + col_idx].clone()) - .collect() - } - - /// Given row and column indexes, returns the stored field element in that position of the table. - pub fn get(&self, row: usize, col: usize) -> &FieldElement { - let idx = row * self.width + col; - &self.data[idx] - } - - pub fn set(&mut self, row: usize, col: usize, value: FieldElement) { - let idx = row * self.width + col; - self.data[idx] = value; - } - - /// Given a step size, converts the given table into a `Frame`. - pub fn into_frame(&'t self, main_trace_columns: usize, step_size: usize) -> Frame<'t, F, F> { - debug_assert!(self.height % step_size == 0); - let steps = (0..self.height) - .step_by(step_size) - .map(|initial_row_idx| { - let end_row_idx = initial_row_idx + step_size; - - let mut step_main_data: Vec<&'t [FieldElement]> = Vec::new(); - let mut step_aux_data: Vec<&'t [FieldElement]> = Vec::new(); - - (initial_row_idx..end_row_idx).for_each(|row_idx| { - let row = self.get_row(row_idx); - step_main_data.push(&row[..main_trace_columns]); - step_aux_data.push(&row[main_trace_columns..]); - }); - - TableView::new(step_main_data, step_aux_data) - }) - .collect(); - - Frame::new(steps) - } -} - -/// A view of a contiguos subset of rows of a table. -#[derive(Clone, Debug, PartialEq, Eq)] -pub struct TableView<'t, F, E> -where - E: IsField, - F: IsSubFieldOf, -{ - pub data: Vec<&'t [FieldElement]>, - pub aux_data: Vec<&'t [FieldElement]>, -} - -impl<'t, F, E> TableView<'t, F, E> -where - E: IsField, - F: IsSubFieldOf, -{ - pub fn new(data: Vec<&'t [FieldElement]>, aux_data: Vec<&'t [FieldElement]>) -> Self { - Self { data, aux_data } - } - - pub fn get_main_evaluation_element(&self, row: usize, col: usize) -> &FieldElement { - &self.data[row][col] - } - - pub fn get_aux_evaluation_element(&self, row: usize, col: usize) -> &FieldElement { - &self.aux_data[row][col] - } -} diff --git a/crates/provers/stark/src/tests/integration_tests.rs b/crates/provers/stark/src/tests/integration_tests.rs deleted file mode 100644 index 39354b09a..000000000 --- a/crates/provers/stark/src/tests/integration_tests.rs +++ /dev/null @@ -1,355 +0,0 @@ -use lambdaworks_crypto::fiat_shamir::default_transcript::DefaultTranscript; -use lambdaworks_math::field::{ - element::FieldElement, fields::fft_friendly::stark_252_prime_field::Stark252PrimeField, -}; - -use lambdaworks_math::field::fields::fft_friendly::{ - babybear::Babybear31PrimeField, quartic_babybear::Degree4BabyBearExtensionField, -}; - -use crate::{ - examples::{ - bit_flags::{self, BitFlagsAIR}, - dummy_air::{self, DummyAIR}, - fibonacci_2_cols_shifted::{self, Fibonacci2ColsShifted}, - fibonacci_2_columns::{self, Fibonacci2ColsAIR}, - fibonacci_rap::{fibonacci_rap_trace, FibonacciRAP, FibonacciRAPPublicInputs}, - quadratic_air::{self, QuadraticAIR, QuadraticPublicInputs}, - read_only_memory::{sort_rap_trace, ReadOnlyPublicInputs, ReadOnlyRAP}, - simple_fibonacci::{self, FibonacciAIR, FibonacciPublicInputs}, - simple_periodic_cols::{self, SimplePeriodicAIR, SimplePeriodicPublicInputs}, // simple_periodic_cols::{self, SimplePeriodicAIR, SimplePeriodicPublicInputs}, - }, - proof::options::ProofOptions, - prover::{IsStarkProver, Prover}, - transcript::StoneProverTranscript, - verifier::{IsStarkVerifier, Verifier}, - Felt252, -}; - -use crate::examples::read_only_memory_logup::{ - read_only_logup_trace, LogReadOnlyPublicInputs, LogReadOnlyRAP, -}; - -#[test_log::test] -fn test_prove_fib() { - let mut trace = simple_fibonacci::fibonacci_trace([Felt252::from(1), Felt252::from(1)], 8); - - let proof_options = ProofOptions::default_test_options(); - - let pub_inputs = FibonacciPublicInputs { - a0: Felt252::one(), - a1: Felt252::one(), - }; - - let proof = Prover::>::prove( - &mut trace, - &pub_inputs, - &proof_options, - StoneProverTranscript::new(&[]), - ) - .unwrap(); - assert!(Verifier::>::verify( - &proof, - &pub_inputs, - &proof_options, - StoneProverTranscript::new(&[]), - )); -} - -#[test_log::test] -fn test_prove_simple_periodic_8() { - let mut trace = simple_periodic_cols::simple_periodic_trace::(8); - - let proof_options = ProofOptions::default_test_options(); - - let pub_inputs = SimplePeriodicPublicInputs { - a0: Felt252::one(), - a1: Felt252::from(8), - }; - - let proof = Prover::>::prove( - &mut trace, - &pub_inputs, - &proof_options, - StoneProverTranscript::new(&[]), - ) - .unwrap(); - assert!(Verifier::>::verify( - &proof, - &pub_inputs, - &proof_options, - StoneProverTranscript::new(&[]), - )); -} - -#[test_log::test] -fn test_prove_simple_periodic_32() { - let mut trace = simple_periodic_cols::simple_periodic_trace::(32); - - let proof_options = ProofOptions::default_test_options(); - - let pub_inputs = SimplePeriodicPublicInputs { - a0: Felt252::one(), - a1: Felt252::from(32768), - }; - - let proof = Prover::>::prove( - &mut trace, - &pub_inputs, - &proof_options, - StoneProverTranscript::new(&[]), - ) - .unwrap(); - assert!(Verifier::>::verify( - &proof, - &pub_inputs, - &proof_options, - StoneProverTranscript::new(&[]), - )); -} - -#[test_log::test] -fn test_prove_fib_2_cols() { - let mut trace = fibonacci_2_columns::compute_trace([Felt252::from(1), Felt252::from(1)], 16); - - let proof_options = ProofOptions::default_test_options(); - let pub_inputs = FibonacciPublicInputs { - a0: Felt252::one(), - a1: Felt252::one(), - }; - - let proof = Prover::>::prove( - &mut trace, - &pub_inputs, - &proof_options, - StoneProverTranscript::new(&[]), - ) - .unwrap(); - - assert!(Verifier::>::verify( - &proof, - &pub_inputs, - &proof_options, - StoneProverTranscript::new(&[]) - )); -} - -#[test_log::test] -fn test_prove_fib_2_cols_shifted() { - let mut trace = fibonacci_2_cols_shifted::compute_trace(FieldElement::one(), 16); - - let claimed_index = 14; - let claimed_value = trace.main_table.get_row(claimed_index)[0]; - let proof_options = ProofOptions::default_test_options(); - - let pub_inputs = fibonacci_2_cols_shifted::PublicInputs { - claimed_value, - claimed_index, - }; - - let proof = Prover::>::prove( - &mut trace, - &pub_inputs, - &proof_options, - StoneProverTranscript::new(&[]), - ) - .unwrap(); - assert!(Verifier::>::verify( - &proof, - &pub_inputs, - &proof_options, - StoneProverTranscript::new(&[]) - )); -} - -#[test_log::test] -fn test_prove_quadratic() { - let mut trace = quadratic_air::quadratic_trace(Felt252::from(3), 32); - - let proof_options = ProofOptions::default_test_options(); - - let pub_inputs = QuadraticPublicInputs { - a0: Felt252::from(3), - }; - - let proof = Prover::>::prove( - &mut trace, - &pub_inputs, - &proof_options, - StoneProverTranscript::new(&[]), - ) - .unwrap(); - assert!(Verifier::>::verify( - &proof, - &pub_inputs, - &proof_options, - StoneProverTranscript::new(&[]) - )); -} - -#[test_log::test] -fn test_prove_rap_fib() { - let steps = 16; - let mut trace = fibonacci_rap_trace([Felt252::from(1), Felt252::from(1)], steps); - - let proof_options = ProofOptions::default_test_options(); - - let pub_inputs = FibonacciRAPPublicInputs { - steps, - a0: Felt252::one(), - a1: Felt252::one(), - }; - - let proof = Prover::>::prove( - &mut trace, - &pub_inputs, - &proof_options, - StoneProverTranscript::new(&[]), - ) - .unwrap(); - assert!(Verifier::>::verify( - &proof, - &pub_inputs, - &proof_options, - StoneProverTranscript::new(&[]) - )); -} - -#[test_log::test] -fn test_prove_dummy() { - let trace_length = 16; - let mut trace = dummy_air::dummy_trace(trace_length); - - let proof_options = ProofOptions::default_test_options(); - - let proof = Prover::::prove( - &mut trace, - &(), - &proof_options, - StoneProverTranscript::new(&[]), - ) - .unwrap(); - assert!(Verifier::::verify( - &proof, - &(), - &proof_options, - StoneProverTranscript::new(&[]) - )); -} - -#[test_log::test] -fn test_prove_bit_flags() { - let mut trace = bit_flags::bit_prefix_flag_trace(32); - let proof_options = ProofOptions::default_test_options(); - - let proof = Prover::::prove( - &mut trace, - &(), - &proof_options, - StoneProverTranscript::new(&[]), - ) - .unwrap(); - - assert!(Verifier::::verify( - &proof, - &(), - &proof_options, - StoneProverTranscript::new(&[]), - )); -} - -#[test_log::test] -fn test_prove_read_only_memory() { - let address_col = vec![ - FieldElement::::from(3), // a0 - FieldElement::::from(2), // a1 - FieldElement::::from(2), // a2 - FieldElement::::from(3), // a3 - FieldElement::::from(4), // a4 - FieldElement::::from(5), // a5 - FieldElement::::from(1), // a6 - FieldElement::::from(3), // a7 - ]; - let value_col = vec![ - FieldElement::::from(10), // v0 - FieldElement::::from(5), // v1 - FieldElement::::from(5), // v2 - FieldElement::::from(10), // v3 - FieldElement::::from(25), // v4 - FieldElement::::from(25), // v5 - FieldElement::::from(7), // v6 - FieldElement::::from(10), // v7 - ]; - - let pub_inputs = ReadOnlyPublicInputs { - a0: FieldElement::::from(3), - v0: FieldElement::::from(10), - a_sorted0: FieldElement::::from(1), // a6 - v_sorted0: FieldElement::::from(7), // v6 - }; - let mut trace = sort_rap_trace(address_col, value_col); - let proof_options = ProofOptions::default_test_options(); - let proof = Prover::>::prove( - &mut trace, - &pub_inputs, - &proof_options, - StoneProverTranscript::new(&[]), - ) - .unwrap(); - assert!(Verifier::>::verify( - &proof, - &pub_inputs, - &proof_options, - StoneProverTranscript::new(&[]) - )); -} - -#[test_log::test] -fn test_prove_log_read_only_memory() { - let address_col = vec![ - FieldElement::::from(3), // a0 - FieldElement::::from(2), // a1 - FieldElement::::from(2), // a2 - FieldElement::::from(3), // a3 - FieldElement::::from(4), // a4 - FieldElement::::from(5), // a5 - FieldElement::::from(1), // a6 - FieldElement::::from(3), // a7 - ]; - let value_col = vec![ - FieldElement::::from(30), // v0 - FieldElement::::from(20), // v1 - FieldElement::::from(20), // v2 - FieldElement::::from(30), // v3 - FieldElement::::from(40), // v4 - FieldElement::::from(50), // v5 - FieldElement::::from(10), // v6 - FieldElement::::from(30), // v7 - ]; - - let pub_inputs = LogReadOnlyPublicInputs { - a0: FieldElement::::from(3), - v0: FieldElement::::from(30), - a_sorted_0: FieldElement::::from(1), - v_sorted_0: FieldElement::::from(10), - m0: FieldElement::::from(1), - }; - let mut trace = read_only_logup_trace(address_col, value_col); - let proof_options = ProofOptions::default_test_options(); - let proof = - Prover::>::prove( - &mut trace, - &pub_inputs, - &proof_options, - DefaultTranscript::::new(&[]), - ) - .unwrap(); - assert!(Verifier::< - LogReadOnlyRAP, - >::verify( - &proof, - &pub_inputs, - &proof_options, - DefaultTranscript::::new(&[]), - )); -} diff --git a/crates/provers/stark/src/tests/mod.rs b/crates/provers/stark/src/tests/mod.rs deleted file mode 100644 index 7345833b7..000000000 --- a/crates/provers/stark/src/tests/mod.rs +++ /dev/null @@ -1 +0,0 @@ -pub mod integration_tests; diff --git a/crates/provers/stark/src/trace.rs b/crates/provers/stark/src/trace.rs deleted file mode 100644 index d0693c650..000000000 --- a/crates/provers/stark/src/trace.rs +++ /dev/null @@ -1,387 +0,0 @@ -use crate::table::Table; -use itertools::Itertools; -use lambdaworks_math::fft::errors::FFTError; -use lambdaworks_math::field::traits::{IsField, IsSubFieldOf}; -use lambdaworks_math::{ - field::{element::FieldElement, traits::IsFFTField}, - polynomial::Polynomial, -}; -#[cfg(feature = "parallel")] -use rayon::prelude::{IntoParallelRefIterator, ParallelIterator}; - -/// A two-dimensional representation of an execution trace of the STARK -/// protocol. -/// -/// For the moment it is mostly a wrapper around the `Table` struct. It is a -/// layer above the raw two-dimensional table, with functionality relevant to the -/// STARK protocol, such as the step size (number of consecutive rows of the table) -/// of the computation being proven. -#[derive(Clone, Default, Debug, PartialEq, Eq)] -pub struct TraceTable -where - E: IsField, - F: IsSubFieldOf + IsField, -{ - pub main_table: Table, - pub aux_table: Table, - pub num_main_columns: usize, - pub num_aux_columns: usize, - pub step_size: usize, -} - -impl TraceTable -where - E: IsField, - F: IsSubFieldOf + IsFFTField, -{ - pub fn new( - main_data: Vec>, - aux_data: Vec>, - num_main_columns: usize, - num_aux_columns: usize, - step_size: usize, - ) -> Self { - let main_table = Table::new(main_data, num_main_columns); - let aux_table = Table::new(aux_data, num_aux_columns); - - Self { - main_table, - aux_table, - num_main_columns, - num_aux_columns, - step_size, - } - } - - /// Creates a new TraceTable from from a one-dimensional array in row major order and the intended width of the table. - /// Step size is how many are needed to represent a state of the VM - pub fn new_main( - main_data: Vec>, - num_main_columns: usize, - step_size: usize, - ) -> Self { - let num_aux_columns = 0; - let main_table = Table::new(main_data, num_main_columns); - let aux_table = Table::new(Vec::new(), num_aux_columns); - - Self { - main_table, - aux_table, - num_main_columns, - num_aux_columns, - step_size, - } - } - - /// Creates a new TraceTable from its colummns - /// Step size is how many are needed to represent a state of the VM - pub fn from_columns( - main_columns: Vec>>, - aux_columns: Vec>>, - step_size: usize, - ) -> Self { - let num_main_columns = main_columns.len(); - let num_aux_columns = aux_columns.len(); - - let main_table = Table::from_columns(main_columns); - let aux_table = Table::from_columns(aux_columns); - - Self { - main_table, - aux_table, - num_main_columns, - num_aux_columns, - step_size, - } - } - - pub fn from_columns_main(columns: Vec>>, step_size: usize) -> Self { - let num_main_columns = columns.len(); - let num_aux_columns = 0; - let main_table = Table::from_columns(columns); - let aux_table = Table::from_columns(Vec::new()); - - Self { - main_table, - aux_table, - num_main_columns, - num_aux_columns, - step_size, - } - } - - pub fn empty() -> Self { - Self::new(Vec::new(), Vec::new(), 0, 0, 0) - } - - pub fn is_empty(&self) -> bool { - self.main_table.width == 0 && self.aux_table.width == 0 - } - - pub fn num_rows(&self) -> usize { - self.main_table.height - } - - pub fn num_steps(&self) -> usize { - debug_assert!((self.main_table.height % self.step_size) == 0); - self.main_table.height / self.step_size - } - - /// Given a particular step of the computation represented on the trace, - /// returns the row of the underlying table. - pub fn step_to_row(&self, step: usize) -> usize { - self.step_size * step - } - - pub fn num_cols(&self) -> usize { - self.main_table.width + self.aux_table.width - } - - pub fn columns_main(&self) -> Vec>> { - self.main_table.columns() - } - - pub fn columns_aux(&self) -> Vec>> { - self.aux_table.columns() - } - - /// Given a row and a column index, gives stored value in that position - pub fn get_main(&self, row: usize, col: usize) -> &FieldElement { - self.main_table.get(row, col) - } - - /// Given a row and a column index, gives stored value in that position - pub fn get_aux(&self, row: usize, col: usize) -> &FieldElement { - self.aux_table.get(row, col) - } - - pub fn set_main(&mut self, row: usize, col: usize, value: FieldElement) { - self.main_table.set(row, col, value); - } - - pub fn set_aux(&mut self, row: usize, col: usize, value: FieldElement) { - self.aux_table.set(row, col, value); - } - - pub fn allocate_with_zeros( - num_steps: usize, - num_main_columns: usize, - num_aux_columns: usize, - step_size: usize, - ) -> TraceTable { - let main_data = vec![FieldElement::::zero(); step_size * num_steps * num_main_columns]; - let aux_data = vec![FieldElement::::zero(); step_size * num_steps * num_aux_columns]; - TraceTable::new( - main_data, - aux_data, - num_main_columns, - num_aux_columns, - step_size, - ) - } - - pub fn compute_trace_polys_main(&self) -> Vec>> - where - S: IsFFTField + IsSubFieldOf, - FieldElement: Send + Sync, - { - let columns = self.columns_main(); - #[cfg(feature = "parallel")] - let iter = columns.par_iter(); - #[cfg(not(feature = "parallel"))] - let iter = columns.iter(); - - iter.map(|col| Polynomial::interpolate_fft::(col)) - .collect::>>, FFTError>>() - .unwrap() - } - - pub fn compute_trace_polys_aux(&self) -> Vec>> - where - S: IsFFTField + IsSubFieldOf, - FieldElement: Send + Sync, - { - let columns = self.columns_aux(); - #[cfg(feature = "parallel")] - let iter = columns.par_iter(); - #[cfg(not(feature = "parallel"))] - let iter = columns.iter(); - - iter.map(|col| Polynomial::interpolate_fft::(col)) - .collect::>>, FFTError>>() - .unwrap() - } - - pub fn get_column_main(&self, col_idx: usize) -> Vec> { - self.main_table.get_column(col_idx) - } - - pub fn get_column_aux(&self, col_idx: usize) -> Vec> { - self.aux_table.get_column(col_idx) - } -} -pub struct LDETraceTable -where - E: IsField, - F: IsSubFieldOf + IsField, -{ - pub(crate) main_table: Table, - pub(crate) aux_table: Table, - pub(crate) lde_step_size: usize, - pub(crate) blowup_factor: usize, -} - -impl LDETraceTable -where - E: IsField, - F: IsSubFieldOf, -{ - pub fn new( - main_data: Vec>, - aux_data: Vec>, - n_columns: usize, - trace_step_size: usize, - blowup_factor: usize, - ) -> Self { - let main_table = Table::new(main_data, n_columns); - let aux_table = Table::new(aux_data, n_columns); - let lde_step_size = trace_step_size * blowup_factor; - - Self { - main_table, - aux_table, - lde_step_size, - blowup_factor, - } - } - - pub fn from_columns( - main_columns: Vec>>, - aux_columns: Vec>>, - trace_step_size: usize, - blowup_factor: usize, - ) -> Self { - let main_table = Table::from_columns(main_columns); - let aux_table = Table::from_columns(aux_columns); - let lde_step_size = trace_step_size * blowup_factor; - - Self { - main_table, - aux_table, - lde_step_size, - blowup_factor, - } - } - - pub fn num_cols(&self) -> usize { - self.main_table.width + self.aux_table.width - } - - pub fn num_rows(&self) -> usize { - self.main_table.height - } - - pub fn get_main_row(&self, row_idx: usize) -> &[FieldElement] { - self.main_table.get_row(row_idx) - } - - pub fn get_aux_row(&self, row_idx: usize) -> &[FieldElement] { - self.aux_table.get_row(row_idx) - } - - pub fn get_main(&self, row: usize, col: usize) -> &FieldElement { - self.main_table.get(row, col) - } - - pub fn get_aux(&self, row: usize, col: usize) -> &FieldElement { - self.aux_table.get(row, col) - } - - pub fn num_steps(&self) -> usize { - debug_assert!((self.main_table.height % self.lde_step_size) == 0); - self.main_table.height / self.lde_step_size - } - - pub fn step_to_row(&self, step: usize) -> usize { - self.lde_step_size * step - } -} - -/// Given a slice of trace polynomials, an evaluation point `x`, the frame offsets -/// corresponding to the computation of the transitions, and a primitive root, -/// outputs the trace evaluations of each trace polynomial over the values used to -/// compute a transition. -/// Example: For a simple Fibonacci computation, if t(x) is the trace polynomial of -/// the computation, this will output evaluations t(x), t(g * x), t(g^2 * z). -pub fn get_trace_evaluations( - main_trace_polys: &[Polynomial>], - aux_trace_polys: &[Polynomial>], - x: &FieldElement, - frame_offsets: &[usize], - primitive_root: &FieldElement, - step_size: usize, -) -> Table -where - F: IsSubFieldOf, - E: IsField, -{ - let evaluation_points = frame_offsets - .iter() - .flat_map(|offset| { - let exponents_range_start = offset * step_size; - let exponents_range_end = (offset + 1) * step_size; - (exponents_range_start..exponents_range_end).collect_vec() - }) - .map(|exponent| primitive_root.pow(exponent) * x) - .collect_vec(); - - let main_evaluations = evaluation_points - .iter() - .map(|eval_point| { - main_trace_polys - .iter() - .map(|main_poly| main_poly.evaluate(eval_point)) - .collect_vec() - }) - .collect_vec(); - - let aux_evaluations = evaluation_points - .iter() - .map(|eval_point| { - aux_trace_polys - .iter() - .map(|aux_poly| aux_poly.evaluate(eval_point)) - .collect_vec() - }) - .collect_vec(); - - debug_assert_eq!(main_evaluations.len(), aux_evaluations.len()); - let mut main_evaluations = main_evaluations; - let mut table_data = Vec::new(); - for (main_row, aux_row) in main_evaluations.iter_mut().zip(aux_evaluations) { - main_row.extend_from_slice(&aux_row); - table_data.extend_from_slice(main_row); - } - - let main_trace_width = main_trace_polys.len(); - let aux_trace_width = aux_trace_polys.len(); - let table_width = main_trace_width + aux_trace_width; - - Table::new(table_data, table_width) -} - -pub fn columns2rows(columns: Vec>) -> Vec> -where - F: Clone, -{ - let num_rows = columns[0].len(); - let num_cols = columns.len(); - - (0..num_rows) - .map(|row_index| { - (0..num_cols) - .map(|col_index| columns[col_index][row_index].clone()) - .collect() - }) - .collect() -} diff --git a/crates/provers/stark/src/traits.rs b/crates/provers/stark/src/traits.rs deleted file mode 100644 index afb1b3014..000000000 --- a/crates/provers/stark/src/traits.rs +++ /dev/null @@ -1,231 +0,0 @@ -use std::collections::HashMap; - -use lambdaworks_crypto::fiat_shamir::is_transcript::IsTranscript; -use lambdaworks_math::{ - field::{ - element::FieldElement, - traits::{IsFFTField, IsField, IsSubFieldOf}, - }, - polynomial::Polynomial, -}; - -use crate::{constraints::transition::TransitionConstraint, domain::Domain}; - -use super::{ - constraints::boundary::BoundaryConstraints, context::AirContext, frame::Frame, - proof::options::ProofOptions, trace::TraceTable, -}; - -type ZerofierGroupKey = (usize, usize, Option, Option, usize); - -/// This enum is necessary because, while both the prover and verifier perform the same operations -/// to compute transition constraints, their frames differ. -/// The prover uses a frame containing elements from both the base field and its extension -/// (common when working with small fields and challengers in the extension). -/// In contrast, the verifier, lacking access to the trace and relying solely on evaluations at the challengers, -/// works with a frame that contains only elements from the extension. -pub enum TransitionEvaluationContext<'a, F, E> -where - F: IsSubFieldOf, - E: IsField, -{ - Prover { - frame: &'a Frame<'a, F, E>, - periodic_values: &'a [FieldElement], - rap_challenges: &'a [FieldElement], - }, - Verifier { - frame: &'a Frame<'a, E, E>, - periodic_values: &'a [FieldElement], - rap_challenges: &'a [FieldElement], - }, -} - -impl<'a, F, E> TransitionEvaluationContext<'a, F, E> -where - F: IsSubFieldOf, - E: IsField, -{ - pub fn new_prover( - frame: &'a Frame<'a, F, E>, - periodic_values: &'a [FieldElement], - rap_challenges: &'a [FieldElement], - ) -> Self { - Self::Prover { - frame, - periodic_values, - rap_challenges, - } - } - - pub fn new_verifier( - frame: &'a Frame<'a, E, E>, - periodic_values: &'a [FieldElement], - rap_challenges: &'a [FieldElement], - ) -> Self { - Self::Verifier { - frame, - periodic_values, - rap_challenges, - } - } -} - -/// AIR is a representation of the Constraints -pub trait AIR { - type Field: IsFFTField + IsSubFieldOf + Send + Sync; - type FieldExtension: IsField + Send + Sync; - type PublicInputs; - - const STEP_SIZE: usize; - - fn new( - trace_length: usize, - pub_inputs: &Self::PublicInputs, - proof_options: &ProofOptions, - ) -> Self; - - fn build_auxiliary_trace( - &self, - _main_trace: &mut TraceTable, - _rap_challenges: &[FieldElement], - ) where - Self::FieldExtension: IsFFTField, - { - } - - fn build_rap_challenges( - &self, - _transcript: &mut impl IsTranscript, - ) -> Vec> { - Vec::new() - } - - /// Returns the amount main trace columns and auxiliary trace columns - fn trace_layout(&self) -> (usize, usize); - - fn has_trace_interaction(&self) -> bool { - let (_main_trace_columns, aux_trace_columns) = self.trace_layout(); - aux_trace_columns != 0 - } - - fn num_auxiliary_rap_columns(&self) -> usize { - self.trace_layout().1 - } - - fn composition_poly_degree_bound(&self) -> usize; - - /// The method called by the prover to evaluate the transitions corresponding to an evaluation frame. - /// In the case of the prover, the main evaluation table of the frame takes values in - /// `Self::Field`, since they are the evaluations of the main trace at the LDE domain. - /// In the case of the verifier, the frame take elements of Self::FieldExtension. - fn compute_transition( - &self, - evaluation_context: &TransitionEvaluationContext, - ) -> Vec> { - let mut evaluations = - vec![FieldElement::::zero(); self.num_transition_constraints()]; - self.transition_constraints() - .iter() - .for_each(|c| c.evaluate(evaluation_context, &mut evaluations)); - - evaluations - } - - fn boundary_constraints( - &self, - rap_challenges: &[FieldElement], - ) -> BoundaryConstraints; - - fn context(&self) -> &AirContext; - - fn trace_length(&self) -> usize; - - fn options(&self) -> &ProofOptions { - &self.context().proof_options - } - - fn blowup_factor(&self) -> u8 { - self.options().blowup_factor - } - - fn coset_offset(&self) -> FieldElement { - FieldElement::from(self.options().coset_offset) - } - - fn trace_primitive_root(&self) -> FieldElement { - let trace_length = self.trace_length(); - let root_of_unity_order = u64::from(trace_length.trailing_zeros()); - - Self::Field::get_primitive_root_of_unity(root_of_unity_order).unwrap() - } - - fn num_transition_constraints(&self) -> usize { - self.context().num_transition_constraints - } - - fn pub_inputs(&self) -> &Self::PublicInputs; - - fn get_periodic_column_values(&self) -> Vec>> { - vec![] - } - - fn get_periodic_column_polynomials(&self) -> Vec>> { - let mut result = Vec::new(); - for periodic_column in self.get_periodic_column_values() { - let values: Vec<_> = periodic_column - .iter() - .cycle() - .take(self.trace_length()) - .cloned() - .collect(); - let poly = - Polynomial::>::interpolate_fft::(&values) - .unwrap(); - result.push(poly); - } - result - } - - fn transition_constraints( - &self, - ) -> &Vec>>; - - fn transition_zerofier_evaluations( - &self, - domain: &Domain, - ) -> Vec>> { - let mut evals = vec![Vec::new(); self.num_transition_constraints()]; - - let mut zerofier_groups: HashMap>> = - HashMap::new(); - - self.transition_constraints().iter().for_each(|c| { - let period = c.period(); - let offset = c.offset(); - let exemptions_period = c.exemptions_period(); - let periodic_exemptions_offset = c.periodic_exemptions_offset(); - let end_exemptions = c.end_exemptions(); - - // This hashmap is used to avoid recomputing with an fft the same zerofier evaluation - // If there are multiple domain and subdomains it can be further optimized - // as to share computation between them - - let zerofier_group_key = ( - period, - offset, - exemptions_period, - periodic_exemptions_offset, - end_exemptions, - ); - zerofier_groups - .entry(zerofier_group_key) - .or_insert_with(|| c.zerofier_evaluations_on_extended_domain(domain)); - - let zerofier_evaluations = zerofier_groups.get(&zerofier_group_key).unwrap(); - evals[c.constraint_idx()] = zerofier_evaluations.clone(); - }); - - evals - } -} diff --git a/crates/provers/stark/src/transcript.rs b/crates/provers/stark/src/transcript.rs deleted file mode 100644 index abd040faf..000000000 --- a/crates/provers/stark/src/transcript.rs +++ /dev/null @@ -1,438 +0,0 @@ -use lambdaworks_crypto::fiat_shamir::is_transcript::IsTranscript; -use lambdaworks_math::{ - field::{ - element::FieldElement, fields::fft_friendly::stark_252_prime_field::Stark252PrimeField, - traits::IsFFTField, - }, - traits::{AsBytes, ByteConversion}, - unsigned_integer::element::U256, -}; -use sha3::{Digest, Keccak256}; - -/// A transcript implementing `IsTranscript` and compatible with Stone (https://github.com/starkware-libs/stone-prover). -pub struct StoneProverTranscript { - state: [u8; 32], - seed_increment: U256, - counter: u32, - spare_bytes: Vec, -} - -impl StoneProverTranscript { - /// The maximum multiple of the modulus of `p` that fits in 256 bits, where - /// `p = 0x800000000000011000000000000000000000000000000000000000000000001` - const MODULUS_MAX_MULTIPLE: U256 = U256::from_hex_unchecked( - "f80000000000020f00000000000000000000000000000000000000000000001f", - ); - - /// The value of `2^{-256} mod p`, where - /// `p = 0x800000000000011000000000000000000000000000000000000000000000001` - const R_INV: U256 = U256::from_hex_unchecked( - "0x40000000000001100000000000012100000000000000000000000000000000", - ); - pub fn new(public_input_data: &[u8]) -> Self { - StoneProverTranscript { - state: Self::keccak_hash(public_input_data), - seed_increment: U256::from_hex_unchecked("1"), - counter: 0, - spare_bytes: vec![], - } - } - - pub fn sample_block(&mut self, used_bytes: usize) -> Vec { - let mut first_part: Vec = self.state.to_vec(); - let mut counter_bytes: Vec = vec![0; 28] - .into_iter() - .chain(self.counter.to_be_bytes().to_vec()) - .collect(); - self.counter += 1; - first_part.append(&mut counter_bytes); - let block = Self::keccak_hash(&first_part); - self.spare_bytes.extend(&block[used_bytes..]); - block[..used_bytes].to_vec() - } - - pub fn sample(&mut self, num_bytes: usize) -> Vec { - let num_blocks = num_bytes / 32; - let mut result: Vec = Vec::new(); - - for _ in 0..num_blocks { - let mut block = self.sample_block(32); - result.append(&mut block); - } - - let rest = num_bytes % 32; - if rest <= self.spare_bytes.len() { - result.append(&mut self.spare_bytes[..rest].to_vec()); - self.spare_bytes.drain(..rest); - } else { - let mut block = self.sample_block(rest); - result.append(&mut block); - } - result - } - - pub fn sample_big_int(&mut self) -> U256 { - U256::from_bytes_be(&self.sample(32)).unwrap() - } - - fn keccak_hash(data: &[u8]) -> [u8; 32] { - let mut hasher = Keccak256::new(); - hasher.update(data); - let mut result_hash = [0_u8; 32]; - result_hash.copy_from_slice(&hasher.finalize_reset()); - result_hash - } -} - -impl IsTranscript for StoneProverTranscript { - fn append_field_element(&mut self, element: &FieldElement) { - let limbs = element.value().limbs; - let mut bytes: [u8; 32] = [0; 32]; - - for i in (0..4).rev() { - let limb_bytes = limbs[i].to_be_bytes(); - for j in 0..8 { - bytes[i * 8 + j] = limb_bytes[j] - } - } - self.append_bytes(&bytes); - } - - fn append_bytes(&mut self, new_bytes: &[u8]) { - let mut result_hash = [0_u8; 32]; - result_hash.copy_from_slice(&self.state); - result_hash.reverse(); - - let digest = U256::from_bytes_be(&self.state).unwrap(); - let new_seed = (digest + self.seed_increment).to_bytes_be(); - self.state = Self::keccak_hash(&[&new_seed, new_bytes].concat()); - self.counter = 0; - self.spare_bytes.clear(); - } - - fn state(&self) -> [u8; 32] { - self.state - } - - fn sample_field_element(&mut self) -> FieldElement { - let mut result = self.sample_big_int(); - while result >= Self::MODULUS_MAX_MULTIPLE { - result = self.sample_big_int(); - } - FieldElement::::new(result) * FieldElement::new(Self::R_INV) - } - - fn sample_u64(&mut self, upper_bound: u64) -> u64 { - // assert!(upper_bound < (1 << 12)); - let mut bytes = [0u8; 8]; - bytes.copy_from_slice(&self.sample(8)); - let u64_val: u64 = u64::from_be_bytes(bytes); - u64_val % upper_bound - } -} - -/// Returns a batch of size `size` of field elements sampled from the transcript `transcript`. -pub fn batch_sample_challenges( - size: usize, - transcript: &mut impl IsTranscript, -) -> Vec> -where - FieldElement: AsBytes, -{ - (0..size) - .map(|_| transcript.sample_field_element()) - .collect() -} - -#[cfg(test)] -mod tests { - use lambdaworks_math::field::{ - element::FieldElement, fields::fft_friendly::stark_252_prime_field::Stark252PrimeField, - }; - - use crate::transcript::{IsTranscript, StoneProverTranscript}; - - use std::num::ParseIntError; - - type FE = FieldElement; - - pub fn decode_hex(s: &str) -> Result, ParseIntError> { - (0..s.len()) - .step_by(2) - .map(|i| u8::from_str_radix(&s[i..i + 2], 16)) - .collect() - } - - #[test] - fn sample_bytes_from_stone_prover_channel() { - let mut transcript = StoneProverTranscript::new(&[0x01, 0x02, 0x03]); - transcript.append_bytes(&[0x04, 0x05, 0x06]); - assert_eq!( - transcript.sample(32), - vec![ - 0x8a, 0x3a, 0x67, 0xd1, 0x25, 0xa5, 0xa5, 0xea, 0x57, 0xc3, 0xfb, 0xe2, 0xc2, 0x55, - 0xb6, 0x0d, 0x0c, 0x89, 0x13, 0xa6, 0x27, 0x13, 0xe0, 0x99, 0xb3, 0x77, 0xc6, 0xc2, - 0x9a, 0x21, 0x85, 0x97 - ] - ); - assert_eq!( - transcript.sample(64), - vec![ - 0x56, 0xde, 0x56, 0x2a, 0xfd, 0x98, 0x19, 0xb9, 0xaa, 0xa0, 0x1b, 0x16, 0xf4, 0xeb, - 0x33, 0x71, 0xd5, 0xd8, 0x0f, 0x35, 0x29, 0xd8, 0xc1, 0x7a, 0x4b, 0xf4, 0x10, 0xe3, - 0x19, 0xb7, 0x64, 0x4a, 0xd2, 0x1c, 0xff, 0x14, 0x3d, 0xfd, 0xca, 0x32, 0x2c, 0x59, - 0xa3, 0x47, 0x5d, 0xd0, 0x34, 0xdf, 0x6d, 0xa7, 0x0c, 0xf5, 0xd2, 0x6a, 0xdd, 0x65, - 0xe0, 0x6d, 0x1e, 0x4f, 0xc7, 0x39, 0x52, 0x32 - ] - ); - assert_eq!( - transcript.sample(48), - vec![ - 0xe4, 0xb6, 0x3c, 0xfc, 0x03, 0xc9, 0x82, 0x8b, 0x63, 0x53, 0xb9, 0xad, 0x73, 0x6d, - 0x23, 0x88, 0x4c, 0x07, 0xb4, 0x9d, 0xf1, 0x1d, 0xef, 0xb9, 0x53, 0xfa, 0x02, 0xb5, - 0x3c, 0x43, 0xcf, 0xa3, 0x30, 0x5a, 0x02, 0x7e, 0xa6, 0x5e, 0x3c, 0x86, 0x3d, 0xdb, - 0x48, 0xea, 0x73, 0xbf, 0xdf, 0xab - ] - ); - assert_eq!( - transcript.sample(32), - vec![ - 0x82, 0xe1, 0xd4, 0xf8, 0xf0, 0x61, 0xa4, 0x17, 0x4b, 0xed, 0x58, 0x4e, 0xb5, 0x73, - 0x26, 0xb7, 0x63, 0x10, 0x37, 0x97, 0xbe, 0x0b, 0x57, 0xaf, 0x74, 0xfe, 0x33, 0x19, - 0xbd, 0xe5, 0x53, 0x21, - ] - ); - assert_eq!( - transcript.sample(16), - vec![ - 0xb0, 0xc6, 0x7a, 0x04, 0x19, 0x0a, 0x25, 0x72, 0xa8, 0x2e, 0xfa, 0x97, 0x92, 0x44, - 0x73, 0xe9 - ] - ); - assert_eq!( - transcript.sample(8), - vec![0xbd, 0x41, 0x28, 0xdd, 0x3a, 0xbc, 0x66, 0x18] - ); - assert_eq!( - transcript.sample(32), - vec![ - 0xcb, 0x66, 0xc9, 0x72, 0x39, 0x85, 0xe8, 0x7c, 0x30, 0xe1, 0xc7, 0x1d, 0x2f, 0x83, - 0x4a, 0xcd, 0x33, 0x85, 0xfb, 0xd5, 0x40, 0x69, 0x22, 0x6e, 0xc0, 0xf1, 0x8c, 0x40, - 0x26, 0x2f, 0x5f, 0x7c, - ] - ); - transcript.append_bytes(&[0x03, 0x02]); - assert_eq!( - transcript.sample(32), - vec![ - 0x69, 0x63, 0x72, 0x01, 0x84, 0x8b, 0x22, 0x82, 0xa6, 0x14, 0x6d, 0x47, 0xbb, 0xa9, - 0xa3, 0xc8, 0xdc, 0x1b, 0x8e, 0x2e, 0x2e, 0x21, 0x87, 0x77, 0xac, 0xe0, 0x3e, 0xce, - 0x6e, 0xa7, 0x9e, 0xb0, - ] - ); - } - - #[test] - fn test_sample_bytes() { - let mut transcript = StoneProverTranscript::new(&[0x01, 0x02]); - assert_eq!( - transcript.sample(8), - vec![89, 27, 84, 161, 127, 200, 195, 181] - ); - } - - #[test] - fn test_sample_field_element() { - let mut transcript = StoneProverTranscript::new(&[0x01, 0x02]); - assert_eq!( - transcript.sample_field_element(), - FE::from_hex_unchecked( - "20b962ed1a29c942e11dc63c00b51de816bcd8bf9acd221f3fa55e5201d69be" - ) - ); - } - - #[test] - fn test_sample_u64_element() { - let mut transcript = StoneProverTranscript::new(&[0x01, 0x02]); - assert_eq!(transcript.sample_u64(1024), 949); - } - - #[test] - fn test_sample_u64_after_appending_and_sampling_bytes() { - let mut transcript = StoneProverTranscript::new(&[0x01, 0x02]); - transcript.append_bytes(&[0x01, 0x02]); - assert_eq!(transcript.sample(4), vec![0x06, 0xe5, 0x36, 0xf5]); - assert_eq!(transcript.sample_u64(16), 5); - } - - #[test] - fn test_transcript_compatibility_with_stone_prover_1() { - // This corresponds to the following run. - // Air: `Fibonacci2ColsShifted` - // `trace_length`: 4 - // `blowup_factor`: 4 - // `fri_number_of_queries`: 1 - let mut transcript = StoneProverTranscript::new(&[0xca, 0xfe, 0xca, 0xfe]); - // Send hash of trace commitment - transcript.append_bytes( - &decode_hex("0eb9dcc0fb1854572a01236753ce05139d392aa3aeafe72abff150fe21175594") - .unwrap(), - ); - // Sample challenge to collapse the constraints for the composition polynomial - assert_eq!( - transcript.sample_field_element(), - FE::from_hex_unchecked( - "86105fff7b04ed4068ecccb8dbf1ed223bd45cd26c3532d6c80a818dbd4fa7" - ) - ); - // Send hash of composition poly commitment H - transcript.append_bytes( - &decode_hex("7cdd8d5fe3bd62254a417e2e260e0fed4fccdb6c9005e828446f645879394f38") - .unwrap(), - ); - // Sample challenge Z to compute t_j(z), H(z) - assert_eq!( - transcript.sample_field_element(), - FE::from_hex_unchecked( - "317629e783794b52cd27ac3a5e418c057fec9dd42f2b537cdb3f24c95b3e550" - ) - ); - // Append t_j(z), H(z) - transcript.append_field_element(&FE::from_hex_unchecked( - "70d8181785336cc7e0a0a1078a79ee6541ca0803ed3ff716de5a13c41684037", - )); - transcript.append_field_element(&FE::from_hex_unchecked( - "29808fc8b7480a69295e4b61600480ae574ca55f8d118100940501b789c1630", - )); - transcript.append_field_element(&FE::from_hex_unchecked( - "7d8110f21d1543324cc5e472ab82037eaad785707f8cae3d64c5b9034f0abd2", - )); - transcript.append_field_element(&FE::from_hex_unchecked( - "1b58470130218c122f71399bf1e04cf75a6e8556c4751629d5ce8c02cc4e62d", - )); - transcript.append_field_element(&FE::from_hex_unchecked( - "1c0b7c2275e36d62dfb48c791be122169dcc00c616c63f8efb2c2a504687e85", - )); - // Sample challenge Gamma to collapse the terms of the deep composition polynomial (batch open). - // Powers of this challenge are used if more than two terms. - assert_eq!( - transcript.sample_field_element(), - FE::from_hex_unchecked( - "a0c79c1c77ded19520873d9c2440451974d23302e451d13e8124cf82fc15dd" - ) - ); - // FRI: Sample challenge Zeta to split the polynomial in half - assert_eq!( - transcript.sample_field_element(), - FE::from_hex_unchecked( - "5c6b5a66c9fda19f583f0b10edbaade98d0e458288e62c2fa40e3da2b293cef" - ) - ); - // FRI: Send hash of commitment at Layer 1 - transcript.append_bytes( - &decode_hex("49c5672520e20eccc72aa28d6fa0d7ef446f1ede38d7c64fbb95d0f34a281803") - .unwrap(), - ); - // FRI: Sample challenge to split the polynomial in half - assert_eq!( - transcript.sample_field_element(), - FE::from_hex_unchecked( - "4243ca9a618e2127590af8e1b38c63a156863fe95e4211cc1ade9b50667bbfa" - ) - ); - // Send field element at final layer of FRI - transcript.append_field_element(&FE::from_hex_unchecked( - "702ddae5809ad82a82556eed2d202202d770962b7d4d82581e183df3efa2da6", - )); - // Send proof of work - transcript.append_bytes(&[0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x30, 0x4d]); // Eight bytes - // Sample query indices - assert_eq!(transcript.sample_u64(8), 0); - } - - #[test] - fn test_transcript_compatibility_with_stone_prover_2() { - // This corresponds to the following run. - // Air: `Fibonacci2ColsShifted` - // `trace_length`: 4 - // `blowup_factor`: 64 - // `fri_number_of_queries`: 2 - let mut transcript = StoneProverTranscript::new(&[0xfa, 0xfa, 0xfa, 0xee]); - // Send hash of trace commitment - transcript.append_bytes( - &decode_hex("99d8d4342895c4e35a084f8ea993036be06f51e7fa965734ed9c7d41104f0848") - .unwrap(), - ); - // Sample challenge to collapse the constraints for the composition polynomial - assert_eq!( - transcript.sample_field_element(), - FE::from_hex_unchecked( - "3fc675742e0692558bb95f36bd34bdfe050697ed0d849e5369808685e548441" - ) - ); - // Send hash of composition poly commitment H(z) - transcript.append_bytes( - &decode_hex("2f4b599828a3f1ac458202ce06ec223bc9f4ad9ac758030109d40eebcf5776fd") - .unwrap(), - ); - // Sample challenge Z to compute t_j(z), H(z) - assert_eq!( - transcript.sample_field_element(), - FE::from_hex_unchecked( - "7298af9e2574933e62e51b107b8ef52f253d20644fc7250e9af118b02bc8a71" - ) - ); - // Append t_j(z), H(z) - transcript.append_field_element(&FE::from_hex_unchecked( - "6791c8cdbd981f7db9786d702b21b87f4128a6941f35683d8b10faafcab83d5", - )); - transcript.append_field_element(&FE::from_hex_unchecked( - "3cd6d8a23d01db66ea4911d6d7b09595b674f0507278fbf1f15cd85aa4ba72d", - )); - transcript.append_field_element(&FE::from_hex_unchecked( - "3123deded538b40c1faa7988310f315860a43e320ae70f8f86eaeadf3828a10", - )); - transcript.append_field_element(&FE::from_hex_unchecked( - "4d2edcc28870d79cbbb87181ffcb5942f7fa1c7b5f5bd5794c43452700e00d7", - )); - transcript.append_field_element(&FE::from_hex_unchecked( - "5c244407085950973147074ee245bd1c7ed6d8a019df997aab1928a4a9a1e19", - )); - // Sample challenge Gamma to collapse the terms of the deep composition polynomial (batch open). - // Powers of this challenge are used if more than two terms. - assert_eq!( - transcript.sample_field_element(), - FE::from_hex_unchecked( - "12f2b9edda6bb334bdf340d99eb0e6815e57aabffb48359117f71e7d0159d93" - ) - ); - // FRI: Sample challenge Zeta to split the polynomial in half - assert_eq!( - transcript.sample_field_element(), - FE::from_hex_unchecked( - "7549307d78354156552667acf19a0ae978d4ec4954d210e23d9979672987dc" - ) - ); - // FRI: Send hash of commitment at Layer 1 - transcript.append_bytes( - &decode_hex("97decf0ad3cd590e7e5a4f85b3d4fa8c02c6d4b5343388c4536127dc8ef0fbf2") - .unwrap(), - ); - // FRI: Sample challenge to split the polynomial in half - assert_eq!( - transcript.sample_field_element(), - FE::from_hex_unchecked( - "4b79e806108567fd0f670ded2be5468009aaefeb993b346579c4f295fa3ddd0" - ) - ); - // Send field element at final layer of FRI - transcript.append_field_element(&FE::from_hex_unchecked( - "7b8aa43aef4d3f2d476608251cffc9fa1c655bedecbcac49e4cafb012c7edf4", - )); - // Send proof of work - transcript.append_bytes(&[0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x3b, 0xb8]); - assert_eq!(transcript.sample_u64(128), 28); - assert_eq!(transcript.sample_u64(128), 31); - } -} diff --git a/crates/provers/stark/src/utils.rs b/crates/provers/stark/src/utils.rs deleted file mode 100644 index e195839d9..000000000 --- a/crates/provers/stark/src/utils.rs +++ /dev/null @@ -1,38 +0,0 @@ -use lambdaworks_crypto::merkle_tree::proof::Proof; -use lambdaworks_math::errors::DeserializationError; - -use super::config::Commitment; - -pub fn serialize_proof(proof: &Proof) -> Vec { - let mut bytes = vec![]; - bytes.extend(proof.merkle_path.len().to_be_bytes()); - for commitment in &proof.merkle_path { - bytes.extend(commitment); - } - bytes -} - -pub fn deserialize_proof(bytes: &[u8]) -> Result<(Proof, &[u8]), DeserializationError> { - let mut bytes = bytes; - let mut merkle_path = vec![]; - let merkle_path_len = usize::from_be_bytes( - bytes - .get(..8) - .ok_or(DeserializationError::InvalidAmountOfBytes)? - .try_into() - .map_err(|_| DeserializationError::InvalidAmountOfBytes)?, - ); - bytes = &bytes[8..]; - - for _ in 0..merkle_path_len { - let commitment = bytes - .get(..32) - .ok_or(DeserializationError::InvalidAmountOfBytes)? - .try_into() - .map_err(|_| DeserializationError::InvalidAmountOfBytes)?; - merkle_path.push(commitment); - bytes = &bytes[32..]; - } - - Ok((Proof { merkle_path }, bytes)) -} diff --git a/crates/provers/stark/src/verifier.rs b/crates/provers/stark/src/verifier.rs deleted file mode 100644 index 57c1bcbda..000000000 --- a/crates/provers/stark/src/verifier.rs +++ /dev/null @@ -1,825 +0,0 @@ -use super::{ - config::BatchedMerkleTreeBackend, - domain::Domain, - fri::fri_decommit::FriDecommitment, - grinding, - proof::{options::ProofOptions, stark::StarkProof}, - traits::{TransitionEvaluationContext, AIR}, -}; -use crate::{config::Commitment, proof::stark::DeepPolynomialOpening}; -use lambdaworks_crypto::{fiat_shamir::is_transcript::IsTranscript, merkle_tree::proof::Proof}; -use lambdaworks_math::{ - fft::cpu::bit_reversing::reverse_index, - field::{ - element::FieldElement, - traits::{IsFFTField, IsField, IsSubFieldOf}, - }, - traits::AsBytes, -}; -#[cfg(not(feature = "test_fiat_shamir"))] -use log::error; -use std::marker::PhantomData; -#[cfg(feature = "instruments")] -use std::time::Instant; - -/// A default STARK verifier implementing `IsStarkVerifier`. -pub struct Verifier { - phantom: PhantomData, -} - -impl IsStarkVerifier for Verifier {} - -/// A container holding the complete list of challenges sent to the prover along with the seed used -/// to validate the proof-of-work nonce. -pub struct Challenges -where - A: AIR, -{ - /// The out-of-domain challenge. - pub z: FieldElement, - /// The composition polynomial coefficients corresponding to the boundary constraints terms. - pub boundary_coeffs: Vec>, - /// The composition polynomial coefficients corresponding to the transition constraints terms. - pub transition_coeffs: Vec>, - /// The deep composition polynomial coefficients corresponding to the trace polynomial terms. - pub trace_term_coeffs: Vec>>, - /// The deep composition polynomial coefficients corresponding to the composition polynomial parts terms. - pub gammas: Vec>, - /// The list of FRI commit phase folding challenges. - pub zetas: Vec>, - /// The list of FRI query phase index challenges. - pub iotas: Vec, - /// The challenges used to build the auxiliary trace. - pub rap_challenges: Vec>, - /// The seed used to verify the proof-of-work nonce. - pub grinding_seed: [u8; 32], -} - -pub type DeepPolynomialEvaluations = (Vec>, Vec>); - -/// The functionality of a STARK verifier providing methods to run the STARK Verify protocol -/// https://lambdaclass.github.io/lambdaworks/starks/protocol.html -pub trait IsStarkVerifier { - fn sample_query_indexes( - number_of_queries: usize, - domain: &Domain, - transcript: &mut impl IsTranscript, - ) -> Vec { - let domain_size = domain.lde_roots_of_unity_coset.len() as u64; - (0..number_of_queries) - .map(|_| (transcript.sample_u64(domain_size >> 1)) as usize) - .collect::>() - } - - /// Returns the list of challenges sent to the prover. - fn step_1_replay_rounds_and_recover_challenges( - air: &A, - proof: &StarkProof, - domain: &Domain, - transcript: &mut impl IsTranscript, - ) -> Challenges - where - FieldElement: AsBytes, - FieldElement: AsBytes, - { - // =================================== - // ==========| Round 1 |========== - // =================================== - - // <<<< Receive commitments:[tⱼ] - transcript.append_bytes(&proof.lde_trace_main_merkle_root); - - let rap_challenges = air.build_rap_challenges(transcript); - - if let Some(root) = proof.lde_trace_aux_merkle_root { - transcript.append_bytes(&root); - } - - // =================================== - // ==========| Round 2 |========== - // =================================== - - // <<<< Receive challenge: 𝛽 - let beta = transcript.sample_field_element(); - let num_boundary_constraints = air.boundary_constraints(&rap_challenges).constraints.len(); - - let num_transition_constraints = air.context().num_transition_constraints; - - let mut coefficients: Vec<_> = (0..num_boundary_constraints + num_transition_constraints) - .map(|i| beta.pow(i)) - .collect(); - - let transition_coeffs: Vec<_> = coefficients.drain(..num_transition_constraints).collect(); - let boundary_coeffs = coefficients; - - // <<<< Receive commitments: [H₁], [H₂] - transcript.append_bytes(&proof.composition_poly_root); - - // =================================== - // ==========| Round 3 |========== - // =================================== - - // >>>> Send challenge: z - let z = transcript.sample_z_ood( - &domain.lde_roots_of_unity_coset, - &domain.trace_roots_of_unity, - ); - - // <<<< Receive values: tⱼ(zgᵏ) - let trace_ood_evaluations_columns = proof.trace_ood_evaluations.columns(); - for col in trace_ood_evaluations_columns.iter() { - for elem in col.iter() { - transcript.append_field_element(elem); - } - } - // <<<< Receive value: Hᵢ(z^N) - for element in proof.composition_poly_parts_ood_evaluation.iter() { - transcript.append_field_element(element); - } - - // =================================== - // ==========| Round 4 |========== - // =================================== - - let num_terms_composition_poly = proof.composition_poly_parts_ood_evaluation.len(); - let num_terms_trace = - air.context().transition_offsets.len() * A::STEP_SIZE * air.context().trace_columns; - let gamma = transcript.sample_field_element(); - - // <<<< Receive challenges: 𝛾, 𝛾' - let mut deep_composition_coefficients: Vec<_> = - core::iter::successors(Some(FieldElement::one()), |x| Some(x * &gamma)) - .take(num_terms_composition_poly + num_terms_trace) - .collect(); - - let trace_term_coeffs: Vec<_> = deep_composition_coefficients - .drain(..num_terms_trace) - .collect::>() - .chunks(air.context().transition_offsets.len() * A::STEP_SIZE) - .map(|chunk| chunk.to_vec()) - .collect(); - - // <<<< Receive challenges: 𝛾ⱼ, 𝛾ⱼ' - let gammas = deep_composition_coefficients; - - // FRI commit phase - let merkle_roots = &proof.fri_layers_merkle_roots; - let mut zetas = merkle_roots - .iter() - .map(|root| { - // >>>> Send challenge 𝜁ₖ - let element = transcript.sample_field_element(); - // <<<< Receive commitment: [pₖ] (the first one is [p₀]) - transcript.append_bytes(root); - element - }) - .collect::>>(); - - // >>>> Send challenge 𝜁ₙ₋₁ - zetas.push(transcript.sample_field_element()); - - // <<<< Receive value: pₙ - transcript.append_field_element(&proof.fri_last_value); - - // Receive grinding value - let security_bits = air.context().proof_options.grinding_factor; - let mut grinding_seed = [0u8; 32]; - if security_bits > 0 { - if let Some(nonce_value) = proof.nonce { - grinding_seed = transcript.state(); - transcript.append_bytes(&nonce_value.to_be_bytes()); - } - } - - // FRI query phase - // <<<< Send challenges 𝜄ₛ (iota_s) - let number_of_queries = air.options().fri_number_of_queries; - let iotas = Self::sample_query_indexes(number_of_queries, domain, transcript); - - Challenges { - z, - boundary_coeffs, - transition_coeffs, - trace_term_coeffs, - gammas, - zetas, - iotas, - rap_challenges, - grinding_seed, - } - } - - /// Checks whether the purported evaluations of the composition polynomial parts and the trace - /// polynomials at the out-of-domain challenge are consistent. - /// See https://lambdaclass.github.io/lambdaworks/starks/protocol.html#step-2-verify-claimed-composition-polynomial - fn step_2_verify_claimed_composition_polynomial( - air: &A, - proof: &StarkProof, - domain: &Domain, - challenges: &Challenges, - ) -> bool { - let boundary_constraints = air.boundary_constraints(&challenges.rap_challenges); - - let trace_length = air.trace_length(); - let number_of_b_constraints = boundary_constraints.constraints.len(); - - #[allow(clippy::type_complexity)] - let (boundary_c_i_evaluations_num, mut boundary_c_i_evaluations_den): ( - Vec>, - Vec>, - ) = (0..number_of_b_constraints) - .map(|index| { - let step = boundary_constraints.constraints[index].step; - let is_aux = boundary_constraints.constraints[index].is_aux; - let point = &domain.trace_primitive_root.pow(step as u64); - let column_idx = boundary_constraints.constraints[index].col; - let trace_evaluation = if is_aux { - let column_idx = air.trace_layout().0 + column_idx; - &proof.trace_ood_evaluations.get_row(0)[column_idx] - } else { - &proof.trace_ood_evaluations.get_row(0)[column_idx] - }; - let boundary_zerofier_challenges_z_den = -point + &challenges.z; - - let boundary_quotient_ood_evaluation_num = - -&boundary_constraints.constraints[index].value + trace_evaluation; - - ( - boundary_quotient_ood_evaluation_num, - boundary_zerofier_challenges_z_den, - ) - }) - .collect::>() - .into_iter() - .unzip(); - - FieldElement::inplace_batch_inverse(&mut boundary_c_i_evaluations_den).unwrap(); - - let boundary_quotient_ood_evaluation: FieldElement = - boundary_c_i_evaluations_num - .iter() - .zip(&boundary_c_i_evaluations_den) - .zip(&challenges.boundary_coeffs) - .map(|((num, den), beta)| num * den * beta) - .fold(FieldElement::::zero(), |acc, x| acc + x); - - let periodic_values = air - .get_periodic_column_polynomials() - .iter() - .map(|poly| poly.evaluate(&challenges.z)) - .collect::>>(); - - let num_main_trace_columns = - proof.trace_ood_evaluations.width - air.num_auxiliary_rap_columns(); - - let ood_frame = - (proof.trace_ood_evaluations).into_frame(num_main_trace_columns, A::STEP_SIZE); - let transition_evaluation_context = TransitionEvaluationContext::new_verifier( - &ood_frame, - &periodic_values, - &challenges.rap_challenges, - ); - let transition_ood_frame_evaluations = - air.compute_transition(&transition_evaluation_context); - - let mut denominators = - vec![FieldElement::::zero(); air.num_transition_constraints()]; - air.transition_constraints().iter().for_each(|c| { - denominators[c.constraint_idx()] = - c.evaluate_zerofier(&challenges.z, &domain.trace_primitive_root, trace_length); - }); - - let transition_c_i_evaluations_sum = itertools::izip!( - transition_ood_frame_evaluations, - &challenges.transition_coeffs, - denominators - ) - .fold(FieldElement::zero(), |acc, (eval, beta, denominator)| { - acc + beta * eval * &denominator - }); - - let composition_poly_ood_evaluation = - &boundary_quotient_ood_evaluation + transition_c_i_evaluations_sum; - - let composition_poly_claimed_ood_evaluation = proof - .composition_poly_parts_ood_evaluation - .iter() - .rev() - .fold(FieldElement::zero(), |acc, coeff| { - acc * &challenges.z + coeff - }); - - composition_poly_claimed_ood_evaluation == composition_poly_ood_evaluation - } - - /// Reconstructs the Deep composition polynomial evaluations at the challenge indices values using the provided - /// openings of the trace polynomials and the composition polynomial parts. It then uses these to verify that the - /// FRI decommitments are valid and correspond to the Deep composition polynomial. - fn step_3_verify_fri( - proof: &StarkProof, - domain: &Domain, - challenges: &Challenges, - ) -> bool - where - FieldElement: AsBytes + Sync + Send, - FieldElement: AsBytes + Sync + Send, - { - let (deep_poly_evaluations, deep_poly_evaluations_sym) = - Self::reconstruct_deep_composition_poly_evaluations_for_all_queries( - challenges, domain, proof, - ); - - // verify FRI - let mut evaluation_point_inverse = challenges - .iotas - .iter() - .map(|iota| Self::query_challenge_to_evaluation_point(*iota, domain)) - .collect::>>(); - FieldElement::inplace_batch_inverse(&mut evaluation_point_inverse).unwrap(); - - proof - .query_list - .iter() - .zip(&challenges.iotas) - .zip(evaluation_point_inverse) - .enumerate() - .fold(true, |mut result, (i, ((proof_s, iota_s), eval))| { - result &= Self::verify_query_and_sym_openings( - proof, - &challenges.zetas, - *iota_s, - proof_s, - eval, - &deep_poly_evaluations[i], - &deep_poly_evaluations_sym[i], - ); - result - }) - } - - /// Returns the field element element of the domain `domain` corresponding to the given FRI query index challenge `iota`. - fn query_challenge_to_evaluation_point( - iota: usize, - domain: &Domain, - ) -> FieldElement { - domain.lde_roots_of_unity_coset - [reverse_index(iota * 2, domain.lde_roots_of_unity_coset.len() as u64)] - .clone() - } - - /// Returns the symmetric field element element of the domain `domain` corresponding to the given FRI query index challenge `iota`. - fn query_challenge_to_evaluation_point_sym( - iota: usize, - domain: &Domain, - ) -> FieldElement { - domain.lde_roots_of_unity_coset - [reverse_index(iota * 2 + 1, domain.lde_roots_of_unity_coset.len() as u64)] - .clone() - } - - /// Verifies the validity of the opening proof. - fn verify_opening( - proof: &Proof, - root: &Commitment, - index: usize, - value: &[FieldElement], - ) -> bool - where - FieldElement: AsBytes + Sync + Send, - FieldElement: AsBytes + Sync + Send, - E: IsField, - A::Field: IsSubFieldOf, - { - proof.verify::>(root, index, &value.to_owned()) - } - - /// Verify opening Open(tⱼ(D_LDE), 𝜐) and Open(tⱼ(D_LDE), -𝜐) for all trace polynomials tⱼ, - /// where 𝜐 and -𝜐 are the elements corresponding to the index challenge `iota`. - fn verify_trace_openings( - proof: &StarkProof, - deep_poly_openings: &DeepPolynomialOpening, - iota: usize, - ) -> bool - where - FieldElement: AsBytes + Sync + Send, - FieldElement: AsBytes + Sync + Send, - { - let index = iota * 2; - let index_sym = iota * 2 + 1; - let mut result = true; - - result &= Self::verify_opening::( - &deep_poly_openings.main_trace_polys.proof, - &proof.lde_trace_main_merkle_root, - index, - &deep_poly_openings.main_trace_polys.evaluations, - ); - result &= Self::verify_opening::( - &deep_poly_openings.main_trace_polys.proof_sym, - &proof.lde_trace_main_merkle_root, - index_sym, - &deep_poly_openings.main_trace_polys.evaluations_sym, - ); - - match ( - proof.lde_trace_aux_merkle_root, - &deep_poly_openings.aux_trace_polys, - ) { - (None, Some(_)) => result = false, - (Some(_), None) => result = false, - (Some(aux_root), Some(aux_trace_polys_opening)) => { - result &= Self::verify_opening::( - &aux_trace_polys_opening.proof, - &aux_root, - index, - &aux_trace_polys_opening.evaluations, - ); - result &= Self::verify_opening::( - &aux_trace_polys_opening.proof_sym, - &aux_root, - index_sym, - &aux_trace_polys_opening.evaluations_sym, - ); - } - _ => {} - } - - result - } - - /// Verify opening Open(Hᵢ(D_LDE), 𝜐) and Open(Hᵢ(D_LDE), -𝜐) for all parts Hᵢof the composition - /// polynomial, where 𝜐 and -𝜐 are the elements corresponding to the index challenge `iota`. - fn verify_composition_poly_opening( - deep_poly_openings: &DeepPolynomialOpening, - composition_poly_merkle_root: &Commitment, - iota: &usize, - ) -> bool - where - FieldElement: AsBytes + Sync + Send, - FieldElement: AsBytes + Sync + Send, - { - let mut value = deep_poly_openings.composition_poly.evaluations.clone(); - value.extend_from_slice(&deep_poly_openings.composition_poly.evaluations_sym); - - deep_poly_openings - .composition_poly - .proof - .verify::>( - composition_poly_merkle_root, - *iota, - &value, - ) - } - - /// Verifies the validity of the purported values of the trace polynomials and the composition polynomial - /// parts at the domain elements and their symmetric counterparts corresponding to all the FRI query - /// index challenges. - fn step_4_verify_trace_and_composition_openings( - proof: &StarkProof, - challenges: &Challenges, - ) -> bool - where - FieldElement: AsBytes + Sync + Send, - FieldElement: AsBytes + Sync + Send, - { - challenges.iotas.iter().zip(&proof.deep_poly_openings).fold( - true, - |mut result, (iota_n, deep_poly_opening)| { - result &= Self::verify_composition_poly_opening( - deep_poly_opening, - &proof.composition_poly_root, - iota_n, - ); - - result &= Self::verify_trace_openings(proof, deep_poly_opening, *iota_n); - result - }, - ) - } - - /// Verifies the openings of a fold polynomial of an inner layer of FRI. - fn verify_fri_layer_openings( - merkle_root: &Commitment, - auth_path_sym: &Proof, - evaluation: &FieldElement, - evaluation_sym: &FieldElement, - iota: usize, - ) -> bool - where - FieldElement: AsBytes + Sync + Send, - FieldElement: AsBytes + Sync + Send, - { - let evaluations = if iota % 2 == 1 { - vec![evaluation_sym.clone(), evaluation.clone()] - } else { - vec![evaluation.clone(), evaluation_sym.clone()] - }; - - auth_path_sym.verify::>( - merkle_root, - iota >> 1, - &evaluations, - ) - } - - /// Verify a single FRI query - /// `zetas`: the vector of all challenges sent by the verifier to the prover at the commit - /// phase to fold polynomials. - /// `iota`: the index challenge of this FRI query. This index uniquely determines two elements 𝜐 and -𝜐 - /// of the evaluation domain of FRI layer 0. - /// `evaluation_point_inv`: precomputed value of 𝜐⁻¹. - /// `deep_composition_evaluation`: precomputed value of p₀(𝜐), where p₀ is the deep composition polynomial. - /// `deep_composition_evaluation_sym`: precomputed value of p₀(-𝜐), where p₀ is the deep composition polynomial. - fn verify_query_and_sym_openings( - proof: &StarkProof, - zetas: &[FieldElement], - iota: usize, - fri_decommitment: &FriDecommitment, - evaluation_point_inv: FieldElement, - deep_composition_evaluation: &FieldElement, - deep_composition_evaluation_sym: &FieldElement, - ) -> bool - where - FieldElement: AsBytes + Sync + Send, - FieldElement: AsBytes + Sync + Send, - { - let fri_layers_merkle_roots = &proof.fri_layers_merkle_roots; - let evaluation_point_vec: Vec> = - core::iter::successors(Some(evaluation_point_inv.square()), |evaluation_point| { - Some(evaluation_point.square()) - }) - .take(fri_layers_merkle_roots.len()) - .collect(); - - let p0_eval = deep_composition_evaluation; - let p0_eval_sym = deep_composition_evaluation_sym; - - // Reconstruct p₁(𝜐²) - let mut v = - (p0_eval + p0_eval_sym) + evaluation_point_inv * &zetas[0] * (p0_eval - p0_eval_sym); - let mut index = iota; - - // For each FRI layer, starting from the layer 1: use the proof to verify the validity of values pᵢ(−𝜐^(2ⁱ)) (given by the prover) and - // pᵢ(𝜐^(2ⁱ)) (computed on the previous iteration by the verifier). Then use them to obtain pᵢ₊₁(𝜐^(2ⁱ⁺¹)). - // Finally, check that the final value coincides with the given by the prover. - fri_layers_merkle_roots - .iter() - .enumerate() - .zip(&fri_decommitment.layers_auth_paths) - .zip(&fri_decommitment.layers_evaluations_sym) - .zip(evaluation_point_vec) - .fold( - true, - |result, - ( - (((i, merkle_root), auth_path_sym), evaluation_sym), - evaluation_point_inv, - )| { - // Verify opening Open(pᵢ(Dₖ), −𝜐^(2ⁱ)) and Open(pᵢ(Dₖ), 𝜐^(2ⁱ)). - // `v` is pᵢ(𝜐^(2ⁱ)). - // `evaluation_sym` is pᵢ(−𝜐^(2ⁱ)). - let openings_ok = Self::verify_fri_layer_openings( - merkle_root, - auth_path_sym, - &v, - evaluation_sym, - index, - ); - - // Update `v` with next value pᵢ₊₁(𝜐^(2ⁱ⁺¹)). - v = (&v + evaluation_sym) + evaluation_point_inv * &zetas[i + 1] * (&v - evaluation_sym); - - // Update index for next iteration. The index of the squares in the next layer - // is obtained by halving the current index. This is due to the bit-reverse - // ordering of the elements in the Merkle tree. - index >>= 1; - - if i < fri_decommitment.layers_evaluations_sym.len() - 1 { - result & openings_ok - } else { - // Check that final value is the given by the prover - result & (v == proof.fri_last_value) & openings_ok - } - }, - ) - } - - fn reconstruct_deep_composition_poly_evaluations_for_all_queries( - challenges: &Challenges, - domain: &Domain, - proof: &StarkProof, - ) -> DeepPolynomialEvaluations { - let mut deep_poly_evaluations = Vec::new(); - let mut deep_poly_evaluations_sym = Vec::new(); - for (i, iota) in challenges.iotas.iter().enumerate() { - let primitive_root = - &A::Field::get_primitive_root_of_unity(domain.root_order as u64).unwrap(); - - let mut evaluations: Vec> = proof.deep_poly_openings[i] - .main_trace_polys - .evaluations - .clone() - .into_iter() - .map(|x| x.to_extension()) - .collect(); - if let Some(aux_trace_polys) = &proof.deep_poly_openings[i].aux_trace_polys { - evaluations.extend_from_slice(&aux_trace_polys.evaluations); - } - - let evaluation_point = Self::query_challenge_to_evaluation_point(*iota, domain); - deep_poly_evaluations.push(Self::reconstruct_deep_composition_poly_evaluation( - proof, - &evaluation_point, - primitive_root, - challenges, - &evaluations, - &proof.deep_poly_openings[i].composition_poly.evaluations, - )); - - let mut evaluations_sym: Vec> = proof - .deep_poly_openings[i] - .main_trace_polys - .evaluations_sym - .clone() - .into_iter() - .map(|x| x.to_extension()) - .collect(); - if let Some(aux_trace_polys) = &proof.deep_poly_openings[i].aux_trace_polys { - evaluations_sym.extend_from_slice(&aux_trace_polys.evaluations_sym); - } - - let evaluation_point = Self::query_challenge_to_evaluation_point_sym(*iota, domain); - deep_poly_evaluations_sym.push(Self::reconstruct_deep_composition_poly_evaluation( - proof, - &evaluation_point, - primitive_root, - challenges, - &evaluations_sym, - &proof.deep_poly_openings[i].composition_poly.evaluations_sym, - )); - } - (deep_poly_evaluations, deep_poly_evaluations_sym) - } - - fn reconstruct_deep_composition_poly_evaluation( - proof: &StarkProof, - evaluation_point: &FieldElement, - primitive_root: &FieldElement, - challenges: &Challenges, - lde_trace_evaluations: &[FieldElement], - lde_composition_poly_parts_evaluation: &[FieldElement], - ) -> FieldElement { - let ood_evaluations_table_height = proof.trace_ood_evaluations.height; - let ood_evaluations_table_width = proof.trace_ood_evaluations.width; - let trace_term_coeffs = &challenges.trace_term_coeffs; - debug_assert_eq!( - ood_evaluations_table_height * ood_evaluations_table_width, - trace_term_coeffs.len() * trace_term_coeffs[0].len() - ); - - let mut denoms_trace = (0..ood_evaluations_table_height) - .map(|row_idx| evaluation_point - primitive_root.pow(row_idx as u64) * &challenges.z) - .collect::>>(); - FieldElement::inplace_batch_inverse(&mut denoms_trace).unwrap(); - - let trace_term = (0..ood_evaluations_table_width) - .zip(&challenges.trace_term_coeffs) - .fold(FieldElement::zero(), |trace_terms, (col_idx, coeff_row)| { - let trace_i = (0..ood_evaluations_table_height).zip(coeff_row).fold( - FieldElement::zero(), - |trace_t, (row_idx, coeff)| { - let poly_evaluation = (lde_trace_evaluations[col_idx].clone() - - proof.trace_ood_evaluations.get_row(row_idx)[col_idx].clone()) - * &denoms_trace[row_idx]; - trace_t + &poly_evaluation * coeff - }, - ); - trace_terms + trace_i - }); - - let number_of_parts = lde_composition_poly_parts_evaluation.len(); - let z_pow = &challenges.z.pow(number_of_parts); - - let denom_composition = (evaluation_point - z_pow).inv().unwrap(); - let mut h_terms = FieldElement::zero(); - for (j, h_i_upsilon) in lde_composition_poly_parts_evaluation.iter().enumerate() { - let h_i_zpower = &proof.composition_poly_parts_ood_evaluation[j]; - let h_i_term = (h_i_upsilon - h_i_zpower) * &challenges.gammas[j]; - h_terms += h_i_term; - } - h_terms *= denom_composition; - - trace_term + h_terms - } - - /// Verifies a STARK proof with public inputs `pub_inputs`. - /// Warning: the transcript must be safely initializated before passing it to this method. - fn verify( - proof: &StarkProof, - pub_input: &A::PublicInputs, - proof_options: &ProofOptions, - mut transcript: impl IsTranscript, - ) -> bool - where - FieldElement: AsBytes + Sync + Send, - FieldElement: AsBytes + Sync + Send, - { - // Verify there are enough queries - if proof.query_list.len() < proof_options.fri_number_of_queries { - return false; - } - - #[cfg(feature = "instruments")] - println!("- Started step 1: Recover challenges"); - #[cfg(feature = "instruments")] - let timer1 = Instant::now(); - - let air = A::new(proof.trace_length, pub_input, proof_options); - let domain = Domain::new(&air); - - let challenges = Self::step_1_replay_rounds_and_recover_challenges( - &air, - proof, - &domain, - &mut transcript, - ); - - // verify grinding - let security_bits = air.context().proof_options.grinding_factor; - if security_bits > 0 { - let nonce_is_valid = proof.nonce.map_or(false, |nonce_value| { - grinding::is_valid_nonce(&challenges.grinding_seed, nonce_value, security_bits) - }); - - if !nonce_is_valid { - error!("Grinding factor not satisfied"); - return false; - } - } - - #[cfg(feature = "instruments")] - let elapsed1 = timer1.elapsed(); - #[cfg(feature = "instruments")] - println!(" Time spent: {:?}", elapsed1); - - #[cfg(feature = "instruments")] - println!("- Started step 2: Verify claimed polynomial"); - #[cfg(feature = "instruments")] - let timer2 = Instant::now(); - - if !Self::step_2_verify_claimed_composition_polynomial(&air, proof, &domain, &challenges) { - error!("Composition Polynomial verification failed"); - return false; - } - - #[cfg(feature = "instruments")] - let elapsed2 = timer2.elapsed(); - #[cfg(feature = "instruments")] - println!(" Time spent: {:?}", elapsed2); - #[cfg(feature = "instruments")] - - println!("- Started step 3: Verify FRI"); - #[cfg(feature = "instruments")] - let timer3 = Instant::now(); - - if !Self::step_3_verify_fri(proof, &domain, &challenges) { - error!("FRI verification failed"); - return false; - } - - #[cfg(feature = "instruments")] - let elapsed3 = timer3.elapsed(); - #[cfg(feature = "instruments")] - println!(" Time spent: {:?}", elapsed3); - - #[cfg(feature = "instruments")] - println!("- Started step 4: Verify deep composition polynomial"); - #[cfg(feature = "instruments")] - let timer4 = Instant::now(); - - #[allow(clippy::let_and_return)] - if !Self::step_4_verify_trace_and_composition_openings(proof, &challenges) { - error!("DEEP Composition Polynomial verification failed"); - return false; - } - - #[cfg(feature = "instruments")] - let elapsed4 = timer4.elapsed(); - #[cfg(feature = "instruments")] - println!(" Time spent: {:?}", elapsed4); - - #[cfg(feature = "instruments")] - { - let total_time = elapsed1 + elapsed2 + elapsed3 + elapsed4; - println!( - " Fraction of verifying time per step: {:.4} {:.4} {:.4} {:.4}", - elapsed1.as_nanos() as f64 / total_time.as_nanos() as f64, - elapsed2.as_nanos() as f64 / total_time.as_nanos() as f64, - elapsed3.as_nanos() as f64 / total_time.as_nanos() as f64, - elapsed4.as_nanos() as f64 / total_time.as_nanos() as f64 - ); - } - - true - } -} diff --git a/crates/provers/sumcheck/Cargo.toml b/crates/provers/sumcheck/Cargo.toml deleted file mode 100644 index 21361dedf..000000000 --- a/crates/provers/sumcheck/Cargo.toml +++ /dev/null @@ -1,9 +0,0 @@ -[package] -name = "lambdaworks-sumcheck" -version = "0.1.0" -edition = "2021" - - -[dependencies] -lambdaworks-math.workspace = true -lambdaworks-crypto.workspace = true diff --git a/crates/provers/sumcheck/README.md b/crates/provers/sumcheck/README.md deleted file mode 100644 index 6bb950a34..000000000 --- a/crates/provers/sumcheck/README.md +++ /dev/null @@ -1,75 +0,0 @@ -# Sumcheck Protocol - -A naive implementation of the Sumcheck Protocol with support for linear, quadratic, and cubic polynomials. - -## Overview - -The Sumcheck Protocol allows a prover to convince a verifier that the sum of a multivariate polynomial over the Boolean hypercube equals a claimed value without the verifier having to compute the entire sum. - -It is an essential building block for many SNARK protocols, given that it reduces the complexity of computing the sum to performing $O(\nu)$ additions, plus an evaluation at a random point. - -The protocol proceeds in rounds, with one round per variable of the multivariate polynomial. In each round, the prover sends a univariate polynomial, and the verifier responds with a random challenge. This process reduces a claim about a multivariate polynomial to a claim about a single evaluation point. - -### Convenience Wrapper Functions - -This implementation provides the following convenience **wrapper functions** for common interaction degrees: -- `prove_linear` / `verify_linear` (Sum of P(x)) -- `prove_quadratic` / `verify_quadratic` (Sum of P₁(x)P₂(x)) -- `prove_cubic` / `verify_cubic` (Sum of P₁(x)P₂(x)P₃(x)) - - - -## Example - -Here's a simple example of how to use the Sumcheck Protocol: - -```rust -use lambdaworks_math::field::fields::u64_prime_field::U64PrimeField; -use lambdaworks_math::field::element::FieldElement; -use lambdaworks_math::polynomial::dense_multilinear_poly::DenseMultilinearPolynomial; -use lambdaworks_sumcheck::{prove, verify}; - -// Define the field -type F = U64PrimeField<17>; -type FE = FieldElement; - -// Create a multilinear polynomial -// P(x1, x2) = evals [3, 4, 5, 7] -let evaluations = vec![ - FE::from(3), - FE::from(4), - FE::from(5), - FE::from(7), -]; -let poly = DenseMultilinearPolynomial::new(evaluations); -let num_vars = poly.num_vars(); - -// Generate a proof using the linear wrapper -let prove_result = prove_linear(poly.clone()); -assert!(prove_result.is_ok()); -let (claimed_sum, proof_polys) = prove_result.unwrap(); - -// Verify the proof using the linear wrapper -let verification_result = verify_linear(num_vars, claimed_sum, proof_polys, poly); -assert!(verification_result.is_ok() && verification_result.unwrap()); -println!("Simple verification successful!"); -``` -To use the quadratic wrapper, you can use the `prove_quadratic` and `verify_quadratic` functions. - -```rust -let prove_result = prove_quadratic(poly1, poly2); -let verification_result = verify_quadratic(num_vars, claimed_sum, proof_polys, poly1, poly2); -``` - -To use the cubic wrapper, you can use the `prove_cubic` and `verify_cubic` functions. - -```rust -let prove_result = prove_cubic(poly1, poly2, poly3); -let verification_result = verify_cubic(num_vars, claimed_sum, proof_polys, poly1, poly2, poly3); -``` - - -## References - -- [Proofs, Arguments, and Zero-Knowledge. Chapter 4](https://people.cs.georgetown.edu/jthaler/ProofsArgsAndZK.pdf) -- [Lambdaclass Blog Post: Have you checked your sums?](https://blog.lambdaclass.com/have-you-checked-your-sums/) diff --git a/crates/provers/sumcheck/src/lib.rs b/crates/provers/sumcheck/src/lib.rs deleted file mode 100644 index b5baaaf4a..000000000 --- a/crates/provers/sumcheck/src/lib.rs +++ /dev/null @@ -1,575 +0,0 @@ -pub mod prover; -pub mod verifier; - -use lambdaworks_crypto::fiat_shamir::default_transcript::DefaultTranscript; -use lambdaworks_crypto::fiat_shamir::is_transcript::IsTranscript; -use lambdaworks_math::field::element::FieldElement; -use lambdaworks_math::field::traits::{HasDefaultTranscript, IsField}; -use lambdaworks_math::polynomial::dense_multilinear_poly::DenseMultilinearPolynomial; -use lambdaworks_math::polynomial::Polynomial; -use lambdaworks_math::traits::ByteConversion; -use std::ops::Mul; - -pub use prover::ProverOutput; -pub use prover::{prove, Prover, ProverError}; -pub use verifier::{verify, Verifier, VerifierError, VerifierRoundResult}; - -// Wrappers for the prover and verifier functions -pub fn prove_linear(poly: DenseMultilinearPolynomial) -> ProverOutput -where - F: IsField + HasDefaultTranscript, - F::BaseType: Send + Sync, - FieldElement: Clone + Mul> + ByteConversion, -{ - prove(vec![poly]) -} - -pub fn verify_linear( - num_vars: usize, - claimed_sum: FieldElement, - proof_polys: Vec>>, - oracle_poly: DenseMultilinearPolynomial, -) -> Result> -where - F: IsField + HasDefaultTranscript, - F::BaseType: Send + Sync, - FieldElement: Clone + Mul> + ByteConversion, -{ - verify(num_vars, claimed_sum, proof_polys, vec![oracle_poly]) -} -pub fn prove_quadratic( - poly1: DenseMultilinearPolynomial, - poly2: DenseMultilinearPolynomial, -) -> ProverOutput -where - F: IsField + HasDefaultTranscript, - F::BaseType: Send + Sync, - FieldElement: Clone + Mul> + ByteConversion, -{ - if poly1.num_vars() != poly2.num_vars() { - return Err(ProverError::FactorMismatch( - "Polynomials must have the same number of variables for quadratic prove.".to_string(), - )); - } - prove(vec![poly1, poly2]) -} - -pub fn verify_quadratic( - num_vars: usize, - claimed_sum: FieldElement, - proof_polys: Vec>>, - oracle_poly1: DenseMultilinearPolynomial, - oracle_poly2: DenseMultilinearPolynomial, -) -> Result> -where - F: IsField + HasDefaultTranscript, - F::BaseType: Send + Sync, - FieldElement: Clone + Mul> + ByteConversion, -{ - verify( - num_vars, - claimed_sum, - proof_polys, - vec![oracle_poly1, oracle_poly2], - ) -} - -pub fn prove_cubic( - poly1: DenseMultilinearPolynomial, - poly2: DenseMultilinearPolynomial, - poly3: DenseMultilinearPolynomial, -) -> ProverOutput -where - F: IsField + HasDefaultTranscript, - F::BaseType: Send + Sync, - FieldElement: Clone + Mul> + ByteConversion, -{ - if poly1.num_vars() != poly2.num_vars() || poly1.num_vars() != poly3.num_vars() { - return Err(ProverError::FactorMismatch( - "Polynomials must have the same number of variables for cubic prove.".to_string(), - )); - } - prove(vec![poly1, poly2, poly3]) -} - -pub fn verify_cubic( - num_vars: usize, - claimed_sum: FieldElement, - proof_polys: Vec>>, - oracle_poly1: DenseMultilinearPolynomial, - oracle_poly2: DenseMultilinearPolynomial, - oracle_poly3: DenseMultilinearPolynomial, -) -> Result> -where - F: IsField + HasDefaultTranscript, - F::BaseType: Send + Sync, - FieldElement: Clone + Mul> + ByteConversion, -{ - verify( - num_vars, - claimed_sum, - proof_polys, - vec![oracle_poly1, oracle_poly2, oracle_poly3], - ) -} - -pub trait Channel { - fn append_felt(&mut self, element: &FieldElement); - fn draw_felt(&mut self) -> FieldElement; -} - -impl Channel for DefaultTranscript -where - F: HasDefaultTranscript, - FieldElement: ByteConversion, -{ - fn append_felt(&mut self, element: &FieldElement) { - self.append_bytes(&element.to_bytes_be()); - } - - fn draw_felt(&mut self) -> FieldElement { - self.sample_field_element() - } -} - -/// Evaluate the product of multiple multilinear polynomials at a point. -pub fn evaluate_product_at_point( - factors: &[DenseMultilinearPolynomial], - point: &[FieldElement], -) -> Result, String> -where - F::BaseType: Send + Sync, - FieldElement: Clone + Mul>, -{ - if factors.is_empty() { - return Err("Cannot evaluate product of zero factors.".to_string()); - } - if factors[0].num_vars() != point.len() { - return Err(format!( - "Point length {} does not match polynomial num_vars {}", - point.len(), - factors[0].num_vars() - )); - } - - factors - .iter() - .map(|factor| factor.evaluate(point.to_vec()).map_err(|e| e.to_string())) // Using to_string assuming Display for MultilinearError - .try_fold(FieldElement::one(), |acc, eval_result| { - eval_result.map(|eval| acc * eval) - }) -} - -/// Sum the product of multiple multilinear polynomials over the boolean hypercube for suffix variables. -pub fn sum_product_over_suffix( - factors: &[DenseMultilinearPolynomial], - prefix: &[FieldElement], -) -> Result, String> -where - F::BaseType: Send + Sync, - FieldElement: Clone + Mul>, -{ - let num_total_vars = factors - .first() - .ok_or_else(|| "Cannot sum product of zero factors.".to_string())? - .num_vars(); - let num_prefix_vars = prefix.len(); - - if num_prefix_vars > num_total_vars { - return Err("Prefix length cannot exceed total number of variables.".to_string()); - } - - let num_suffix_vars = num_total_vars - num_prefix_vars; - - (0..(1 << num_suffix_vars)) - .map(|i| { - let mut current_point = prefix.to_vec(); - current_point.resize(num_total_vars, FieldElement::zero()); - - for k in 0..num_suffix_vars { - if (i >> k) & 1 == 1 { - current_point[num_prefix_vars + k] = FieldElement::one(); - } else { - current_point[num_prefix_vars + k] = FieldElement::zero(); - } - } - current_point - }) - .map(|current_point| evaluate_product_at_point(factors, ¤t_point)) - .try_fold(FieldElement::zero(), |acc, result_product_at_point| { - result_product_at_point.map(|product_at_point| acc + product_at_point) - }) -} - -#[cfg(test)] -mod tests { - use super::*; - use lambdaworks_math::field::fields::u64_prime_field::U64PrimeField; - use lambdaworks_math::polynomial::dense_multilinear_poly::DenseMultilinearPolynomial; - - const MODULUS: u64 = 101; - type F = U64PrimeField; - type FE = FieldElement; - - #[test] - fn test_sumcheck_linear() { - let poly = DenseMultilinearPolynomial::new(vec![ - FE::from(3), - FE::from(5), - FE::from(7), - FE::from(11), - ]); - let num_vars = poly.num_vars(); - - let prove_result = prove_linear(poly.clone()); - - let (claimed_sum, proof_polys) = prove_result.unwrap(); - - let result = verify_linear(num_vars, claimed_sum, proof_polys, poly); - assert!(result.unwrap_or(false), "Valid proof should be accepted"); - } - - #[test] - fn test_interactive_sumcheck() { - let poly = DenseMultilinearPolynomial::new(vec![ - FE::from(1), - FE::from(2), - FE::from(1), - FE::from(4), - ]); - let num_vars = poly.num_vars(); - let mut prover = Prover::new(vec![poly.clone()]).expect("Prover::new failed"); - let c_1 = prover - .compute_initial_sum() - .expect("compute_initial_sum failed"); - println!("\nInitial claimed sum c₁: {c_1:?}"); - assert_eq!(c_1, FE::from(8)); - let mut transcript = DefaultTranscript::::default(); - let mut verifier = - Verifier::new(num_vars, vec![poly.clone()], c_1).expect("Verifier::new failed"); - let mut current_challenge: Option> = None; - - println!("\n-- Round 0 --"); - let g0 = prover - .round(current_challenge.as_ref()) - .expect("prover.round for g0 failed"); - println!( - "Univariate polynomial g₀(X) coefficients: {:?}", - g0.coefficients() - ); - let eval_0_g0 = g0.evaluate(&FE::zero()); - let eval_1_g0 = g0.evaluate(&FE::one()); - println!( - "g₀(0) = {:?}, g₀(1) = {:?}, sum = {:?}", - eval_0_g0, - eval_1_g0, - eval_0_g0 + eval_1_g0 - ); - let res0 = verifier.do_round(g0, &mut transcript).unwrap(); - let r0 = if let VerifierRoundResult::NextRound(chal) = res0 { - println!("Challenge r₀: {chal:?}"); - chal - } else { - panic!("Expected NextRound result for round 0"); - }; - current_challenge = Some(r0); - - println!("\n-- Round 1 (Final) --"); - let g1 = prover - .round(current_challenge.as_ref()) - .expect("prover.round for g1 failed"); - println!( - "Univariate polynomial g₁(X) coefficients: {:?}", - g1.coefficients() - ); - let eval_0_g1 = g1.evaluate(&FE::zero()); - let eval_1_g1 = g1.evaluate(&FE::one()); - println!( - "g₁(0) = {:?}, g₁(1) = {:?}, sum = {:?}", - eval_0_g1, - eval_1_g1, - eval_0_g1 + eval_1_g1 - ); - let res1 = verifier.do_round(g1, &mut transcript).unwrap(); - if let VerifierRoundResult::Final(ok) = res1 { - println!( - "\nFinal verification result: {}", - if ok { "ACCEPTED" } else { "REJECTED" } - ); - assert!(ok, "Final round verification failed"); - } else { - panic!("Expected Final result for round 1"); - } - println!("Interactive test passed!"); - } - - #[test] - fn test_from_book() { - let poly = DenseMultilinearPolynomial::new(vec![ - FE::from(1), - FE::from(2), - FE::from(3), - FE::from(4), - FE::from(5), - FE::from(6), - FE::from(7), - FE::from(8), - ]); - let num_vars = poly.num_vars(); - assert_eq!(num_vars, 3); - let mut prover = Prover::new(vec![poly.clone()]).expect("Prover::new failed"); - let c_1 = prover - .compute_initial_sum() - .expect("compute_initial_sum failed"); - println!("\nInitial claimed sum c₁: {c_1:?}"); - assert_eq!(c_1, FE::from(36)); - let mut transcript = DefaultTranscript::::default(); - let mut verifier = - Verifier::new(num_vars, vec![poly.clone()], c_1).expect("Verifier::new failed"); - let mut current_challenge_opt: Option> = None; - for round_idx in 0..num_vars { - println!( - "\n-- Round {} {} --", - round_idx, - if round_idx == num_vars - 1 { - "(Final)" - } else { - "" - } - ); - let g_j = prover - .round(current_challenge_opt.as_ref()) - .unwrap_or_else(|e| panic!("Prover::round failed at round {round_idx}: {e:?}")); - println!( - "Univariate polynomial g{}(X) coefficients: {:?}", - round_idx, - g_j.coefficients() - ); - let eval_0 = g_j.evaluate(&FE::zero()); - let eval_1 = g_j.evaluate(&FE::one()); - println!( - "g{}(0) = {:?}, g{}(1) = {:?}, sum = {:?}", - round_idx, - eval_0, - round_idx, - eval_1, - eval_0 + eval_1 - ); - let res = verifier.do_round(g_j, &mut transcript).unwrap_or_else(|e| { - panic!("Verifier::do_round failed at round {round_idx}: {e:?}") - }); - match res { - VerifierRoundResult::NextRound(chal) => { - println!("Challenge r{round_idx}: {chal:?}"); - current_challenge_opt = Some(chal); - } - VerifierRoundResult::Final(ok) => { - println!( - "\nFinal verification result: {}", - if ok { "ACCEPTED" } else { "REJECTED" } - ); - assert!(ok, "Final round verification failed"); - assert_eq!(round_idx, num_vars - 1, "Final result occurred too early"); - break; - } - } - } - } - - #[test] - fn test_from_book_ported() { - let poly = DenseMultilinearPolynomial::new(vec![ - FE::from(0), - FE::from(2), - FE::from(0), - FE::from(2), - FE::from(0), - FE::from(3), - FE::from(1), - FE::from(4), - ]); - let num_vars = poly.num_vars(); - assert_eq!(num_vars, 3); - let mut prover = Prover::new(vec![poly.clone()]).expect("Prover::new failed"); - let c_1 = prover - .compute_initial_sum() - .expect("compute_initial_sum failed"); - println!("\nInitial claimed sum c₁: {c_1:?}"); - assert_eq!(c_1, FE::from(12)); - let mut transcript = DefaultTranscript::::default(); - let mut verifier = - Verifier::new(num_vars, vec![poly.clone()], c_1).expect("Verifier::new failed"); - - // Round 0: - println!("\n-- Round 0 --"); - let g0 = prover.round(None).expect("prover.round for g0 failed"); - println!( - "Univariate polynomial g₀(X) coefficients: {:?}", - g0.coefficients() - ); - let eval_0_g0 = g0.evaluate(&FE::zero()); - let eval_1_g0 = g0.evaluate(&FE::one()); - println!( - "g₀(0) = {:?}, g₀(1) = {:?}, sum = {:?}", - eval_0_g0, - eval_1_g0, - eval_0_g0 + eval_1_g0 - ); - let res0 = verifier.do_round(g0, &mut transcript).unwrap(); - let r0 = if let VerifierRoundResult::NextRound(chal) = res0 { - println!("Challenge r₀: {chal:?}"); - chal - } else { - panic!("Expected NextRound result for round 0"); - }; - - // Round 1: - println!("\n-- Round 1 --"); - let g1 = prover.round(Some(&r0)).expect("prover.round for g1 failed"); - println!( - "Univariate polynomial g₁(X) coefficients: {:?}", - g1.coefficients() - ); - let eval_0_g1 = g1.evaluate(&FE::zero()); - let eval_1_g1 = g1.evaluate(&FE::one()); - println!( - "g₁(0) = {:?}, g₁(1) = {:?}, sum = {:?}", - eval_0_g1, - eval_1_g1, - eval_0_g1 + eval_1_g1 - ); - let res1 = verifier.do_round(g1, &mut transcript).unwrap(); - let r1 = if let VerifierRoundResult::NextRound(chal) = res1 { - println!("Challenge r₁: {chal:?}"); - chal - } else { - panic!("Expected NextRound result for round 1"); - }; - - // Round 2 (final round): - println!("\n-- Round 2 (Final) --"); - let g2 = prover.round(Some(&r1)).expect("prover.round for g2 failed"); - println!( - "Univariate polynomial g₂(X) coefficients: {:?}", - g2.coefficients() - ); - let eval_0_g2 = g2.evaluate(&FE::zero()); - let eval_1_g2 = g2.evaluate(&FE::one()); - println!( - "g₂(0) = {:?}, g₂(1) = {:?}, sum = {:?}", - eval_0_g2, - eval_1_g2, - eval_0_g2 + eval_1_g2 - ); - let res2 = verifier.do_round(g2, &mut transcript).unwrap(); - if let VerifierRoundResult::Final(ok) = res2 { - println!( - "\nFinal verification result: {}", - if ok { "ACCEPTED" } else { "REJECTED" } - ); - assert!(ok, "Final round verification failed"); - } else { - panic!("Expected Final result for round 2"); - } - } - - #[test] - fn failing_verification_test() { - let poly = DenseMultilinearPolynomial::new(vec![ - FE::from(1), - FE::from(2), - FE::from(1), - FE::from(4), - ]); - let num_vars = poly.num_vars(); - let factors = vec![poly.clone()]; - let incorrect_c1 = FE::from(999); - println!("\nInitial (incorrect) claimed sum c₁: {incorrect_c1:?}"); - let mut transcript = DefaultTranscript::::default(); - let mut verifier = - Verifier::new(num_vars, factors.clone(), incorrect_c1).expect("Verifier new failed"); - println!("\n-- Round 0 --"); - let mut correct_prover = Prover::new(factors.clone()).expect("Correct Prover new failed"); - let g0 = correct_prover - .round(None) - .expect("prover.round for g0 failed"); - println!( - "Univariate polynomial g₀(X) coefficients: {:?}", - g0.coefficients() - ); - let eval_0_g0 = g0.evaluate(&FE::zero()); - let eval_1_g0 = g0.evaluate(&FE::one()); - println!( - "g₀(0) = {:?}, g₀(1) = {:?}, sum = {:?}", - eval_0_g0, - eval_1_g0, - eval_0_g0 + eval_1_g0 - ); - assert_eq!(eval_0_g0 + eval_1_g0, FE::from(8)); - let res0 = verifier.do_round(g0, &mut transcript); - if let Err(VerifierError::InconsistentSum { - round, - expected, - s0, - s1, - }) = &res0 - { - println!("\nExpected verification error (InconsistentSum) at round {}, expected sum {:?}, got sum {:?}", round, expected, *s0 + *s1); - assert_eq!(*round, 0, "Error should occur in round 0"); - assert_eq!( - *expected, incorrect_c1, - "Expected sum should be the incorrect one" - ); - } else { - panic!("Expected InconsistentSum error, got {res0:?}"); - } - assert!(res0.is_err(), "Expected verification error"); - } - - #[test] - fn test_sumcheck_quadratic() { - let poly_a = DenseMultilinearPolynomial::new(vec![ - FE::from(1), - FE::from(2), - FE::from(3), - FE::from(4), - ]); - let poly_b = DenseMultilinearPolynomial::new(vec![ - FE::from(5), - FE::from(6), - FE::from(7), - FE::from(8), - ]); - let num_vars = poly_a.num_vars(); - - let prove_result = prove_quadratic(poly_a.clone(), poly_b.clone()); - let (claimed_sum, proof_polys) = prove_result.unwrap(); - - let verification_result = - verify_quadratic(num_vars, claimed_sum, proof_polys, poly_a, poly_b); - - assert!( - verification_result.unwrap_or(false), - "Quadratic verification failed" - ); - } - - #[test] - fn test_sumcheck_cubic() { - let poly_a = DenseMultilinearPolynomial::new(vec![FE::from(1), FE::from(2)]); - let poly_b = DenseMultilinearPolynomial::new(vec![FE::from(3), FE::from(4)]); - let poly_c = DenseMultilinearPolynomial::new(vec![FE::from(5), FE::from(1)]); - let num_vars = poly_a.num_vars(); - - let prove_result = prove_cubic(poly_a.clone(), poly_b.clone(), poly_c.clone()); - let (claimed_sum, proof_polys) = prove_result.unwrap(); - - let verification_result = - verify_cubic(num_vars, claimed_sum, proof_polys, poly_a, poly_b, poly_c); - - assert!( - verification_result.unwrap_or(false), - "Cubic verification failed" - ); - } -} diff --git a/crates/provers/sumcheck/src/prover.rs b/crates/provers/sumcheck/src/prover.rs deleted file mode 100644 index df6bf2e7a..000000000 --- a/crates/provers/sumcheck/src/prover.rs +++ /dev/null @@ -1,200 +0,0 @@ -use crate::sum_product_over_suffix; -use crate::Channel; -use lambdaworks_crypto::fiat_shamir::default_transcript::DefaultTranscript; -use lambdaworks_crypto::fiat_shamir::is_transcript::IsTranscript; -use lambdaworks_math::{ - field::{ - element::FieldElement, - traits::{HasDefaultTranscript, IsField}, - }, - polynomial::{ - dense_multilinear_poly::DenseMultilinearPolynomial, InterpolateError, Polynomial, - }, - traits::ByteConversion, -}; -use std::ops::Mul; - -pub type ProofResult = (FieldElement, Vec>>); -pub type ProverOutput = Result, ProverError>; - -#[derive(Debug)] -pub enum ProverError { - /// Error when the input factors are inconsistent - FactorMismatch(String), - /// Error occurring during the calculation within a specific round. - RoundError(String), - /// Error during the polynomial interpolation step in a round. - InterpolationError(InterpolateError), - /// Error during the initial sum calculation. - SummationError(String), - /// Error indicating the Prover is in an invalid state - InvalidState(String), -} - -impl From for ProverError { - fn from(e: InterpolateError) -> Self { - ProverError::InterpolationError(e) - } -} - -/// Represents the Prover for the Sum-Check protocol operating on a product of DenseMultilinearPolynomials. -pub struct Prover -where - F::BaseType: Send + Sync, -{ - num_vars: usize, - /// The factors \( P_i \) of the product being summed. - factors: Vec>, - /// Challenges \( r_1, r_2, ... \) received from the Verifier in previous rounds. - challenges: Vec>, -} - -impl Prover -where - F::BaseType: Send + Sync, - FieldElement: Clone + Mul>, -{ - /// Creates a new Prover instance for the given factors. - pub fn new(factors: Vec>) -> Result { - if factors.is_empty() { - return Err(ProverError::FactorMismatch( - "At least one polynomial factor is required.".to_string(), - )); - } - let num_vars = factors[0].num_vars(); - if factors.iter().any(|p| p.num_vars() != num_vars) { - return Err(ProverError::FactorMismatch( - "All factors must have the same number of variables.".to_string(), - )); - } - Ok(Self { - num_vars, - factors, - challenges: Vec::with_capacity(num_vars), - }) - } - - /// Returns the number of variables in the polynomials handled by this prover. - pub fn num_vars(&self) -> usize { - self.num_vars - } - - /// Computes the initial claimed sum \( C = \\sum_{x \\in \\{0,1\\}^n} \\prod_i P_i(x) \). - pub fn compute_initial_sum(&self) -> Result, ProverError> { - sum_product_over_suffix(&self.factors, &[]) - .map_err(|e| ProverError::SummationError(format!("Error computing initial sum: {e}"))) - } - - /// Executes a round of the Sum-Check protocol. - /// - /// Given the challenge `r_prev` from the previous round (if any), this function - /// computes the round polynomial \( g_j(X_j) \). - /// \( g_j(X_j) = \\sum_{x_{j+1}, ..., x_n \\in \\{0,1\\}} \\prod_i P_i(r_1, ..., r_{j-1}, X_j, x_{j+1}, ..., x_n) \) - /// This is achieved by evaluating the sum at `deg(g_j) + 1` points and interpolating. - pub fn round( - &mut self, - r_prev: Option<&FieldElement>, - ) -> Result>, ProverError> { - // Store the challenge from the previous round - if let Some(r) = r_prev { - if self.challenges.len() >= self.num_vars { - return Err(ProverError::InvalidState( - "Received challenge when all variables are already fixed.".to_string(), - )); - } - self.challenges.push(r.clone()); - } - - // Check if all rounds are completed - let current_round_idx = self.challenges.len(); - if current_round_idx >= self.num_vars { - return Err(ProverError::InvalidState( - "All variables already fixed, no more rounds to run.".to_string(), - )); - } - - // Calculate the polynomial g_j(X_j) by interpolation. - // The degree of g_j is at most the number of factors. - let num_eval_points = self.factors.len() + 1; - let mut evaluation_points_x = Vec::with_capacity(num_eval_points); - let mut evaluations_y = Vec::with_capacity(num_eval_points); - - // Prefix for evaluation points: (r1, r2, ..., r_{j-1}, eval_point_x) - let mut current_point_prefix = self.challenges.clone(); - current_point_prefix.push(FieldElement::zero()); - - for i in 0..num_eval_points { - // Point at which to evaluate X_j - let eval_point_x = FieldElement::from(i as u64); - evaluation_points_x.push(eval_point_x.clone()); - - // Set the actual value for X_j in the prefix - *current_point_prefix.last_mut().unwrap() = eval_point_x; - - let g_j_at_eval_point = sum_product_over_suffix(&self.factors, ¤t_point_prefix) - .map_err(|e| { - ProverError::RoundError(format!("Error in sum for g_j({i}): {e}")) - })?; - evaluations_y.push(g_j_at_eval_point); - } - - let poly_g_j = Polynomial::interpolate(&evaluation_points_x, &evaluations_y)?; - - Ok(poly_g_j) - } -} - -pub fn prove(factors: Vec>) -> ProverOutput -where - F: IsField + HasDefaultTranscript, - F::BaseType: Send + Sync, - FieldElement: Clone + Mul> + ByteConversion, -{ - // Initialize the prover - let mut prover = Prover::new(factors.clone())?; - let num_vars = prover.num_vars(); - // Compute the claimed sum C - let claimed_sum = prover.compute_initial_sum()?; - - // Initialize Fiat-Shamir transcript - let mut transcript = DefaultTranscript::::default(); - transcript.append_bytes(b"initial_sum"); - transcript.append_felt(&FieldElement::from(num_vars as u64)); - transcript.append_felt(&FieldElement::from(factors.len() as u64)); - transcript.append_felt(&claimed_sum); - - let mut proof_polys = Vec::with_capacity(num_vars); - let mut current_challenge: Option> = None; - - // Execute rounds - for j in 0..num_vars { - // Prover computes the round polynomial g_j - let g_j = prover.round(current_challenge.as_ref())?; - - // Append g_j information to transcript for the verifier to derive challenge - let round_label = format!("round_{j}_poly"); - transcript.append_bytes(round_label.as_bytes()); - - let coeffs = g_j.coefficients(); - transcript.append_bytes(&(coeffs.len() as u64).to_be_bytes()); - if coeffs.is_empty() { - transcript.append_felt(&FieldElement::zero()); - } else { - for coeff in coeffs { - transcript.append_felt(coeff); - } - } - - proof_polys.push(g_j); - - // Derive challenge for the next round from transcript (if not the last round) - if j < num_vars - 1 { - current_challenge = Some(transcript.draw_felt()); - } else { - // No challenge needed after the last round polynomial is sent - current_challenge = None; - } - } - - Ok((claimed_sum, proof_polys)) -} diff --git a/crates/provers/sumcheck/src/verifier.rs b/crates/provers/sumcheck/src/verifier.rs deleted file mode 100644 index e389336ec..000000000 --- a/crates/provers/sumcheck/src/verifier.rs +++ /dev/null @@ -1,231 +0,0 @@ -use crate::evaluate_product_at_point; -use crate::Channel; -use lambdaworks_crypto::fiat_shamir::default_transcript::DefaultTranscript; -use lambdaworks_crypto::fiat_shamir::is_transcript::IsTranscript; -use lambdaworks_math::{ - field::{ - element::FieldElement, - traits::{HasDefaultTranscript, IsField}, - }, - polynomial::{dense_multilinear_poly::DenseMultilinearPolynomial, Polynomial}, - traits::ByteConversion, -}; -use std::ops::Mul; -use std::vec::Vec; - -/// Represents the result of a single round of verification. -#[derive(Debug)] -pub enum VerifierRoundResult { - /// The round was successful, providing the challenge for the next round. - NextRound(FieldElement), - Final(bool), -} - -#[derive(Debug)] -pub enum VerifierError -where - F::BaseType: Send + Sync, -{ - /// The sum check g(0) + g(1) == expected failed. - InconsistentSum { - round: usize, - s0: FieldElement, - s1: FieldElement, - expected: FieldElement, - }, - /// Error evaluating the product of oracle polynomials at the claimed point. - OracleEvaluationError(String), - /// The degree of the polynomial sent by the prover is invalid for the current round. - InvalidDegree { - round: usize, - actual_degree: usize, - max_allowed: usize, - }, - /// The proof contains an incorrect number of polynomials. - IncorrectProofLength { expected: usize, actual: usize }, - /// The list of oracle factors provided was empty. - MissingOracle, - /// Error indicating the Verifier is in an invalid state (e.g., inconsistent factors, unexpected round result). - InvalidState(String), -} - -/// Represents the Verifier for the Sum-Check protocol operating on a product of DenseMultilinearPolynomials. -#[derive(Debug)] -pub struct Verifier -where - F::BaseType: Send + Sync, - FieldElement: Clone + Mul>, -{ - num_vars: usize, - round: usize, - /// The claimed factors \( P_i \) of the product, used for the final check. - oracle_factors: Vec>, - current_sum: FieldElement, - challenges: Vec>, -} - -impl Verifier -where - F::BaseType: Send + Sync, - FieldElement: Clone + Mul>, -{ - /// Creates a new Verifier instance. - pub fn new( - num_vars: usize, - oracle_factors: Vec>, - claimed_sum: FieldElement, - ) -> Result> { - if oracle_factors.is_empty() { - // Need at least one factor for the product evaluation. - return Err(VerifierError::MissingOracle); - } - if oracle_factors.iter().any(|p| p.num_vars() != num_vars) { - // All factors must operate on the same number of variables. - return Err(VerifierError::InvalidState( - "Oracle factors have inconsistent number of variables.".to_string(), - )); - } - - Ok(Self { - num_vars, - round: 0, - oracle_factors, - // Starts with the initial claimed sum C - current_sum: claimed_sum, - challenges: Vec::with_capacity(num_vars), - }) - } - - /// Executes a verification round based on the polynomial \( g_j \) received from the prover. - pub fn do_round>( - &mut self, - g_j: Polynomial>, - transcript: &mut C, - ) -> Result, VerifierError> { - // Check if we are past the expected number of rounds. - if self.round >= self.num_vars { - return Err(VerifierError::InvalidState( - "Round number exceeds number of variables.".to_string(), - )); - } - - // 1. Check degree of g_j. - // The degree of g_j(X_j) = sum_{...} prod P_i(...) can be at most the number of factors. - let max_degree = self.oracle_factors.len(); - if g_j.degree() > max_degree { - return Err(VerifierError::InvalidDegree { - round: self.round, - actual_degree: g_j.degree(), - max_allowed: max_degree, - }); - } - - // 2. Check consistency: g_j(0) + g_j(1) == expected_sum (current_sum) - let zero = FieldElement::::zero(); - let one = FieldElement::::one(); - let eval_0 = g_j.evaluate(&zero); - let eval_1 = g_j.evaluate(&one); - let sum_evals = eval_0.clone() + eval_1.clone(); - - if sum_evals != self.current_sum { - // The prover's polynomial g_j does not match the expected sum from the previous round (or initial C). - return Err(VerifierError::InconsistentSum { - round: self.round, - s0: eval_0, - s1: eval_1, - expected: self.current_sum.clone(), - }); - } - - // 3. Obtain challenge r_j for this round from the transcript. - let r_j = transcript.draw_felt(); - self.challenges.push(r_j.clone()); - - // 4. Update the expected sum for the *next* round: current_sum = g_j(r_j) - self.current_sum = g_j.evaluate(&r_j); - self.round += 1; - - // 5. Check if this is the final round. - if self.round == self.num_vars { - // Perform the final check: evaluate prod P_i(r1, ..., rn) - match evaluate_product_at_point(&self.oracle_factors, &self.challenges) { - Ok(expected_final_eval) => { - let success = expected_final_eval == self.current_sum; - Ok(VerifierRoundResult::Final(success)) - } - Err(e) => Err(VerifierError::OracleEvaluationError(e)), - } - } else { - Ok(VerifierRoundResult::NextRound(r_j)) - } - } -} - -pub fn verify( - num_vars: usize, - claimed_sum: FieldElement, - proof_polys: Vec>>, - oracle_factors: Vec>, -) -> Result> -where - F: IsField + HasDefaultTranscript, - F::BaseType: Send + Sync, - FieldElement: Clone + Mul> + ByteConversion, -{ - // ensure the number of polynomials matches the number of variables. - if proof_polys.len() != num_vars { - return Err(VerifierError::IncorrectProofLength { - expected: num_vars, - actual: proof_polys.len(), - }); - } - - let mut verifier = Verifier::new(num_vars, oracle_factors.clone(), claimed_sum.clone())?; - - let mut transcript = DefaultTranscript::::default(); - transcript.append_bytes(b"initial_sum"); - transcript.append_felt(&FieldElement::from(num_vars as u64)); - transcript.append_felt(&FieldElement::from(oracle_factors.len() as u64)); - transcript.append_felt(&claimed_sum); - - // Process each round polynomial from the proof. - for (j, g_j) in proof_polys.into_iter().enumerate() { - // Reconstruct the transcript state before drawing the challenge. - let round_label = format!("round_{j}_poly"); - transcript.append_bytes(round_label.as_bytes()); - - let coeffs = g_j.coefficients(); - transcript.append_bytes(&(coeffs.len() as u64).to_be_bytes()); - if coeffs.is_empty() { - transcript.append_felt(&FieldElement::zero()); - } else { - for coeff in coeffs { - transcript.append_felt(coeff); - } - } - - match verifier.do_round(g_j, &mut transcript)? { - VerifierRoundResult::NextRound(_) => { - // Consistency checks passed, challenge r_j generated and stored. - // Continue to the next round. - continue; - } - VerifierRoundResult::Final(result) => { - // This was the last round (j == num_vars - 1). - if j == num_vars - 1 { - // Return the final result from the last round check. - return Ok(result); - } else { - // Should not get Final result before the last round. - return Err(VerifierError::InvalidState( - "Final result obtained before the last round.".to_string(), - )); - } - } - } - } - - Err(VerifierError::InvalidState( - "Verification loop finished unexpectedly.".to_string(), - )) -} diff --git a/crates/provers/winterfell_adapter/Cargo.toml b/crates/provers/winterfell_adapter/Cargo.toml deleted file mode 100644 index a8346b430..000000000 --- a/crates/provers/winterfell_adapter/Cargo.toml +++ /dev/null @@ -1,32 +0,0 @@ -[package] -name = "lambdaworks-winterfell-adapter" -version.workspace = true -edition.workspace = true -license.workspace = true - -[dependencies] -lambdaworks-math = { git = "https://github.com/lambdaclass/lambdaworks", branch = "miden-version", features = [ - "winter_compatibility", -] } -stark-platinum-prover = { git = "https://github.com/lambdaclass/lambdaworks", branch = "miden-version", features = [ - "winter_compatibility", -] } - -rand = "0.8.5" -winter-air = { package = "winter-air", version = "0.6.4", default-features = false } -winter-prover = { package = "winter-prover", version = "0.6.4", default-features = false } -winter-math = { package = "winter-math", version = "0.6.4", default-features = false } -winter-utils = { package = "winter-utils", version = "0.6.4", default-features = false } -miden-air = { package = "miden-air", version = "0.7", default-features = false } -miden-core = { package = "miden-core", version = "0.7", default-features = false } -miden-assembly = { package = "miden-assembly", version = "0.7", default-features = false } -miden-processor = { package = "miden-processor", version = "0.7", default-features = false } -sha3 = "0.10" - -[dev-dependencies] -criterion = { version = "0.4", default-features = false } -miden-prover = { package = "miden-prover", version = "0.7", default-features = false } - -[[bench]] -name = "proving" -harness = false diff --git a/crates/provers/winterfell_adapter/README.md b/crates/provers/winterfell_adapter/README.md deleted file mode 100644 index 03e6e11f6..000000000 --- a/crates/provers/winterfell_adapter/README.md +++ /dev/null @@ -1,66 +0,0 @@ -# Winterfell adapter -This package helps converting your Winterfell AIR into a Lambdaworks AIR via an adapter. This `AIRAdapter` can then be used with the lambdaworks prover. - -# Examples -## Fibonacci -Suppose you want to run the Lambdaworks prover with a `WinterfellFibonacciAIR`. - -```rust -use winterfell::Air; - -struct WinterfellFibonacciAIR { - /// ... -} - -impl Air for WinterfellFibonacciAIR { - /// ... -} -``` - -### Step 1: Convert your Winterfell trace table -Use the Lambdaworks `AirAdapter` to convert your Winterfell trace: -```rust -let trace = &AirAdapter::convert_winterfell_trace_table(winterfell_trace) -``` - -### Step 2: Convert your public inputs -Create the `AirAdapterPublicInputs` by supplying your `winterfell_public_inputs` and the additional parameters required by the Lambdaworks prover: - -```rust -let pub_inputs = AirAdapterPublicInputs { - winterfell_public_inputs: AdapterFieldElement(trace.columns()[1][7]), - transition_degrees: vec![1, 1], /// The degrees of each transition - transition_exemptions: vec![1, 1], /// The steps at the end where the transitions do not apply. - transition_offsets: vec![0, 1], /// The size of the frame. This is probably [0, 1] for every Winterfell AIR. - composition_poly_degree_bound: 8, /// A bound over the composition degree polynomial, used for choosing the number of parts for H(x). - trace_info: TraceInfo::new(2, 8), /// Your winterfell trace info. -}; -``` - -Note that you might have to also convert your field elements to `AdapterFieldElement`, as in this case. - -### Step 3: Make the proof - -```rust -let proof = Prover::prove::>>( - &trace, - &pub_inputs, /// Public inputs - &proof_options, - StoneProverTranscript::new(&[]), -); -``` - -Here `TraceTable` is the Winterfell type that represents your trace table. To check more examples you can see the `examples` folder inside this crate. - -# Benchmarks -To run the fibonacci Miden benchmark run: - -```rust -cargo bench -``` - -To run it with parallelization run: - -```rust -cargo bench --features stark-platinum-prover/parallel,winter-prover/concurrent -``` diff --git a/crates/provers/winterfell_adapter/benches/proving.rs b/crates/provers/winterfell_adapter/benches/proving.rs deleted file mode 100644 index 504c4ab6f..000000000 --- a/crates/provers/winterfell_adapter/benches/proving.rs +++ /dev/null @@ -1,119 +0,0 @@ -use criterion::{black_box, criterion_group, criterion_main, Criterion}; -use lambdaworks_winterfell_adapter::adapter::public_inputs::AirAdapterPublicInputs; -use lambdaworks_winterfell_adapter::adapter::QuadFeltTranscript; -use lambdaworks_winterfell_adapter::examples::miden_vm::{ - ExecutionTraceMetadata, MidenVMQuadFeltAir, -}; -use miden_air::{HashFunction, ProcessorAir, ProvingOptions, PublicInputs}; -use miden_assembly::Assembler; -use miden_core::{Program, StackInputs}; -use miden_processor::DefaultHost; -use miden_processor::{self as processor}; -use miden_prover::prove; -use stark_platinum_prover::proof::options::ProofOptions; -use stark_platinum_prover::prover::{IsStarkProver, Prover}; -use winter_air::FieldExtension; -use winter_prover::Trace; - -struct BenchInstance { - program: Program, - stack_inputs: StackInputs, - lambda_proof_options: ProofOptions, -} - -fn create_bench_instance(fibonacci_number: usize) -> BenchInstance { - let program = format!( - "begin - repeat.{} - swap dup.1 add - end - end", - fibonacci_number - 1 - ); - let program = Assembler::default().compile(program).unwrap(); - let stack_inputs = StackInputs::try_from_values([0, 1]).unwrap(); - let mut lambda_proof_options = ProofOptions::default_test_options(); - lambda_proof_options.blowup_factor = 8; - - BenchInstance { - program, - stack_inputs, - lambda_proof_options, - } -} - -pub fn bench_prove_miden_fibonacci(c: &mut Criterion) { - let instance = create_bench_instance(100); - - c.bench_function("winterfell_prover", |b| { - b.iter(|| { - let proving_options = ProvingOptions::new( - instance.lambda_proof_options.fri_number_of_queries, - instance.lambda_proof_options.blowup_factor as usize, - instance.lambda_proof_options.grinding_factor as u32, - FieldExtension::Quadratic, - 2, - 0, - HashFunction::Blake3_192, - ); - - let (_outputs, _proof) = black_box( - prove( - &instance.program, - instance.stack_inputs.clone(), - DefaultHost::default(), - proving_options, - ) - .unwrap(), - ); - }) - }); - - c.bench_function("lambda_prover", |b| { - b.iter(|| { - // This is here because the only pub method in miden - // is a prove function that executes AND proves. - // This makes the benchmark a more fair - // in the case that the program execution takes - // too long. - let winter_trace = processor::execute( - &instance.program, - instance.stack_inputs.clone(), - DefaultHost::default(), - *ProvingOptions::default().execution_options(), - ) - .unwrap(); - - let program_info = winter_trace.program_info().clone(); - let stack_outputs = winter_trace.stack_outputs().clone(); - let pub_inputs = AirAdapterPublicInputs::::new( - PublicInputs::new( - program_info, - instance.stack_inputs.clone(), - stack_outputs.clone(), - ), - vec![2; 182], - vec![0, 1], - winter_trace.get_info(), - winter_trace.clone().into(), - ); - - let trace = MidenVMQuadFeltAir::convert_winterfell_trace_table( - winter_trace.main_segment().clone(), - ); - - let _proof = black_box( - Prover::::prove( - &trace, - &pub_inputs, - &instance.lambda_proof_options, - QuadFeltTranscript::new(&[]), - ) - .unwrap(), - ); - }) - }); -} - -criterion_group!(benches, bench_prove_miden_fibonacci); -criterion_main!(benches); diff --git a/crates/provers/winterfell_adapter/src/adapter/air.rs b/crates/provers/winterfell_adapter/src/adapter/air.rs deleted file mode 100644 index 256deedf8..000000000 --- a/crates/provers/winterfell_adapter/src/adapter/air.rs +++ /dev/null @@ -1,368 +0,0 @@ -use crate::utils::{ - matrix_lambda2winter, matrix_winter2lambda, vec_lambda2winter, vec_winter2lambda, -}; -use lambdaworks_math::field::element::FieldElement; -use lambdaworks_math::field::traits::{IsFFTField, IsField, IsSubFieldOf}; -use lambdaworks_math::traits::ByteConversion; -use miden_core::Felt; -use stark_platinum_prover::{ - constraints::boundary::{BoundaryConstraint, BoundaryConstraints}, - traits::AIR, -}; -use std::marker::PhantomData; -use winter_air::{Air, AuxTraceRandElements, EvaluationFrame, FieldExtension, ProofOptions}; -use winter_math::{FieldElement as IsWinterfellFieldElement, StarkField}; -use winter_prover::{ColMatrix, Trace, TraceTable}; - -use super::public_inputs::AirAdapterPublicInputs; - -pub trait FromColumns { - fn from_cols(columns: Vec>, metadata: &M) -> Self; -} - -impl FromColumns for TraceTable { - fn from_cols(columns: Vec>, _: &()) -> Self { - TraceTable::init(columns) - } -} - -#[derive(Clone)] -pub struct AirAdapter -where - FE: IsWinterfellFieldElement - + StarkField - + ByteConversion - + Unpin - + IsFFTField - + IsSubFieldOf, - E: IsField, - A: Air, - A::PublicInputs: Clone, - T: Trace + Clone + FromColumns, - M: Clone, -{ - winterfell_air: A, - public_inputs: AirAdapterPublicInputs, - air_context: stark_platinum_prover::context::AirContext, - trace: PhantomData, - extension: PhantomData, -} - -impl AirAdapter -where - FE: IsWinterfellFieldElement - + StarkField - + ByteConversion - + Unpin - + IsFFTField - + IsField - + IsSubFieldOf, - E: IsField + IsWinterfellFieldElement, - A: Air + Clone, - A::PublicInputs: Clone, - T: Trace + Clone + FromColumns, - M: Clone, -{ - pub fn convert_winterfell_trace_table( - trace: ColMatrix, - ) -> stark_platinum_prover::trace::TraceTable { - let mut columns = Vec::new(); - for i in 0..trace.num_cols() { - columns.push(trace.get_column(i).to_owned()); - } - - stark_platinum_prover::trace::TraceTable::from_columns(matrix_winter2lambda(&columns), 1) - } -} - -impl AIR for AirAdapter -where - FE: IsWinterfellFieldElement - + StarkField - + ByteConversion - + Unpin - + IsFFTField - + IsField - + IsSubFieldOf, - E: IsField + IsWinterfellFieldElement, - A: Air + Clone, - A::PublicInputs: Clone, - T: Trace + Clone + FromColumns, - M: Clone, -{ - type Field = FE; - type FieldExtension = E; - type RAPChallenges = Vec; - type PublicInputs = AirAdapterPublicInputs; - const STEP_SIZE: usize = 1; - - fn new( - _trace_length: usize, - pub_inputs: &Self::PublicInputs, - lambda_proof_options: &stark_platinum_prover::proof::options::ProofOptions, - ) -> Self { - let winter_proof_options = ProofOptions::new( - lambda_proof_options.fri_number_of_queries, - lambda_proof_options.blowup_factor as usize, - lambda_proof_options.grinding_factor as u32, - FieldExtension::None, - 2, - 0, - ); - - let winterfell_air = A::new( - pub_inputs.trace_info.clone(), - pub_inputs.winterfell_public_inputs.clone(), - winter_proof_options, - ); - let winterfell_context = winterfell_air.context(); - - let lambda_context = stark_platinum_prover::context::AirContext { - proof_options: lambda_proof_options.clone(), - transition_exemptions: pub_inputs.transition_exemptions.to_owned(), - transition_offsets: pub_inputs.transition_offsets.to_owned(), - num_transition_constraints: winterfell_context.num_transition_constraints(), - trace_columns: pub_inputs.trace_info.width(), - }; - - Self { - winterfell_air, - public_inputs: pub_inputs.clone(), - air_context: lambda_context, - trace: PhantomData, - extension: PhantomData, - } - } - - fn build_auxiliary_trace( - &self, - main_trace: &stark_platinum_prover::trace::TraceTable, - rap_challenges: &Self::RAPChallenges, - ) -> stark_platinum_prover::trace::TraceTable { - // We support at most a one-stage RAP. This covers most use cases. - if let Some(winter_trace) = T::from_cols( - matrix_lambda2winter(&main_trace.columns()), - &self.pub_inputs().metadata, - ) - .build_aux_segment(&[], rap_challenges) - { - let mut columns = Vec::new(); - for i in 0..winter_trace.num_cols() { - columns.push(winter_trace.get_column(i).to_owned()); - } - stark_platinum_prover::trace::TraceTable::::from_columns( - matrix_winter2lambda(&columns), - 1, - ) - } else { - stark_platinum_prover::trace::TraceTable::::empty() - } - } - - fn build_rap_challenges( - &self, - transcript: &mut impl stark_platinum_prover::transcript::IsStarkTranscript, - ) -> Self::RAPChallenges { - let trace_layout = self.winterfell_air.trace_layout(); - let num_segments = trace_layout.num_aux_segments(); - - if num_segments == 1 { - let mut result = Vec::new(); - for _ in 0..trace_layout.get_aux_segment_rand_elements(0) { - result.push(transcript.sample_field_element()); - } - vec_lambda2winter(&result) - } else if num_segments == 0 { - Vec::new() - } else { - panic!("The winterfell adapter does not support AIR's with more than one auxiliary segment"); - } - } - - fn number_auxiliary_rap_columns(&self) -> usize { - self.winterfell_air.trace_layout().aux_trace_width() - } - - fn composition_poly_degree_bound(&self) -> usize { - self.winterfell_air - .context() - .num_constraint_composition_columns() - * self.trace_length() - } - - fn compute_transition_prover( - &self, - frame: &stark_platinum_prover::frame::Frame, - periodic_values: &[FieldElement], - rap_challenges: &Self::RAPChallenges, - ) -> Vec> { - let first_step = frame.get_evaluation_step(0); - let second_step = frame.get_evaluation_step(1); - - let main_frame = EvaluationFrame::from_rows( - vec_lambda2winter(first_step.get_row_main(0)), - vec_lambda2winter(second_step.get_row_main(0)), - ); - - let periodic_values = vec_lambda2winter(periodic_values); - - let main_result = vec![ - FieldElement::zero(); - self.winterfell_air - .context() - .num_main_transition_constraints() - ]; - - let mut main_result_winter = vec_lambda2winter(&main_result); - self.winterfell_air.evaluate_transition::( - &main_frame, - &periodic_values, - &mut main_result_winter, - ); - - let mut result: Vec<_> = vec_winter2lambda(&main_result_winter) - .into_iter() - .map(|element| element.to_extension()) - .collect(); - - if self.winterfell_air.trace_layout().num_aux_segments() == 1 { - let mut rand_elements = AuxTraceRandElements::new(); - rand_elements.add_segment_elements(rap_challenges.clone()); - - let first_step = frame.get_evaluation_step(0); - let second_step = frame.get_evaluation_step(1); - - let aux_frame = EvaluationFrame::from_rows( - vec_lambda2winter(first_step.get_row_aux(0)), - vec_lambda2winter(second_step.get_row_aux(0)), - ); - - let mut aux_result = vec![ - FieldElement::zero(); - self.winterfell_air - .context() - .num_aux_transition_constraints() - ]; - let mut winter_aux_result = vec_lambda2winter(&aux_result); - self.winterfell_air.evaluate_aux_transition( - &main_frame, - &aux_frame, - &periodic_values, - &rand_elements, - &mut winter_aux_result, - ); - aux_result = vec_winter2lambda(&winter_aux_result); - result.extend_from_slice(&aux_result); - } - result - } - - fn boundary_constraints( - &self, - rap_challenges: &Self::RAPChallenges, - ) -> stark_platinum_prover::constraints::boundary::BoundaryConstraints { - let mut result = Vec::new(); - for assertion in self.winterfell_air.get_assertions() { - assert!(assertion.is_single()); - result.push(BoundaryConstraint::new_main( - assertion.column(), - assertion.first_step(), - FieldElement::::const_from_raw(assertion.values()[0]).to_extension(), - )); - } - - let mut rand_elements = AuxTraceRandElements::new(); - rand_elements.add_segment_elements(rap_challenges.clone()); - - for assertion in self.winterfell_air.get_aux_assertions(&rand_elements) { - assert!(assertion.is_single()); - result.push(BoundaryConstraint::new_aux( - assertion.column(), - assertion.first_step(), - FieldElement::::const_from_raw(assertion.values()[0]), - )); - } - - BoundaryConstraints::from_constraints(result) - } - - fn context(&self) -> &stark_platinum_prover::context::AirContext { - &self.air_context - } - - fn trace_length(&self) -> usize { - self.winterfell_air.context().trace_len() - } - - fn pub_inputs(&self) -> &Self::PublicInputs { - &self.public_inputs - } - - fn get_periodic_column_values(&self) -> Vec>> { - matrix_winter2lambda(&self.winterfell_air.get_periodic_column_values()) - } - - fn compute_transition_verifier( - &self, - frame: &stark_platinum_prover::frame::Frame, - periodic_values: &[FieldElement], - rap_challenges: &Self::RAPChallenges, - ) -> Vec> { - let first_step = frame.get_evaluation_step(0); - let second_step = frame.get_evaluation_step(1); - - let main_frame = EvaluationFrame::from_rows( - vec_lambda2winter(first_step.get_row_main(0)), - vec_lambda2winter(second_step.get_row_main(0)), - ); - - let periodic_values = vec_lambda2winter(periodic_values); - - let main_result = vec![ - FieldElement::zero(); - self.winterfell_air - .context() - .num_main_transition_constraints() - ]; - - let mut main_result_winter = vec_lambda2winter(&main_result); - self.winterfell_air.evaluate_transition::( - &main_frame, - &periodic_values, - &mut main_result_winter, - ); - - let mut result: Vec> = vec_winter2lambda(&main_result_winter); - - if self.winterfell_air.trace_layout().num_aux_segments() == 1 { - let mut rand_elements = AuxTraceRandElements::new(); - rand_elements.add_segment_elements(rap_challenges.clone()); - - let first_step = frame.get_evaluation_step(0); - let second_step = frame.get_evaluation_step(1); - - let aux_frame = EvaluationFrame::from_rows( - vec_lambda2winter(first_step.get_row_aux(0)), - vec_lambda2winter(second_step.get_row_aux(0)), - ); - - let mut aux_result = vec![ - FieldElement::zero(); - self.winterfell_air - .context() - .num_aux_transition_constraints() - ]; - let mut winter_aux_result = vec_lambda2winter(&aux_result); - self.winterfell_air.evaluate_aux_transition( - &main_frame, - &aux_frame, - &periodic_values, - &rand_elements, - &mut winter_aux_result, - ); - aux_result = vec_winter2lambda(&winter_aux_result); - result.extend_from_slice(&aux_result); - } - result - } -} diff --git a/crates/provers/winterfell_adapter/src/adapter/mod.rs b/crates/provers/winterfell_adapter/src/adapter/mod.rs deleted file mode 100644 index 032280846..000000000 --- a/crates/provers/winterfell_adapter/src/adapter/mod.rs +++ /dev/null @@ -1,87 +0,0 @@ -use lambdaworks_math::{field::fields::winterfell::QuadFelt, traits::ByteConversion}; -use miden_core::Felt; -use sha3::{Digest, Keccak256}; -use stark_platinum_prover::{fri::FieldElement, transcript::IsStarkTranscript}; -use winter_math::StarkField; - -pub mod air; -pub mod public_inputs; - -pub struct FeltTranscript { - hasher: Keccak256, -} - -impl FeltTranscript { - pub fn new(data: &[u8]) -> Self { - let mut res = Self { - hasher: Keccak256::new(), - }; - res.append_bytes(data); - res - } -} - -impl IsStarkTranscript for FeltTranscript { - fn append_field_element(&mut self, element: &FieldElement) { - self.append_bytes(&element.value().to_bytes_be()); - } - - fn append_bytes(&mut self, new_bytes: &[u8]) { - self.hasher.update(new_bytes); - } - - fn state(&self) -> [u8; 32] { - self.hasher.clone().finalize().into() - } - - fn sample_field_element(&mut self) -> FieldElement { - let mut bytes = self.state()[..8].try_into().unwrap(); - let mut x = u64::from_be_bytes(bytes); - while x >= Felt::MODULUS { - self.append_bytes(&bytes); - bytes = self.state()[..8].try_into().unwrap(); - x = u64::from_be_bytes(bytes); - } - FieldElement::const_from_raw(Felt::new(x)) - } - - fn sample_u64(&mut self, upper_bound: u64) -> u64 { - u64::from_be_bytes(self.state()[..8].try_into().unwrap()) % upper_bound - } -} - -pub struct QuadFeltTranscript { - felt_transcript: FeltTranscript, -} - -impl QuadFeltTranscript { - pub fn new(data: &[u8]) -> Self { - Self { - felt_transcript: FeltTranscript::new(data), - } - } -} - -impl IsStarkTranscript for QuadFeltTranscript { - fn append_field_element(&mut self, element: &FieldElement) { - self.append_bytes(&element.value().to_bytes_be()); - } - - fn append_bytes(&mut self, new_bytes: &[u8]) { - self.felt_transcript.append_bytes(new_bytes); - } - - fn state(&self) -> [u8; 32] { - self.felt_transcript.state() - } - - fn sample_field_element(&mut self) -> FieldElement { - let x = self.felt_transcript.sample_field_element(); - let y = self.felt_transcript.sample_field_element(); - FieldElement::const_from_raw(QuadFelt::new(*x.value(), *y.value())) - } - - fn sample_u64(&mut self, upper_bound: u64) -> u64 { - u64::from_be_bytes(self.state()[..8].try_into().unwrap()) % upper_bound - } -} diff --git a/crates/provers/winterfell_adapter/src/adapter/public_inputs.rs b/crates/provers/winterfell_adapter/src/adapter/public_inputs.rs deleted file mode 100644 index fc5e49ba9..000000000 --- a/crates/provers/winterfell_adapter/src/adapter/public_inputs.rs +++ /dev/null @@ -1,38 +0,0 @@ -use winter_air::{Air, TraceInfo}; - -#[derive(Clone)] -pub struct AirAdapterPublicInputs -where - A: Air, - A::PublicInputs: Clone, - M: Clone, -{ - pub(crate) winterfell_public_inputs: A::PublicInputs, - pub(crate) transition_exemptions: Vec, - pub(crate) transition_offsets: Vec, - pub(crate) trace_info: TraceInfo, - pub(crate) metadata: M, -} - -impl AirAdapterPublicInputs -where - A: Air, - A::PublicInputs: Clone, - M: Clone, -{ - pub fn new( - winterfell_public_inputs: A::PublicInputs, - transition_exemptions: Vec, - transition_offsets: Vec, - trace_info: TraceInfo, - metadata: M, - ) -> Self { - Self { - winterfell_public_inputs, - transition_exemptions, - transition_offsets, - trace_info, - metadata, - } - } -} diff --git a/crates/provers/winterfell_adapter/src/examples/cubic.rs b/crates/provers/winterfell_adapter/src/examples/cubic.rs deleted file mode 100644 index b250abea1..000000000 --- a/crates/provers/winterfell_adapter/src/examples/cubic.rs +++ /dev/null @@ -1,121 +0,0 @@ -use miden_core::Felt; -use winter_air::{ - Air, AirContext, Assertion, EvaluationFrame, ProofOptions, TraceInfo, - TransitionConstraintDegree, -}; -use winter_math::FieldElement as IsWinterfellFieldElement; -use winter_prover::TraceTable; - -/// A fibonacci winterfell AIR example. Two terms are computed -/// at each step. This was taken from the original winterfell -/// repository and adapted to work with lambdaworks. -#[derive(Clone)] -pub struct Cubic { - context: AirContext, - result: Felt, -} - -impl Air for Cubic { - type BaseField = Felt; - type PublicInputs = Felt; - - fn new(trace_info: TraceInfo, pub_inputs: Self::BaseField, options: ProofOptions) -> Self { - let degrees = vec![TransitionConstraintDegree::new(3)]; - Cubic { - context: AirContext::new(trace_info, degrees, 2, options), - result: pub_inputs, - } - } - - fn context(&self) -> &AirContext { - &self.context - } - - fn evaluate_transition>( - &self, - frame: &EvaluationFrame, - _periodic_values: &[E], - result: &mut [E], - ) { - let current = frame.current(); - let next = frame.next(); - - // s_i = (s_{i-1})³ - result[0] = next[0] - (current[0] * current[0] * current[0]); - } - - fn get_assertions(&self) -> Vec> { - // A valid Fibonacci sequence should start with two ones and terminate with - // the expected result - let last_step = self.trace_length() - 1; - vec![ - Assertion::single(0, 0, Self::BaseField::from(2u16)), - Assertion::single(0, last_step, self.result), - ] - } -} - -pub fn build_trace(sequence_length: usize) -> TraceTable { - assert!( - sequence_length.is_power_of_two(), - "sequence length must be a power of 2" - ); - - let mut accum = Felt::from(2u16); - let mut column = vec![accum]; - while column.len() < sequence_length { - accum = accum * accum * accum; - column.push(accum); - } - TraceTable::init(vec![column]) -} - -#[cfg(test)] -mod tests { - use miden_core::Felt; - use stark_platinum_prover::{ - proof::options::ProofOptions, - prover::{IsStarkProver, Prover}, - verifier::{IsStarkVerifier, Verifier}, - }; - use winter_air::TraceInfo; - use winter_prover::{Trace, TraceTable}; - - use crate::{ - adapter::{air::AirAdapter, public_inputs::AirAdapterPublicInputs, FeltTranscript}, - examples::cubic::{self, Cubic}, - }; - - #[test] - fn prove_and_verify_a_winterfell_cubic_air() { - let lambda_proof_options = ProofOptions::default_test_options(); - let winter_trace = cubic::build_trace(16); - let trace = - AirAdapter::, Felt, Felt, ()>::convert_winterfell_trace_table( - winter_trace.main_segment().clone(), - ); - let pub_inputs = AirAdapterPublicInputs { - winterfell_public_inputs: *trace.columns()[0][15].value(), - transition_exemptions: vec![1], - transition_offsets: vec![0, 1], - trace_info: TraceInfo::new(1, 16), - metadata: (), - }; - - let proof = Prover::, Felt, Felt, _>>::prove( - &trace, - &pub_inputs, - &lambda_proof_options, - FeltTranscript::new(&[]), - ) - .unwrap(); - assert!( - Verifier::, Felt, Felt, _>>::verify( - &proof, - &pub_inputs, - &lambda_proof_options, - FeltTranscript::new(&[]), - ) - ); - } -} diff --git a/crates/provers/winterfell_adapter/src/examples/fibonacci_2_terms.rs b/crates/provers/winterfell_adapter/src/examples/fibonacci_2_terms.rs deleted file mode 100644 index 227f8ab2a..000000000 --- a/crates/provers/winterfell_adapter/src/examples/fibonacci_2_terms.rs +++ /dev/null @@ -1,135 +0,0 @@ -use miden_core::Felt; -use winter_air::{ - Air, AirContext, Assertion, EvaluationFrame, ProofOptions, TraceInfo, - TransitionConstraintDegree, -}; -use winter_math::FieldElement as IsWinterfellFieldElement; -use winter_prover::TraceTable; - -/// A fibonacci winterfell AIR example. Two terms are computed -/// at each step. This was taken from the original winterfell -/// repository and adapted to work with lambdaworks. -#[derive(Clone)] -pub struct FibAir2Terms { - context: AirContext, - result: Felt, -} - -impl Air for FibAir2Terms { - type BaseField = Felt; - type PublicInputs = Felt; - - fn new(trace_info: TraceInfo, pub_inputs: Self::BaseField, options: ProofOptions) -> Self { - let degrees = vec![ - TransitionConstraintDegree::new(1), - TransitionConstraintDegree::new(1), - ]; - FibAir2Terms { - context: AirContext::new(trace_info, degrees, 3, options), - result: pub_inputs, - } - } - - fn context(&self) -> &AirContext { - &self.context - } - - fn evaluate_transition>( - &self, - frame: &EvaluationFrame, - _periodic_values: &[E], - result: &mut [E], - ) { - let current = frame.current(); - let next = frame.next(); - - // Constraints of Fibonacci sequence (2 terms per step): - // s_{0, i+1} = s_{0, i} + s_{1, i} - // s_{1, i+1} = s_{1, i} + s_{0, i+1} - result[0] = next[0] - (current[0] + current[1]); - result[1] = next[1] - (current[1] + next[0]); - } - - fn get_assertions(&self) -> Vec> { - // A valid Fibonacci sequence should start with two ones and terminate with - // the expected result - let last_step = self.trace_length() - 1; - vec![ - Assertion::single(0, 0, Self::BaseField::ONE), - Assertion::single(1, 0, Self::BaseField::ONE), - Assertion::single(1, last_step, self.result), - ] - } -} - -pub fn build_trace(sequence_length: usize) -> TraceTable { - assert!( - sequence_length.is_power_of_two(), - "sequence length must be a power of 2" - ); - - let mut trace = TraceTable::new(2, sequence_length / 2); - trace.fill( - |state| { - state[0] = Felt::ONE; - state[1] = Felt::ONE; - }, - |_, state| { - state[0] += state[1]; - state[1] += state[0]; - }, - ); - - trace -} - -#[cfg(test)] -mod tests { - use miden_core::Felt; - use stark_platinum_prover::{ - proof::options::ProofOptions, - prover::{IsStarkProver, Prover}, - verifier::{IsStarkVerifier, Verifier}, - }; - use winter_air::TraceInfo; - use winter_prover::{Trace, TraceTable}; - - use crate::{ - adapter::{air::AirAdapter, public_inputs::AirAdapterPublicInputs, FeltTranscript}, - examples::fibonacci_2_terms::{self, FibAir2Terms}, - }; - - #[test] - fn prove_and_verify_a_winterfell_fibonacci_2_terms_air() { - let lambda_proof_options = ProofOptions::default_test_options(); - let winter_trace = fibonacci_2_terms::build_trace(16); - let trace = - AirAdapter::, Felt, Felt, ()>::convert_winterfell_trace_table( - winter_trace.main_segment().clone(), - ); - let pub_inputs = AirAdapterPublicInputs { - winterfell_public_inputs: *trace.columns()[1][7].value(), - transition_exemptions: vec![1, 1], - transition_offsets: vec![0, 1], - trace_info: TraceInfo::new(2, 8), - metadata: (), - }; - - let proof = Prover::, Felt, Felt, _>>::prove( - &trace, - &pub_inputs, - &lambda_proof_options, - FeltTranscript::new(&[]), - ) - .unwrap(); - - assert!(Verifier::< - AirAdapter, Felt, Felt, _>, - >::verify( - &proof, - &pub_inputs, - &lambda_proof_options, - FeltTranscript::new(&[]), - )); - } -} diff --git a/crates/provers/winterfell_adapter/src/examples/fibonacci_rap.rs b/crates/provers/winterfell_adapter/src/examples/fibonacci_rap.rs deleted file mode 100644 index 7315c3d09..000000000 --- a/crates/provers/winterfell_adapter/src/examples/fibonacci_rap.rs +++ /dev/null @@ -1,293 +0,0 @@ -use crate::adapter::air::FromColumns; -use miden_core::Felt; -use rand::seq::SliceRandom; -use rand::thread_rng; -use winter_air::{ - Air, AirContext, Assertion, AuxTraceRandElements, EvaluationFrame, ProofOptions, TraceInfo, - TraceLayout, TransitionConstraintDegree, -}; -use winter_math::{ExtensionOf, FieldElement as IsWinterfellFieldElement, StarkField}; -use winter_prover::{ColMatrix, Trace, TraceTable}; -use winter_utils::{collections::Vec, uninit_vector}; - -#[derive(Clone)] -pub struct RapTraceTable { - layout: TraceLayout, - trace: ColMatrix, - meta: Vec, -} - -impl RapTraceTable { - pub fn new(width: usize, length: usize) -> Self { - let columns = unsafe { (0..width).map(|_| uninit_vector(length)).collect() }; - Self { - layout: TraceLayout::new(width, [3], [3]), - trace: ColMatrix::new(columns), - meta: vec![], - } - } - - pub fn init(columns: Vec>) -> Self { - let trace_length = columns[0].len(); - - for column in columns.iter().skip(1) { - assert_eq!( - column.len(), - trace_length, - "all columns traces must have the same length" - ); - } - - Self { - layout: TraceLayout::new(columns.len(), [0], [0]), - trace: ColMatrix::new(columns), - meta: vec![], - } - } - - pub fn fill(&mut self, init: I, update: U) - where - I: Fn(&mut [B]), - U: Fn(usize, &mut [B]), - { - let mut state = vec![B::ZERO; self.main_trace_width()]; - init(&mut state); - self.update_row(0, &state); - - for i in 0..self.length() - 1 { - update(i, &mut state); - self.update_row(i + 1, &state); - } - } - - pub fn update_row(&mut self, step: usize, state: &[B]) { - self.trace.update_row(step, state); - } - - pub fn width(&self) -> usize { - self.main_trace_width() - } - - pub fn get(&self, column: usize, step: usize) -> B { - self.trace.get(column, step) - } - - pub fn read_row_into(&self, step: usize, target: &mut [B]) { - self.trace.read_row_into(step, target); - } -} - -impl Trace for RapTraceTable { - type BaseField = B; - - fn layout(&self) -> &TraceLayout { - &self.layout - } - - fn length(&self) -> usize { - self.trace.num_rows() - } - - fn meta(&self) -> &[u8] { - &self.meta - } - - fn read_main_frame(&self, row_idx: usize, frame: &mut EvaluationFrame) { - let next_row_idx = (row_idx + 1) % self.length(); - self.trace.read_row_into(row_idx, frame.current_mut()); - self.trace.read_row_into(next_row_idx, frame.next_mut()); - } - - fn main_segment(&self) -> &ColMatrix { - &self.trace - } - - fn build_aux_segment( - &mut self, - aux_segments: &[ColMatrix], - rand_elements: &[E], - ) -> Option> - where - E: IsWinterfellFieldElement, - { - // We only have one auxiliary segment for this example - if !aux_segments.is_empty() { - return None; - } - - let mut rap_column = vec![E::ZERO; self.length()]; - let gamma = rand_elements[0]; - - rap_column[0] = (>::into(self.get(0, 0)) + gamma) - / (>::into(self.get(2, 0)) + gamma); - for step in 1..self.length() { - rap_column[step] = (>::into(self.get(0, step)) + gamma) - / (>::into(self.get(2, step)) + gamma) - * rap_column[step - 1]; - } - - Some(ColMatrix::new(vec![rap_column])) - } -} - -impl FromColumns for RapTraceTable { - fn from_cols(columns: Vec>, _: &()) -> Self { - RapTraceTable::init(columns) - } -} - -#[derive(Clone)] -pub struct FibonacciRAP { - context: AirContext, - result: Felt, -} - -impl Air for FibonacciRAP { - type BaseField = Felt; - type PublicInputs = Felt; - - fn new(trace_info: TraceInfo, pub_inputs: Self::BaseField, options: ProofOptions) -> Self { - let degrees = vec![ - TransitionConstraintDegree::new(1), - TransitionConstraintDegree::new(1), - ]; - let aux_degrees = vec![TransitionConstraintDegree::new(2)]; - FibonacciRAP { - context: AirContext::new_multi_segment(trace_info, degrees, aux_degrees, 3, 1, options), - result: pub_inputs, - } - } - - fn context(&self) -> &AirContext { - &self.context - } - - fn evaluate_transition>( - &self, - frame: &EvaluationFrame, - _periodic_values: &[E], - result: &mut [E], - ) { - let current = frame.current(); - let next = frame.next(); - - // constraints of Fibonacci sequence (1 aux variable): - result[0] = next[0] - current[1]; - result[1] = next[1] - (current[1] + current[0]); - } - - fn evaluate_aux_transition( - &self, - main_frame: &EvaluationFrame, - aux_frame: &EvaluationFrame, - _periodic_values: &[F], - aux_rand_elements: &AuxTraceRandElements, - result: &mut [E], - ) where - F: IsWinterfellFieldElement, - E: IsWinterfellFieldElement + ExtensionOf, - { - let gamma = aux_rand_elements.get_segment_elements(0)[0]; - let curr_aux = aux_frame.current(); - let next_aux = aux_frame.next(); - let next_main = main_frame.next(); - - // curr_aux[0] * ((next_main[0] + gamma) / (next_main[2] + gamma)) = next_aux[0] - // curr_aux[0] * (next_main[0] + gamma) - next_aux[0] * (next_main[2] + gamma) == 0 - result[0] = curr_aux[0] * (>::into(next_main[0]) + gamma) - - next_aux[0] * (>::into(next_main[2]) + gamma); - } - - fn get_assertions(&self) -> Vec> { - let last_step = self.trace_length() - 1; - vec![ - Assertion::single(0, 0, Self::BaseField::ONE), - Assertion::single(1, 0, Self::BaseField::ONE), - Assertion::single(1, last_step, self.result), - ] - } - - fn get_aux_assertions>( - &self, - _aux_rand_elements: &AuxTraceRandElements, - ) -> Vec> { - let last_step = self.trace_length() - 1; - vec![Assertion::single(0, last_step, Self::BaseField::ONE.into())] - } -} - -pub fn build_trace(sequence_length: usize) -> TraceTable { - assert!( - sequence_length.is_power_of_two(), - "sequence length must be a power of 2" - ); - - let mut fibonacci = vec![Felt::ONE, Felt::ONE]; - for i in 2..(sequence_length + 1) { - fibonacci.push(fibonacci[i - 2] + fibonacci[i - 1]) - } - - let mut permuted = fibonacci[..fibonacci.len() - 1].to_vec(); - let mut rng = thread_rng(); - permuted.shuffle(&mut rng); - - TraceTable::init(vec![ - fibonacci[..fibonacci.len() - 1].to_vec(), - fibonacci[1..].to_vec(), - permuted, - ]) -} - -#[cfg(test)] -mod tests { - use lambdaworks_math::field::fields::winterfell::QuadFelt; - use miden_core::Felt; - use stark_platinum_prover::{ - proof::options::ProofOptions, - prover::{IsStarkProver, Prover}, - verifier::{IsStarkVerifier, Verifier}, - }; - use winter_air::{TraceInfo, TraceLayout}; - use winter_prover::Trace; - - use crate::{ - adapter::{air::AirAdapter, public_inputs::AirAdapterPublicInputs, QuadFeltTranscript}, - examples::fibonacci_rap::{self, FibonacciRAP, RapTraceTable}, - }; - - #[test] - fn prove_and_verify_a_winterfell_fibonacci_rap_air() { - let lambda_proof_options = ProofOptions::default_test_options(); - let winter_trace = fibonacci_rap::build_trace(16); - let trace = - AirAdapter::, Felt, QuadFelt, ()>::convert_winterfell_trace_table( - winter_trace.main_segment().clone(), - ); - let trace_layout = TraceLayout::new(3, [1], [1]); - let trace_info = TraceInfo::new_multi_segment(trace_layout, 16, vec![]); - let fibonacci_result = trace.columns()[1][15]; - let pub_inputs = AirAdapterPublicInputs:: { - winterfell_public_inputs: *fibonacci_result.value(), - transition_exemptions: vec![1, 1, 1], - transition_offsets: vec![0, 1], - trace_info, - metadata: (), - }; - - let proof = Prover::, Felt, QuadFelt, _>>::prove( - &trace, - &pub_inputs, - &lambda_proof_options, - QuadFeltTranscript::new(&[]), - ) - .unwrap(); - assert!(Verifier::< - AirAdapter, Felt, QuadFelt, _>, - >::verify( - &proof, - &pub_inputs, - &lambda_proof_options, - QuadFeltTranscript::new(&[]), - )); - } -} diff --git a/crates/provers/winterfell_adapter/src/examples/miden_vm.rs b/crates/provers/winterfell_adapter/src/examples/miden_vm.rs deleted file mode 100644 index 89208f787..000000000 --- a/crates/provers/winterfell_adapter/src/examples/miden_vm.rs +++ /dev/null @@ -1,188 +0,0 @@ -use lambdaworks_math::field::fields::winterfell::QuadFelt; -use miden_air::ProcessorAir; -use miden_core::{Felt, ProgramInfo, StackOutputs}; -use miden_processor::{AuxTraceHints, ExecutionTrace, TraceLenSummary}; -use winter_air::TraceLayout; -use winter_prover::ColMatrix; - -use crate::adapter::air::{AirAdapter, FromColumns}; - -pub type MidenVMQuadFeltAir = - AirAdapter; - -#[derive(Clone)] -pub struct ExecutionTraceMetadata { - meta: Vec, - layout: TraceLayout, - aux_trace_hints: AuxTraceHints, - program_info: ProgramInfo, - stack_outputs: StackOutputs, - trace_len_summary: TraceLenSummary, -} - -impl From for ExecutionTraceMetadata { - fn from(value: ExecutionTrace) -> Self { - Self { - meta: value.meta, - layout: value.layout, - aux_trace_hints: value.aux_trace_hints, - program_info: value.program_info, - stack_outputs: value.stack_outputs, - trace_len_summary: value.trace_len_summary, - } - } -} - -impl FromColumns for ExecutionTrace { - fn from_cols(columns: Vec>, metadata: &ExecutionTraceMetadata) -> Self { - ExecutionTrace { - meta: metadata.meta.clone(), - layout: metadata.layout.clone(), - main_trace: ColMatrix::new(columns), - aux_trace_hints: metadata.aux_trace_hints.clone(), - program_info: metadata.program_info.clone(), - stack_outputs: metadata.stack_outputs.clone(), - trace_len_summary: metadata.trace_len_summary, - } - } -} - -#[cfg(test)] -mod tests { - use crate::adapter::public_inputs::AirAdapterPublicInputs; - use crate::adapter::QuadFeltTranscript; - use crate::examples::miden_vm::MidenVMQuadFeltAir; - use miden_air::{ProvingOptions, PublicInputs}; - use miden_assembly::Assembler; - use miden_core::{Felt, StackInputs}; - use miden_processor::DefaultHost; - use miden_processor::{self as processor}; - use stark_platinum_prover::prover::Prover; - use stark_platinum_prover::verifier::Verifier; - use stark_platinum_prover::{ - proof::options::ProofOptions, prover::IsStarkProver, verifier::IsStarkVerifier, - }; - use winter_math::{FieldElement, StarkField}; - use winter_prover::Trace; - - #[test] - fn prove_and_verify_miden_readme_example() { - let mut lambda_proof_options = ProofOptions::default_test_options(); - lambda_proof_options.blowup_factor = 32; - let assembler = Assembler::default(); - - let program = assembler.compile("begin push.3 push.5 add end").unwrap(); - - let winter_trace = processor::execute( - &program, - StackInputs::default(), - DefaultHost::default(), - *ProvingOptions::default().execution_options(), - ) - .unwrap(); - let program_info = winter_trace.program_info().clone(); - let stack_outputs = winter_trace.stack_outputs().clone(); - - let pub_inputs = PublicInputs::new(program_info, StackInputs::default(), stack_outputs); - - let pub_inputs = AirAdapterPublicInputs { - winterfell_public_inputs: pub_inputs, - transition_exemptions: vec![2; 182], - transition_offsets: vec![0, 1], - trace_info: winter_trace.get_info(), - metadata: winter_trace.clone().into(), - }; - - let trace = - MidenVMQuadFeltAir::convert_winterfell_trace_table(winter_trace.main_segment().clone()); - - let proof = Prover::::prove( - &trace, - &pub_inputs, - &lambda_proof_options, - QuadFeltTranscript::new(&[]), - ) - .unwrap(); - - assert!(Verifier::::verify( - &proof, - &pub_inputs, - &lambda_proof_options, - QuadFeltTranscript::new(&[]), - )); - } - - fn compute_fibonacci(n: usize) -> Felt { - let mut t0 = Felt::ZERO; - let mut t1 = Felt::ONE; - - for _ in 0..n { - t1 = t0 + t1; - core::mem::swap(&mut t0, &mut t1); - } - t0 - } - - #[test] - fn prove_and_verify_miden_fibonacci() { - let fibonacci_number = 16; - let program = format!( - "begin - repeat.{} - swap dup.1 add - end - end", - fibonacci_number - 1 - ); - let program = Assembler::default().compile(program).unwrap(); - let expected_result = vec![compute_fibonacci(fibonacci_number).as_int()]; - let stack_inputs = StackInputs::try_from_values([0, 1]).unwrap(); - - let mut lambda_proof_options = ProofOptions::default_test_options(); - lambda_proof_options.blowup_factor = 8; - - let winter_trace = processor::execute( - &program, - stack_inputs.clone(), - DefaultHost::default(), - *ProvingOptions::default().execution_options(), - ) - .unwrap(); - let program_info = winter_trace.program_info().clone(); - let stack_outputs = winter_trace.stack_outputs().clone(); - - let pub_inputs = PublicInputs::new(program_info, stack_inputs, stack_outputs.clone()); - - assert_eq!( - expected_result, - stack_outputs.clone().stack_truncated(1), - "Program result was computed incorrectly" - ); - - let pub_inputs = AirAdapterPublicInputs { - winterfell_public_inputs: pub_inputs, - transition_exemptions: vec![2; 182], - transition_offsets: vec![0, 1], - trace_info: winter_trace.get_info(), - metadata: winter_trace.clone().into(), - }; - - let trace = - MidenVMQuadFeltAir::convert_winterfell_trace_table(winter_trace.main_segment().clone()); - - let proof = Prover::::prove( - &trace, - &pub_inputs, - &lambda_proof_options, - QuadFeltTranscript::new(&[]), - ) - .unwrap(); - - assert!(Verifier::::verify( - &proof, - &pub_inputs, - &lambda_proof_options, - QuadFeltTranscript::new(&[]), - )); - } -} diff --git a/crates/provers/winterfell_adapter/src/examples/mod.rs b/crates/provers/winterfell_adapter/src/examples/mod.rs deleted file mode 100644 index 117c0609a..000000000 --- a/crates/provers/winterfell_adapter/src/examples/mod.rs +++ /dev/null @@ -1,4 +0,0 @@ -pub mod cubic; -pub mod fibonacci_2_terms; -pub mod fibonacci_rap; -pub mod miden_vm; diff --git a/crates/provers/winterfell_adapter/src/field_element/element.rs b/crates/provers/winterfell_adapter/src/field_element/element.rs deleted file mode 100644 index 802743abc..000000000 --- a/crates/provers/winterfell_adapter/src/field_element/element.rs +++ /dev/null @@ -1,392 +0,0 @@ -use crate::field_element::positive_integer::AdapterPositiveInteger; -use core::fmt; -use core::{ - mem, - ops::{DivAssign, MulAssign, SubAssign}, - slice, -}; -use lambdaworks_math::{ - field::{ - element::FieldElement, fields::fft_friendly::stark_252_prime_field::Stark252PrimeField, - traits::IsField, - }, - traits::ByteConversion, -}; -use std::ops::{Add, AddAssign, Div, Mul, Neg, Sub}; -use winter_math::{ExtensibleField, FieldElement as IsWinterfellFieldElement, StarkField}; -use winter_utils::{AsBytes, Deserializable, DeserializationError, Randomizable, Serializable}; - -#[derive(Debug, Copy, Clone, Default)] -pub struct AdapterFieldElement(pub FieldElement); - -impl AdapterFieldElement { - pub const fn from_hex_unchecked(hex: &str) -> AdapterFieldElement { - AdapterFieldElement(FieldElement::from_hex_unchecked(hex)) - } -} - -impl IsWinterfellFieldElement for AdapterFieldElement { - type PositiveInteger = AdapterPositiveInteger; - type BaseField = Self; - const EXTENSION_DEGREE: usize = 1; - const ELEMENT_BYTES: usize = 32; - const IS_CANONICAL: bool = false; // Check if related to montgomery - const ZERO: Self = Self::from_hex_unchecked("0"); - const ONE: Self = Self::from_hex_unchecked("1"); - - fn inv(self) -> Self { - AdapterFieldElement(FieldElement::::inv(&self.0).unwrap()) - } - - fn conjugate(&self) -> Self { - *self - } - - fn base_element(&self, i: usize) -> Self::BaseField { - match i { - 0 => *self, - _ => panic!("element index must be 0, but was {i}"), - } - } - - fn slice_as_base_elements(elements: &[Self]) -> &[Self::BaseField] { - elements - } - - fn slice_from_base_elements(elements: &[Self::BaseField]) -> &[Self] { - elements - } - - fn elements_as_bytes(elements: &[Self]) -> &[u8] { - let p = elements.as_ptr(); - let len = elements.len() * Self::ELEMENT_BYTES; - unsafe { slice::from_raw_parts(p as *const u8, len) } - } - - unsafe fn bytes_as_elements(bytes: &[u8]) -> Result<&[Self], DeserializationError> { - if !bytes.len().is_multiple_of(Self::ELEMENT_BYTES) { - return Err(DeserializationError::InvalidValue(format!( - "number of bytes ({}) does not divide into whole number of field elements", - bytes.len(), - ))); - } - - let p = bytes.as_ptr(); - let len = bytes.len() / Self::ELEMENT_BYTES; - - if !(p as usize).is_multiple_of(mem::align_of::()) { - return Err(DeserializationError::InvalidValue( - "slice memory alignment is not valid for this field element type".to_string(), - )); - } - - Ok(slice::from_raw_parts(p as *const Self, len)) - } -} - -impl StarkField for AdapterFieldElement { - const MODULUS: Self::PositiveInteger = AdapterPositiveInteger::from_hex_unchecked( - "800000000000011000000000000000000000000000000000000000000000001", - ); - - const MODULUS_BITS: u32 = 252; - - const GENERATOR: Self = Self::from_hex_unchecked("3"); - - const TWO_ADICITY: u32 = 192; - - const TWO_ADIC_ROOT_OF_UNITY: Self = - Self::from_hex_unchecked("5282db87529cfa3f0464519c8b0fa5ad187148e11a61616070024f42f8ef94"); - - fn get_modulus_le_bytes() -> Vec { - Self::MODULUS.to_bytes_le() - } - - fn as_int(&self) -> Self::PositiveInteger { - AdapterPositiveInteger(FieldElement::representative(&self.0)) - } -} - -impl Div<&AdapterFieldElement> for &AdapterFieldElement { - type Output = AdapterFieldElement; - - fn div(self, rhs: &AdapterFieldElement) -> Self::Output { - AdapterFieldElement(self.0.div(&rhs.0)) - } -} - -impl Div for AdapterFieldElement { - type Output = AdapterFieldElement; - - fn div(self, rhs: AdapterFieldElement) -> Self::Output { - &self / &rhs - } -} - -impl Div<&AdapterFieldElement> for AdapterFieldElement { - type Output = AdapterFieldElement; - - fn div(self, rhs: &AdapterFieldElement) -> Self::Output { - &self / rhs - } -} - -impl Div for &AdapterFieldElement { - type Output = AdapterFieldElement; - - fn div(self, rhs: AdapterFieldElement) -> Self::Output { - self / &rhs - } -} - -impl Mul<&AdapterFieldElement> for &AdapterFieldElement { - type Output = AdapterFieldElement; - - fn mul(self, rhs: &AdapterFieldElement) -> Self::Output { - AdapterFieldElement(self.0.mul(&rhs.0)) - } -} - -impl Mul for AdapterFieldElement { - type Output = AdapterFieldElement; - - fn mul(self, rhs: AdapterFieldElement) -> Self::Output { - &self * &rhs - } -} - -impl Mul<&AdapterFieldElement> for AdapterFieldElement { - type Output = AdapterFieldElement; - - fn mul(self, rhs: &AdapterFieldElement) -> Self::Output { - &self * rhs - } -} - -impl Mul for &AdapterFieldElement { - type Output = AdapterFieldElement; - - fn mul(self, rhs: AdapterFieldElement) -> Self::Output { - self * &rhs - } -} - -impl Sub<&AdapterFieldElement> for &AdapterFieldElement { - type Output = AdapterFieldElement; - - fn sub(self, rhs: &AdapterFieldElement) -> Self::Output { - AdapterFieldElement(self.0.sub(&rhs.0)) - } -} - -impl Sub for AdapterFieldElement { - type Output = AdapterFieldElement; - - fn sub(self, rhs: AdapterFieldElement) -> Self::Output { - &self - &rhs - } -} - -impl Sub<&AdapterFieldElement> for AdapterFieldElement { - type Output = AdapterFieldElement; - - fn sub(self, rhs: &AdapterFieldElement) -> Self::Output { - &self - rhs - } -} - -impl Sub for &AdapterFieldElement { - type Output = AdapterFieldElement; - - fn sub(self, rhs: AdapterFieldElement) -> Self::Output { - self - &rhs - } -} - -impl Add<&AdapterFieldElement> for &AdapterFieldElement { - type Output = AdapterFieldElement; - - fn add(self, rhs: &AdapterFieldElement) -> Self::Output { - AdapterFieldElement(self.0.add(&rhs.0)) - } -} - -impl Add for AdapterFieldElement { - type Output = AdapterFieldElement; - - fn add(self, rhs: AdapterFieldElement) -> Self::Output { - &self + &rhs - } -} - -impl Add<&AdapterFieldElement> for AdapterFieldElement { - type Output = AdapterFieldElement; - - fn add(self, rhs: &AdapterFieldElement) -> Self::Output { - &self + rhs - } -} - -impl Add for &AdapterFieldElement { - type Output = AdapterFieldElement; - - fn add(self, rhs: AdapterFieldElement) -> Self::Output { - self + &rhs - } -} - -impl Neg for AdapterFieldElement { - type Output = AdapterFieldElement; - - fn neg(self) -> Self::Output { - AdapterFieldElement(self.0.neg()) - } -} - -impl PartialEq for AdapterFieldElement { - fn eq(&self, other: &AdapterFieldElement) -> bool { - self.0 == other.0 - } -} - -impl Eq for AdapterFieldElement {} - -impl fmt::Display for AdapterFieldElement { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - self.0.fmt(f) - } -} - -impl From for AdapterFieldElement { - fn from(value: u8) -> Self { - Self::from(value as u64) - } -} - -impl From for AdapterFieldElement { - fn from(value: u16) -> Self { - Self::from(value as u64) - } -} - -impl From for AdapterFieldElement { - fn from(value: u32) -> Self { - Self::from(value as u64) - } -} - -impl From for AdapterFieldElement { - fn from(value: u64) -> Self { - AdapterFieldElement(FieldElement::new(Stark252PrimeField::from_u64(value))) - } -} - -impl From for AdapterFieldElement { - fn from(_value: u128) -> Self { - todo!() - } -} - -impl DivAssign for AdapterFieldElement { - #[allow(clippy::suspicious_op_assign_impl)] - fn div_assign(&mut self, rhs: AdapterFieldElement) { - *self *= rhs.inv(); - } -} - -impl MulAssign for AdapterFieldElement { - fn mul_assign(&mut self, rhs: AdapterFieldElement) { - *self = *self * rhs; - } -} -impl SubAssign for AdapterFieldElement { - fn sub_assign(&mut self, rhs: AdapterFieldElement) { - *self = *self - rhs; - } -} - -impl AddAssign for AdapterFieldElement { - fn add_assign(&mut self, rhs: AdapterFieldElement) { - *self = *self + rhs; - } -} - -// Extension fields are still not needed. Examples on how to implement these can be found in -// https://github.com/facebook/winterfell/blob/08f9d9da3ee33b596d8c34ef8a549fdca1cda3ff/math/src/field/f62/mod.rs#L388 - -impl ExtensibleField<2> for AdapterFieldElement { - #[inline(always)] - fn mul(_a: [Self; 2], _b: [Self; 2]) -> [Self; 2] { - todo!() - } - - #[inline(always)] - fn mul_base(_a: [Self; 2], _b: Self) -> [Self; 2] { - todo!() - } - - #[inline(always)] - fn frobenius(_x: [Self; 2]) -> [Self; 2] { - todo!() - } -} - -impl ExtensibleField<3> for AdapterFieldElement { - #[inline(always)] - fn mul(_a: [Self; 3], _b: [Self; 3]) -> [Self; 3] { - todo!() - } - - #[inline(always)] - fn mul_base(_a: [Self; 3], _b: Self) -> [Self; 3] { - todo!() - } - - #[inline(always)] - fn frobenius(_x: [Self; 3]) -> [Self; 3] { - todo!() - } -} - -/* - Many of the following traits are required by Winterfell, but these - are not needed for the adapter to work. E.g.: the AIR adapter only needs - to compute the main and auxiliary transitions, and be able to construct - the auxiliary RAP trace. Serializing or sampling random elements is not - needed, because these are already covered by the lambdaworks field element. -*/ -impl Deserializable for AdapterFieldElement { - fn read_from( - _source: &mut R, - ) -> Result { - todo!() - } -} - -impl Serializable for AdapterFieldElement { - fn write_into(&self, _target: &mut W) { - todo!() - } -} - -impl Randomizable for AdapterFieldElement { - const VALUE_SIZE: usize = 8; - - fn from_random_bytes(_source: &[u8]) -> Option { - todo!() - } -} - -impl AsBytes for AdapterFieldElement { - fn as_bytes(&self) -> &[u8] { - todo!() - } -} - -impl<'a> TryFrom<&'a [u8]> for AdapterFieldElement { - type Error = DeserializationError; - - fn try_from(_value: &'a [u8]) -> Result { - todo!() - } -} diff --git a/crates/provers/winterfell_adapter/src/field_element/mod.rs b/crates/provers/winterfell_adapter/src/field_element/mod.rs deleted file mode 100644 index 248fcbe8a..000000000 --- a/crates/provers/winterfell_adapter/src/field_element/mod.rs +++ /dev/null @@ -1,3 +0,0 @@ -#[allow(clippy::op_ref)] -pub mod element; -pub mod positive_integer; diff --git a/crates/provers/winterfell_adapter/src/field_element/positive_integer.rs b/crates/provers/winterfell_adapter/src/field_element/positive_integer.rs deleted file mode 100644 index 8ec3579ac..000000000 --- a/crates/provers/winterfell_adapter/src/field_element/positive_integer.rs +++ /dev/null @@ -1,92 +0,0 @@ -use lambdaworks_math::{traits::ByteConversion, unsigned_integer::element::U256}; -use std::{ - cmp::Ordering, - ops::{BitAnd, Shl, Shr, ShrAssign}, -}; - -#[derive(Debug, Copy, Clone, PartialEq, Eq)] -pub struct AdapterPositiveInteger(pub U256); - -impl AdapterPositiveInteger { - pub const fn from_hex_unchecked(hex: &str) -> AdapterPositiveInteger { - AdapterPositiveInteger(U256::from_hex_unchecked(hex)) - } -} - -impl BitAnd for AdapterPositiveInteger { - type Output = Self; - - #[inline(always)] - fn bitand(self, rhs: Self) -> Self::Output { - AdapterPositiveInteger(self.0.bitand(rhs.0)) - } -} - -impl PartialOrd for AdapterPositiveInteger { - fn partial_cmp(&self, other: &Self) -> Option { - self.0.partial_cmp(&other.0) - } -} - -impl From for AdapterPositiveInteger { - fn from(value: u64) -> Self { - Self(U256::from(value)) - } -} -impl From for AdapterPositiveInteger { - fn from(value: u32) -> Self { - Self::from(value as u64) - } -} - -impl Shr for AdapterPositiveInteger { - type Output = AdapterPositiveInteger; - - fn shr(self, rhs: u32) -> Self::Output { - AdapterPositiveInteger(self.0 >> rhs as usize) - } -} - -impl Shl for AdapterPositiveInteger { - type Output = AdapterPositiveInteger; - - fn shl(self, rhs: u32) -> Self::Output { - AdapterPositiveInteger(self.0 << rhs as usize) - } -} - -impl ShrAssign for AdapterPositiveInteger { - fn shr_assign(&mut self, rhs: Self) { - if rhs >= AdapterPositiveInteger::from(256u64) { - *self = Self::from(0u64); - } else { - *self = *self >> rhs.0.limbs[3] as u32; - } - } -} - -impl ByteConversion for AdapterPositiveInteger { - fn to_bytes_be(&self) -> Vec { - U256::to_bytes_be(&self.0) - } - - fn to_bytes_le(&self) -> Vec { - U256::to_bytes_le(&self.0) - } - - fn from_bytes_be(bytes: &[u8]) -> Result - where - Self: Sized, - { - let u = U256::from_bytes_be(bytes)?; - Ok(AdapterPositiveInteger(u)) - } - - fn from_bytes_le(bytes: &[u8]) -> Result - where - Self: Sized, - { - let u = U256::from_bytes_le(bytes)?; - Ok(AdapterPositiveInteger(u)) - } -} diff --git a/crates/provers/winterfell_adapter/src/lib.rs b/crates/provers/winterfell_adapter/src/lib.rs deleted file mode 100644 index a268004ef..000000000 --- a/crates/provers/winterfell_adapter/src/lib.rs +++ /dev/null @@ -1,4 +0,0 @@ -pub mod adapter; -pub mod examples; -pub mod field_element; -pub mod utils; diff --git a/crates/provers/winterfell_adapter/src/utils.rs b/crates/provers/winterfell_adapter/src/utils.rs deleted file mode 100644 index 12591cd7c..000000000 --- a/crates/provers/winterfell_adapter/src/utils.rs +++ /dev/null @@ -1,25 +0,0 @@ -use lambdaworks_math::field::traits::IsField; -use stark_platinum_prover::fri::FieldElement; - -pub fn vec_lambda2winter + Copy>(input: &[FieldElement]) -> Vec { - input.iter().map(|&e| *e.value()).collect() -} - -pub fn vec_winter2lambda + Copy>(input: &[FE]) -> Vec> { - input - .iter() - .map(|&e| FieldElement::::const_from_raw(e)) - .collect() -} - -pub fn matrix_lambda2winter + Copy>( - input: &[Vec>], -) -> Vec> { - input.iter().map(|v| vec_lambda2winter(v)).collect() -} - -pub fn matrix_winter2lambda + Copy>( - input: &[Vec], -) -> Vec>> { - input.iter().map(|v| vec_winter2lambda(v)).collect() -} diff --git a/css/chrome.css b/css/chrome.css new file mode 100644 index 000000000..21c08b930 --- /dev/null +++ b/css/chrome.css @@ -0,0 +1,495 @@ +/* CSS for UI elements (a.k.a. chrome) */ + +@import 'variables.css'; + +::-webkit-scrollbar { + background: var(--bg); +} +::-webkit-scrollbar-thumb { + background: var(--scrollbar); +} +html { + scrollbar-color: var(--scrollbar) var(--bg); +} +#searchresults a, +.content a:link, +a:visited, +a > .hljs { + color: var(--links); +} + +/* Menu Bar */ + +#menu-bar, +#menu-bar-hover-placeholder { + z-index: 101; + margin: auto calc(0px - var(--page-padding)); +} +#menu-bar { + position: relative; + display: flex; + flex-wrap: wrap; + background-color: var(--bg); + border-bottom-color: var(--bg); + border-bottom-width: 1px; + border-bottom-style: solid; +} +#menu-bar.sticky, +.js #menu-bar-hover-placeholder:hover + #menu-bar, +.js #menu-bar:hover, +.js.sidebar-visible #menu-bar { + position: -webkit-sticky; + position: sticky; + top: 0 !important; +} +#menu-bar-hover-placeholder { + position: sticky; + position: -webkit-sticky; + top: 0; + height: var(--menu-bar-height); +} +#menu-bar.bordered { + border-bottom-color: var(--table-border-color); +} +#menu-bar i, #menu-bar .icon-button { + position: relative; + padding: 0 8px; + z-index: 10; + line-height: var(--menu-bar-height); + cursor: pointer; + transition: color 0.5s; +} +@media only screen and (max-width: 420px) { + #menu-bar i, #menu-bar .icon-button { + padding: 0 5px; + } +} + +.icon-button { + border: none; + background: none; + padding: 0; + color: inherit; +} +.icon-button i { + margin: 0; +} + +.right-buttons { + margin: 0 15px; +} +.right-buttons a { + text-decoration: none; +} + +.left-buttons { + display: flex; + margin: 0 5px; +} +.no-js .left-buttons { + display: none; +} + +.menu-title { + display: inline-block; + font-weight: 200; + font-size: 2.4rem; + line-height: var(--menu-bar-height); + text-align: center; + margin: 0; + flex: 1; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} +.js .menu-title { + cursor: pointer; +} + +.menu-bar, +.menu-bar:visited, +.nav-chapters, +.nav-chapters:visited, +.mobile-nav-chapters, +.mobile-nav-chapters:visited, +.menu-bar .icon-button, +.menu-bar a i { + color: var(--icons); +} + +.menu-bar i:hover, +.menu-bar .icon-button:hover, +.nav-chapters:hover, +.mobile-nav-chapters i:hover { + color: var(--icons-hover); +} + +/* Nav Icons */ + +.nav-chapters { + font-size: 2.5em; + text-align: center; + text-decoration: none; + + position: fixed; + top: 0; + bottom: 0; + margin: 0; + max-width: 150px; + min-width: 90px; + + display: flex; + justify-content: center; + align-content: center; + flex-direction: column; + + transition: color 0.5s, background-color 0.5s; +} + +.nav-chapters:hover { + text-decoration: none; + background-color: var(--theme-hover); + transition: background-color 0.15s, color 0.15s; +} + +.nav-wrapper { + margin-top: 50px; + display: none; +} + +.mobile-nav-chapters { + font-size: 2.5em; + text-align: center; + text-decoration: none; + width: 90px; + border-radius: 5px; + background-color: var(--sidebar-bg); +} + +.previous { + float: left; +} + +.next { + float: right; + right: var(--page-padding); +} + +@media only screen and (max-width: 1080px) { + .nav-wide-wrapper { display: none; } + .nav-wrapper { display: block; } +} + +@media only screen and (max-width: 1380px) { + .sidebar-visible .nav-wide-wrapper { display: none; } + .sidebar-visible .nav-wrapper { display: block; } +} + +/* Inline code */ + +:not(pre) > .hljs { + display: inline; + padding: 0.1em 0.3em; + border-radius: 3px; +} + +:not(pre):not(a) > .hljs { + color: var(--inline-code-color); + overflow-x: initial; +} + +a:hover > .hljs { + text-decoration: underline; +} + +pre { + position: relative; +} +pre > .buttons { + position: absolute; + z-index: 100; + right: 5px; + top: 5px; + + color: var(--sidebar-fg); + cursor: pointer; +} +pre > .buttons :hover { + color: var(--sidebar-active); +} +pre > .buttons i { + margin-left: 8px; +} +pre > .buttons button { + color: inherit; + background: transparent; + border: none; + cursor: inherit; +} +pre > .result { + margin-top: 10px; +} + +/* Search */ + +#searchresults a { + text-decoration: none; +} + +mark { + border-radius: 2px; + padding: 0 3px 1px 3px; + margin: 0 -3px -1px -3px; + background-color: var(--search-mark-bg); + transition: background-color 300ms linear; + cursor: pointer; +} + +mark.fade-out { + background-color: rgba(0,0,0,0) !important; + cursor: auto; +} + +.searchbar-outer { + margin-left: auto; + margin-right: auto; + max-width: var(--content-max-width); +} + +#searchbar { + width: 100%; + margin: 5px auto 0px auto; + padding: 10px 16px; + transition: box-shadow 300ms ease-in-out; + border: 1px solid var(--searchbar-border-color); + border-radius: 3px; + background-color: var(--searchbar-bg); + color: var(--searchbar-fg); +} +#searchbar:focus, +#searchbar.active { + box-shadow: 0 0 3px var(--searchbar-shadow-color); +} + +.searchresults-header { + font-weight: bold; + font-size: 1em; + padding: 18px 0 0 5px; + color: var(--searchresults-header-fg); +} + +.searchresults-outer { + margin-left: auto; + margin-right: auto; + max-width: var(--content-max-width); + border-bottom: 1px dashed var(--searchresults-border-color); +} + +ul#searchresults { + list-style: none; + padding-left: 20px; +} +ul#searchresults li { + margin: 10px 0px; + padding: 2px; + border-radius: 2px; +} +ul#searchresults li.focus { + background-color: var(--searchresults-li-bg); +} +ul#searchresults span.teaser { + display: block; + clear: both; + margin: 5px 0 0 20px; + font-size: 0.8em; +} +ul#searchresults span.teaser em { + font-weight: bold; + font-style: normal; +} + +/* Sidebar */ + +.sidebar { + position: fixed; + left: 0; + top: 0; + bottom: 0; + width: var(--sidebar-width); + font-size: 0.875em; + box-sizing: border-box; + -webkit-overflow-scrolling: touch; + overscroll-behavior-y: contain; + background-color: var(--sidebar-bg); + color: var(--sidebar-fg); +} +.sidebar-resizing { + -moz-user-select: none; + -webkit-user-select: none; + -ms-user-select: none; + user-select: none; +} +.js:not(.sidebar-resizing) .sidebar { + transition: transform 0.3s; /* Animation: slide away */ +} +.sidebar code { + line-height: 2em; +} +.sidebar .sidebar-scrollbox { + overflow-y: auto; + position: absolute; + top: 0; + bottom: 0; + left: 0; + right: 0; + padding: 10px 10px; +} +.sidebar .sidebar-resize-handle { + position: absolute; + cursor: col-resize; + width: 0; + right: 0; + top: 0; + bottom: 0; +} +.js .sidebar .sidebar-resize-handle { + cursor: col-resize; + width: 5px; +} +.sidebar-hidden .sidebar { + transform: translateX(calc(0px - var(--sidebar-width))); +} +.sidebar::-webkit-scrollbar { + background: var(--sidebar-bg); +} +.sidebar::-webkit-scrollbar-thumb { + background: var(--scrollbar); +} + +.sidebar-visible .page-wrapper { + transform: translateX(var(--sidebar-width)); +} +@media only screen and (min-width: 620px) { + .sidebar-visible .page-wrapper { + transform: none; + margin-left: var(--sidebar-width); + } +} + +.chapter { + list-style: none outside none; + padding-left: 0; + line-height: 2.2em; +} + +.chapter ol { + width: 100%; +} + +.chapter li { + display: flex; + color: var(--sidebar-non-existant); +} +.chapter li a { + display: block; + padding: 0; + text-decoration: none; + color: var(--sidebar-fg); +} + +.chapter li a:hover { + color: var(--sidebar-active); +} + +.chapter li a.active { + color: var(--sidebar-active); +} + +.chapter li > a.toggle { + cursor: pointer; + display: block; + margin-left: auto; + padding: 0 10px; + user-select: none; + opacity: 0.68; +} + +.chapter li > a.toggle div { + transition: transform 0.5s; +} + +/* collapse the section */ +.chapter li:not(.expanded) + li > ol { + display: none; +} + +.chapter li.chapter-item { + line-height: 1.5em; + margin-top: 0.6em; +} + +.chapter li.expanded > a.toggle div { + transform: rotate(90deg); +} + +.spacer { + width: 100%; + height: 3px; + margin: 5px 0px; +} +.chapter .spacer { + background-color: var(--sidebar-spacer); +} + +@media (-moz-touch-enabled: 1), (pointer: coarse) { + .chapter li a { padding: 5px 0; } + .spacer { margin: 10px 0; } +} + +.section { + list-style: none outside none; + padding-left: 20px; + line-height: 1.9em; +} + +/* Theme Menu Popup */ + +.theme-popup { + position: absolute; + left: 10px; + top: var(--menu-bar-height); + z-index: 1000; + border-radius: 4px; + font-size: 0.7em; + color: var(--fg); + background: var(--theme-popup-bg); + border: 1px solid var(--theme-popup-border); + margin: 0; + padding: 0; + list-style: none; + display: none; +} +.theme-popup .default { + color: var(--icons); +} +.theme-popup .theme { + width: 100%; + border: 0; + margin: 0; + padding: 2px 10px; + line-height: 25px; + white-space: nowrap; + text-align: left; + cursor: pointer; + color: inherit; + background: inherit; + font-size: inherit; +} +.theme-popup .theme:hover { + background-color: var(--theme-hover); +} +.theme-popup .theme:hover:first-child, +.theme-popup .theme:hover:last-child { + border-top-left-radius: inherit; + border-top-right-radius: inherit; +} diff --git a/css/general.css b/css/general.css new file mode 100644 index 000000000..2cf347f92 --- /dev/null +++ b/css/general.css @@ -0,0 +1,177 @@ +/* Base styles and content styles */ + +@import 'variables.css'; + +:root { + /* Browser default font-size is 16px, this way 1 rem = 10px */ + font-size: 62.5%; +} + +html { + font-family: "Open Sans", sans-serif; + color: var(--fg); + background-color: var(--bg); + text-size-adjust: none; +} + +body { + margin: 0; + font-size: 1.6rem; + overflow-x: hidden; +} + +code { + font-family: "Source Code Pro", Consolas, "Ubuntu Mono", Menlo, "DejaVu Sans Mono", monospace, monospace !important; + font-size: 0.875em; /* please adjust the ace font size accordingly in editor.js */ +} + +/* Don't change font size in headers. */ +h1 code, h2 code, h3 code, h4 code, h5 code, h6 code { + font-size: unset; +} + +.left { float: left; } +.right { float: right; } +.boring { opacity: 0.6; } +.hide-boring .boring { display: none; } +.hidden { display: none !important; } + +h2, h3 { margin-top: 2.5em; } +h4, h5 { margin-top: 2em; } + +.header + .header h3, +.header + .header h4, +.header + .header h5 { + margin-top: 1em; +} + +h1:target::before, +h2:target::before, +h3:target::before, +h4:target::before, +h5:target::before, +h6:target::before { + display: inline-block; + content: "»"; + margin-left: -30px; + width: 30px; +} + +/* This is broken on Safari as of version 14, but is fixed + in Safari Technology Preview 117 which I think will be Safari 14.2. + https://bugs.webkit.org/show_bug.cgi?id=218076 +*/ +:target { + scroll-margin-top: calc(var(--menu-bar-height) + 0.5em); +} + +.page { + outline: 0; + padding: 0 var(--page-padding); + margin-top: calc(0px - var(--menu-bar-height)); /* Compensate for the #menu-bar-hover-placeholder */ +} +.page-wrapper { + box-sizing: border-box; +} +.js:not(.sidebar-resizing) .page-wrapper { + transition: margin-left 0.3s ease, transform 0.3s ease; /* Animation: slide away */ +} + +.content { + overflow-y: auto; + padding: 0 15px; + padding-bottom: 50px; +} +.content main { + margin-left: auto; + margin-right: auto; + max-width: var(--content-max-width); +} +.content p { line-height: 1.45em; } +.content ol { line-height: 1.45em; } +.content ul { line-height: 1.45em; } +.content a { text-decoration: none; } +.content a:hover { text-decoration: underline; } +.content img, .content video { max-width: 100%; } +.content .header:link, +.content .header:visited { + color: var(--fg); +} +.content .header:link, +.content .header:visited:hover { + text-decoration: none; +} + +table { + margin: 0 auto; + border-collapse: collapse; +} +table td { + padding: 3px 20px; + border: 1px var(--table-border-color) solid; +} +table thead { + background: var(--table-header-bg); +} +table thead td { + font-weight: 700; + border: none; +} +table thead th { + padding: 3px 20px; +} +table thead tr { + border: 1px var(--table-header-bg) solid; +} +/* Alternate background colors for rows */ +table tbody tr:nth-child(2n) { + background: var(--table-alternate-bg); +} + + +blockquote { + margin: 20px 0; + padding: 0 20px; + color: var(--fg); + background-color: var(--quote-bg); + border-top: .1em solid var(--quote-border); + border-bottom: .1em solid var(--quote-border); +} + + +:not(.footnote-definition) + .footnote-definition, +.footnote-definition + :not(.footnote-definition) { + margin-top: 2em; +} +.footnote-definition { + font-size: 0.9em; + margin: 0.5em 0; +} +.footnote-definition p { + display: inline; +} + +.tooltiptext { + position: absolute; + visibility: hidden; + color: #fff; + background-color: #333; + transform: translateX(-50%); /* Center by moving tooltip 50% of its width left */ + left: -8px; /* Half of the width of the icon */ + top: -35px; + font-size: 0.8em; + text-align: center; + border-radius: 6px; + padding: 5px 8px; + margin: 5px; + z-index: 1000; +} +.tooltipped .tooltiptext { + visibility: visible; +} + +.chapter li.part-title { + color: var(--sidebar-fg); + margin: 5px 0px; + font-weight: bold; +} diff --git a/css/print.css b/css/print.css new file mode 100644 index 000000000..5e690f755 --- /dev/null +++ b/css/print.css @@ -0,0 +1,54 @@ + +#sidebar, +#menu-bar, +.nav-chapters, +.mobile-nav-chapters { + display: none; +} + +#page-wrapper.page-wrapper { + transform: none; + margin-left: 0px; + overflow-y: initial; +} + +#content { + max-width: none; + margin: 0; + padding: 0; +} + +.page { + overflow-y: initial; +} + +code { + background-color: #666666; + border-radius: 5px; + + /* Force background to be printed in Chrome */ + -webkit-print-color-adjust: exact; +} + +pre > .buttons { + z-index: 2; +} + +a, a:visited, a:active, a:hover { + color: #4183c4; + text-decoration: none; +} + +h1, h2, h3, h4, h5, h6 { + page-break-inside: avoid; + page-break-after: avoid; +} + +pre, code { + page-break-inside: avoid; + white-space: pre-wrap; +} + +.fa { + display: none !important; +} diff --git a/css/variables.css b/css/variables.css new file mode 100644 index 000000000..9ff64d6b3 --- /dev/null +++ b/css/variables.css @@ -0,0 +1,253 @@ + +/* Globals */ + +:root { + --sidebar-width: 300px; + --page-padding: 15px; + --content-max-width: 750px; + --menu-bar-height: 50px; +} + +/* Themes */ + +.ayu { + --bg: hsl(210, 25%, 8%); + --fg: #c5c5c5; + + --sidebar-bg: #14191f; + --sidebar-fg: #c8c9db; + --sidebar-non-existant: #5c6773; + --sidebar-active: #ffb454; + --sidebar-spacer: #2d334f; + + --scrollbar: var(--sidebar-fg); + + --icons: #737480; + --icons-hover: #b7b9cc; + + --links: #0096cf; + + --inline-code-color: #ffb454; + + --theme-popup-bg: #14191f; + --theme-popup-border: #5c6773; + --theme-hover: #191f26; + + --quote-bg: hsl(226, 15%, 17%); + --quote-border: hsl(226, 15%, 22%); + + --table-border-color: hsl(210, 25%, 13%); + --table-header-bg: hsl(210, 25%, 28%); + --table-alternate-bg: hsl(210, 25%, 11%); + + --searchbar-border-color: #848484; + --searchbar-bg: #424242; + --searchbar-fg: #fff; + --searchbar-shadow-color: #d4c89f; + --searchresults-header-fg: #666; + --searchresults-border-color: #888; + --searchresults-li-bg: #252932; + --search-mark-bg: #e3b171; +} + +.coal { + --bg: hsl(200, 7%, 8%); + --fg: #98a3ad; + + --sidebar-bg: #292c2f; + --sidebar-fg: #a1adb8; + --sidebar-non-existant: #505254; + --sidebar-active: #3473ad; + --sidebar-spacer: #393939; + + --scrollbar: var(--sidebar-fg); + + --icons: #43484d; + --icons-hover: #b3c0cc; + + --links: #2b79a2; + + --inline-code-color: #c5c8c6;; + + --theme-popup-bg: #141617; + --theme-popup-border: #43484d; + --theme-hover: #1f2124; + + --quote-bg: hsl(234, 21%, 18%); + --quote-border: hsl(234, 21%, 23%); + + --table-border-color: hsl(200, 7%, 13%); + --table-header-bg: hsl(200, 7%, 28%); + --table-alternate-bg: hsl(200, 7%, 11%); + + --searchbar-border-color: #aaa; + --searchbar-bg: #b7b7b7; + --searchbar-fg: #000; + --searchbar-shadow-color: #aaa; + --searchresults-header-fg: #666; + --searchresults-border-color: #98a3ad; + --searchresults-li-bg: #2b2b2f; + --search-mark-bg: #355c7d; +} + +.light { + --bg: hsl(0, 0%, 100%); + --fg: hsl(0, 0%, 0%); + + --sidebar-bg: #fafafa; + --sidebar-fg: hsl(0, 0%, 0%); + --sidebar-non-existant: #aaaaaa; + --sidebar-active: #1f1fff; + --sidebar-spacer: #f4f4f4; + + --scrollbar: #8F8F8F; + + --icons: #747474; + --icons-hover: #000000; + + --links: #20609f; + + --inline-code-color: #301900; + + --theme-popup-bg: #fafafa; + --theme-popup-border: #cccccc; + --theme-hover: #e6e6e6; + + --quote-bg: hsl(197, 37%, 96%); + --quote-border: hsl(197, 37%, 91%); + + --table-border-color: hsl(0, 0%, 95%); + --table-header-bg: hsl(0, 0%, 80%); + --table-alternate-bg: hsl(0, 0%, 97%); + + --searchbar-border-color: #aaa; + --searchbar-bg: #fafafa; + --searchbar-fg: #000; + --searchbar-shadow-color: #aaa; + --searchresults-header-fg: #666; + --searchresults-border-color: #888; + --searchresults-li-bg: #e4f2fe; + --search-mark-bg: #a2cff5; +} + +.navy { + --bg: hsl(226, 23%, 11%); + --fg: #bcbdd0; + + --sidebar-bg: #282d3f; + --sidebar-fg: #c8c9db; + --sidebar-non-existant: #505274; + --sidebar-active: #2b79a2; + --sidebar-spacer: #2d334f; + + --scrollbar: var(--sidebar-fg); + + --icons: #737480; + --icons-hover: #b7b9cc; + + --links: #2b79a2; + + --inline-code-color: #c5c8c6;; + + --theme-popup-bg: #161923; + --theme-popup-border: #737480; + --theme-hover: #282e40; + + --quote-bg: hsl(226, 15%, 17%); + --quote-border: hsl(226, 15%, 22%); + + --table-border-color: hsl(226, 23%, 16%); + --table-header-bg: hsl(226, 23%, 31%); + --table-alternate-bg: hsl(226, 23%, 14%); + + --searchbar-border-color: #aaa; + --searchbar-bg: #aeaec6; + --searchbar-fg: #000; + --searchbar-shadow-color: #aaa; + --searchresults-header-fg: #5f5f71; + --searchresults-border-color: #5c5c68; + --searchresults-li-bg: #242430; + --search-mark-bg: #a2cff5; +} + +.rust { + --bg: hsl(60, 9%, 87%); + --fg: #262625; + + --sidebar-bg: #3b2e2a; + --sidebar-fg: #c8c9db; + --sidebar-non-existant: #505254; + --sidebar-active: #e69f67; + --sidebar-spacer: #45373a; + + --scrollbar: var(--sidebar-fg); + + --icons: #737480; + --icons-hover: #262625; + + --links: #2b79a2; + + --inline-code-color: #6e6b5e; + + --theme-popup-bg: #e1e1db; + --theme-popup-border: #b38f6b; + --theme-hover: #99908a; + + --quote-bg: hsl(60, 5%, 75%); + --quote-border: hsl(60, 5%, 70%); + + --table-border-color: hsl(60, 9%, 82%); + --table-header-bg: #b3a497; + --table-alternate-bg: hsl(60, 9%, 84%); + + --searchbar-border-color: #aaa; + --searchbar-bg: #fafafa; + --searchbar-fg: #000; + --searchbar-shadow-color: #aaa; + --searchresults-header-fg: #666; + --searchresults-border-color: #888; + --searchresults-li-bg: #dec2a2; + --search-mark-bg: #e69f67; +} + +@media (prefers-color-scheme: dark) { + .light.no-js { + --bg: hsl(200, 7%, 8%); + --fg: #98a3ad; + + --sidebar-bg: #292c2f; + --sidebar-fg: #a1adb8; + --sidebar-non-existant: #505254; + --sidebar-active: #3473ad; + --sidebar-spacer: #393939; + + --scrollbar: var(--sidebar-fg); + + --icons: #43484d; + --icons-hover: #b3c0cc; + + --links: #2b79a2; + + --inline-code-color: #c5c8c6;; + + --theme-popup-bg: #141617; + --theme-popup-border: #43484d; + --theme-hover: #1f2124; + + --quote-bg: hsl(234, 21%, 18%); + --quote-border: hsl(234, 21%, 23%); + + --table-border-color: hsl(200, 7%, 13%); + --table-header-bg: hsl(200, 7%, 28%); + --table-alternate-bg: hsl(200, 7%, 11%); + + --searchbar-border-color: #aaa; + --searchbar-bg: #b7b7b7; + --searchbar-fg: #000; + --searchbar-shadow-color: #aaa; + --searchresults-header-fg: #666; + --searchresults-border-color: #98a3ad; + --searchresults-li-bg: #2b2b2f; + --search-mark-bg: #355c7d; + } +} diff --git a/default.nix b/default.nix deleted file mode 100644 index 086568ace..000000000 --- a/default.nix +++ /dev/null @@ -1,7 +0,0 @@ -{ pkgs ? import {} }: -pkgs.mkShell { - name = "cuda-env-shell"; - buildInputs = with pkgs; [ - git curl cargo rustc - ]; -} diff --git a/docs/.gitignore b/docs/.gitignore deleted file mode 100644 index 7585238ef..000000000 --- a/docs/.gitignore +++ /dev/null @@ -1 +0,0 @@ -book diff --git a/docs/book.toml b/docs/book.toml deleted file mode 100644 index 16bede951..000000000 --- a/docs/book.toml +++ /dev/null @@ -1,8 +0,0 @@ -[book] -authors = ["Javier Chatruc", "Estéfano Bargas", "Mauro Toscano", "Diego Kingston", "Sergio Chouhy", "Agustín Garassino", "Mariano Nicolini"] -language = "en" -multilingual = false -src = "src" -title = "docs" - -[preprocessor.katex] diff --git a/docs/src/SUMMARY.md b/docs/src/SUMMARY.md deleted file mode 100644 index 1ab0c5c06..000000000 --- a/docs/src/SUMMARY.md +++ /dev/null @@ -1,30 +0,0 @@ -# Lambdaworks Documentation - -- [Introduction](./introduction.md) - -- [FFT Library]() - - [Benchmarks](./fft/benchmarks.md) - -- [Plonk]() - - [Recap](./plonk/recap.md) - - [Protocol](./plonk/protocol.md) - - [Implementation](./plonk/implementation.md) - - [Circuit API](./plonk/constraint_system.md) - -- [STARKs](./starks/starks.md) - - [Recap](./starks/recap.md) - - [Protocol overview](./starks/protocol_overview.md) - - [Protocol](./starks/protocol.md) - - [Implementation](./starks/implementation.md) - - [High Level API](./starks/api.md) - - [Under the hood](./starks/under_the_hood.md) - - [Stone prover](./starks/stone_prover/introduction.md) - - [Plain layout trace description](./starks/stone_prover/trace_plain_layout.md) - -- [Cairo](./starks/cairo.md) - - [Trace Formal Description](./starks/cairo_trace_succinct.md) - - [Trace Detailed Description](./starks/cairo_trace_descriptive.md) - - [RAP](./starks/cairo_rap.md) - - [Virtual Columns](./starks/virtual_cols.md) - - [Built-ins](./starks/builtins.md) - - [CLI](./starks/cairo_cli.md) diff --git a/docs/src/fft/benchmarks.md b/docs/src/fft/benchmarks.md deleted file mode 100644 index f2c8c02e2..000000000 --- a/docs/src/fft/benchmarks.md +++ /dev/null @@ -1,23 +0,0 @@ -# FFT Benchmarks - -## Polynomial interpolation methods comparison - -Three methods of polynomial interpolation were benchmarked, with different input sizes each time: - -- **CPU Lagrange:** Finding the Lagrange polynomial of a set of random points via a naive algorithm (see `math/src/polynomial.rs:interpolate()`) -- **CPU FFT:** Finding the lowest degree polynomial that interpolates pairs of twiddle factors and Fourier coefficients (the results of applying the Fourier transform to the coefficients of a polynomial) (see `math/src/polynomial.rs:interpolate_fft()`). - - -All values of time are in milliseconds. Those cases which were greater than 30 seconds were marked respectively as they're too slow and weren't worth to be benchmarked. The input size refers to *d + 1* where *d* is the polynomial's degree (so size is amount of coefficients). - -| Input size | CPU Lagrange | CPU FFT | -|------------|--------------|-----------| -| 2^4 | 2.2 ms | 0.2 ms | -| 2^5 | 9.6 ms | 0.4 ms | -| 2^6 | 42.6 ms | 0.8 ms | -| 2^7 | 200.8 ms | 1.7 ms | -| ... | ... | .. | -| 2^21 | >30000 ms | 28745 ms | -| 2^22 | >30000 ms | >30000 ms | -| 2^23 | >30000 ms | >30000 ms | -| 2^24 | >30000 ms | >30000 ms | diff --git a/docs/src/introduction.md b/docs/src/introduction.md deleted file mode 100644 index 522db22da..000000000 --- a/docs/src/introduction.md +++ /dev/null @@ -1,9 +0,0 @@ -# Introduction - -This site hosts the main documentation for Lambdaworks as a whole. It is still a work in progress. - -## Crates - -- [lambdaworks-math](https://crates.io/crates/lambdaworks-math) -- [lambdaworks-crypto](https://crates.io/crates/lambdaworks-crypto) -- [lambdaworks-gpu](https://crates.io/crates/lambdaworks-gpu) diff --git a/docs/src/plonk/SUMMARY.md b/docs/src/plonk/SUMMARY.md deleted file mode 100644 index 150298155..000000000 --- a/docs/src/plonk/SUMMARY.md +++ /dev/null @@ -1,6 +0,0 @@ -# Lambdaworks Plonk Prover - -- [Recap](./recap.md) -- [Protocol](./protocol.md) -- [Implementation](./implementation.md) -- [Circuit API](./constraint_system.md) \ No newline at end of file diff --git a/docs/src/plonk/constraint_system.md b/docs/src/plonk/constraint_system.md deleted file mode 100644 index 9d961a615..000000000 --- a/docs/src/plonk/constraint_system.md +++ /dev/null @@ -1,103 +0,0 @@ -# Circuit API - -In this section, we'll discuss how to build your own constraint system to prove the execution of a particular program. - -## Simple Example - -Let's take the following simple program as an example. We have two public inputs: `x` and `y`. We want to prove to a verifier that we know a private input `e` such that `x * e = y`. You can achieve this by building the following constraint system: - -```rust -use lambdaworks_plonk::constraint_system::ConstraintSystem; -use lambdaworks_math::elliptic_curve::short_weierstrass::curves::bls12_381::default_types::FrField; - -fn main() { - let system = &mut ConstraintSystem::::new(); - let x = system.new_public_input(); - let y = system.new_public_input(); - let e = system.new_variable(); - - let z = system.mul(&x, &e); - - // This constraint system asserts that x * e == y - system.assert_eq(&y, &z); -} -``` - -This code creates a constraint system over the field of the BLS12381 curve. Then, it creates three variables: two public inputs `x` and `y`, and a private variable `e`. Note that every variable is private except for the public inputs. Finally, it adds the constraints that represent a multiplication and an assertion. - -Before generating proofs for this system, we need to run a setup and obtain a verifying key: - -```rust -let common = CommonPreprocessedInput::from_constraint_system(&system, &ORDER_R_MINUS_1_ROOT_UNITY); -let srs = test_srs(common.n); -let kzg = KZG::new(srs); // The commitment scheme for plonk. -let vk = setup(&common, &kzg); -``` - -Now we can generate proofs for our system. We just need to specify the public inputs and obtain a witness that is a solution for our constraint system: - -```rust -let inputs = HashMap::from([(x, FieldElement::from(4)), (e, FieldElement::from(3))]); -let assignments = system.solve(inputs).unwrap(); -let witness = Witness::new(assignments, &system); -``` - -Once you have all these ingredients, you can call the prover: - -```rust -let public_inputs = system.public_input_values(&assignments); -let prover = Prover::new(kzg.clone(), TestRandomFieldGenerator {}); -let proof = prover.prove(&witness, &public_inputs, &common, &vk); -``` - -and verify: - -```rust -let verifier = Verifier::new(kzg); -assert!(verifier.verify(&proof, &public_inputs, &common, &vk)); -``` - -## Building Complex Systems - -Some operations are common, and it makes sense to wrap the set of constraints that do these operations in a function and use it several times. Lambdaworks comes with a collection of functions to help you build your own constraint systems, such as conditionals, inverses, and hash functions. - -However, if you have an operation that does not come with Lambdaworks, you can easily extend Lambdaworks functionality. Suppose that the exponentiation operation is something common in your program. You can write the [square and multiply](https://en.wikipedia.org/wiki/Exponentiation_by_squaring) algorithm and put it inside a function: - -```rust -pub fn pow( - system: &mut ConstraintSystem, - base: Variable, - exponent: Variable, -) -> Variable { - let exponent_bits = system.new_u32(&exponent); - let mut result = system.new_constant(FieldElement::one()); - - for i in 0..32 { - if i != 0 { - result = system.mul(&result, &result); - } - let result_times_base = system.mul(&result, &base); - result = system.if_else(&exponent_bits[i], &result_times_base, &result); - } - result -} -``` - -This function can then be used to modify our simple program from the previous section. The following circuit checks that the prover knows `e` such that `pow(x, e) = y`: - -```rust -use lambdaworks_plonk::constraint_system::ConstraintSystem; -use lambdaworks_math::elliptic_curve::short_weierstrass::curves::bls12_381::default_types::FrField; - -fn main() { - let system = &mut ConstraintSystem::::new(); - let x = system.new_public_input(); - let y = system.new_public_input(); - let e = system.new_variable(); - - let z = pow(system, &x, &e); - system.assert_eq(&y, &z); -} -``` - -You can keep composing these functions in order to create more complex systems. \ No newline at end of file diff --git a/docs/src/plonk/implementation.md b/docs/src/plonk/implementation.md deleted file mode 100644 index a06aa61e3..000000000 --- a/docs/src/plonk/implementation.md +++ /dev/null @@ -1,298 +0,0 @@ -# Implementation - -In this section we discuss the implementation details of the PLONK algorithm. We use the notation and terminology of the [protocol](./protocol.md) and [recap](./recap.md) sections. - -At the moment our API supports the backend of PLONK, that is, all the setup, prove and verify algorithms. We temporarily rely on external sources for the definition of a circuit and the creation of the $Q$ and $V$ matrices, as well as the execution of it to obtain the trace matrix $T$. We mainly use gnark temporarily for that purpose. - -To generate proofs and validate them, we need to feed the algorithms with precomputed values of the $Q$, $V$ and $T$ matrices, and the primitive root of unity $\omega$. - -Let's see our API on a test circuit that provides all these values. The program in this case is the one that takes an input $x$, a private input $e$ and computes $y = xe + 5$. As in the toy example of the recap, the output of the program is added to the public inputs and the circuit actually asserts that the output is the claimed value. So more precisely, the prover will generate a proof for the statement `ASSERT(x*e+5==y)`, where both $x,y$ are public inputs. - -# Usage - -Here is the happy path. - -```rust -// This is the common preprocessed input for -// the test circuit ( ASSERT(x * e + 5 == y) ) -let common_preprocessed_input = test_common_preprocessed_input_2(); - -// Input -let x = FieldElement::from(2_u64); - -// Private input -let e = FieldElement::from(3_u64); - -let y, witness = test_witness_2(x, e); - -let srs = test_srs(common_preprocessed_input.n); -let kzg = KZG::new(srs); - -let verifying_key = setup(&common_preprocessed_input, &kzg); - -let random_generator = TestRandomFieldGenerator {}; -let prover = Prover::new(kzg.clone(), random_generator); - -let public_input = vec![x.clone(), y]; - -let proof = prover.prove( - &witness, - &public_input, - &common_preprocessed_input, - &verifying_key, -); - -let verifier = Verifier::new(kzg); -assert!(verifier.verify( - &proof, - &public_input, - &common_preprocessed_input, - &verifying_key -)); -``` - -Let's brake it down. The helper function `test_common_preprocessed_input_2()` returns an instance of the following struct for the particular test circuit: -```rust -pub struct CommonPreprocessedInput { - pub n: usize, - pub domain: Vec>, - pub omega: FieldElement, - pub k1: FieldElement, - - pub ql: Polynomial>, - pub qr: Polynomial>, - pub qo: Polynomial>, - pub qm: Polynomial>, - pub qc: Polynomial>, - - pub s1: Polynomial>, - pub s2: Polynomial>, - pub s3: Polynomial>, - - pub s1_lagrange: Vec>, - pub s2_lagrange: Vec>, - pub s3_lagrange: Vec>, -} -``` -Apart from the eight polynomials in the canonical basis, we store also here the number of constraints $n$, the domain $H$, the primitive $n$-th of unity $\omega$ and the element $k_1$. The element $k_2$ will be $k_1^2$. For convenience, we also store the polynomials $S_{\sigma i}$ in Lagrange form. - -The following lines define the particular values of the program input $x$ and the private input $e$. -```rust -// Input -let x = FieldElement::from(2_u64); - -// Private input -let e = FieldElement::from(3_u64); -let y, witness = test_witness_2(x, e); -``` - The function `test_witness_2(x, e)` returns an instance of the following struct, that holds the polynomials that interpolate the columns $A, B, C$ of the trace matrix $T$. -```rust -pub struct Witness { - pub a: Vec>, - pub b: Vec>, - pub c: Vec>, -} -``` - -Next the commitment scheme KZG (Kate-Zaverucha-Goldberg) is instantiated. - -```rust -let srs = test_srs(common_preprocessed_input.n); -let kzg = KZG::new(srs); -``` -The `setup` function performs the setup phase. It only needs the common preprocessed input and the commitment scheme. - -```rust -let verifying_key = setup(&common_preprocessed_input, &kzg); -``` - -It outputs an instance of the struct `VerificationKey`: - -```rust -pub struct VerificationKey { - pub qm_1: G1Point, - pub ql_1: G1Point, - pub qr_1: G1Point, - pub qo_1: G1Point, - pub qc_1: G1Point, - - pub s1_1: G1Point, - pub s2_1: G1Point, - pub s3_1: G1Point, -} -``` - -It stores the commitments of the eight polynomials of the common preprocessed input. The suffix `_1` means it is a commitment. It comes from the notation $[f]_1$, where $f$ is a polynomial. - -Then a prover is instantiated -```rust -let random_generator = TestRandomFieldGenerator {}; -let prover = Prover::new(kzg.clone(), random_generator); -``` -The prover is an instance of the struct `Prover`: -```rust -pub struct Prover -where - F: IsField, - CS: IsCommitmentScheme, - R: IsRandomFieldElementGenerator - { - commitment_scheme: CS, - random_generator: R, - phantom: PhantomData, -} -``` - -It stores an instance of a commitment scheme and a random field element generator needed for blinding polynomials. - -Then the public input is defined. As we mentioned in the recap, the public input contains the output of the program. -```rust -let public_input = vec![x.clone(), y]; -``` - -We then generate a proof using the prover's method `prove` -```rust -let proof = prover.prove( - &witness, - &public_input, - &common_preprocessed_input, - &verifying_key, -); -``` - -The output is an instance of the struct `Proof`. - -```rust -pub struct Proof> { - // Round 1. - /// Commitment to the wire polynomial `a(x)` - pub a_1: CS::Commitment, - /// Commitment to the wire polynomial `b(x)` - pub b_1: CS::Commitment, - /// Commitment to the wire polynomial `c(x)` - pub c_1: CS::Commitment, - - // Round 2. - /// Commitment to the copy constraints polynomial `z(x)` - pub z_1: CS::Commitment, - - // Round 3. - /// Commitment to the low part of the quotient polynomial t(X) - pub t_lo_1: CS::Commitment, - /// Commitment to the middle part of the quotient polynomial t(X) - pub t_mid_1: CS::Commitment, - /// Commitment to the high part of the quotient polynomial t(X) - pub t_hi_1: CS::Commitment, - - // Round 4. - /// Value of `a(ζ)`. - pub a_zeta: FieldElement, - /// Value of `b(ζ)`. - pub b_zeta: FieldElement, - /// Value of `c(ζ)`. - pub c_zeta: FieldElement, - /// Value of `S_σ1(ζ)`. - pub s1_zeta: FieldElement, - /// Value of `S_σ2(ζ)`. - pub s2_zeta: FieldElement, - /// Value of `z(ζω)`. - pub z_zeta_omega: FieldElement, - - // Round 5 - /// Value of `p_non_constant(ζ)`. - pub p_non_constant_zeta: FieldElement, - /// Value of `t(ζ)`. - pub t_zeta: FieldElement, - /// Batch opening proof for all the evaluations at ζ - pub w_zeta_1: CS::Commitment, - /// Single opening proof for `z(ζω)`. - pub w_zeta_omega_1: CS::Commitment, -} -``` - -Finally, we instantiate a verifier. -```rust -let verifier = Verifier::new(kzg); -``` - -It's an instance of `Verifier`: -```rust -struct Verifier> { - commitment_scheme: CS, - phantom: PhantomData, -} -``` - -Finally, we call the verifier's method `verify` that outputs a `bool`. -```rust -assert!(verifier.verify( - &proof, - &public_input, - &common_preprocessed_input, - &verifying_key -)); -``` - -## Padding - -All the matrices $Q, V, T, PI$ are padded with dummy rows so that their length is a power of two. To be able to interpolate their columns, we need a primitive root of unity $\omega$ of that order. Given the particular field used in our implementation, that means that the maximum possible size for a circuit is $2^{32}$. - -The entries of the dummy rows are filled in with zeroes in the $Q$, $V$ and $PI$ matrices. The $T$ matrix needs to be consistent with the $V$ matrix. Therefore it is filled with the value of the variable with index $0$. - -Some other rows in the $V$ matrix have also dummy values. These are the rows corresponding to the $B$ and $C$ columns of the public input rows. In the recap we denoted them with the empty `-` symbol. They are filled in with the same logic as the padding rows, as well as the corresponding values in the $T$ matrix. - -# Implementation details - -The implementation pretty much follows the rounds as are described in the [protocol](./protocol.md) section. There are a few details that are worth mentioning. - -## Commitment Scheme - -The commitment scheme we use is the [Kate-Zaverucha-Goldberg](https://www.iacr.org/archive/asiacrypt2010/6477178/6477178.pdf) scheme with the `BLS 12 381` curve and the ate pairing. It can be found in the `commitments` module of the `lambdaworks_crypto` package. - -The order $r$ of the cyclic subgroup is - -``` -0x73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001 -``` - -The maximum power of two that divides $r-1$ is $2^{32}$. Therefore, that is the maximum possible order for a primitive root of unity in $\mathbb{F}_r$ with order a power of two. - -## Fiat-Shamir - -### Transcript strategy - -Here we describe our implementation of the transcript used for the Fiat-Shamir heuristic. - -A `Transcript` exposes two methods: `append` and `challenge`. - -The method `append` adds a message to the transcript by updating the internal state of the hasher with the raw bytes of the message. - -The method `challenge` returns the result of the hasher using the current internal state of the hasher. It subsequently resets the hasher and updates the internal state with the last result. - -Here is an example of this process: - -1. Start a fresh transcript. -2. Call `append` and pass `message_1`. -3. Call `append` and pass `message_2`. -4. The internal state of the hasher at this point is `message_2 || message_1`. -5. Call `challenge`. The output is `Hash(message_2 || message_1)`. -6. Call `append` and pass `message_3`. -7. Call `challenge`. The output is `Hash(message_3 || Hash(message_2 || message_1))`. -8. Call `append` and pass `message_4`. - -The internal state of the hasher at the end of this exercise is `message_4 || Hash(message_3 || Hash(message_2 || message_1))` - -The underlying hasher function we use is `h=sha3`. - -### Field elements - -The result of every challenge is a $256$-bit string, which is interpreted as an integer in big-endian order. A field element is constructed out of it by taking modulo the field order. The prime field used in this implementation has a $255$-bit order. Therefore some field elements are more probable to occur than others because they have more representatives as 256-bit integers. - -### Strong Fiat-Shamir - -The first messages added to the transcript are all commitments of the polynomials of the common preprocessed input and the values of the public inputs. This prevents a known vulnerability called "weak Fiat-Shamir". -Check out the following resources to learn more about it. - -- [What can go wrong (zkdocs)](https://www.zkdocs.com/docs/zkdocs/protocol-primitives/fiat-shamir/#what-can-go-wrong) -- [How not to Prove Yourself: Pitfalls of the Fiat-Shamir Heuristic and Applications to Helios](https://eprint.iacr.org/2016/771.pdf) -- [Weak Fiat-Shamir Attacks on Modern Proof Systems](https://eprint.iacr.org/2023/691) \ No newline at end of file diff --git a/docs/src/plonk/plonk.md b/docs/src/plonk/plonk.md deleted file mode 100644 index 4bf083370..000000000 --- a/docs/src/plonk/plonk.md +++ /dev/null @@ -1,11 +0,0 @@ -# PLONK - -In this document, we present an in-depth analysis of PLONK and the specific version that has been implemented in Lambdaworks. Additionally, we provide a step-by-step guide on how to utilize our high-level API, which is designed to simplify the user experience. - -Our version of PLONK is heavily inspired by [gnark](https://github.com/ConsenSys/gnark). We would like to take this opportunity to express our gratitude to ConsenSys for generously sharing their source code with the community. - -We have written this document with the aim of making it accessible to both novice and advanced users. So, whether you're a newcomer to PLONK or an experienced user, you will find this document to be a valuable resource. - -- [Recap](./recap.md) -- [Protocol](./protocol.md) -- [Implementation](./implementation.md) \ No newline at end of file diff --git a/docs/src/plonk/protocol.md b/docs/src/plonk/protocol.md deleted file mode 100644 index 49e788e76..000000000 --- a/docs/src/plonk/protocol.md +++ /dev/null @@ -1,245 +0,0 @@ -# Protocol - -## Details and tricks - -### Polynomial commitment scheme - -A polynomial commitment scheme (PCS) is a cryptographic tool that allows one party to commit to a polynomial, and later prove properties of that polynomial. -This commitment polynomial hides the original polynomial's coefficients and can be publicly shared without revealing any information about the original polynomial. -Later, the party can use the commitment to prove certain properties of the polynomial, such as that it satisfies certain constraints or that it evaluates to a certain value at a specific point. - -In the implementation section we'll explain the inner workings of the Kate-Zaverucha-Goldberg scheme, a popular PCS chosen in Lambdaworks for PLONK. - -For the moment we only need the following about it: - -It consists of a finite group $\mathbb{G}$ and the following algorithms: -- **Commit($f$)**: This algorithm takes a polynomial $f$ and produces an element of the group $\mathbb{G}$. It is called the commitment of $f$ and is denoted by $\left[f\right]_1$. It is homomorphic in the sense that $\left[f + g\right]_1 = \left[f\right]_1 + \left[g\right]_1$. The former sum being addition of polynomials. The latter is addition in the group $\mathbb{G}$. -- **Open($f$, $\zeta$ )**: It takes a polynomial $f$ and a field element $\zeta$ and produces an element $\pi$ of the group $\mathbb{G}$. This element is called an opening proof for $f(\zeta)$. It is the proof that $f$ evaluated at $\zeta$ gives $f(\zeta)$. -- **Verify($\left[f\right]_1$, $\pi$, $\zeta$, $y$)**: It takes group elements $\left[f\right]_1$ and $\pi$, and also field elements $\zeta$ and $y$. With overwhelming probability it outputs _Accept_ if $f(z)=y$ and _Reject_ otherwise. - - -### Blindings - -As you will see in the protocol, the prover reveals the value taken by a bunch of the polynomials at a random $\zeta$. In order for the protocol to be _Honest Verifier Zero Knowledge_, these polynomials need to be _blinded_. This is a process that makes the values of these polynomials at $\zeta$ seemingly random by forcing them to be of certain degree. Here's how it works. - -Let's take for example the polynomial $a$ the prover constructs. This is the interpolation of the first column of the trace matrix $T$ at the domain $H$. -This matrix has all of the left operands of all the gates. The prover wishes to keep them secret. -Say the trace matrix $T$ has $N$ rows. $H$ is $\{1, \omega,\omega^2, \dots, \omega^{N - 1}\}$. The invariant that the prover cannot violate is that $a_{\text{blinded}}(\omega^i)$ must take the value $T_{0, i}$, for all $i$. This is what the interpolation polynomial $a$ satisfies. And is the unique such polynomial of degree at most $N - 1$ with such property. But for higher degrees, there are many such polynomials. - -The _blinding_ process takes $a$ and a desired degree $M\geq N$, and produces a new polynomial $a_{\text{blinded}}$ of degree exactly $M$. This new polynomial satisfies that $a_{\text{blinded}}(\omega^i) = a(\omega^i)$ for all $i$. But outside $H$ differs from $a$. - -This may seem hard but it's actually very simple. Let $z_H$ be the polynomial $z_H = X^N - 1$. If $M = N + k$, with $k \geq 0$, then sample random values $b_0, \dots, b_k$ and define -$$a_{\text{blinded}} := (b_0 + b_1 X + \cdots + b_k X^k )z_H + a$$ - -The reason why this does the job is that $z_H(\omega^i) = 0$ for all $i$. Therefore the added term vanishes at $H$ and leaves the values of $a$ at $H$ unchanged. - -### Linearization trick - -This is an optimization in PLONK to reduce the number of checks of the verifier. - -One of the main checks in PLONK boils down to check that $p(\zeta) = z_H(\zeta) t(\zeta)$, with $p$ some polynomial that looks like $p = a q_L + b q_R + ab q_M + \cdots$, and so on. In particular the verifier needs to get the value $p(\zeta)$ from somewhere. - -For the sake of simplicity, in this section assume $p$ is exactly $a q_L + bq_R$. Secret to the prover here are only $a, b$. The polynomials $q_L$ and $q_R$ are known also to the verifier. The verifier will already have the commitments $\left[a\right]_1, \left[b\right]_1, \left[q_L\right]_1$ and $\left[q_R\right]_1$. So the prover could send just $a( \zeta )$, $b( \zeta )$ along with their opening proofs and let the verifier compute by himself $q_L(\zeta)$ and $q_R(\zeta)$. Then with all these values the verifier could compute $p(\zeta) = a(\zeta) q_L (\zeta) + b(\zeta) q_R (\zeta)$. And also use his commitments to validate the opening proofs of $a(\zeta)$ and $b(\zeta)$. - -This has the problem that computing $q_L (\zeta)$ and $q_R (\zeta)$ is expensive. The prover can instead save the verifier this by sending also $q_L (\zeta), q_R (\zeta)$ along with opening proofs. Since the verifier will have the commitments $\left[q_L\right]_1$ and $\left[q_R\right]_1$ beforehand, he can check that the prover is not cheating and cheaply be convinced that the claimed values are actually $q_L(\zeta)$ and $q_R(\zeta)$. This is much better. It involves the check of four opening proofs and the computation of $p(\zeta)$ off the values received from the prover. But it can be further improved as follows. - -As before, the prover sends $a(\zeta), b(\zeta)$ along with their opening proofs. She constructs the polynomial $f = a(\zeta)q_L + b(\zeta)q_R$. She sends the value $f(\zeta)$ along with an opening proof of it. Notice that the value of $f(\zeta)$ is exactly $p(\zeta)$. The verifier can compute by himself $\left[f\right]_1$ as $a(\zeta)\left[q_L\right]_1 + b(\zeta)\left[q_R\right]_1$. The verifier has everything to check all three openings and get convinced that the claimed value $f(\zeta)$ is true. And this value is actually $p(\zeta)$. So this means no more work for the verifier. And the whole thing got reduced to three openings. - -This is called the linearization trick. The polynomial $f$ is called the _linearization_ of $p$. - - -## Setup - -There's a one time setup phase to compute some values common to any execution and proof of the particular circuit. Precisely, the following commitments are computed and published. -$$\left[q_L\right]_1 , \left[q_R\right]_1 , \left[q_M\right]_1 , \left[q_O\right]_1 , \left[q_C\right]_1 , \left[S_{ \sigma 1 }\right]_1 , \left[S_{ \sigma 2 }\right]_1 , \left[S_{ \sigma 3 }\right]_1$$ - -## Proving algorithm - -Next we describe the proving algorithm for a program of size $N$. That includes public inputs. Let $\omega$ be a primitive $N$-th root of unity. Let $H=\{1, \omega, \omega^2, \dots, \omega^{N - 1}\}$. Define $Z_H := X^N - 1$. - -Assume the eight polynomials of common preprocessed input are already given. - -The prover computes the trace matrix $T$ as described in the first sections. That means, with the first rows corresponding to the public inputs. It should be a $N \times 3$ matrix. - -### Round 1 - -Add to the transcript the following: -$$\left[ S_{\sigma 1 } \right]_1, \left[ S_ { \sigma 2 } \right]_1, \left[ S { \sigma 3 } \right]_1, \left[ q_L \right]_1, \left[ q_R \right]_1, \left[ q_M \right]_1, \left[ q_O \right]_1, \left[ q_C \right]_1$$ - -Compute polynomials $a',b',c'$ as the interpolation polynomials of the columns of $T$ at the domain $H$. -Sample random $b_1, b_2, b_3, b_4, b_5, b_6$ -Let - -$a := (b_1 X + b_2 )Z_H + a'$ - -$b := (b_3 X + b_4 )Z_H + b'$ - -$c := (b_5 X + b_6 )Z_H + c'$ - -Compute $\left[a\right]_1, \left[b\right]_1, \left[c\right]_1$ and add them to the transcript. - -### Round 2 - -Sample $\beta, \gamma$ from the transcript. - -Let $z_0 = 1$ and define recursively for $0\leq k < N$. - -$$ -z_{k + 1} = z_k \frac{(a_k + \beta \omega^k + \gamma) (b_k + \beta \omega^k k_1 + \gamma)(c_k + \beta \omega^k k_2 + \gamma)}{(a_k + \beta S_{ \sigma 1} (\omega^k ) + \gamma) (b_k + \beta S_{ \sigma 2} (\omega^k) + \gamma)(c_k + \beta S_{ \sigma 3} (\omega^k) + \gamma)} -$$ - -Compute the polynomial $z'$ as the interpolation polynomial at the domain $H$ of the values $(z_0, \dots, z_{ N - 1 })$. - -Sample random values $b_7, b_8, b_9$ and let $z = (b_7 X^2 + b_8 X + b_9 )Z_H + z'$. - -Compute $\left[z\right]_1$ and add it to the transcript. - -### Round 3 - -Sample $\alpha$ from the transcript. - -Let $pi$ be the interpolation of the public input matrix $PI$ at the domain $H$. - -Let - -$$ -\begin{aligned} -p_1 &= aq_L + bq_R + abq_M + cq_O + q_C + pi \\ -p_2 &= (a + \beta X + \gamma)(b + \beta k_1 X + \gamma)(c + \beta k_2 X + \gamma)z - (a + \beta S_{ \sigma 1} + \gamma)(b + \beta S_{ \sigma 2} + \gamma)(c + \beta S_{ \sigma 3} + \gamma)z(\omega X)\\ -p_3 &= (z - 1)L_1 -\end{aligned} -$$ - -and define $p = p_1 + \alpha p_2 + \alpha^2 p_3$. Compute $t$ such that $p = t Z_H$. Write $t = t_{lo}' + X^{N + 2} t_{mid}' + X^{ 2 ( N + 2 )} t_{hi}'$ with $t_{lo}', t_{mid}'$ and $t_{hi}'$ polynomials of degree at most $N + 1$. - -Sample random $b_{10}, b_{11}$ and define - -$$ -\begin{aligned} -t_{lo} &= t_{lo}' + b_{10} X^{ N + 2} \\ -t_{mid} &= t_{mid}' - b_{10} + b_{11} X^{ N + 2} \\ -t_{hi} &= t_{hi}' - b_{11} -\end{aligned} -$$ - -Compute $\left[t_{lo}\right]_1, \left[t_{mid}\right]_1,\left[t_{hi}\right]_1$ and add them to the transcript. - -### Round 4 - -Sample $\zeta$ from the transcript. - -Compute $\bar a = a(\zeta), \bar b = b(\zeta), \bar c = c(\zeta), \bar s_{\sigma 1} = S_{\sigma 1}(\zeta), \bar s_{\sigma 2} = S_{\sigma 2}(\zeta), \bar z_\omega = z(\zeta\omega)$ and add them to the transcript. - -### Round 5 - -Sample $\upsilon$ from the transcript. - -Let - -$$ -\begin{aligned} -\hat p_{nc1} &= \bar aq_L + \bar bq_R + \bar a\bar bq_M + \bar cq_O + q_C \\ -\hat p_{nc2} &=(\bar a + \beta\zeta + \gamma)(\bar b + \beta k_1\zeta + \gamma)(\bar c + \beta k_2 \zeta + \gamma)z - (\bar a + \beta \bar s_{ \sigma 1 } + \gamma)(\bar b + \beta \bar s_{ \sigma 2 } + \gamma)\beta \bar z_\omega S_{ \sigma 3 } \\ -\hat p_{nc3} &= L_1(\zeta) z -\end{aligned} -$$ - -Define - -$$ -\begin{aligned} -p_{nc} &= p_{nc1} + \alpha p_{nc2} + \alpha^2 p_{nc3} \\ -t_{\text{partial}} &= t_{lo} + \zeta^{ N + 2}t_{mid} + \zeta^{ 2 ( N + 2 )}t_{hi} -\end{aligned} -$$ - -The subscript $nc$ stands for "non-constant", as is the part of the linearization of $p$ that has non-constant factors. The subscript "partial" indicates that it is a partial evaluation of $t$ at $\zeta$. Partial meaning that only some power of $X$ ar replaced by the powers of $\zeta$. So in particular $t_{\text{partial}}(\zeta) = t(\zeta)$. - -Let $\pi_{\text{batch}}$ be the opening proof at $\zeta$ of the polynomial $f_{\text{batch}}$ defined as -$$t_{\text{partial}} +\upsilon p_{nc} + \upsilon^2 a + \upsilon^3 b + \upsilon^4 c + \upsilon^5 S_{\sigma 1} + \upsilon^6 S_{\sigma 2}$$ - -Let $\pi_{\text{single}}$ be the opening proof at $\zeta\omega$ of the polynomial $z$. - -Compute $\bar p_{nc} := p_{nc}(\zeta)$ and $\bar t = t(\zeta)$. - -### Proof - -The proof is: -$$\left[a\right]_1, \left[b\right]_1 , \left[c\right]_1, \left[z\right]_1, \left[t_{lo}\right]_1, \left[t_{mid}\right]_1, \left[t_{hi}\right]_1, \bar{a}, \bar{b}, \bar{c}, \bar{s_{ \sigma 1 }}, \bar{s_{ \sigma 2 }}, \bar{z_\omega}, \pi_{\mathrm{batch}}, \pi_{\mathrm{single}}, \bar p_{nc}, \bar t$$ - -## Verification algorithm - -### Transcript initialization - -The first step is to initialize the transcript in the same way the prover did, adding to it the following elements. -$$\left[ S_{ \sigma 1 } \right]_1, \left[ S_{ \sigma 2 } \right]_1, \left[ S_{ \sigma 3 }\right]_1, \left[ q_L \right]_1, \left[ q_R \right]_1, \left[ q_M \right]_1, \left[ q_O \right]_1, \left[ q_C \right]_1$$ - -### Extraction of values and commitments - -#### Challenges - -Firstly, the verifier needs to compute all the challenges. For that, he follows these steps: - -- Add $\left[a\right]_1, \left[b\right]_1, \left[c\right]_1$ to the transcript. -- Sample two challenges $\beta, \gamma$. -- Add $\left[z\right]_1$ to the transcript. -- Sample a challenge $\alpha$. -- Add $\left[t_{lo}\right]_1, \left[t_{mid}\right]_1, \left[t_{hi}\right]_1$ to the transcript. -- Sample a challenge $\zeta$. -- Add $\bar a, \bar b, \bar c, \bar s_{\sigma 1}, \bar s_{\sigma 2}, \bar z_\omega$ to the transcript. -- Sample a challenge $\upsilon$. - -#### Compute $p.i ( \zeta )$ - -Also he needs compute a few values off all these data. First, he computes the $PI$ matrix with the public inputs and outputs. He needs to compute $pi(\zeta)$, where $pi$ is the interpolation of $PI$ at the domain $H$. But he doesn't need to compute $pi$. He can instead compute $pi(\zeta)$ as -$$\sum_{i = 0 }^n L_i(\zeta) (PI)_i,$$ -where $n$ is the number of public inputs and $L_i$ is the Lagrange basis at the domain $H$. - -#### Compute claimed values of $p( \zeta )$ and $t( \zeta )$ - -He computes $\bar p_{c} := pi(\zeta) + \alpha \bar z_\omega (\bar c + \gamma) (\bar a + \beta \bar s_{\sigma 1} + \gamma) (\bar b + \beta \bar s_{\sigma 2} + \gamma) - \alpha^2 L_1(\zeta)$ - -This is the _constant_ part of the linearization of $p$. So adding it to what the prover claims to be $\bar p_{nc}$, he obtains -$$p(\zeta) = \bar p_{c} + \bar p_{nc}$$ - -With respect to $t(\zeta)$, this is actually already $\bar{t}$. - -#### Compute $\left[t_{\mathrm{partial}} \right]_1$ and $\left[p_{nc} \right]_1$ - -He computes these off the commitments in the proof as follows -$$\left[t_{\mathrm{partial}}\right]_1 = \left[t_{lo}\right]_1 + \zeta^{ N + 2 }\left[t_{mid}\right]_1 + \zeta^{ 2 (N + 2) }\left[t_{hi}\right]_1$$ - -For $\left[p_{nc}\right]_1$, first compute - -$$ -\begin{aligned} -\left[\hat p_{nc1} \right]_1 &= \bar a\left[q_L\right]_1 + \bar b\left[q_R\right]_1 + (\bar a\bar b)\left[q_M\right]_1 + \bar c\left[q_O\right]_1 + \left[q_C\right]_1 \\ -\left[\hat p_{nc2} \right]_1 &= (\bar a + \beta\zeta + \gamma)(\bar b + \beta k_1 \zeta + \gamma)(\bar c + \beta k_2 \zeta + \gamma)\left[z\right]_1 - (\bar a + \beta \bar s_{\sigma 1} + \gamma)(\bar b + \beta \bar s_{\sigma 2} + \gamma)\beta \bar z_\omega \left[S_{\sigma 3}\right]_1 \\ -\left[\hat p_{nc3} \right]_1 &= L_1(\zeta)\left[z\right]_1 -\end{aligned} -$$ - -Then $\left[p_{nc} \right]_1 = \left[p_{nc1} \right]_1 + \left[p_{nc2} \right]_1 + \left[p_{nc3} \right]_1$. - -#### Compute claimed value $f_{\text{batch} } (\zeta)$ and $\left[f_{{batch} }\right]_1$ - -Compute $f_{\text{batch}} (\zeta)$ as - -$$ -f_{\mathrm{batch}} (\zeta) = -\bar t +\upsilon \bar p_{nc} + \upsilon^2 \bar a + \upsilon^3 \bar b + \upsilon^4 \bar c + \upsilon^5 \bar s_{\sigma1} + \upsilon^6 \bar s_{\sigma2} -$$ - -Also, the commitment of the polynomial $f_{\text{batch}}$ is -$$\left[f_{\mathrm{batch} }\right]_1 = \left[t_{\mathrm{partial}}\right]_1 + \upsilon \left[p_{nc}\right]_1 + \upsilon^2 \left[a\right]_1 + \upsilon^3 \left[b\right]_1 + \upsilon^4 \left[c\right]_1 + \upsilon^5 \left[S_{\sigma 1}\right]_1 + \upsilon^6 \left[S_{\sigma 2}\right]_1$$ - -### Proof check - -Now the verifier has all the necessary values to proceed with the checks. - -- Check that $p(\zeta)$ equals $(\zeta^N - 1)t(\zeta)$. -- Verify the opening of $f_{\text{batch}}$ at $\zeta$. That is, check that $\mathrm{Verify}(\left[f_{\mathrm{batch} } \right]_1, \pi_{\mathrm{batch}}, \zeta, f_{\mathrm{batch} } (\zeta))$ outputs _Accept_. -- Verify the opening of $z$ at $\zeta\omega$. That is, check the validity of the proof $\pi_{single}$ using the commitment $\left[z\right]_1$ and the value $\bar z_\omega$. -That is, check that $\mathrm{Verify}(\left[z\right]_1, \pi_{\mathrm{single}}, \zeta \omega, \bar z_\omega)$ outputs _Accept_. - -If all checks pass, he outputs _Accept_. Otherwise outputs _Reject_. \ No newline at end of file diff --git a/docs/src/plonk/recap.md b/docs/src/plonk/recap.md deleted file mode 100644 index c3e0ef0ce..000000000 --- a/docs/src/plonk/recap.md +++ /dev/null @@ -1,447 +0,0 @@ -# PLONK - -PLONK is a popular cryptographic proving system within the Zero Knowledge (ZK) community due to its efficiency and flexibility. It enables the verification of complex computations executed by untrusted parties through the transformation of programs into circuit representations. The system relies on a process called arithmetization, which converts logical circuits into polynomial representations. The main idea behind arithmetization is to express the computation as a set of polynomial equations. The solutions to these equations correspond to the outputs of the circuit. In this section, we will delve into the mechanics of how arithmetization works in PLONK, as well as the protocol used to generate and verify proofs. - -The paper can be found [here](https://eprint.iacr.org/2019/953.pdf) - -## Notation -We use the following notation. - -The symbol $\mathbb{F}$ denotes a finite field. It is fixed all along. The symbol $\omega$ denotes a primitive root of unity in $\mathbb{F}$. - -All polynomials have coefficients in $\mathbb{F}$ and the variable is usually denoted by $X$. We denote polynomials by single letters like $p, a, b, z$. We only denote them as $z(X)$ when we want to emphasize the fact that it is a polynomial in $X$, or we need that to explicitly define a polynomial from another one. For example when composing a polynomial $z$ with the polynomial $\omega X$, the result being denoted by $z' := z(\omega X)$. The symbol $'$ is **not** used to denote derivatives. - -When interpolating at a domain $H=\{h_0, \dots, h_n\} \subset \mathbb{F}$, the symbols $L_i$ denote the Lagrange basis. That is $L_i$ is the polynomial such that $L_i(h_j) = 0$ for all $j\neq i$, and that $L_i(h_i) = 1$. - -If $M$ is a matrix, then $M_{i,j}$ denotes the value at the row $i$ and column $j$. - -# The ideas and components - -## Programs. Our toy example - -For better clarity, we'll be using the following toy program throughout this recap. - -``` -INPUT: - x - -PRIVATE INPUT: - e - -OUTPUT: - e * x + x - 1 -``` - -The observer would have noticed that this program could also be written as $(e + 1) * x - 1$, which is more sensible. But the way it is written now serves us to better explain the arithmetization of PLONK. So we'll stick to it. - -The idea here is that the verifier holds some value $x$, say $x=3$. He gives it to the prover. She executes the program using her own chosen value $e$, and sends the output value, say $8$, along with a proof $\pi$ demonstrating correct execution of the program and obtaining the correct output. - -In the context of PLONK, both the inputs and outputs of the program are considered _public inputs_. This may sound odd, but it is because these are the inputs to the verification algorithm. This is the algorithm that takes, in this case, the tuple $(3, 8, \pi)$ and outputs _Accept_ if the toy program was executed with input $x=3$, some private value $e$ not revealed to the verifier, and out came $8$. Otherwise it outputs _Reject_. - -PLONK can be used to delegate program executions to untrusted parties, but it can also be used as a proof of knowledge. Our program could be used by a prover to demostrate that she knows the multiplicative inverse of some value $x$ in the finite field without revealing it. She would do it by sending the verifier the tuple $(x, 0, \pi)$, where $\pi$ is the proof of the execution of our toy program. - -In our toy example this is pointless because inverting field elements is easily performed by any verifier. But change our program to the following and you get proofs of knowledge of the preimage of SHA256 digests. - -``` -PRIVATE INPUT: - e - -OUTPUT: - SHA256(e) -``` - -Here there's no input aside from the prover's private input. As we mentioned, the output $h$ of the program is then part of the inputs to the verification algorithm. Which in this case just takes $(h, \pi)$. - -## PLONK Arithmetization - -This is the process that takes the circuit of a particular program and produces a set of mathematical tools that can be used to generate and verify proofs of execution. The end result will be a set of eight polynomials. To compute them we need first to define two matrices. We call them the $Q$ matrix and the $V$ matrix. The polynomials and the matrices depend only on the program and not on any particular execution of it. So they can be computed once and used for every execution instance. To understand what they are useful for, we need to start from _execution traces_. - -### Circuits and execution traces - -See the program as a sequence of gates that have left operand, a right operand and an output. The two most basic gates are multiplication and addition gates. In our example, one way of seeing our toy program is as a composition of three gates. - -Gate 1: left: e, right: x, output: u = e \* x -Gate 2: left: u, right: x, output: v = e + x -Gate 3: left: v, right: 1, output: w = v - 1 - -On executing the circuit, all these variables will take a concrete value. All that information can be put in table form. It will be a matrix with all left, right and output values of all the gates. One row per gate. We call the columns of this matrix $L, R, O$. Let's build them for $x=3$ and $e=2$. We get $u=6$, $v=9$ and $w=8$. So the first matrix is: - -| A | B | C | -| --- | --- | --- | -| 2 | 3 | 6 | -| 6 | 3 | 9 | -| 9 | - | 8 | - -The last gate subtracts a constant value that is part of the program and is not a variable. So it actually has only one input instead of two. And the output is the result of subtracting $1$ to it. That's why it is handled a bit different from the second gate. The symbol "-" in the $R$ column is a consequence of that. With that we mean "any value" because it won't change the result. In the next section we'll see how we implement that. Here we'll use this notation when any value can be put there. In case we have to choose some, we'll default to $0$. - -What we got is a valid execution trace. Not all matrices of that shape will be the trace of an execution. The matrices $Q$ and $V$ will be the tool we need to distinguish between valid and invalid execution traces. - -### The $Q$ matrix - -As we said, it only depends on the program itself and not on any particular evaluation of it. It has one row for each gate and its columns are called $Q_L, Q_R, Q_O, Q_M, Q_C$. They encode the type of gate of the rows and are designed to satisfy the following. - -**Claim:** if columns $L, R, O$ correspond to a valid evaluation of the circuit then for all $i$ the following equality holds $$A_i (Q_L)_i + B_i (Q_R)_i + A_i B_i Q_M + C_i (Q_O)_i + (Q_C)_i = 0$$ - -This is better seen with examples. A multiplication gate is represented by the row: - -| $Q_L$ | $Q_R$ | $Q_M$ | $Q_O$ | $Q_C$ | -| ----- | ----- | ----- | ----- | ----- | -| 0 | 0 | 1 | -1 | 0 | - -And the row in the trace matrix that corresponds to the execution of that gate is - -| A | B | C | -| --- | --- | --- | -| 2 | 3 | 6 | - -The equation in the claim for that row is that $2 \times 0 + 3 \times 0 + 2 \times 3 \times 1 + 6 \times (-1) + 0$, which equals $0$. The next is an addition gate. This is represented by the row - -| $Q_L$ | $Q_R$ | $Q_M$ | $Q_O$ | $Q_C$ | -| ----- | ----- | ----- | ----- | ----- | -| 1 | 1 | 0 | -1 | 0 | - -The corresponding row in the trace matrix its - -| A | B | C | -| --- | --- | --- | -| 6 | 3 | 9 | - -And the equation of the claim is $6 \times 1 + 3 \times 1 + 2 \times 3 \times 0 + 9 \times (-1) + 0$, which adds up to $0$. Our last row is the gate that adds a constant. Addition by constant C can be represented by the row - -| $Q_L$ | $Q_R$ | $Q_M$ | $Q_O$ | $Q_C$ | -| ----- | ----- | ----- | ----- | ----- | -| 1 | 0 | 0 | -1 | C | - -In our case $C=-1$. The corresponding row in the execution trace is - -| A | B | C | -| --- | --- | --- | -| 9 | - | 8 | - -And the equation of the claim is $9 \times 1 + 0 \times 0 + 9 \times 0 \times 0 + 8 \times (-1) + C$. This is also zero. - -Putting it altogether, the full $Q$ matrix is - -| $Q_L$ | $Q_R$ | $Q_M$ | $Q_O$ | $Q_C$ | -| ----- | ----- | ----- | ----- | ----- | -| 0 | 0 | 1 | -1 | 0 | -| 1 | 1 | 0 | -1 | 0 | -| 1 | 0 | 0 | -1 | -1 | - -And we saw that the claim is true for our particular execution: -$$ 2 \times 0 + 3 \times 0 + 2 \times 3 \times 1 + 6 \times (-1) + 0 = 0 $$ -$$ 6 \times 1 + 3 \times 1 + 6 \times 3 \times 0 + 9 \times (-1) + 0 = 0 $$ -$$ 9 \times 1 + 0 \times 0 + 9 \times 0 \times 0 + 8 \times (-1) + (-1) = 0 $$ - -Not important to our example, but multiplication by constant C can be represented by: - -| $Q_L$ | $Q_R$ | $Q_M$ | $Q_O$ | $Q_C$ | -| ----- | ----- | ----- | ----- | ----- | -| C | 0 | 0 | -1 | 0 | - -As you might have already noticed, there are several ways of representing the same gate in some cases. We'll exploit this in a moment. - -### The $V$ matrix - -The claim in the previous section is clearly not an "if and only if" statement because the following trace columns do satisfy the equations but do not correspond to a valid execution: - -| A | B | C | -| --- | --- | --- | -| 2 | 3 | 6 | -| 0 | 0 | 0 | -| 20 | - | 19 | - -The $V$ matrix encodes the carry of the results from one gate to the right or left operand of a subsequent one. These are called _wirings_. Like the $Q$ matrix, it's independent of the particular evaluation. It consists of indices for all input and intermediate variables. In this case that matrix is: - -| L | R | O | -| --- | --- | --- | -| 0 | 1 | 2 | -| 2 | 1 | 3 | -| 3 | - | 4 | - -Here $0$ is the index of $e$, $1$ is the index of $x$, $2$ is the index of $u$, $3$ is the index of $v$ and $4$ is the index of the output $w$. Now we can update the claim to have an "if and only if" statement. - -**Claim:** Let $T$ be a matrix with columns $A, B, C$. It correspond to a valid evaluation of the circuit if and only if a) for all $i$ the following equality holds $$A_i (Q_L)_i + B_i (Q_R)_i + A_i B_i Q_M + C_i (Q_O)_i + (Q_C)_i = 0,$$ b) for all $i,j,k,l$ such that $V_{i,j} = V_{k, l}$ we have $T_{i,j} = T_{k, l}$. - -So now our malformed example does not pass the second check. - -### Custom gates - -Our matrices are fine now. But they can be optimized. Let's do that to showcase this flexibility of PLONK and also reduce the size of our example. - -PLONK has the flexibility to construct more sophisticated gates as combinations of the five columns. And therefore the same program can be expressed in multiple ways. In our case all three gates can actually be merged into a single custom gate. The $Q$ matrix ends up being a single row. - -| $Q_L$ | $Q_R$ | $Q_M$ | $Q_O$ | $Q_C$ | -| ----- | ----- | ----- | ----- | ----- | -| 0 | 1 | 1 | -1 | 1 | - -and also the $V$ matrix - -| L | R | O | -| --- | --- | --- | -| 0 | 1 | 2 | - -The trace matrix for this representation is just - -| A | B | C | -| --- | --- | --- | -| 2 | 3 | 8 | - -And we check that it satisfies the equation - -$$ 2 \times 0 + 3 \times 1 + 2 \times 3 \times 1 + 8 \times (-1) + (-1) = 0$$ - -Of course, we can't always squash an entire program into a single gate. - -### Public inputs - -Aside from the gates that execute the program operations, additional rows must be incorporated into these matrices. This is due to the fact that the prover must demonstrate not only that she executed the program, but also that she used the appropriate inputs. Furthermore, the proof must include an assertion of the output value. As a result, a few extra rows are necessary. In our case these are the first two and the last one. The original one sits now in the third row. - -| $Q_L$ | $Q_R$ | $Q_M$ | $Q_O$ | $Q_C$ | -| ----- | ----- | ----- | ----- | ----- | -| -1 | 0 | 0 | 0 | 3 | -| -1 | 0 | 0 | 0 | 8 | -| 1 | 1 | 1 | -1 | 1 | -| 1 | -1 | 0 | 0 | 0 | - -And this is the updated $V$ matrix - -| L | R | O | -| --- | --- | --- | -| 0 | - | - | -| 1 | - | - | -| 2 | 0 | 3 | -| 1 | 3 | - | - -The first row is there to force the variable with index $0$ to take the value $3$. Similarly the second row forces variable with index $1$ to take the value $8$. These two will be the public inputs of the verifier. The last row checks that the output of the program is the claimed one. - -And the trace matrix is now - -| A | B | C | -| --- | --- | --- | -| 3 | - | - | -| 8 | - | - | -| 2 | 3 | 8 | -| 8 | 8 | - | - -With these extra rows, equations add up to zero only for valid executions of the program with input $3$ and output $8$. - -An astute observer would notice that by incorporating these new rows, the matrix $Q$ is no longer independent of the specific evaluation. This is because the first two rows of the $Q_C$ column contain concrete values that are specific to a particular execution instance. To maintain independence, we can remove these values and consider them as part of an extra one-column matrix called $PI$ (stands for Public Input). This column has zeros in all rows not related to public inputs. We put zeros in the $Q_C$ columns. The responsibility of filling in the $PI$ matrix is of the prover and verifier. In our example it is - -| $PI$ | -| ---- | -| 3 | -| 8 | -| 0 | -| 0 | - -And the final $Q$ matrix is - -| $Q_L$ | $Q_R$ | $Q_M$ | $Q_O$ | $Q_C$ | -| ----- | ----- | ----- | ----- | ----- | -| -1 | 0 | 0 | 0 | 0 | -| -1 | 0 | 0 | 0 | 0 | -| 1 | 1 | 1 | -1 | 1 | -| 1 | -1 | 0 | 0 | 0 | - -We ended up with two matrices that depend only on the program, $Q$ and $V$. And two matrices that depend on a particular evaluation, namely the $ABC$ and $PI$ matrices. The updated version of the claim is the following: - -**Claim:** Let $T$ be a matrix with columns $A, B, C$. It corresponds to a evaluation of the circuit if and only if a) for all $i$ the following equality holds $$A_i (Q_L)_i + B_i (Q_R)_i + A_i B_i Q_M + C_i (Q_O)_i + (Q_C)_i + (PI)_i = 0,$$ b) for all $i,j,k,l$ such that $V_{i,j} = V_{k,l}$ we have $T_{i,j} = T_{k,l}$. - -### From matrices to polynomials - -In the previous section we showed how the arithmetization process works in PLONK. For a program with $n$ public inputs and $m$ gates, we constructed two matrices $Q$ and $V$, of sizes $(n + m + 1) \times 5$ and $(n + m + 1) \times 3$ that satisfy the following. Let $N = n + m + 1.$ - -**Claim:** Let $T$ be a $N \times 3$ matrix with columns $A, B, C$ and $PI$ a $N \times 1$ matrix. They correspond to a valid execution instance with public input given by $PI$ if and only if a) for all $i$ the following equality holds $$A_i (Q_L)_i + B_i (Q_R)_i + A_i B_i Q_M + C_i (Q_O)_i + (Q_C)_i + (PI)_i = 0,$$ b) for all $i,j,k,l$ such that $V_{i,j} = V_{k,l}$ we have $T_{i,j} = T_{k,l}$, c) $(PI)_i = 0$ for all $i>n$. - -Polynomials enter now to squash most of these equations. We will traduce the set of all equations in conditions (a) and (b) to just a few equations on polynomials. - -Let $\omega$ be a primitive $N$-th root of unity and let $H = {\omega^i: 0 \leq i < N}$. Let $a, b, c, q_L, q_R, q_M, q_O, q_C, pi$ be the polynomials of degree at most $N$ that interpolate the columns $A, B, C, Q_L, Q_R, Q_M, Q_O, Q_C, PI$ at the domain $H$. This means for example that $a(\omega^i) = A_i$ for all $i$. And similarly for all the other columns. - -With this, condition (a) of the claim is equivalent to $$a(x) q_L(x) + b(x) q_R(x) + a(x) b(x) q_M(x) + c(x) q_O(x) + q_c(x) + pi(x) = 0$$ for all $x$ in $H$.This is just by definition of the polynomials. But in polynomials land this is also equivalent to (a) there exists a polynomial $t$ such that $$a q_L + b q_R + a b q_M + c q_O + q_c + pi = z_H t$$, where $z_H$ is the polynomial $X^N -1$. - -To reduce condition (b) to polynomial equations we need to introduce the concept of permutation. A permutation is a rearrangement of a set. Usually denoted $\sigma$. For finite sets it is a map from a set to itself that takes all values. In our case the set will be the set of all pairs -$$I=\{(i,j): \text{ such that }0\leq i < N, \text{ and } 0\leq j < 3\}$$ -The matrix $V$ induces a permutation of this set where $\sigma((i,j))$ is equal to the indices of the _next_ occurrence of the value at position $(i,j)$. If already at the last occurrence, go to the first one. By _next_ we mean the following occurrence as if the columns were stacked on each other. Let's see how this works in the example circuit. Recall $V$ is - -| L | R | O | -| --- | --- | --- | -| 0 | - | - | -| 1 | - | - | -| 2 | 0 | 3 | -| 1 | 3 | - | - -The permutation in this case is the map $\sigma((0,0)) = (2,1)$, $\sigma((0,1)) = (0, 3)$, $\sigma((0,2)) = (0,2)$, $\sigma((0,3)) = (0,1)$, $\sigma((2,1)) = (0,0)$, $\sigma((3,1)) = (2,2)$, $\sigma((2,2)) = (3,1)$. For the positions with `-` values doesn't really matter right now. - -It's not hard to see that condition (b) is equivalent to: for all $(i,j)\in I$, $T_{i,j} = T_{\sigma((i,j))}$. - -A little less obvious is that this condition is in turn equivalent to checking whether the following sets $A$ and $B$ are equal -$$A = \{((i,j), T_{i,j}): (i,j) \in I\}$$ -$$B = \{(\sigma((i,j)), T_{i,j}): (i,j) \in I\}.$$ -The proof this equivalence is straightforward. Give it a try! - -In our example the sets in question are respectively -$$\{((0,0), T_{0,0}), ((0,1), T_{0,1}), ((0,2), T_{0,2}), ((0,3), T_{0,3}), ((2,1), T_{2,1}), ((3,1), T_{3,1}), ((2,2), T_{2,2})\},$$ -and -$$\{((2,1), T_{0,0}), ((0,3), T_{0,1}), ((0,2), T_{0,2}), ((0,1), T_{0,3}), ((0,0), T_{2,1}), ((2,2), T_{3,1}), ((3,1), T_{2,2})\},$$ - -You can check these sets coincide by inspection. Recall our trace matrix $T$ is - -| A | B | C | -| --- | --- | --- | -| 3 | - | - | -| 8 | - | - | -| 2 | 3 | 8 | -| 8 | 8 | - | - -Checking equality of these sets is something that can be reduced to polynomial equations. It is a very nice method that PLONK uses. To understand it better let's start with a simpler case. - -#### Equality of sets - -Suppose we have two sets $A=\{a_0, a_1\}$ $B=\{b_0, b_1\}$ of two field elements in $\mathbb{F}$. And we are interested in checking whether they are equal. - -One thing we could do is compute $a_0a_1$ and $b_0b_1$ and compare them. If the sets are equal, then those elements are necessarily equal. - -But the converse is not true. For example the sets $A=\{4, 15\}$ and $B=\{6, 10\}$ both have $60$ as the result of the product of their elements. But they are not equal. So this is not good to check equality. - -Polynomials come to rescue here. What we can do instead is consider the following sets _of polynomials_ $A'=\{a_0 + X, a_1 + X\}$, $B'=\{b_0 + X, b_1 + X\}$. Sets $A$ and $B$ are equal if and only if sets $A'$ and $B'$ are equal. This is because equality of polynomials boils down to equality of their coefficients. But the difference with $A'$ and $B'$ is that now the approach of multiplying the elements works. That is, $A'$ and $B'$ are equal if and only if $(a_0 + X)(a_1 + X) = (b_0 + X)(b_1 + X)$. This is not entirely evident but follows from a property that polynomials have, called _unique factorization_. Here the important fact is that linear polynomials act as sort of prime factors. Anyway, you can take that for granted. The last part of this trick is to use the Schwartz-Zippel lemma and go back to the land of field elements. That means, if for some random element $\gamma$ we have $(a_0 + \gamma)(a_1 + \gamma) = (b_0 + \gamma)(b_1 + \gamma)$, then with overwhelming probability the equality $(a_0 + X)(a_1 + X) = (b_0 + X)(b_1 + X)$ holds. - -Putting this altogether, if for some random element $\gamma$ we have $(a_0 + \gamma)(a_1 + \gamma) = (b_0 + \gamma)(b_1 + \gamma)$, then the sets $A$ and $B$ are equal. Of course this also holds for sets with more than two elements. Let's write that down. - -_Fact:_ Let $A=\{a_0, \dots, a_{k - 1}\}$ and $B=\{b_0, \dots, b_{k - 1}\}$ be sets of field elements. If for some random $\gamma$ the following equality holds -$$\prod_{i = 0}^{ k - 1}(a_i + \gamma) = \prod_{i = 0}^{ k - 1 }(b_i + \gamma),$$ -then with overwhelming probability $A$ is equal to $B$. - -And here comes the trick that reduces this check to polynomial equations. Let -$H$ be a domain of the form $\{1, \omega, \dots, \omega^{k - 1}\}$ for some primitive $k$-th root of unity $\omega$. Let $f$ and $g$ be respectively the polynomials that interpolate the following values at $H$. -$$(a_0 + \gamma, \dots, a_{k-1} + \gamma),$$ -$$(b_0 + \gamma, \dots, b_{k-1} + \gamma),$$ - -Then $\prod_{i=0}^{k-1}(a_i + \gamma)$ equals $\prod_{i=0}^{k-1}(b_i + \gamma)$ if and only if there exists a polynomial $Z$ such that -$$Z(\omega^0) = 1$$ -$$Z(h)f(h) = g(h)Z(\omega h)$$ -for all $h\in H$. - -Let's see why. Suppose that $\prod_{i=0}^{k-1}(a_i + \gamma)$ equals $\prod_{i=0}^{k-1}(b_i + \gamma)$. Construct $Z$ as the polynomial that interpolates the following values $$(1, \frac{a_0 + \gamma}{b_0 + \gamma}, \frac{(a_0 + \gamma)(a_1 + \gamma)}{(b_0 + \gamma)(b_1 + \gamma)}, \dots, \prod_{i=0}^{k-2} \frac{a_i + \gamma}{b_i + \gamma}),$$ -in the same domain as $f$ and $g$. That works. Conversely, suppose such a polynomial $Z$ exists. By evaluating the equation $Z(X)f(X) = g(X)Z(\omega X)$ at $1, \omega, \dots, \omega^{k-2}$ and using recursion we get that $Z(\omega^{k-1}) = \prod_{i=0}^{k-2}(a_i + \gamma)/\prod_{i=0}^{k-2}(b_i + \gamma)$. Moreover, evaluating it at $\omega^{k-1}$ we obtain that $$Z(\omega^{k-1})\frac{f(\omega^{k-1})}{g(\omega^{k-1})} = Z(\omega^k) = Z(w^0) = 1.$$ -The second equality holds because $\omega^k = \omega^0$ since it is a $k$-th root of unity. Expanding with the values of $f, g$ and $Z$ one obtains that $\prod_{i=0}^{k-1}(a_i + \gamma)/\prod_{i=0}^{k-1}(b_i + \gamma)$ equals $1$. Which is what we wanted. - -In summary. We proved the following: - -_Fact:_ Let $A=\{a_0, \dots, a_{k-1}\}$ and $B=\{b_0, \dots, b_{k-1}\}$ be sets of field elements. Let $\gamma$ be a random field element. Let $\omega$ be a primitive $k$-th root of unity and $H=\{1, \omega, \omega^2, \dots, \omega^{k-1}\}$. Let $f$ and $g$ be respectively the polynomials that interpolate the values $\{a_0 + \gamma, \dots, a_{k-1} + \gamma\}$ and $\{b_0 + \gamma, \dots, b_{k-1} + \gamma\}$ at $H$. If there exists a polynomial $Z$ such that -$$Z(\omega^0) = 1$$ -$$Z(X)f(X) = g(X)Z(\omega X)$$ -for all $h\in H$, then with overwhelming probability the sets $A$ and $B$ are equal. - -#### Sets of tuples - -In the previous section we saw how to check whether two sets of field elements are equal using polynomial equations. To be able to use it in our context we need to extend it to sets of tuples of field elements. This is pretty straightforward. - -Let's start with the easy case. Let $A=\{(a_0, a_1), (a_2, a_3)\}$ and $B=\{(b_0, b_1), (b_2, b_3)\}$ be two sets of pairs of field elements. That is $a_i, b_i \in \mathbb{F}$ for all $i$. The trick is very similar to the previous section. -$$A'=\{a_0 + a_1 Y + X, a_2 + a_3 Y + X\}$$ -$$B'=\{b_0 + b_1 Y + X, b_2 + b_3 Y + X\}$$ - -Just as before, by looking at coefficients we can see that the sets $A$ and $B$ are equal if and only if $A'$ and $B'$ are equal. -And notice that these are sets of polynomials, we got rid of the tuples! And now the situation is very similar to the previous section. We have that $A'$ and $B'$ are equal if and only if the product of their elements coincide. This is true also because polynomials in two variables are a unique factorization domain. So as before, we can use the Schwartz-Zippel lemma. Precisely, if for random $\beta, \gamma$, the elements -$$(a_0 + \beta a_1 + \gamma)(a_2 + \beta a_3 + \gamma),$$ -and -$$(b_0 + \beta b_1 + \gamma)(b_2 + \beta b_3 + \gamma)$$ -coincide, then $A$ and $B$ are equal with overwhelming probability. - -Here is the statement for sets of more than two pairs of field elements. - -_Fact:_ Let $A=\{\bar a_0, \dots, \bar a_{k-1}\}$ and $B=\{\bar b_0, \dots, \bar b_{k-1}\}$ be sets of pairs of field elements. So that $\bar a_i = (a_{i,0}, a_{i,1})$ and the same for $\bar b_i$. Let $\beta, \gamma$ be a random field elements. Let $\omega$ be a $k$-th root of unity and $H=\{1, \omega, \omega^2, \dots, \omega^{k-1}\}$. Let $f$ and $g$ be respectively the polynomials that interpolate the values -$$\{a_{i,0} + a_{i,1}\beta + \gamma, \dots, a_{k-1,0} + a_{k-1,1}\beta + \gamma\},$$ -and -$$\{b_{i,0} + b_{i,1}\beta + \gamma, \dots, b_{k-1,0} + b_{k-1,1}\beta + \gamma\},$$ -at $H$. If there exists a polynomial $Z$ such that -$$Z(\omega^0) = 1$$ -$$Z(X)f(X) = g(X)Z(\omega X)$$ -for all $h\in H$, then with overwhelming probability the sets $A$ and $B$ are equal. - -#### Going back to our case - -Recall we want to rephrase condition (b) in terms of polynomials. We have already seen that condition (b) is equivalent to $A$ and $B$ being equal, where -$$A = \{((i,j), T_{i,j}): (i,j) \in I\}$$ -and -$$B = \{(\sigma((i,j)), T_{i,j}): (i,j) \in I\}.$$ - -We cannot directly use the facts of the previous sections because our sets are not sets of field elements. Nor are they sets of pairs of field elements. They are sets of pairs with some indexes $(i,j)$ in the first coordinate and a field element $v$ in the second one. So the solution is to convert them to sets of pairs of field elements and apply the result of the previous section. So how do we map an element of the form $((i,j), v)$ to something of the form $(a_0, a_1)$ with $a_0$ and $a_1$ field elements? The second coordinate is trivial, we can just leave $v$ as it is and take $a_1 = v$. For the indexes pair $(i,j)$ there are multiple ways. The important thing to achieve here is that different pairs get mapped to different field elements. Recall that $i$ ranges from $0$ to $N-1$ and $j$ ranges from $0$ to $2$. One way is to take a $3N$-th primitive root of unity $\eta$ and define $a_0 = \eta^{3i + j}$. Putting it altogether, we are mapping the pair $((i,j), v)$ to the pair $(\eta^{3i + j}, v)$, which is a pair of field elements. Now we can consider the sets -$$A = \{(\eta^{3i + j}, T_{i,j}): (i,j) \in I\}$$ -and -$$B = \{(\eta^{3k + l}, T_{i,j}): (i,j) \in I, \sigma((i,j)) = (k, l)\}.$$ -We have that condition (b) is equivalent to $A$ and $B$ being equal. - -Applying the method of the previous section to these sets, we obtain the following. - -_Fact:_ Let $\eta$ be a $3N$-th root of unity and $\beta$ and $\gamma$ random field elements. Let $D = \{1, \eta, \eta^2, \dots, \eta^{3N-1}\}$. Let $f$ and $g$ be the polynomials that interpolate, respectively, the following values at $D$: -$$\{T_{i,j} + \eta^{3i + j}\beta + \gamma: (i,j) \in I\},$$ -and -$$\{T_{i,j} + \eta^{3k + l}\beta + \gamma: (i,j) \in I, \sigma((i,j)) = (k,l)\},$$ -Suppose there exists a polynomial $Z$ such that -$$Z(\eta^0) = 1$$ -$$Z(d)f(d) = g(d)Z(\eta d),$$ -for all $h\in D$. -Then the sets $A = \{((i,j), T_{i,j}): (i,j) \in I\}$ and $B = \{(\sigma((i,j)), T_{i,j}): (i,j) \in I\}$ are equal with overwhelming probability. - -One last minute definitions. Notice that $\omega=\eta^3$ is a primitive $N$-th root of unity. Let $H = \{1, \omega, \omega^2, \dots, \omega^{N-1}\}$. - -Define $S_{\sigma 1}$ to be the interpolation at $H$ of -$$\{\eta^{3k + l}: (i,0) \in I, \sigma((i,0)) = (k,l)\},$$ -Similarly define $S_{\sigma 2}$ and $S_{\sigma 3}$ to be the interpolation at $H$ of the sets of values -$$\{\eta^{3k + l}: (i,1) \in I, \sigma((i,1)) = (k,l)\},$$ -$$\{\eta^{3k + l}: (i,2) \in I, \sigma((i,2)) = (k,l)\},$$ -These will be useful during the protocol to work with such polynomials $Z$ and the above equations. - -#### A more compact form - -The last fact is equivalent the following. There's no new idea here, just a more compact form of the same thing that allows the polynomial $Z$ to be of degree at most $N$. - -_Fact:_ Let $\omega$ be a $N$-th root of unity. Let $H = \{1, \omega, \omega^2, \dots, \omega^{N-1}\}$. Let $k_1$ and $k_2$ be two field elements such that $\omega^i \neq \omega^jk_1 \neq \omega^lk_2$ for all $i,j,l$. Let $\beta$ and $\gamma$ be random field elements. Let $f$ and $g$ be the polynomials that interpolate, respectively, the following values at $H$: -$$\{(T_{0,j} + \omega^{i}\beta + \gamma)(T_{1,j} + \omega^{i}k_1\beta + \gamma)(T_{2,j} + \omega^{i}k_2\beta + \gamma): 0\leq in$. - -Then we constructed polynomials $q_L, q_R, q_M, q_O, q_C, S_{\sigma1},S_{\sigma2}, S_{\sigma3}$, $f$, $g$ off the matrices $Q$ and $V$. They are the result of interpolating at a domain $H = \{1, \omega, \omega^2, \dots, \omega^{N-1}\}$ for some $N$-th primitive root of unity and a few random values. And also constructed polynomials $a,b,c, pi$ off the matrices $T$ and $PI$. Loosely speaking, the above fact can be reformulated in terms of polynomial equations as follows. - -**Fact:** Let $z_H = X^N - 1$. Let $T$ be a $N \times 3$ matrix with columns $A, B, C$ and $PI$ a $N \times 1$ matrix. They correspond to a valid execution instance with public input given by $PI$ if and only if - -a) There is a polynomial $t_1$ such that the following equality holds $$a q_L + b q_R + a b q_M + c q_O + q_C + pi = z_H t_1,$$ - -b) There are polynomials $t_2, t_3$, $z$ such that $zf - gz' = z_H t_2$ and $(z-1)L_1 = z_H t_3$, where $z'(X) = z(X\omega)$ - -You might be wondering where the polynomials $t_i$ came from. Recall that for a polynomial $F$, we have $F(h) = 0$ for all $h \in H$ if and only if $F = z_H t$ for some polynomial $t$. - -Finally both conditions (a) and (b) are equivalent to a single equation (c) if we let more randomness to come into play. This is: - -(c) Let $\alpha$ be a random field element. There is a polynomial $t$ such that -$$ -\begin{aligned} -z_H t = &a q_L + b q_R + a b q_M + c q_O + q_C + pi \\ - &+ \alpha(gz' - fz) \\ - &+ \alpha^2(z-1)L_1 \\ -\end{aligned} -$$ - -This last step is not obvious. You can check the paper to see the proof. Anyway, this is the equation you'll recognize below in the description of the protocol. - -Randomness is a delicate matter and an important part of the protocol is where it comes from, who chooses it and when they choose it. Check out the protocol to see how it works. \ No newline at end of file diff --git a/docs/src/starks/api.md b/docs/src/starks/api.md deleted file mode 100644 index 275696925..000000000 --- a/docs/src/starks/api.md +++ /dev/null @@ -1,160 +0,0 @@ -# High level API: Fibonacci example - -Let's go over the main test we use for our prover, where we compute a STARK proof for a fibonacci trace with 4 rows and then verify it. - -```rust -fn test_prove_fib() { - let trace = simple_fibonacci::fibonacci_trace([FE::from(1), FE::from(1)], 8); - let proof_options = ProofOptions::default_test_options(); - - let pub_inputs = FibonacciPublicInputs { - a0: FE::one(), - a1: FE::one(), - }; - - let proof = prove::>(&trace, &pub_inputs, &proof_options).unwrap(); - assert!(verify::>(&proof, &pub_inputs, &proof_options)); -} -``` - -The proving system revolves around the `prove` function, that takes a trace, public inputs and proof options as inputs to generate a proof, and a `verify` function that takes the generated proof, the public inputs and the proof options as inputs, outputting `true` when the proof is verified correctly and `false` otherwise. Note that the public inputs and proof options should be the same for both. Public inputs should be shared by the Cairo runner to prover and verifier, and the proof options should have been agreed on beforehand by the two entities beforehand. - -Below we go over the main things involved in this code. - -## AIR - -To prove the integrity of a fibonacci trace, we first need to define what it means for a trace to be valid. As we've talked about in the recap, this involves defining an `AIR` for our computation where we specify both the boundary and transition constraints for a fibonacci sequence. - -In code, this is done through the `AIR` trait. Implementing `AIR` requires defining a couple methods, but the two most important ones are `boundary_constraints` and `compute_transition`, which encode the boundary and transition constraints of our computation. - - -### Boundary Constraints -For our Fibonacci `AIR`, boundary constraints look like this: - -```rust -fn boundary_constraints( - &self, - _rap_challenges: &Self::RAPChallenges, -) -> BoundaryConstraints { - let a0 = BoundaryConstraint::new_simple(0, self.pub_inputs.a0.clone()); - let a1 = BoundaryConstraint::new_simple(1, self.pub_inputs.a1.clone()); - - BoundaryConstraints::from_constraints(vec![a0, a1]) -} -``` - -The `BoundaryConstraint` struct represents a specific boundary constraint, meaning "column `i` at row `j` should be equal to `x`". In this case, because we have only one column, we are using the `new_simple` method to simply say - -- Row `0` should equal the public input `a0`, which in the typical fibonacci is set to 1. -- Row `1` should equal the public input `a1`, which in the typical fibonacci is set to 1. - -In the case of multiple columns, the `new` method exists so you can also specify column number. - -After instantiating each of these constraints, we return all of them through the struct `BoundaryConstraints`. - -### Transition Constraints - -The way we specify our fibonacci transition constraint looks like this: - -```rust -fn compute_transition( - &self, - frame: &air::frame::Frame, - _rap_challenges: &Self::RAPChallenges, -) -> Vec> { - let first_row = frame.get_row(0); - let second_row = frame.get_row(1); - let third_row = frame.get_row(2); - - vec![third_row[0] - second_row[0] - first_row[0]] -} -``` - -It's not completely obvious why this is how we chose to express transition constraints, so let's talk a little about it. - -What we need to specify in this method is the relationship that has to hold between the current step of computation and the previous ones. For this, we get a `Frame` as an argument. This is a struct holding the current step (i.e. the current row of the trace) and all previous ones needed to encode our constraint. In our case, this is the current row and the two previous ones. To access rows we use the `get_row` method. The current step is always the last row (in our case `2`), with the others coming before it. - -In our `compute_transition` method we get the three rows we need and return - -```rust -third_row[0] - second_row[0] - first_row[0] -``` - -which is the value that needs to be zero for our constraint to hold. Because we support multiple transition constraints, we actually return a vector with one value per constraint, so the first element holds the first constraint value and so on. - -## TraceTable - -After defining our AIR, we create our specific trace to prove against it. - -```rust -let trace = fibonacci_trace([FE17::new(1), FE17::new(1)], 4); - -let trace_table = TraceTable { - table: trace.clone(), - num_cols: 1, -}; -``` - -`TraceTable` is the struct holding execution traces; the `num_cols` says how many columns the trace has, the `table` field is a `vec` holding the actual values of the trace in row-major form, meaning if the trace looks like this - -``` -| 1 | 2 | -| 3 | 4 | -| 5 | 6 | -``` - -then its corresponding `TraceTable` is - -```rust -let trace_table = TraceTable { - table: vec![1, 2, 3, 4, 5, 6], - num_cols: 2, -}; -``` - -In our example, `fibonacci_trace` is just a helper function we use to generate the fibonacci trace with `4` rows and `[1, 1]` as the first two values. - -## AIR Context - -After specifying our constraints and trace, the only thing left to do is provide a few parameters related to the STARK protocol and our `AIR`. These specify things such as the number of columns of the trace and proof configuration, among others. They are all encapsulated in the `AirContext` struct, which in our example we instantiate like this: - -```rust -let context = AirContext { - options: ProofOptions { - blowup_factor: 2, - fri_number_of_queries: 1, - coset_offset: 3, - }, - trace_columns: trace_table.n_cols, - transition_degrees: vec![1], - transition_exemptions: vec![2], - transition_offsets: vec![0, 1, 2], - num_transition_constraints: 1, -}; -``` - -Let's go over each of them: - -- `options` requires a `ProofOptions` struct holding specific parameters related to the STARK protocol to be used when proving. They are: - - The `blowup_factor` used for the trace LDE extension, a parameter related to the security of the protocol. - - The number of queries performed by the verifier when doing `FRI`, also related to security. - - The `offset` used for the LDE coset. This depends on the field being used for the STARK proof. -- `trace_columns` are the number of columns of the trace, respectively. -- `transition_degrees` holds the degree of each transition constraint. -- `transition_exemptions` is a `Vec` which tells us, for each column, the number of rows the transition constraints should not apply, starting from the end of the trace. In the example, the transition constraints won't apply on the last two rows of the trace. -- `transition_offsets` holds the indexes that define a frame for our `AIR`. In our fibonacci case, these are `[0, 1, 2]` because we need the current row and the two previous one to define our transition constraint. -- `num_transition_constraints` simply says how many transition constraints our `AIR` has. - -## Proving execution - -Having defined all of the above, proving our fibonacci example amounts to instantiating the necessary structs and then calling `prove` passing the trace, public inputs and proof options. We use a simple implementation of a hasher called `TestHasher` to handle merkle proof building. - -```rust -let proof = prove(&trace_table, &pub_inputs, &proof_options); -``` - -Verifying is then done by passing the proof of execution along with the same `AIR` to the `verify` function. - -```rust -assert!(verify(&proof, &pub_inputs, &proof_options)); -``` diff --git a/docs/src/starks/builtins.md b/docs/src/starks/builtins.md deleted file mode 100644 index 3f52dce48..000000000 --- a/docs/src/starks/builtins.md +++ /dev/null @@ -1,27 +0,0 @@ -# Builtins - -We can understand the built-in as a small machine, that we can use to efficiently prove a subprogram. For example, it may be able to prove a hash, like Poseidon or Keccak, verify a signature, or check that some variable is in a range, and the cost would be less than what we would have if using the Cairo VM instructions. - -For each subprogram we want to prove, we will have a machine, which will have its own set of constraints in the prover. Let's take for example the Range Check built-in. This builtin enforces that a value $X$ is between 0 and $2^{128}$. - -The logic behind the built-in is pretty straightforward. We split $X$ into 8 parts. So we will say that $X = X_{0} + X_{1} * 2^{16} + X_{2} * 2^{32} + X_{3} * 2^{48} + ... + X_{7} * 2^{112}$ - -Then we require that each is in the range $0 < X_{i} < 2^{16}$. The idea here is to reuse the Range Check constraint that checks if the offsets are between $0$ and $2^{16}$. If we can decompose the number in eight limbs of 16 bits, and we don't need any more limbs, it follows that the number will be less than $2^{128}$ - -The missing ingredient is how we make sure that each value $X$ that should be constrained by the built-in is actually constrained. - -The process starts with the VM designating special memory positions for the built-in. You can think of this as a way of communicating the VM with the specific built-in machine by sharing memory. - -The VM won't save any instruction associated with how the built-in gets to the result and will assume the output is correct. You can think of this as an IO device in any computer, which works in a similar fashion. The VM delegates the work to an external device and takes the result from the memory. - -Knowing which specific positions of the memory are used by the built-in, the prover can add more constraints that enforce the calculations of the built-in were done correctly. Let's see how it's done. - -In the constraint system of the VM, we will treat every memory cell associated with the built-in as any other, treating it as a pair of addresses and values with the usual constraints. Additionally, we will add more that are specific to the builtin. - -Let's say we have multiple values $x_{i}$, such that each $x_{i}$ needs to be range checked by the built-in. Let each value be stored in a memory address $m_{i}$. Let the initial expected memory position for the range check built-in be $r_{0}$. Here $r_{0}$ is a value known and a public input. - -We need to enforce then that $m_{0} = r_{0}$, and that the built in $m_{i+1} = m_{i} + 1$. These constraints have to be put on top of the constraints that are used by the memory, and that's the key to all of this. If these constraints weren't in place, there wouldn't be an enforced link between the Builtin and the VM, which would lead to security issues. - -As one last detail, since the memory cells share the same constraints, and we add more for the ones in the builtin, we can treat the builtin cells as a subcolumn. In that case, we can assign one cell for the memory every N cell, giving a ratio that will be observable in the layout. - -This gives a better relationship between the number of cells used for the VM, and the builtin, giving an improvement in performance. diff --git a/docs/src/starks/cairo.md b/docs/src/starks/cairo.md deleted file mode 100644 index e669b2514..000000000 --- a/docs/src/starks/cairo.md +++ /dev/null @@ -1 +0,0 @@ -# Cairo diff --git a/docs/src/starks/cairo_cli.md b/docs/src/starks/cairo_cli.md deleted file mode 100644 index 3f213d436..000000000 --- a/docs/src/starks/cairo_cli.md +++ /dev/null @@ -1 +0,0 @@ -# CLI diff --git a/docs/src/starks/cairo_rap.md b/docs/src/starks/cairo_rap.md deleted file mode 100644 index 0037a51f9..000000000 --- a/docs/src/starks/cairo_rap.md +++ /dev/null @@ -1,45 +0,0 @@ -## Extended columns - -The verifier sends challenges $\alpha, z \in \mathbb{F}$ (or the prover samples them from the transcript). Additional columns are added to incorporate the memory constraints. To define them the prover follows these steps: -1. Stack the rows of the submatrix of $T$ defined by the columns `pc, dst_addr, op0_addr, op1_addr` into a vector `a` of length $2^{n+2}$ (this means that the first entries of `a` are `pc[0], dst_addr[0], op0_addr[0], op1_addr[0], pc[1], dst_addr[1],...`). -1. Stack the the rows of the submatrix defined by the columns `inst, dst, op0, op1` into a vector `v` of length $2^{n+2}$. -1. Define $M_{\text{Mem}}\in\mathbb{F}^{2^{n+2}\times 2}$ to be the matrix with columns $a$, $v$. -1. Define $M_{\text{MemRepl}}\in\mathbb{F}^{2^{n+2}\times 2}$ to be the matrix that's equal to $M_{\text{Mem}}$ in the first $2^{n+2} - L_{\text{pub}}$ rows, and its last $L_{\text{pub}}$ entries are the addresses and values of the actual public memory (program code). -1. Sort $M_{\text{MemRepl}}$ by the first column in increasing order. The result is a matrix $M_{\text{MemReplSorted}}$ of size $2^{n+2}\times 2$. Denote its columns by $a'$ and $v'$. -1. Compute the vector $p$ of size $2^{n+2}$ with entries -$$ p_i := \prod_{j=0}^i\frac{z - (a_i' + \alpha v_i')}{z - (a_i + \alpha v_i)}$$ -1. Reshape the matrix $M_{\text{MemReplSorted}}$ into a $2^n\times 8$ in row-major. Reshape the vector $p$ into a $2^n \times 4$ matrix in row-major. -1. Concatenate these 12 rows. The result is a matrix $M_\text{MemRAP2}$ of size $2^n \times 12$ - -The verifier sends challenge $z' \in \mathbb{F}$. Further columns are added to incorporate the range check constraints following these steps: - -1. Stack the rows of the submatrix of $T$ defined by the columns in the group `offsets` into a vector $b$ of length $3\cdot 2^n$. -1. Sort the values of $b$ in increasing order. Let $b'$ be the result. -1. Compute the vector $p'$ of size $3\cdot 2^n$ with entries -$$ p_i' := \prod_{j=0}^i\frac{z' - b_i'}{z' - b_i}$$ -1. Reshape $b'$ and $p'$ into matrices of size $2^n \times 3$ each and concatenate them into a matrix $M_\text{RangeCheckRAP2}$ of size $2^n \times 6$. -1. Concatenate $M_\text{MemRAP2}$ and $M_\text{RangeCheckRAP2}$ into a matrix $M_\text{RAP2}$ of size $2^n \times 18$. - - -Using the notation described at the beginning, $m'=33$, $m''=18$ and $m=52$. They are respectively the columns of the first and second part of the rap, and the total number of columns. - - -Putting all together, the final layout of the trace is the following - -``` - A. flags (16) : Decoded instruction flags - B. res (1) : Res value - C. pointers (2) : Temporary memory pointers (ap and fp) - D. mem_a (4) : Memory addresses (pc, dst_addr, op0_addr, op1_addr) - E. mem_v (4) : Memory values (inst, dst, op0, op1) - F. offsets (3) : (off_dst, off_op0, off_op1) - G. derived (3) : (t0, t1, mul) - H. mem_a' (4) : Sorted memory addresses - I. mem_v' (4) : Sorted memory values - J. mem_p (4) : Memory permutation argument columns - K. offsets_b' (3) : Sorted offset columns - L. offsets_p' (3) : Range check permutation argument columns - - A B C D E F G H I J K L -|xxxxxxxxxxxxxxxx|x|xx|xxxx|xxxx|xxx|xxx|xxxx|xxxx|xxxx|xxx|xxx| -``` diff --git a/docs/src/starks/cairo_trace_descriptive.md b/docs/src/starks/cairo_trace_descriptive.md deleted file mode 100644 index 52e6adea0..000000000 --- a/docs/src/starks/cairo_trace_descriptive.md +++ /dev/null @@ -1,172 +0,0 @@ -# Cairo execution trace - -## Raw materials - -After the execution of a Cairo program in the Cairo VM, three files are generated that are the core components for the construction of the execution trace, needed for the proving system: - -* **trace file**: Has the information on the state of the three Cairo VM registers `ap`, -`fp`, and `pc` at every cycle of the execution of the program. To reduce ambiguity in terms, -we should call these the *register states* of the Cairo VM, and leave the term *trace* to -the final product that is passed to the prover to generate a proof. -* **memory file**: A file with the information of the VM's memory at the end of the program -run, after the memory has been relocated. -* **public inputs**: A file with all the information that must be publicly available to the prover -and verifier, such as the total number of execution steps, public memory, used builtins and -their respective addresses range in memory. - -The next section will explain in detail how these elements are used to build the final execution -trace. - -## Construction details - -The execution trace is built in two stages. In the first one, the information on the files -described in the previous section is aggregated to build a main trace table. -In the second stage, there is an interaction with the verifier to add some extension -columns to the main trace. - -### Main trace construction - -The layout of the main execution trace is as follows: -``` - A. flags (16): Decoded instruction flags - B. res (1): Res value - C. pointers (2): Temporary memory pointers (ap and fp) - D. mem_a (4): Memory addresses (pc, dst_addr, op0_addr, op1_addr) - E. mem_v (4): Memory values (inst, dst, op0, op1) - F. offsets (3) : (off_dst, off_op0, off_op1) - G. derived (3) : (t0, t1, mul) - - A B C D E F G -|xxxxxxxxxxxxxxxx|x|xx|xxxx|xxxx|xxx|xxx| -``` -Each letter from A to G represents some subsection of columns, and the number specifies how many -columns correspond to that subsection. - -#### Cairo instructions -It is important to have in mind the information that each executed Cairo instruction holds, since it -is a key component of the construction of the execution trace. For a detailed explanation of how the -building components of the instruction interact to change the VM state, refer to the Cairo -whitepaper, sections 4.4 and 4.5. - -Structure of the 63-bit that forms the first word of each instruction: -``` - ┌─────────────────────────────────────────────────────────────────────────┐ - │ off_dst (biased representation) │ - ├─────────────────────────────────────────────────────────────────────────┤ - │ off_op0 (biased representation) │ - ├─────────────────────────────────────────────────────────────────────────┤ - │ off_op1 (biased representation) │ - ├─────┬─────┬───────┬───────┬───────────┬────────┬───────────────────┬────┤ - │ dst │ op0 │ op1 │ res │ pc │ ap │ opcode │ 0 │ - │ reg │ reg │ src │ logic │ update │ update │ │ │ - ├─────┼─────┼───┬───┼───┬───┼───┬───┬───┼───┬────┼────┬────┬────┬────┼────┤ - │ 0 │ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │ 8 │ 9 │ 10 │ 11 │ 12 │ 13 │ 14 │ 15 │ - └─────┴─────┴───┴───┴───┴───┴───┴───┴───┴───┴────┴────┴────┴────┴────┴────┘ - ``` - -#### Columns -The construction of the following columns corresponds to a colloquial explanation of what is done in -the `build_cairo_execution_trace` function. - -##### Section A - Flags -The flags section **A** corresponds to the 16 bits that represent the configuration of the `dst_reg`, -`op0_reg`, `op1_src`, `res_logic`, `pc_update`, `ap_update` and `opcode` flags, as well as the zero -flag. So there is one column for each bit of the flags decomposition. - -##### Section C - Temporary memory pointers -The two columns in this section, as well as the `pc` column from section **D**, are the most trivial. -For each step of the register states, the corresponding values are added to the columns, which are -pointers to some memory cell in the VM's memory. - -##### Section D - Memory addresses -As already mentioned, the first column of this section, `pc`, is trivially obtained from the register -states for each cycle. -Columns `dst_addr`, `op0_addr`, and `op1_addr` from section **D** are addresses constructed from pointers -stored at `ap` or `fp`, and their respective offsets `off_dst`, `off_op0` and `off_op1`. The exact way these -are computed depends on the particular values of the flags for each instruction. - -##### Section E - Memory values -The `inst` column, is obtained by fetching in memory the value stored at pointer `pc`, which -corresponds to a 63-bit Cairo instruction. -Columns `dst`, `op0`, and `op1` are computed by fetching in memory by their respective addresses. - -##### Section F - Offsets/Range-checked values -These columns represent integer values that are used to construct addresses `dst_addr`, `op0_addr` and -`op1_addr` and are decoded directly from the instruction. -These values have the property to be numbered in the range from 0 to 2^16. - -##### Section B - Res -This column is computed depending on the decoded `opcode` and `res_logic` of every instruction. -In some cases, `res` is unused in the instruction, and the value for (`dst`)^(-1) is used in that -place as an optimization. - -##### Section G - Derived -To have constraints of max degree two, some more columns are derived from the already calculated, -`t0`, `t1`, and `mul`: -* `t0` is the product of the values of `DST` and the `PC_JNZ` flag for each step. -* `t1` is the product of `t0` and `res` for each step. -* `mul` is the product of `op0` and `op1` for each step. -#### Range check and Memory holes - -For the values constrained between ranges $0$ and $2^16$, the offsets, the prover uses a permutation argument to optimize enforcing this. In particular, it checks an ordered list with the offsets are the same as the original one, is continuous, the first value is $rc_{min}$, and the last one is less than $rc_{max}$. - -Since not all values are used, there may be unused values, and so the ordered offset may not be continuous. These unused values are called holes, and they need to be filled with the missing values, so the checks can be done. - -This is explained in section 9.9 of the Cairo Paper - -In the case of memory, something similar happens, where the values should be continuous, but if there are built-ins, this may not be the case. For example, the built-in may be using addresses in ranges higher than the ones used by the program. - -To fix this, holes in the memory cells are filled, just like the ones of the RC. - -It's something important to note that when filling the holes, we can't use dedicated columns like `op0_addr`, since this would break the constraints. For this to work, we either need new columns for holes, or make use of subcolumns, which are explained in their dedicated section. - -No matter which approach is used , either by subcolumns or columns, we will need cells where the constraints of the range check and memory are applied, but not the specific ones related to the instructions. - -Finally, using these columns, we can fill the holes without breaking the constraint system. - -#### Dummy memory accesses - -As part of proving the execution of a Cairo program, we need to prove that memory used by the program extends the public memory. This is important since public memory contains for example the bytecode that was executed and the outputs. - -The bytecode is something critical for the verifier to not only know that something was executed correctly but to know what was executed. - -To do this, we the permutation check, which that proves the memory is continuous and single-valued - -To do this, the permutation check of the memory is modified. - -Initially, we had $ M $, $ V $ and $ M' $, $ V' $ pairs of memory addresses and values, where $ M $ and $ V $ are the values as used and without order, and $ M' $, $ V' $ the pairs ordered by address. - -For the public memory, we will add it directly to $ M' $, $ V' $. We also need to add dummy accesses to the pairs $ M $ $ V $. These dummy accesses are just pairs of $(M, V) = (0,0)$. - -This change makes the statement that $ (M', V') $ is a permutation of $ (M, V) $, which means that they are the same values in a different order, no longer true. This means that the two cumulative products used to check the statement are no longer equal, and their division - -Luckily, we can know the new expected value on the verifier, since we have access to the public memory. Even more, it's this fact that enables the verifier to check the memory is an extension of the public memory. - -The math is quite straightforward. When the memory was the same, we expected a final value $ p_{n-1} = 1 $. This came from two cumulative products that should equal, one from the unordered pairs, and one from the ordered ones. - -Now, adding zeros to one side and the real values to the other unbalances the cumulative products, so the verifier will need to balance it by dividing $ p_{n-1} $ by the extra factors that appeared with the addition of the public memory. Doing so will make the final value $ 1 $ again. - -Since they only depend on the public memory, the verifier has enough data to recalculate them and use them. Even more, if the prover lies, the equality that the verifier is expecting won't hold. - -In reality, instead of dividing and expecting the result to equal to $ 1 $, we can just check the equality against the new expected value, and avoid doing that inversion. - -All of this is explained in section 9.8 of the Cairo paper. - -#### Trace extension / Padding - -The last step is padding the trace to a power of two for efficiency. We may also need to pad the trace if for some reason some unbalance is given by the layout. - -For this, we will copy the last executed instruction until reaching the desired length. - -But there's a trick. If the last executed instruction is any instruction, and it's copied, the transition constraints won't be satisfied. To be able to do this, we need to use something called "proof mode". In proof mode, the main function of the program is wrapped in another one, which calls it and returns to an infinite loop. This loop is a jump relative 0. - -Since this loop can be executed many times without changing the validity of the trace, it can be copied as many times as -needed, solving the issues mentioned before. - -#### Summary - -To construct the execution trace, we augment the `RegisterStates` with the information obtained from the Memory. This includes decoding instruction of each steps, and writing all the data needed to check the execution is valid. - -Additionally, memory holes have to be filled, public memory added, and a final pad using an infinite loop is needed for everything to work properly. - -Adding all of that, we create an execution trace that's ready for the prover to generate a proof. diff --git a/docs/src/starks/cairo_trace_succinct.md b/docs/src/starks/cairo_trace_succinct.md deleted file mode 100644 index 740095196..000000000 --- a/docs/src/starks/cairo_trace_succinct.md +++ /dev/null @@ -1,27 +0,0 @@ -# Trace - -The execution of a Cairo program produces a memory vector $V$ and a matrix $M$ of size $L \times 3$ with the evolution of the three registers `pc`, `ap`, `fp`. All of them with entries in $\mathbb{F}$. - -## Construction of execution trace $T$: -In this section we describe the construction of the execution trace $T$. This is the matrix mentioned [here](#definitions) in the description of the STARK protocol - -1. Augment each row of $M$ with information about the pointed instruction as follows: For each entry $(\text{pc}_i, \text{ap}_i, \text{fp}_i)$ of $M$, unpack the $\text{pc}_i$-th value of $V$. The result is a new matrix $M \in \mathbb{F}^{L\times 33}$ with the following layout -``` - A. flags (16) : Decoded instruction flags - B. res (1) : Res value - C. pointers (2) : Temporary memory pointers (ap and fp) - D. mem_a (4) : Memory addresses (pc, dst_addr, op0_addr, op1_addr) - E. mem_v (4) : Memory values (inst, dst, op0, op1) - F. offsets (3) : (off_dst, off_op0, off_op1) - G. derived (3) : (t0, t1, mul) - - A B C D E F G -|xxxxxxxxxxxxxxxx|x|xx|xxxx|xxxx|xxx|xxx| -``` -1. Let $r_\text{min}$ and $r_\text{max}$ be respectively the minimum and maximum values of the entries of the submatrix $M_\text{offsets}$ defined by the columns of the group `offsets`. Let $v$ be the vector of all the values between $r_\text{min}$ and $r_\text{max}$ that are not in $M_\text{offsets}$. If the length of $v$ is not a multiple of three, extend it to the nearest multiple of three using one arbitrary value of $v$. - -1. Let $R$ be the last row of $M$, and let $R'$ be the vector that's equal to $R$ except that it has zeroes in entries corresponding to the ordered set of columns `mem_a` and `mem_v`. The set is ordered incrementally by `mem_a`. Let $L_{\text{pub}}$ be the length of the public input (program code). Extend $M$ with additional $L':=\lceil L_{\text{pub}}/4 \rceil$ rows to obtain a matrix $M \in \mathbb{F}^{(L + L')\times 33}$ by appending copies of $R'$ at the bottom (the notation $\lceil x \rceil$ means the _ceiling function_, defined as the smallest integer that is not smaller than $x$). - -1. Let $R''$ be the vector that's equal to $R$ except that it has zeroes in entries corresponding to the set of columns `mem_a` and `mem_v`, let $M_\text{addr}$ be the submatrix defined by the columns of the group `addresses`, let $L'' = (L''_0, L''_1, ..., L''_J)^T$ the submatrix that asserts $M_\text{addr,i,j} < L''_\text{0,j}$, $L''_\text{I,j} < M_\text{addr,i+1,j}$ where $M_\text{addr,i+1,j} - M_\text{addr,i,j} > 1$ and $0 \le j \le J$ and $I = |L''_j|$. Extend $M$ with additional $L''$ rows to obtain a matrix $M \in \mathbb{F}^{(L + L' + L'')\times 33}$ by appending copies of $R''$ at the bottom. - -1. Pad $M$ with copies of its last row until it has a power of two number of rows. As a result we obtain a matrix $T\in\mathbb{F}^{2^n\times 33}$. diff --git a/docs/src/starks/implementation.md b/docs/src/starks/implementation.md deleted file mode 100644 index 6ad12e37c..000000000 --- a/docs/src/starks/implementation.md +++ /dev/null @@ -1,5 +0,0 @@ -# STARKs Prover Lambdaworks Implementation - -The goal of this section will be to go over the details of the implementation of the proving system. To this end, we will follow the flow the example in the `recap` chapter, diving deeper into the code when necessary and explaining how it fits into a more general case. - -This implementation couldn't be done without checking Facebook's [Winterfell](https://github.com/facebook/winterfell) and Max Gillett's [Giza](https://github.com/maxgillett/giza). We want to thank everyone involved in them, along with Shahar Papini and Lior Goldberg from Starkware who also provided us valuable insight. diff --git a/docs/src/starks/protocol.md b/docs/src/starks/protocol.md deleted file mode 100644 index 679e04909..000000000 --- a/docs/src/starks/protocol.md +++ /dev/null @@ -1,292 +0,0 @@ -# STARKs protocol - -In this section we describe precisely the STARKs protocol used in Lambdaworks. - -We begin with some additional considerations and notation for most of the relevant objects and values to refer to them later on. - -### Grinding -This is a technique to increase the soundness of the protocol by adding proof of work. It works as follows. At some fixed point in the protocol, the prover needs to find a string `nonce` such that `H(H(prefix || state || grinding_factor) || nonce)` has `grinding_factor` number of zeros to the left, where `H` is a hash function, `prefix` is the bit-string `0x0123456789abcded` and `state` is the state of the transcript. Here `x || y` denotes the concatenation of the bit-strings `x` and `y`. - -### Transcript - -The Fiat-Shamir heuristic is used to make the protocol noninteractive. We assume there is a transcript object to which values can be added and from which challenges can be sampled. - -## General notation - -- $\mathbb{F}$ denotes a finite field. -- Given a vector $D=(y_1,\dots,y_L)$ and a function $f:\text{set}(D) \to \mathbb{F}$, denote by $f(D)$ the vector $(f(y_1),\dots,f(y_L))$. Here $\text{set}(D)$ denotes the underlying set of $A$. -- A polynomial $p \in \mathbb{F}[X]$ induces a function $f:A \to \mathbb{F}$ for every subset $A$ of $\mathbb{F}$, where $f(a) := p(a)$. -- Let $p, q \in \mathbb{F}[X]$ be two polynomials. A function $f: A \to \mathbb{F}$ can be induced from them for every subset $A$ disjoint from the set of roots of $q$, defined by $f(a) := p(a) q(a)^{-1}$. We abuse notation and denote $f$ by $p/q$. - -## Definitions - -We assume the prover has already obtained the trace of the execution of the program. This is a matrix $T$ with entries in a finite field $\mathbb{F}$. We assume the number of rows of $T$ is $2^n$ for some $n$ in $\mathbb{N}$. - -#### Values known by the prover and verifier prior to the interactions - -These values are determined the program, the specifications of the AIR being used and the security parameters chosen. - -- $m'$ is the number of columns of the trace matrix $T$. -- $r$ the number of RAP challenges. -- $m''$ is the number of extended columns of the trace matrix in the (optional) second round of RAP. -- $m$ is the total number of columns: $m := m' + m''$. -- $P_k^T$ denote the transition constraint polynomials for $k=1,\dots,n_T$. We are assuming these are of degree at most 2. -- $Z_j^T$ denote the transition constraint zerofiers for $k=1,\dots,n_T$. -- $b=2^l$ is the *[blowup factor](/starks/protocol_overview.html#fri)*. -- $c$ is the *grinding factor*. -- $Q$ is number of FRI queries. -- We assume there is a fixed hash function from $\mathbb{F}$ to binary strings. We also assume all Merkle trees are constructed using this hash function. - -#### Values computed by the prover -These values are computed by the prover from the execution trace and are sent to the verifier along with the proof. -- $2^n$ is the number of rows of the trace matrix after RAP. -- $\omega$ a primitive $2^{n+l}$-th root of unity. -- $g = \omega^{b}$. -- An element $h\in\mathbb{F} \setminus \{\omega^i\}_{i \geq 0}$. This is called the *coset factor*. -- Boundary constraints polynomials $P_j^B$ for $j=1,\dots,m$. -- Boundary constraint zerofiers $Z_j^B$ for $j=1,\dots,m$.. - -#### Derived values -Both prover and verifier compute the following. - -- The interpolation domain: the vector $D_S=(1, g, \dots, g^{2^n-1})$. -- The Low Degree Extension $D_{\text{LDE}} =(h, h\omega, h\omega^2,\dots, h\omega^{2^{n+l} - 1})$. Recall $2^l$ is the blowup factor. - -### Notation of important operations -#### Vector commitment scheme - -Given a vector $A=(y_0, \dots, y_L)$. The operation $\text{Commit}(A)$ returns the root $r$ of the Merkle tree that has the hash of the elements of $A$ as leaves. - -For $i\in[0,2^{n+k})$, the operation $\text{Open}(A, i)$ returns the pair $(y_i, s)$, where $s$ is the authentication path to the Merkle tree root. - -The operation $\text{Verify}(i,y,r,s)$ returns _Accept_ or _Reject_ depending on whether the $i$-th element of $A$ is $y$. It checks whether the authentication path $s$ is compatible with $i$, $y$ and the Merkle tree root $r$. - - -In our cases the sets $A$ will be of the form $A=(f(a), f(ab), f(ab^2), \dots, f(ab^L))$ for some elements $a,b\in\mathbb{F}$. It will be convenient to use the following abuse of notation. We will write $\text{Open}(A, ab^i)$ to mean $\text{Open}(A, i)$. Similarly, we will write $\text{Verify}(ab^i, y, r, s)$ instead of $\text{Verify}(i, y, r, s)$. Note that this is only notation and $\text{Verify}(ab^i, y, r, s)$ is only checking that the $y$ is the $i$-th element of the commited vector. - -##### Batch -As we mentioned in the [protocol overview](protocol_overview.html#batch). When committing to multiple vectors $A_1, \dots, A_k$, where $A_i = (y_0^{(i), \dots, y_L^{(i)}})$ one can build a single Merkle tree. Its $j$-th leaf is the concatenation of all the $j$-th coordinates of all vectors, that is, $(y_j^{(1)}||\dots||y_j^{(k)})$. The commitment to this batch of vectors is the root of this Merkle tree. - -## Protocol - -### Prover - -#### Round 0: Transcript initialization - -- Start a new transcript. -- (Strong Fiat Shamir) Add to it all the public values. - -#### Round 1: Arithmetization and commitment of the execution trace - -##### Round 1.1: Commit main trace - -- For each column $M_j$ of the execution trace matrix $T$, interpolate its values at the domain $D_S$ and obtain polynomials $t_j$ such that $t_j(g^i)=T_{i,j}$. -- Compute $[t_j] := \text{Commit}(t_j(D_{\text{LED}}))$ for all $j=1,\dots,m'$ (*Batch commitment optimization applies here*). -- Add $[t_j]$ to the transcript in increasing order. - -##### Round 1.2: Commit extended trace - -- Sample random values $a_1,\dots,a_l$ in $\mathbb{F}$ from the transcript. -- Use $a_1,\dots,a_l$ to build $M_{\text{RAP2}}\in\mathbb{F}^{2^n\times m''}$ following the specifications of the RAP process. -- For each column $\hat M_j$ of the matrix $M_{\text{RAP2}}$, interpolate its values at the domain $D_S$ and obtain polynomials $t_{m'+1}, \dots, t_{m' + m''}$ such that $t_j(g^i)=\hat M_{i,j}$. -- Compute $[t_j] := \text{Commit}(t_j(D_{\text{LED}}))$ for all $j=m'+1,\dots,m'+m''$ (*Batch commitment optimization applies here*). -- Add $[t_j]$ to the transcript in increasing order for all $j=m'+1,\dots,m'+m''$. - -#### Round 2: Construction of composition polynomial $H$ - -- Sample $\beta_1^B,\dots,\beta_{m}^B$ in $\mathbb{F}$ from the transcript. -- Sample $\beta_1^T,\dots,\beta_{n_T}^T$ in $\mathbb{F}$ from the transcript. -- Compute $B_j := \frac{t_j - P^B_j}{Z_j^B}$. -- Compute $C_k := \frac{P^T_k(t_1, \dots, t_m, t_1(gX), \dots, t_m(gX))}{Z_k^T}$. -- Compute the _composition polynomial_ - $$H := \sum_{k} \beta_k^TC_k + \sum_j \beta_j^BB_j$$ -- Decompose $H$ as - $$H = H_1(X^2) + XH_2(X^2)$$ -- Compute commitments $[H_1]$ and $[H_2]$ (*Batch commitment optimization applies here*). -- Add $[H_1]$ and $[H_2]$ to the transcript. - -#### Round 3: Evaluation of polynomials at $z$ - -- Sample from the transcript until obtaining $z\in\mathbb{F}\setminus D_{\text{LDE}}$. -- Compute $H_1(z^2)$, $H_2(z^2)$, and $t_j(z)$ and $t_j(gz)$ for all $j$. -- Add $H_1(z^2)$, $H_2(z^2)$, and $t_j(z)$ and $t_j(gz)$ for all $j$ to the transcript. - -#### Round 4: Run batch open protocol - -- Sample $\gamma$, $\gamma'$, and $\gamma_1,\dots,\gamma_m$, $\gamma_1',\dots,\gamma_m'$ in $\mathbb{F}$ from the transcript. -- Compute $p_0$ as $$\gamma\frac{H_1 - H_1(z^2)}{X - z^2} + \gamma'\frac{H_2 - H_2(z^2)}{X - z^2} + \sum_j \gamma_j\frac{t_j - t_j(z)}{X - z} + \gamma_j'\frac{t_j - t_j(gz)}{X - gz}$$ - -##### Round 4.1.k: FRI commit phase - -- Let $D_0:=D_{\text{LDE}}$. -- For $k=1,\dots,n$ do the following: - - Sample $\zeta_{k-1}$ from the transcript. - - Decompose $p_{k-1}$ into even and odd parts, that is, $p_{k-1}=p_{k-1}^{odd}(X^2)+ X p_{k-1}^{even}(X^2)$. - - Define $p_k:= p_{k-1}^{odd}(X) + \zeta_{k-1}p_{k-1}^{even}(X)$. - - If $k < n$: - - Let $L = |D_{k-1}|/2$. Define $D_{k}:=(d_0^2, \dots, d_{L-1}^2)$, where $D_{k-1}=(d_0, \dots, d_{2L-1})$. - - Let $[p_k]:=\text{Commit}(p_k(D_k))$. - - Add $[p_k]$ to the transcript. -- $p_n$ is a constant polynomial and therefore $p_n\in\mathbb{F}$. Add $p_n$ to the transcript. - -##### Round 4.2: Grinding -- Let $x$ be the internal state of the transcript. -- Compute $y$ such that $\text{Keccak256}(x || y)$ has $c$ leading zeroes. -- Add $y$ to the transcript. - -##### Round 4.3: FRI query phase - -- For $s=0,\dots,Q-1$ do the following: - - Sample random index $\iota_s \in [0, 2^{n+l-1}]$ from the transcript and let $\upsilon_s := h\omega^{\iota_s}$. - - Compute $\text{Open}(t_j(D_{\text{LDE}}), \upsilon_s)$ and $\text{Open}(t_j(D_{\text{LDE}}), -\upsilon_s)$ for all $j=1,\dots, m$. - - Compute $\text{Open}(H_1(D_{\text{LDE}}), \upsilon_s)$ and $\text{Open}(H_1(D_{\text{LDE}}), -\upsilon_s)$. - - Compute $\text{Open}(H_2(D_{\text{LDE}}), \upsilon_s)$ and $\text{Open}(H_2(D_{\text{LDE}}), -\upsilon_s)$. - - Compute $\text{Open}(p_k(D_k), \upsilon_s^{2^k})$ and $\text{Open}(p_k(D_k), -\upsilon_s^{2^k})$ for all $k=1,\dots,n-1$. - -#### Build proof - -- Send the proof to the verifier: -$$ -\begin{align} -\Pi = ( &\\ -&\{[t_j], t_j(z), t_j(gz): 0\leq j < m\}, \\ -&[H_1], H_1(z^2),[H_2], H_2(z^2), \\ -&\{[p_k]: 1\leq k < n\}, \\ -&p_n, \\ -&y, \\ -&\{\text{Open}(t_j(D_{\text{LDE}}), \upsilon_s): 0 \leq j< m, 0 \leq s < Q\}, \\ -&\{\text{Open}(H_1(D_{\text{LDE}}), \upsilon_s): 0 \leq s < Q\}, \\ -&\{\text{Open}(H_2(D_{\text{LDE}}), \upsilon_s): 0 \leq s < Q\}, \\ -&\{\text{Open}(p_k(D_k), \upsilon_s^{2^k}): 1\leq k< n, 0\leq s < Q\}, \\ -&\{\text{Open}(t_j(D_{\text{LDE}}), -\upsilon_s): 0 \leq j< m, 0 \leq s < Q\}, \\ -&\{\text{Open}(H_1(D_{\text{LDE}}), -\upsilon_s): 0 \leq s < Q\}, \\ -&\{\text{Open}(H_2(D_{\text{LDE}}), -\upsilon_s): 0 \leq s < Q\}, \\ -&\{\text{Open}(p_k(D_k), -\upsilon_s^{2^k}): 1\leq k< n, 0\leq s < Q\}, \\ -) & -\end{align} -$$ - -### Verifier -From the point of view of the verifier, the proof they receive is a bunch of values that may or may not be what they claim to be. To make this explicit, we avoid denoting values like $t_j(z)$ as such, because that implicitly assumes that the value was obtained after evaluating a polynomial $t_j$ at $z$. And that's something the verifier can't assume. We use the following convention. - -- Bold capital letters refer to commitments. For example $\mathbf{T}_j$ is the claimed commitment $[t_j]$. -- Greek letters with superscripts refer to claimed function evaluations. For example $\tau_j^z$ is the claimed evaluation $t_j(z)$ and $\tau_j^{gz}$ is the claimed evaluation of $t_j(gz)$. Note that field elements in superscripts never indicate powers. They are just notation. -- Gothic letters refer to authentication paths. For example $\mathfrak{T}_j$ is the authentication path of a opening of $t_j$. -- Recall that every opening $\text{Open}(A, i)$ is a pair $(y, s)$, where $y$ is the claimed value at index $i$ and $s$ is the authentication path. So for example, $\text{Open}(t_j(D_{\text{LDE}}), \upsilon_s)$ is denoted as $(\tau_j^{\upsilon_s}, \mathfrak{T}_j)$ from the verifier's end. - -#### Input -This is the proof using the notation described above. The elements appear in the same exact order as they are in the [Prover](#build-proof) section, serving also as a complete reference of the meaning of each value. - -$$ -\begin{align} -\Pi = ( &\\ -&\{\mathbf{T}_j, \tau_j^z, \tau_j^{gz}: 0\leq j < m\}, \\ -&\mathbf{H}_1, \eta_1^{z^2},\mathbf{H}_2, \eta_2^{z^2}, \\ -&\{\mathbf{P}_k: 1\leq k < n\}, \\ -&\pi, \\ -&y, \\ -&\{(\tau_j^{\upsilon_s}, \mathfrak{T}_j): 0 \leq j< m, 0 \leq s < Q\}, \\ -&\{(\eta_1^{\upsilon_s}, \mathfrak{H}_1): 0 \leq s < Q\}\\ -&\{(\eta_2^{\upsilon_s}, \mathfrak{H}_2): 0 \leq s < Q\},\\ -&\{(\pi_k^{\upsilon_s^{2^k}}, \mathfrak{P}_k): 1\leq k< n, 0\leq s < Q\}, \\ -&\{(\tau_j^{-\upsilon_s}, \mathfrak{T}_j'): 0 \leq j< m, 0 \leq s < Q\}, \\ -&\{(\eta_1^{-\upsilon_s}, \mathfrak{H}_1'): 0 \leq s < Q\}\\ -&\{(\eta_2^{-\upsilon_s}, \mathfrak{H}_2'): 0 \leq s < Q\},\\ -&\{(\pi_k^{-\upsilon_s^{2^k}}, \mathfrak{P}_k'): 1\leq k< n, 0\leq s < Q\}, \\ -) & -\end{align} -$$ - -#### Step 1: Replay interactions and recover challenges - -- Start a transcript -- (Strong Fiat Shamir) Add all public values to the transcript. -- Add $\mathbf{T}_j$ to the transcript for all $j=1, \dots, m'$. -- Sample random values $a_1, \dots, a_l$ from the transcript. -- Add $\mathbf{T}_j$ to the transcript for $j=m' +1, \dots, m' + m''$. -- Sample $\alpha_1^B,\dots,\alpha_{m}^B$ and $\beta_1^B,\dots,\beta_{m}^B$ in $\mathbb{F}$ from the transcript. -- Sample $\alpha_1^T,\dots,\alpha_{n_T}^T$ and $\beta_1^T,\dots,\beta_{n_T}^T$ in $\mathbb{F}$ from the transcript. -- Add $\mathbf{H}_1$ and $\mathbf{H}_2$ to the transcript. -- Sample $z$ from the transcript. -- Add $\eta_1^{z^2}$, $\eta_2^{z^2}$, $\tau_j^z$ and $\tau_j^{gz}$ to the transcript. -- Sample $\gamma$, $\gamma'$, and $\gamma_1, \dots, \gamma_m, \gamma'_1, \dots, \gamma'_m$ from the transcript. -- For $k=1, \dots, n$ do the following: - - Sample $\zeta_{k-1}$ - - If $k < n$: add $\mathbf{P}_k$ to the transcript -- Add $\pi$ to the transcript. -- Add $y$ to the transcript. -- For $s=0, \dots, Q-1$: - - Sample random index $\iota_s \in [0, 2^{n+l-1}]$ from the transcript and let $\upsilon_s := h\omega^{\iota_s}$. - -#### Verify grinding: -Check that $\text{Keccak256}(x || y)$ has $c$ leading zeroes. - - -#### Step 2: Verify claimed composition polynomial - -- Compute $h := \eta_1^{z^2} + z \eta_2^{z^2}$ -- Compute $b_j := \frac{\tau_j^z - P^B_j(z)}{Z_j^B(z)}$ -- Compute $c_k := \frac{P^T_k(\tau_1^z, \dots, \tau_m^z, \tau_1^{gz}, \dots, \tau_m^{gz})}{Z_k^T(z)}$ -- Verify - $$h = \sum_{k} \beta_k^Tc_k + \sum_j \beta_j^Bb_j$$ - -#### Step 3: Verify FRI - -- Reconstruct the deep composition polynomial values at $\upsilon_s$ and $-\upsilon_s$. That is, define - $$\begin{align}\pi_0^{\upsilon_s}&:= - \gamma\frac{\eta_1^{\upsilon_s} - \eta_1^{z^2}}{\upsilon_s - z^2} + \gamma'\frac{\eta_2^{\upsilon_s} - \eta_2^{z^2}}{\upsilon_s - z^2} + \sum_j \gamma_j\frac{\tau_j^{\upsilon_s} - \tau_j^{z}}{\upsilon_s - z} + \gamma_j'\frac{\tau_j^{\upsilon_s} - \tau_j^{gz}}{\upsilon_s - gz}, \\ - \pi_0^{-\upsilon_s}&:= - \gamma\frac{\eta_1^{-\upsilon_s} - \eta_1^{z^2}}{-\upsilon_s - z^2} + \gamma'\frac{\eta_2^{-\upsilon_s} - \eta_2^{z^2}}{-\upsilon_s - z^2} + \sum_j \gamma_j\frac{\tau_j^{-\upsilon_s} - \tau_j^{z}}{-\upsilon_s - z} + \gamma_j'\frac{\tau_j^{-\upsilon_s} - \tau_j^{gz}}{-\upsilon_s - gz}. - \end{align} - $$ -- For all $s=0,\dots,Q-1$: - - For all $k=0,\dots,n-1$: - - Check that $\text{Verify}((\upsilon_s^{2^k}, \pi_k^{-\upsilon_s^{2^k}}), \mathbf{P}_k, \mathfrak{P}_k)$ and $\text{Verify}((-\upsilon_s^{2^k}, \pi_k^{-\upsilon_s^{2^k}}), \mathbf{P}_k, \mathfrak{P}_k')$ are _Accept_. - - Solve the following system of equations on the variables $G, H$ - $$ - \begin{aligned} - \pi_k^{\upsilon_s^{2^{k}}} &= G + \upsilon_s^{2^k}H \\ - \pi_k^{-\upsilon_s^{2^{k}}} &= G - \upsilon_s^{2^k}H - \end{aligned} - $$ - - If $k < n - 1$, check that $\pi_{k+1}^{\upsilon_s^{2^{k+1}}}$ equals $G + \zeta_{k}H$ - - If $k = n$, check that $\pi$ equals $G + \zeta_{k}H$. - -#### Step 4: Verify trace and composition polynomials openings - -- For $s=0,\dots,Q-1$ do the following: - - Check that the following are all _Accept_: - - $\text{Verify}((\upsilon_s, \tau_j^{\upsilon_s}), \mathbf{T}_j, \mathfrak{T}_j)$ for all $0\leq j < m$. - - $\text{Verify}((\upsilon_s, \eta_1^{\upsilon_s}), \mathbf{H}_1, \mathfrak{h}_1)$. - - $\text{Verify}((\upsilon_s, \eta_2^{\upsilon_s}), \mathbf{H}_2, \mathfrak{h}_2)$. - - $\text{Verify}((-\upsilon_s, \tau_j^{\upsilon_s}), \mathbf{T}_j, \mathfrak{T}_j')$ for all $0\leq j < m$. - - $\text{Verify}((-\upsilon_s, \eta_1^{\upsilon_s}), \mathbf{H}_1, \mathfrak{h}_1')$. - - $\text{Verify}((-\upsilon_s, \eta_2^{\upsilon_s}), \mathbf{H}_2, \mathfrak{h}_2')$. - - -## Notes on Optimizations and variants -### Sampling of challenges variant -To build the composition the prover samples challenges $\beta_k^T$ and $\beta_j^B$ for $k = 1,\dots,n_T$ and $j=1,\dots,m$. A variant of this is sampling a single challenge $\beta$ and defining $\beta_k^T$ and $\beta_j^B$ as powers of $\beta$. That is, define $\beta_k^T := \beta^{k-1}$ for $k=1,\dots,n_T$ and $\beta_j^B := \beta^{j + n_T - 1}$ for $j =1, \dots, m$. - -The same variant applies for the challenges $\gamma, \gamma', \gamma_j, \gamma_j'$ for $j = 1, \dots, m$ used to build the deep composition polynomial. In this case the variant samples a single challenge $\alpha$ and defines $\gamma_j := \alpha^j$, $\gamma_j' := \alpha^{j + m - 1}$ for all $j=1,\dots,m$, and $\gamma := \alpha^{2m}, \gamma' := \alpha^{2m+1}$. - -### Batch inversion -Inversions of finite field elements are slow. There is a very well known trick to batch invert many elements at once replacing inversions by multiplications. See [here](https://en.wikipedia.org/wiki/Modular_multiplicative_inverse#Multiple_inverses) for the algorithm. - -### FFT -One of the most computationally intensive operations performed is polynomial division. These can be optimized by utilizing [Fast Fourier Transform](http://web.cecs.pdx.edu/~maier/cs584/Lectures/lect07b-11-MG.pdf) (FFT) to divide each field element in Lagrange form. - -### Ruffini's rule -In specific scenarios, such as dividing by a polynomial of the form $X-a$, for example when building the deep composition polynomial, [Ruffini's rule](https://en.wikipedia.org/wiki/Ruffini%27s_rule) can be employed to further enhance performance. - -### Bit-reversal ordering of Merkle tree leaves -As one can see from inspecting the protocol, there are multiple times where, for a polynomial $p$, the prover sends both openings $\text{Open}(p(D), h\omega^i)$ and $\text{Open}(p(D), -h\omega^i)$. This implies, a priori, sending two authentication paths. Domains can be indexed using bit-reverse ordering to reduce this to a single authentication path for both openings, as follows. - -The natural way of building a Merkle tree to commit to a vector $(p(h), p(h\omega), p(h\omega^2), \dots, p(h\omega^{2^k-1}))$, is assigning the value $p(h\omega^i)$ to leaf $i$. If this is the case, the value $p(h\omega^i)$ is at position $i$ and the value $p(-h\omega^i)$ is at position $i + 2^{k-1}$. This is because $-1$ equals $\omega{2^{k-1}}$ for the value $\omega$ used in the protocol. - -Instead of this naive approach, a better solution is to assign the value $p(h\omega^{\sigma(i)})$ to leaf $i$, where $\sigma$ is the bit-reversal permutation. This is the permutation that maps $i$ to the index whose binary representation (padded to $k$ bits), is the binary representation of $i$ but in reverse order. For example, if $k=3$ and $i=1$, then its binary representation is $001$, which reversed is $100$. Therefore $\sigma(1) = 8$. In the same way $\sigma(0) = 0$ and $\sigma(2) = 4$. Check out the [wikipedia](https://en.wikipedia.org/wiki/Bit-reversal_permutation) article. With this ordering of the leaves, if $i$ is even, element $p(h\omega^{\sigma(i)})$ is at index $i$ and $p(-h\omega^{\sigma(i)})$ is at index $i + 1$. Which means that a single authentication path serves to validate both points simultaneously. - -### Redundant values in the proof -The prover opens the polynomials $p_k$ of the FRI layers at $\upsilon_s^{2^k}$ and $-\upsilon_s^{2^k}$ for all $k>1$. Later on, the verifier uses each of those pairs to reconstruct one of the values of the next layer, namely $p_{k+1}(\upsilon^{2^{k+1}})$. So there's no need to add the value $p_k(\upsilon^{2^{k+1}})$ to the proof, as the verifier reconstructs them. The prover only needs to send the authentication paths $\mathfrak{P}_k$ for them. - -The protocol is only modified at Step 3 of the verifier as follows. Checking that $\text{Verify}((\upsilon_s^{2^k}, \pi_k^{\upsilon_s^{2^k}}), \mathbf{P}_k, \mathfrak{P}_k)$ is skipped. After computing $x := G + \zeta_{k}H$, the verifier uses it to check that $\text{Verify}((\upsilon_s^{2^k}, x), \mathbf{P}_k, \mathfrak{P}_k)$ is _Accept_, which proves that $x$ is actually $\pi_k^{\upsilon_s^{2^k}}$, and continues to the next iteration of the loop. diff --git a/docs/src/starks/protocol_overview.md b/docs/src/starks/protocol_overview.md deleted file mode 100644 index deea73965..000000000 --- a/docs/src/starks/protocol_overview.md +++ /dev/null @@ -1,162 +0,0 @@ -# Protocol Overview - -In this section, we start diving deeper before showing the formal protocol. If you haven't done so, we recommend reading the "Recap" section first. - -At a high level, the protocol works as follows. The starting point is a matrix $T$ that encodes the trace of a valid execution of the program. This matrix needs to be in a particular format so that its correctness is equivalent to checking a finite number of polynomial equations on its rows. Transforming the execution to this matrix is what's called the arithmetization process. - -Then a single polynomial $F$ is constructed that encodes the set of all the polynomial constraints. The satisfiability of all these constraints is equivalent to $F$ being divisible by some public polynomial $G$. So the prover constructs $H$ as the quotient $F/G$ called the composition polynomial. - -Then the verifier chooses a random point $z$ and challenges the prover to reveal the values $F(z)$ and $H(z)$. Then the verifier checks that $H(z) = F(z)/G(z)$, which convinces him that the same relation holds at a level of polynomials and, in consequence, convinces the verifier that the private trace $T$ of the prover is valid. - -In summary, at a very high level, the STARK protocol can be organized into three major parts: - -- Arithmetization and commitment of execution trace. -- Construction and commitment of composition polynomial $H$. -- Opening of polynomials at random $z$. - -# Arithmetization - -As the Recap mentions, the trace is a table containing the system's state at every step. In this section, we will denote the trace as $T$. A trace can have several columns to store different aspects or features of a particular state at a specific moment. We will refer to the $j$-th column as $T_j$. You can think of a trace as a matrix $T$ where the entry $T_{ij}$ is the $j$-th element of the $i$-th state. - -Most proving systems' primary tool is polynomials over a finite field $\mathbb{F}$. Each column $T_j$ of the trace $T$ will be interpreted as evaluations of such a polynomial $t_j$. Consequently, any information about the states must be encoded somehow as an element in $\mathbb{F}$. - -To ease notation, we will assume here and in the protocol that the constraints encoding transition rules depend only on a state and the previous one. Everything can be easily generalized to transitions that depend on many preceding states. Then, constraints can be expressed as multivariate polynomials in $2m$ variables -$$P_k^T(X_1, \dots, X_m, Y_1, \dots, Y_m)$$ -A transition from state $i$ to state $i+1$ will be valid if and only if when we plug row $i$ of $T$ in the first $m$ variables and row $i+1$ in the second $m$ variables of $P_k^T$, we get $0$ for all $k$. In mathematical notation, this is -$$P_k^T(T_{i, 0}, \dots, T_{i, m}, T_{i+1, 0}, \dots, T_{i+1, m}) = 0 \text{ for all }k$$ - -These are called _transition constraints_ and check the trace's local properties, where local means relative to specific rows. There is another type of constraint, called _boundary constraint_, and denoted $P_j^B$. These enforce parts of the trace to take particular values. It is helpful, for example, to verify the initial states. - -So far, these constraints can only express the local properties of the trace. There are situations where the global properties of the trace need to be checked for consistency. For example, a column may need to take all values in a range but not in any predefined way. Several methods exist to express these global properties as local by adding redundant columns. Usually, they need to involve randomness from the verifier to make sense, and they turn into an interactive protocol called _Randomized AIR with Preprocessing_. - -# Polynomial commitment scheme - -To make interactions possible, a crucial cryptographic primitive is the Polynomial Commitment Scheme. This prevents the prover from changing the polynomials to adjust them to what the verifier expects. - -Such a scheme consists of the commit and the open protocols. STARK uses a univariate polynomial commitment scheme that internally combines a vector commitment scheme and a protocol called FRI. Let's begin with these two components and see how they build up the polynomial commitment scheme. - -## Vector commitments - -Given a vector $Y = (y_0, \dots, y_M)$, commiting to $Y$ means the following. The prover builds a Merkle tree out of it and sends its root to the verifier. The verifier can then ask the prover to reveal, or _open_, the value of the vector $Y$ at some index $i$. The prover won't have any choice except to send the correct value. The verifier will expect the corresponding value $y_i$ and the authentication path to the tree's root to check its authenticity. The authentication path also encodes the vector's position $i$ and its length $M$. - -The root of the Merkle tree is said to be the **commitment** of $Y$, and we denote it here by $[Y]$. - -## FRI - -In STARKs, all commited vectors are of the form $Y = (p(d_1), \dots, p(d_M))$ for some polynomial $p$ and some fixed domain $D = (d_1, \dots, d_M)$. The domain is always known to the prover and the verifier. It can be proved, as long as $M$ is less than the total number of field elements, that every vector $(y_0, \dots, y_M)$ is equal to $(p(d_1), \dots, p(d_M))$ for a unique polynomial $p$ of degree at most $M-1$. This is called the Lagrange interpolation theorem. It means, there is a unique polynomial of degree at most $M-1$ such that $p(d_i) = y_i$ for all $i$. And $M-1$ is an upper bound to the degree of $p$. It could be less. For example, the vector of all ones $Y = (1,1,\dots,1)$ is the evaluation of the constant polynomial $p = 1$, which has degree $0$. - -Suppose the vector $Y=(y_1, \dots, y_M)$ is the vector of evaluations of a polynomial $p$ of degree strictly less than $M-1$. Suppose one party holds the vector $Y$ and another party holds only the commitment $[Y]$ of it. The FRI protocol is an efficient interactive protocol with which the former can convince the latter that the commitment they hold corresponds to the vector of evaluations of a polynomial $p$ of degree strictly less than $M$. - -More precisely, the protocol depends on the following parameters - -- Powers of two $N = 2^n$ and $M = 2^m$ with $n < m$. -- A vector $D=(d_1,\dots,d_M)$, with $d_i = h\omega^i$, with $h$ a nonzero value in $\mathbb{F}$ and $\omega$ a primitive $M$-root of unity - -A prover holds a vector $Y=(y_1,\dots,y_M)$, and the verifier holds the commitment $[Y]$ of it. The result of the FRI protocol will be _Accept_ if the unique polynomial $p$ of degree less than $M-1$ such that $Y=(p(d_1),\dots,p(d_M))$ has degree less than $N-1$. Even more precisely, the protocol proves that $Y$ is very close to a vector $(p(d_1),\dots,p(d_M))$ with $p$ of degree less than $N-1$, but it may differ in negligible proportion of the coordinates. - -The number $b = M/N = 2^{m-n}$ is called the **blowup factor** and the security of the protocol depends in part on this parameter. The specific shape of the domain set $D$ has some symmetric properties important for the inner workings of FRI, such as $-d_i \in D$ for all $i$. -### Variant useful for STARKs - -FRI is usually described as above. In STARK, FRI is used as a building block for the polynomial commitment scheme of the next section. For that, a small variant of FRI is needed. - -Suppose the prover holds a vector $Y = (y_1, \dots, y_M)$ and the verifier holds its commitment $[Y]$ as before. Suppose further that both parties know a function $F$ that takes two field elements and outputs another field element. For example $F$ could be the function $F(a,b) = a + b^{-1}$. More precisely, the kind of functions we need are $F: \mathbb{F} \times D \to \mathbb{F}$. - -The protocol can be used to prove that the transformed vector $(F(y_1, d_1), \dots, F(y_M, d_M))$ is the vector of evaluations of a polynomial $q$ of degree at most $N-1$. Note that in this variant, the verifier holds originally the commitment of the vector $Y$ and not the commitment of the transformed vector. In the example, the verifier holds the commitment $[Y]$ and FRI will return _Accept_ if $(y_1 + d_1^{-1}, \dots, y_M + d_M^{-1})$ is the vector of evaluations of a polynomial of degree at most $N-1$. - -## Polynomial commitments - -STARK uses a univariate polynomial commitment scheme. The following is what is expected from the **commit** and **open** protocols: - -- _Commit_: given a polynomial $p$, the prover produces a sort of hash of it. We denote it here by $[p]$, called the _commitment_ of $p$. This hash is unique to $p$. The prover usually sends $[p]$ to the verifier. -- _Open_: this is an interactive protocol between the prover and the verifier. The prover holds the polynomial $p$. The verifier only has the commitment $[p]$. The verifier sends a value $z$ to the prover at which he wants to know the value $y=p(z)$. The prover sends a value $y$ to the verifier, and then they engage in the _Open_ protocol. As a result, the verifier gets convinced that the polynomial corresponding to the hash $[p]$ evaluates to $y$ at $z$. - -Let's see how both of these protocols work in detail. The same configuration parameters of FRI are needed: - -- Powers of two $N = 2^n$ and $M = 2^m$ with $n < m$. -- A vector $D=(d_1,\dots,d_M)$, with $d_i = h\omega^i$, with $h$ a nonzero value in $\mathbb{F}$ and $\omega$ a primitive $M$-root of unity - -The commitment scheme will only work for polynomials of degree at most $N$ (polynomials of degree $N$ are allowed). This means: anyone can commit to any polynomial, but the Open protocol will pass only for polynomials satisfying that degree bound. - -### Commit - -Given a polynomial $p$, the commitment $[p]$ is just the commitment of the vector $(p(d_1), \dots, p(d_M))$. That is, $[p]$ is the root of the Merkle tree of the vector of evaluations of $p$ at $D$. - -### Open - -It is an interactive protocol. So assume there is a prover and a verifier. We describe the process considering an honest prover. In the next section, we analyze what happens for malicious provers. - -The prover holds the polynomial $p$, and the verifier only the commitment $[p]$ of it. There is also an element $z$ chosen by the verifier. The prover evaluates $p(z)$ and sends the result back. As we mentioned, the goal is to generate proof of the validity of the evaluation. Let us denote $y$ the value received by the verifier. - -Now they engage in the variant of the FRI protocol for the function $F(a,b) = (a - y) / (b - z)$. The verifier accepts the value $y$ if and only if the result of FRI is _Accept_. - -Let's see why this makes sense. - -### Completeness - -If the prover is honest, $p$ is of degree at most $N$ and $y$ equals $p(z)$. That means that -$$p - y = (X - z) q$$ -for some polynomial $q$. Since $p$ is of degree at most $N$, then $q$ is of degree at most $N-1$. The vector $(q(d_1), \dots, q(d_M))$ is then a vector of evaluations of a polynomial of degree at most $N-1$. And it is equal to $(F(p(d_1), d_1), \dots, F(p(d_M), d_M))$. So the FRI protocol will succeed. - -### Soundness - -Let's sketch an idea of the soundness. Note that the value $z$ is chosen by the verifier after receiving the commitment $[p]$ of $p$. So the prover does not know in advance, at the moment of sending $[p]$, what $z$ will be. - -Suppose the prover is trying to cheat and sends the commitment $[Y]$ of a vector $Y=(y_1,\dots,y_M)$ that's not the vector of evaluations of a polynomial of degree at most $N$. Then the coordinates of the transformed vector are $(y_i - y) / (d_i - z)$. Since $z$ was chosen by the verifier, dividing by $d_i - z$ shuffles all the elements in a very unpredictable way for the prover. So it is extremely unlikely that the cheating prover can craft an invalid vector $Y$ such that the transformed vector turns out to be of degree at most $N-1$. The expected degree of the polynomial associated with a random vector is $M-1$. - -### Batch - -During proof generation, polynomials are committed and opened several times. Computing these for each polynomial independently is costly. In this section, we'll see how batching polynomials can reduce the amount of computation. Let $P=\{p_1, \dots, p_L\}$ be a set of polynomials. We will commit and open $P$ as a whole. We note this batch commitment as $[P]$. - -We need the same configuration parameters as before: $N=2^n$, $M=2^m$ with $N, - ) -> Vec> { - let first_row = frame.get_row(0); - let second_row = frame.get_row(1); - let third_row = frame.get_row(2); - - vec![third_row[0] - second_row[0] - first_row[0]] -} -``` - -So how do we get to $C(x)$ from this? The answer is interpolation. What the method above is doing is the following: if you pass it a frame that looks like this - -$$ -\begin{bmatrix} t(x_0) \\ t(x_0g) \\ t(x_0g^2) \end{bmatrix} -$$ - -for any given point $x_0$, it will return the value - -$$ -t(x_0g^2) - t(x_0g) - t(x_0) -$$ - -which is the numerator in $C(x_0)$. Using the `transition_exemptions` field we defined in our `AIR`, we can also compute evaluations in the denominator, i.e. the zerofier evaluations. This is done under the hood by the `transition_divisors()` method. - -The above means that even though we don't explicitly have the polynomial $C(x)$, we can evaluate it on points given an appropriate frame. If we can evaluate it on enough points, we can then interpolate them to recover $C(x)$. This is exactly how we construct both transition constraint polynomials and subsequently the composition polynomial `H`. - -The job of evaluating `H` on enough points so we can then interpolate it is done by the `ConstraintEvaluator` struct. You'll notice `prove` does the following - -```rust -let constraint_evaluations = evaluator.evaluate( - &lde_trace, - &lde_roots_of_unity_coset, - &alpha_and_beta_transition_coefficients, - &alpha_and_beta_boundary_coefficients, -); -``` - -This function call will return the evaluations of the boundary terms - -$$ -\beta_i^B B_i(x) -$$ - -and constraint terms - -$$ -\beta_i^T C_i(x) -$$ - -for every $i$. The `constraint_evaluations` value returned is a `ConstraintEvaluationTable` struct, which is nothing more than a big list of evaluations of each polynomial required to construct `H`. - -With this in hand, we just call - -```rust -let composition_poly = - constraint_evaluations.compute_composition_poly(& lde_roots_of_unity_coset); -``` - -which simply interpolates the sum of all evaluations to obtain `H`. - -Let's go into more detail on how the `evaluate` method reconstructs $C(x)$ in our fibonacci example. It receives the `lde_trace` as an argument, which is this: - -$$ -\begin{bmatrix} t(\omega^0) \\ t(\omega^1) \\ \dots \\ t(\omega^{15}) \end{bmatrix} -$$ - -where $\omega$ is the primitive root of unity used for the `LDE`, that is, $\omega$ satisfies $\omega^2 = g$. We need to recover $C(x)$, a polynomial whose degree can't be more than $t(x)$'s. Because $t$ was built by interpolating `8` points (the trace), we know we can recover $C(x)$ by interpolating it on 16 points. We choose these points to be the `LDE` roots of unity - -$$ -\{\omega^0, \omega, \omega^2, \dots, \omega^{15}\} -$$ - -Remember that to evaluate $C(x)$ on these points, all we need are the evaluations of the polynomial - -$$ -t(xg^2) - t(xg) - t(x) -$$ - -as the zerofier ones we can compute easily. These become: - -$$ -t(\omega^0 g^2) - t(\omega^0 g) - t(\omega^0) \\ -t(\omega g^2) - t(\omega g) - t(\omega) \\ -t(\omega^2 g^2) - t(\omega^2 g) - t(\omega^2) \\ -\vdots \\ -t(\omega^{15} g^2) - t(\omega^{15} g) - t(\omega^{15}) \\ -$$ - -If we remember that $\omega^2 = g$, this is - -$$ -t(\omega^4) - t(\omega^2) - t(\omega^0) \\ -t(\omega^5) - t(\omega^3) - t(\omega) \\ -t(\omega^6) - t(\omega^4) - t(\omega^2) \\ -\vdots \\ -t(\omega^{3}) - t(\omega) - t(\omega^{15}) \\ -$$ - -and we can compute each evaluation here by calling `compute_transition` on the appropriate frame built from the `lde_trace`. Specifically, for the first evaluation we can build the frame: - -$$ -\begin{bmatrix} t(\omega^0) \\ t(\omega^2) \\ t(\omega^{4}) \end{bmatrix} -$$ - -Calling `compute_transition` on this frame gives us the first evaluation. We can get the rest in a similar fashion, which is what this piece of code in the `evaluate` method does: - -```rust -for (i, d) in lde_domain.iter().enumerate() { - let frame = Frame::read_from_trace( - lde_trace, - i, - blowup_factor, - &self.air.context().transition_offsets, - ) - - let mut evaluations = self.air.compute_transition(&frame); - - ... -} -``` - -Each iteration builds a frame as above and computes one of the evaluations needed. The rest of the code just adds the zerofier evaluations, along with the alphas and betas. It then also computes boundary polynomial evaluations by explicitly constructing them. - -### Verifier - -The verifier employs the same trick to reconstruct the evaluations on the out of domain point $C_i(z)$ for the consistency check. - -# Even/odd decomposition for `H` - -At the end of the recap we talked about how in our code we don't actually commit to `H`, but rather an even/odd decomposition for it. These are two polynomials `H_1` and `H_2` that satisfy - -$$ -H(x) = H_1(x^2) + x H_2(x^2) -$$ - -This all happens on this piece of code - -```rust -let composition_poly = - constraint_evaluations.compute_composition_poly(&lde_roots_of_unity_coset); - -let (composition_poly_even, composition_poly_odd) = composition_poly.even_odd_decomposition(); - -// Evaluate H_1 and H_2 in z^2. -let composition_poly_evaluations = vec![ - composition_poly_even.evaluate(&z_squared), - composition_poly_odd.evaluate(&z_squared), -]; -``` - -After this, we don't really use `H` anymore, but rather `H_1` and `H_2`. There's not that much to say other than that. - -# Out of Domain Frame - -As part of the consistency check, the prover needs to provide evaluations of the trace polynomials in all the points needed by the verifier to check that `H` was constructed correctly. In the fibonacci example, these are $t(z)$, $t(zg)$, and $t(zg^2)$. In code, the prover passes these evaluations as a `Frame`, which we call the out of domain (`ood`) frame. - -The reason we do this is simple: with the frame in hand, the verifier can reconstruct the evaluations of the constraint polynomials $C_i(z)$ by calling the `compute_transition` method on the ood frame and then adding the alphas, betas, and so on, just like we explained in the section above. - -# Transcript - -Throughout the protocol, there are a number of times where the verifier randomly samples some values that the prover needs to use (think of the alphas and betas used when constructing `H`). Because we don't actually have an interaction between prover and verifier, we emulate it by using a hash function, which we assume is a source of randomness the prover can't control. - -The job of providing these samples for both prover and verifier is done by the `Transcript` struct, which you can think of as a stateful `rng`; whenever you call `challenge()` on a transcript you get a random value and the internal state gets mutated, so the next time you call `challenge()` you get a different one. You can also call `append` on it to mutate its internal state yourself. This is done a number of times throughout the protocol to keep the prover honest so it can't predict or manipulate the outcome of `challenge()`. - -Notice that to sample the same values, both prover and verifier need to call `challenge` and `append` in the same order (and with the same values in the case of `append`) and the same number of times. - -The idea explained above is called the Fiat-Shamir heuristic or just `Fiat-Shamir`, and is more generally used throughout proving systems to remove interaction between prover and verifier. Though the concept is very simple, getting it right so the prover can't cheat is not, but we won't go into that here. - -# Proof - -The generated proof has got all the information needed for the verifier to verify it: -- Trace length: The number of rows of the trace table, needed to know the max degree of the polynomials that appear in the system. -- LDE trace commitments. -- DEEP composition polynomial out of domain even and odd evaluations. -- DEEP composition polynomial root. -- FRI layers merkle roots. -- FRI last layer value. -- Query list. -- DEEP composition poly openings. -- Nonce: Proof of work setting used to generate the proof. - -# Special considerations - -## FFT evaluation and interpolation -When evaluating or interpolating a polynomial, if the input (be it coefficients or evaluations) size isn't a power of two then the FFT API will extend it with zero padding until this requirement is met. This is because the library currently only uses a radix-2 FFT algorithm. - -Also, right now FFT only supports inputs with a size up to $2^{2^64}$ elements. - -# Other - -## Why use roots of unity? - -Whenever we interpolate or evaluate trace, boundary and constraint polynomials, we use some $2^n$-th roots of unity. There are a few reasons for this: - -- Using roots of unity means we can use the [Fast Fourier Transform](https://en.wikipedia.org/wiki/Fast_Fourier_transform) and its inverse to evaluate and interpolate polynomials. This method is much faster than the naive Lagrange interpolation one. Since a huge part of the STARK protocol involves both evaluating and interpolating, this is a huge performance improvement. -- When computing boundary and constraint polynomials, we divide them by their `zerofiers`, polynomials that vanish on a few points (the trace elements where the constraints do not hold). These polynomials take the form - - $$ - Z(X) = \prod (X - x_i) - $$ - - where the $x_i$ are the points where we want it to vanish. - - When implementing this, evaluating this polynomial can be very expensive as it involves a huge product. However, if we are using roots of unity, we can use the following trick. The vanishing polynomial for all the $2^n$ roots of unity is - - $$ - X^{2^n} - 1 - $$ - - Instead of expressing the zerofier as a product of the places where it should vanish, we express it as the vanishing polynomial above divided by the `exemptions` polynomial; the polynomial whose roots are the places where constraints don't need to hold. - - $$ - Z(X) = \dfrac{X^{2^n} - 1}{\prod{(X - e_i)}} - $$ - - where the $e_i$ are now the points where we don't want it to vanish. This `exemptions` polynomial in the denominator is usually much smaller, and because the vanishing polynomial in the numerator is only two terms, evaluating it is really fast. - -## What is a primitive root of unity? - -The $n$-th roots of unity are the numbers $x$ that satisfy - -$$ -x^n = 1 -$$ - -There are $n$ such numbers, because they are the roots of the polynomial $X^n - 1$. The set of $n$-th roots of unity always has a `generator`, a root $g$ that can be used to obtain every other root of unity by exponentiating. What this means is that the set of $n$-th roots of unity is - -$$ -\{g^i : 0 \leq i < n\} -$$ - -Any such generator `g` is called a *primitive root of unity*. It's called primitive because it allows us to recover any other root. - -Here are a few important things to keep in mind, some of which we use throughout our implementation: - -- There are always several primitive roots. If $g$ is primitive, then any power $g^k$ with $k$ coprime with $n$ is also primitive. As an example, if $g$ is a primitive $8$-th root of unity, then $g^3$ is also primitive. -- We generally will not care about which primitive root we choose; what we do care about is being *consistent*. We should always choose the same one throughout our code, otherwise computations will go wrong. -- Because $g^n = 1$, the powers of $g$ wrap around. This means - - $$ - g^{n + 1} = g \\ - g^{n + 2} = g^2 - $$ - - and so on. -- If $w$ is a primitive $2^{n + 1}$-th root of unity, then $w^2$ is a primitive $2^n$-th root of unity. In general, if $w$ is a primitive $2^{n + k}$-th primitive root of unity, then $w^{2^k}$ is a primitive $2^n$-th root of unity. - -## Why use Cosets? - -When we perform `FRI` on the `DEEP` composition polynomial, the low degree extension we use is not actually over a set of higher roots of unity than the ones used for the trace, but rather a *coset* of it. A coset is simply a set of numbers all multiplied by the same element. We call said element the `offset`. In our case, a coset of the $2^n$-th roots of unity with primitive root $\omega$ and offset `h` is the set - -$$ -\{h \omega^i : 0 \leq i < 2^n\} -$$ - -So why not just do the LDE without the offset? The problem is in how we construct and evaluate the composition polynomial `H`. Let's say our trace polynomial was interpolated over the $2^n$-th roots of unity with primitive root $g$, and we are doing the LDE over the $2^{n + 1}$-th roots of unity with primitive root $\omega$, so $\omega^2 = g$ (i.e. the blowup factor is `2`). - -Recall that `H` is a sum of terms that include boundary and transition constraint polynomials, and each one of them includes a division by a `zerofier`; a polynomial that vanishes on some roots of unity $g^i$. This is because the zerofier is what tells us which rows of the trace our constraint should apply on. - -When doing `FRI`, we have to provide evaluations over the LDE domain we are using. If we don't include the offset, our domain is - -$$ -\{\omega^i : 0 \leq i < 2^{n + 1}\} -$$ - -Note that, because $w^2 = g$, some of the elements on this set (actually, half of them) are powers of $g$. If while doing `FRI` we evaluate `H` on them, the zerofier could vanish and we'd be dividing by zero. We introduce the offset to make sure this can't happen. - -NOTE: a careful reader might note that we can actually evaluate `H` on the elements $g^i$, since on a valid trace the zerofiers will actually divide the polynomials on their numerator. The problem still remains, however, because of performance. We don't want to do polynomial division if we don't need to, it's much cheaper to just evaluate numerator and denominator and then divide. Of course, this only works if the denominator doesn't vanish; hence, cosets. - diff --git a/docs/src/starks/virtual_cols.md b/docs/src/starks/virtual_cols.md deleted file mode 100644 index aaf67c48b..000000000 --- a/docs/src/starks/virtual_cols.md +++ /dev/null @@ -1,145 +0,0 @@ -# Virtual columns and Subcolumns -## Virtual Columns - -In previous chapters, we have seen how the registers states and the memory are augmented to generate a provable trace. - -While we have shown a way of doing that, there isn't only one possible provable trace. In fact, there are multiple configurations possible. - -For example, in the Cairo VM, we have 15 flags. These flags include "DstReg", "Op0Reg", "OpCode" and others. For simplification, let's imagine we have 3 flags with letters from "A" to "C", where "A" is the first flag. - -Now, let's assume we have 4 steps in our trace. If we were to only use plain columns, the layout would look like this: - -| FlagA| FlagB| FlagB| -| -- | -- | -- | -| A0 | B0 | C0 | -| A1 | B1 | C1 | -| A2 | B2 | C2 | -| A3 | B3 | C3 | - -But, we could also organize them like this - -| Flags| -| -- | -| A0 | -| B0 | -| C0 | -| A1 | -| B1 | -| C1 | -| A2 | -| B2 | -| C2 | -| A3 | -| B3 | -| C3 | - -The only problem is that now the constraints for each transition of the rows are not the same. We will have to define then a concept called "Virtual Column". - -A Virtual Column is like a traditional column, which has its own set of constraints, but it exists interleaved with another one. In the previous example, each row is associated with a column, but in practice, we could have different ratios. We could have 3 rows corresponding to one Virtual Column, and the next one corresponding to another one. For the time being, let's focus on this simpler example. - -Each row corresponding to Flag A will have the constraints associated with its own Virtual Column, and the same will apply to Flag B and Flag C. - -Now, to do this, we will need to evaluate the multiple rows taking into account that they are part of the same step. For a real case, we will add a dummy flag D, whose purpose is to make the evaluation move in a number that is a power of 2. - -Let's see how it works. If we were evaluating the Frame where the constraints should give 0, the frame movement would look like this: - -```diff -+ A0 | B0 | C0 -+ A1 | B1 | C1 - A2 | B2 | C2 - A3 | B3 | C3 -``` -```diff - A0 | B0 | C0 -+ A1 | B1 | C1 -+ A2 | B2 | C2 - A3 | B3 | C3 -``` -```diff - A0 | B0 | C0 - A1 | B1 | C1 -+ A2 | B2 | C2 -+ A3 | B3 | C3 -``` - -In the second case, the evaluation would look like this: - -```diff -+ A0 | -+ B0 | -+ C0 | -+ D0 | -+ A1 | -+ B1 | -+ C1 | -+ D1 | - A2 | - B2 | - C2 | - D2 | - A3 | - B3 | - C3 | - D3 | -``` -```diff - A0 | - B0 | - C0 | - D0 | -+ A1 | -+ B1 | -+ C1 | -+ D1 | -+ A2 | -+ B2 | -+ C2 | -+ D2 | - A3 | - B3 | - C3 | - D3 | -``` - -```diff - A0 | - B0 | - C0 | - D0 | - A1 | - B1 | - C1 | - D1 | -+ A2 | -+ B2 | -+ C2 | -+ D2 | -+ A3 | -+ B3 | -+ C3 | -+ D3 | -``` - -When evaluating the composition polynomial, we will do it over the points on the LDE, where the constraints won't evaluate to 0, but we will use the same spacing. Assume we have three constraints for each flag, $C_{0}$, $C_{1}$, and $C_{2}$, and that they don't involve other trace cells. Let's call the index of the frame evaluation i, starting from 0. - -In the first case, the constraint $C_{0}$, $C_{1}$ and $C_{2}$ would be applied over the same rows, giving an equation that looks like this: - -$`C_{k}(w^i, w^{i+1})`$ - -In the second case, the equations would look like: - -$`C_{0}(w^{i*4}, w^{i*4+4})`$ - -$`C_{1}(w^{i*4+1}, w^{i*4+5})`$ - -$`C_{2}(w^{i*4+2}, w^{i*4+6})`$ - -## Virtual Subcolumns - -Assume now we have 3 columns that share some constraints. For example, let's have three flags that can be either 0 or 1. Each flag will also have its own set of dedicated constraints. - -Let's denote the shared constraint $B$, and the independent constraints $C_{i}$. - -What we can do is define a Column for the flags, where the binary constraint $B$ is enforced. Additionally, we will define a subcolumn for each flag, which will enforce each $C_{i}$. - -In summary, if we have a set of shared constraints to apply, we will be using a Column. If we want to mix or interleave Columns, we will define them as Virtual Columns. And if we want to apply more constraints to a subset of a Column of Virtual Columns, or share constraints between columns, we will define Virtual Subcolumns. diff --git a/elasticlunr.min.js b/elasticlunr.min.js new file mode 100644 index 000000000..94b20dd2e --- /dev/null +++ b/elasticlunr.min.js @@ -0,0 +1,10 @@ +/** + * elasticlunr - http://weixsong.github.io + * Lightweight full-text search engine in Javascript for browser search and offline search. - 0.9.5 + * + * Copyright (C) 2017 Oliver Nightingale + * Copyright (C) 2017 Wei Song + * MIT Licensed + * @license + */ +!function(){function e(e){if(null===e||"object"!=typeof e)return e;var t=e.constructor();for(var n in e)e.hasOwnProperty(n)&&(t[n]=e[n]);return t}var t=function(e){var n=new t.Index;return n.pipeline.add(t.trimmer,t.stopWordFilter,t.stemmer),e&&e.call(n,n),n};t.version="0.9.5",lunr=t,t.utils={},t.utils.warn=function(e){return function(t){e.console&&console.warn&&console.warn(t)}}(this),t.utils.toString=function(e){return void 0===e||null===e?"":e.toString()},t.EventEmitter=function(){this.events={}},t.EventEmitter.prototype.addListener=function(){var e=Array.prototype.slice.call(arguments),t=e.pop(),n=e;if("function"!=typeof t)throw new TypeError("last argument must be a function");n.forEach(function(e){this.hasHandler(e)||(this.events[e]=[]),this.events[e].push(t)},this)},t.EventEmitter.prototype.removeListener=function(e,t){if(this.hasHandler(e)){var n=this.events[e].indexOf(t);-1!==n&&(this.events[e].splice(n,1),0==this.events[e].length&&delete this.events[e])}},t.EventEmitter.prototype.emit=function(e){if(this.hasHandler(e)){var t=Array.prototype.slice.call(arguments,1);this.events[e].forEach(function(e){e.apply(void 0,t)},this)}},t.EventEmitter.prototype.hasHandler=function(e){return e in this.events},t.tokenizer=function(e){if(!arguments.length||null===e||void 0===e)return[];if(Array.isArray(e)){var n=e.filter(function(e){return null===e||void 0===e?!1:!0});n=n.map(function(e){return t.utils.toString(e).toLowerCase()});var i=[];return n.forEach(function(e){var n=e.split(t.tokenizer.seperator);i=i.concat(n)},this),i}return e.toString().trim().toLowerCase().split(t.tokenizer.seperator)},t.tokenizer.defaultSeperator=/[\s\-]+/,t.tokenizer.seperator=t.tokenizer.defaultSeperator,t.tokenizer.setSeperator=function(e){null!==e&&void 0!==e&&"object"==typeof e&&(t.tokenizer.seperator=e)},t.tokenizer.resetSeperator=function(){t.tokenizer.seperator=t.tokenizer.defaultSeperator},t.tokenizer.getSeperator=function(){return t.tokenizer.seperator},t.Pipeline=function(){this._queue=[]},t.Pipeline.registeredFunctions={},t.Pipeline.registerFunction=function(e,n){n in t.Pipeline.registeredFunctions&&t.utils.warn("Overwriting existing registered function: "+n),e.label=n,t.Pipeline.registeredFunctions[n]=e},t.Pipeline.getRegisteredFunction=function(e){return e in t.Pipeline.registeredFunctions!=!0?null:t.Pipeline.registeredFunctions[e]},t.Pipeline.warnIfFunctionNotRegistered=function(e){var n=e.label&&e.label in this.registeredFunctions;n||t.utils.warn("Function is not registered with pipeline. This may cause problems when serialising the index.\n",e)},t.Pipeline.load=function(e){var n=new t.Pipeline;return e.forEach(function(e){var i=t.Pipeline.getRegisteredFunction(e);if(!i)throw new Error("Cannot load un-registered function: "+e);n.add(i)}),n},t.Pipeline.prototype.add=function(){var e=Array.prototype.slice.call(arguments);e.forEach(function(e){t.Pipeline.warnIfFunctionNotRegistered(e),this._queue.push(e)},this)},t.Pipeline.prototype.after=function(e,n){t.Pipeline.warnIfFunctionNotRegistered(n);var i=this._queue.indexOf(e);if(-1===i)throw new Error("Cannot find existingFn");this._queue.splice(i+1,0,n)},t.Pipeline.prototype.before=function(e,n){t.Pipeline.warnIfFunctionNotRegistered(n);var i=this._queue.indexOf(e);if(-1===i)throw new Error("Cannot find existingFn");this._queue.splice(i,0,n)},t.Pipeline.prototype.remove=function(e){var t=this._queue.indexOf(e);-1!==t&&this._queue.splice(t,1)},t.Pipeline.prototype.run=function(e){for(var t=[],n=e.length,i=this._queue.length,o=0;n>o;o++){for(var r=e[o],s=0;i>s&&(r=this._queue[s](r,o,e),void 0!==r&&null!==r);s++);void 0!==r&&null!==r&&t.push(r)}return t},t.Pipeline.prototype.reset=function(){this._queue=[]},t.Pipeline.prototype.get=function(){return this._queue},t.Pipeline.prototype.toJSON=function(){return this._queue.map(function(e){return t.Pipeline.warnIfFunctionNotRegistered(e),e.label})},t.Index=function(){this._fields=[],this._ref="id",this.pipeline=new t.Pipeline,this.documentStore=new t.DocumentStore,this.index={},this.eventEmitter=new t.EventEmitter,this._idfCache={},this.on("add","remove","update",function(){this._idfCache={}}.bind(this))},t.Index.prototype.on=function(){var e=Array.prototype.slice.call(arguments);return this.eventEmitter.addListener.apply(this.eventEmitter,e)},t.Index.prototype.off=function(e,t){return this.eventEmitter.removeListener(e,t)},t.Index.load=function(e){e.version!==t.version&&t.utils.warn("version mismatch: current "+t.version+" importing "+e.version);var n=new this;n._fields=e.fields,n._ref=e.ref,n.documentStore=t.DocumentStore.load(e.documentStore),n.pipeline=t.Pipeline.load(e.pipeline),n.index={};for(var i in e.index)n.index[i]=t.InvertedIndex.load(e.index[i]);return n},t.Index.prototype.addField=function(e){return this._fields.push(e),this.index[e]=new t.InvertedIndex,this},t.Index.prototype.setRef=function(e){return this._ref=e,this},t.Index.prototype.saveDocument=function(e){return this.documentStore=new t.DocumentStore(e),this},t.Index.prototype.addDoc=function(e,n){if(e){var n=void 0===n?!0:n,i=e[this._ref];this.documentStore.addDoc(i,e),this._fields.forEach(function(n){var o=this.pipeline.run(t.tokenizer(e[n]));this.documentStore.addFieldLength(i,n,o.length);var r={};o.forEach(function(e){e in r?r[e]+=1:r[e]=1},this);for(var s in r){var u=r[s];u=Math.sqrt(u),this.index[n].addToken(s,{ref:i,tf:u})}},this),n&&this.eventEmitter.emit("add",e,this)}},t.Index.prototype.removeDocByRef=function(e){if(e&&this.documentStore.isDocStored()!==!1&&this.documentStore.hasDoc(e)){var t=this.documentStore.getDoc(e);this.removeDoc(t,!1)}},t.Index.prototype.removeDoc=function(e,n){if(e){var n=void 0===n?!0:n,i=e[this._ref];this.documentStore.hasDoc(i)&&(this.documentStore.removeDoc(i),this._fields.forEach(function(n){var o=this.pipeline.run(t.tokenizer(e[n]));o.forEach(function(e){this.index[n].removeToken(e,i)},this)},this),n&&this.eventEmitter.emit("remove",e,this))}},t.Index.prototype.updateDoc=function(e,t){var t=void 0===t?!0:t;this.removeDocByRef(e[this._ref],!1),this.addDoc(e,!1),t&&this.eventEmitter.emit("update",e,this)},t.Index.prototype.idf=function(e,t){var n="@"+t+"/"+e;if(Object.prototype.hasOwnProperty.call(this._idfCache,n))return this._idfCache[n];var i=this.index[t].getDocFreq(e),o=1+Math.log(this.documentStore.length/(i+1));return this._idfCache[n]=o,o},t.Index.prototype.getFields=function(){return this._fields.slice()},t.Index.prototype.search=function(e,n){if(!e)return[];e="string"==typeof e?{any:e}:JSON.parse(JSON.stringify(e));var i=null;null!=n&&(i=JSON.stringify(n));for(var o=new t.Configuration(i,this.getFields()).get(),r={},s=Object.keys(e),u=0;u0&&t.push(e);for(var i in n)"docs"!==i&&"df"!==i&&this.expandToken(e+i,t,n[i]);return t},t.InvertedIndex.prototype.toJSON=function(){return{root:this.root}},t.Configuration=function(e,n){var e=e||"";if(void 0==n||null==n)throw new Error("fields should not be null");this.config={};var i;try{i=JSON.parse(e),this.buildUserConfig(i,n)}catch(o){t.utils.warn("user configuration parse failed, will use default configuration"),this.buildDefaultConfig(n)}},t.Configuration.prototype.buildDefaultConfig=function(e){this.reset(),e.forEach(function(e){this.config[e]={boost:1,bool:"OR",expand:!1}},this)},t.Configuration.prototype.buildUserConfig=function(e,n){var i="OR",o=!1;if(this.reset(),"bool"in e&&(i=e.bool||i),"expand"in e&&(o=e.expand||o),"fields"in e)for(var r in e.fields)if(n.indexOf(r)>-1){var s=e.fields[r],u=o;void 0!=s.expand&&(u=s.expand),this.config[r]={boost:s.boost||0===s.boost?s.boost:1,bool:s.bool||i,expand:u}}else t.utils.warn("field name in user configuration not found in index instance fields");else this.addAllFields2UserConfig(i,o,n)},t.Configuration.prototype.addAllFields2UserConfig=function(e,t,n){n.forEach(function(n){this.config[n]={boost:1,bool:e,expand:t}},this)},t.Configuration.prototype.get=function(){return this.config},t.Configuration.prototype.reset=function(){this.config={}},lunr.SortedSet=function(){this.length=0,this.elements=[]},lunr.SortedSet.load=function(e){var t=new this;return t.elements=e,t.length=e.length,t},lunr.SortedSet.prototype.add=function(){var e,t;for(e=0;e1;){if(r===e)return o;e>r&&(t=o),r>e&&(n=o),i=n-t,o=t+Math.floor(i/2),r=this.elements[o]}return r===e?o:-1},lunr.SortedSet.prototype.locationFor=function(e){for(var t=0,n=this.elements.length,i=n-t,o=t+Math.floor(i/2),r=this.elements[o];i>1;)e>r&&(t=o),r>e&&(n=o),i=n-t,o=t+Math.floor(i/2),r=this.elements[o];return r>e?o:e>r?o+1:void 0},lunr.SortedSet.prototype.intersect=function(e){for(var t=new lunr.SortedSet,n=0,i=0,o=this.length,r=e.length,s=this.elements,u=e.elements;;){if(n>o-1||i>r-1)break;s[n]!==u[i]?s[n]u[i]&&i++:(t.add(s[n]),n++,i++)}return t},lunr.SortedSet.prototype.clone=function(){var e=new lunr.SortedSet;return e.elements=this.toArray(),e.length=e.elements.length,e},lunr.SortedSet.prototype.union=function(e){var t,n,i;this.length>=e.length?(t=this,n=e):(t=e,n=this),i=t.clone();for(var o=0,r=n.toArray();o ! { - loop {} -} - -/// This function is called on panic. -#[panic_handler] -fn panic(_info: &PanicInfo) -> ! { - loop {} -} - -#[global_allocator] -static ALLOC: wee_alloc::WeeAlloc = wee_alloc::WeeAlloc::INIT; - -#[allow(unused_imports)] -use lambdaworks_crypto; -#[allow(unused_imports)] -use lambdaworks_math; diff --git a/examples/README.md b/examples/README.md deleted file mode 100644 index c55d1094b..000000000 --- a/examples/README.md +++ /dev/null @@ -1,157 +0,0 @@ -# lambdaworks examples - -This folder contains examples designed to learn how to use the different tools in lambdaworks, such as finite field arithmetics, elliptic curves, provers, and adapters. - -## Examples - -Below is a list of all lambdaworks examples in the folder: -- [Shamir Secret Sharing](https://github.com/lambdaclass/lambdaworks/tree/main/examples/shamir_secret_sharing): implements example of [Shamir's secret sharing](https://en.wikipedia.org/wiki/Shamir%27s_secret_sharing). Shows use of polynomials and finite fields in lambdaworks. -- [Merkle tree CLI](https://github.com/lambdaclass/lambdaworks/tree/main/examples/merkle-tree-cli): generate inclusion proofs for an element inside a Merkle tree and verify them using a CLI. -- [Proving Miden using lambdaworks STARK Platinum prover](https://github.com/lambdaclass/lambdaworks/tree/main/examples/prove-miden): Executes a Miden vm Fibonacci program, gets the execution trace and generates a proof (and verifies it) using STARK Platinum. -- [BabySNARK](https://github.com/lambdaclass/lambdaworks/tree/main/examples/baby-snark): a simple SNARK to start learning the basics of elliptic curve-based proof systems. -- [Pinocchio](https://github.com/lambdaclass/lambdaworks/tree/main/examples/pinocchio): the first practical SNARK. A good starting point to start learning about zero-knowledge proofs. -- [Circom to Lambdaworks](https://github.com/lambdaclass/lambdaworks/tree/main/examples/prove-verify-circom/circom_lambdaworks_tutorial.md): A walkthrough to create a circuit in Circom, generate a proof, and verify it using with Groth16 using Lambdaworks. - -You can also check [lambdaworks exercises](https://github.com/lambdaclass/lambdaworks/tree/main/exercises) to learn more. - -## Basic use of Finite Fields - -This library works with [finite fields](https://en.wikipedia.org/wiki/Finite_field). A `Field` is an abstract definition. It knows the modulus and defines how the operations are performed. - -We usually create a new `Field` by instantiating an optimized backend. For example, this is the definition of the Pallas field: - -```rust -// 4 is the number of 64-bit limbs needed to represent the field -type PallasMontgomeryBackendPrimeField = MontgomeryBackendPrimeField; - -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct MontgomeryConfigPallas255PrimeField; -impl IsModulus for MontgomeryConfigPallas255PrimeField { - const MODULUS: U256 = U256::from_hex_unchecked( - "40000000000000000000000000000000224698fc094cf91b992d30ed00000001", - ); -} - -pub type Pallas255PrimeField = - PallasMontgomeryBackendPrimeField; -``` - -Internally, it resolves all the constants needed and creates all the required operations for the field. - -Suppose we want to create a `FieldElement`. This is as easy as instantiating the `FieldElement` over a `Field` and calling a `from_hex` function. - -For example: - -```rust - let an_element = FieldElement::::from_hex_unchecked("030e480bed5fe53fa909cc0f8c4d99b8f9f2c016be4c41e13a4848797979c662") -``` - -Notice we can alias the `FieldElement` to something like - -```rust -type FE = FieldElement::; -``` - -Once we have a field, we can make all the operations. We usually suggest working with references, but copies work too. - -```rust -let field_a = FE::from_hex("3").unwrap(); -let field_b = FE::from_hex("7").unwrap(); - -// We can use pointers to avoid copying the values internally -let operation_result = &field_a * &field_b - -// But all the combinations of pointers and values works -let operation_result = field_a * field_b -``` - -Sometimes, optimized operations are preferred. For example, - -```rust -// We can make a square multiplying two numbers -let squared = field_a * field_a; -// Using exponentiation -let squared = -field_a.pow(FE::from_hex("2").unwrap()) -// Or using an optimized function -let squared = field_a.square() -``` - -ome useful instantiation methods are also provided for common constants and whenever const functions can be called. This is when creating functions that do not rely on the `IsField` trait since Rust does not support const functions in traits yet, - -```rust -// Defined for all field elements -// Efficient, but nonconst for the compiler -let zero = FE::zero() -let one = FE::one() - -// Const alternatives of the functions are provided, -// But the backend needs to be known at compile time. -// This requires adding a where clause to the function - -let zero = F::ZERO -let one = F::ONE -let const_instantiated = FE::from_hex_unchecked("A1B2C3"); -``` - -You will notice traits are followed by an `Is`, so instead of accepting something of the form `IsField`, you can use `IsPrimeField` and access more functions. The most relevant is `.representative()`. This function returns a canonical representation of the element as a number, not a field. - -## Basic use of Elliptic curves - -lambdaworks supports different elliptic curves. Currently, we support the following elliptic curve types: -- Short Weiestrass: points $(x, y)$ satisfy the equation $y^2 = x^3 + a x + b$. The curve parameters are $a$ and $b$. All elliptic curves can be cast in this form. -- Edwards: points $(x, y)$ satisfy the equation $a x^2 + y^2 = 1 + d x^2 y^2$. The curve parameters are $a$ and $d$. -- Montgomery: points $(x, y)$ satisfy the equation $b y^2 = x^3 + a x^2 + x$. The curve parameters are $b$ and $a$. - -To create an elliptic curve in Short Weiestrass form, we have to implement the traits `IsEllipticCurve` and `IsShortWeierstrass`. Below we show how the Pallas curve is defined: -```rust -#[derive(Clone, Debug)] -pub struct PallasCurve; - -impl IsEllipticCurve for PallasCurve { - type BaseField = Pallas255PrimeField; - type PointRepresentation = ShortWeierstrassProjectivePoint; - - fn generator() -> Self::PointRepresentation { - Self::PointRepresentation::new([ - -FieldElement::::one(), - FieldElement::::from(2), - FieldElement::one(), - ]) - } -} - -impl IsShortWeierstrass for PallasCurve { - fn a() -> FieldElement { - FieldElement::from(0) - } - - fn b() -> FieldElement { - FieldElement::from(5) - } -} -``` - -All curve models have their `defining_equation` method, which allows us to check whether a given $(x,y)$ belongs to the elliptic curve. The `BaseField` is where the coordinates $x,y$ of the curve live. `generator()` provides a point $P$ in the elliptic curve such that, by repeatedly adding $P$ to itself, we can obtain all the points in the elliptic curve group. - -The `generator()` returns a vector with three components $(x,y,z)$, instead of the two $(x,y)$. lambdaworks represents points in projective coordinates, where operations like scalar multiplication are much faster. We can generate points by providing $(x,y)$ -```rust -let x = FE::from_hex_unchecked( - "bd1e740e6b1615ae4c508148ca0c53dbd43f7b2e206195ab638d7f45d51d6b5", - ); -let y = FE::from_hex_unchecked( - "13aacd107ca10b7f8aab570da1183b91d7d86dd723eaa2306b0ef9c5355b91d8", - ); -PallasCurve::create_point_from_affine(x, y).unwrap() -``` -If you provide an invalid point, there will be an error. You can obtain the point at infinity (which is the neutral element for the curve operation) by doing -```rust -let point_at_infinity = PallasCurve::neutral_element() -``` -Once we have points, we can do operations between points. We have the methods `operate_with_self` and `operate_with_other`. For example, -```rust -let g = PallasCurve::generator(); -let g2 = g.operate_with_self(2_u16); -let g3 = g.operate_with_other(&g2); -``` -`operate_with_self` takes as argument anything that implements the `IsUnsignedInteger` trait. This operator represents scalar multiplication. `operate_with_other` takes as argument another point in the elliptic curve. When we operate this way, the $z$ coordinate in the result may be different from $1$. We can transform it back to affine form by using `to_affine`. diff --git a/examples/baby-snark/Cargo.toml b/examples/baby-snark/Cargo.toml deleted file mode 100644 index ce781abf2..000000000 --- a/examples/baby-snark/Cargo.toml +++ /dev/null @@ -1,14 +0,0 @@ -[package] -name = "baby-snark" -version.workspace = true -edition.workspace = true -license.workspace = true -repository.workspace = true - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] -lambdaworks-math = { workspace = true } -lambdaworks-crypto = { workspace = true } -rand_chacha = "0.3.1" -rand = "0.8.5" diff --git a/examples/baby-snark/README.md b/examples/baby-snark/README.md deleted file mode 100644 index 1a35a4479..000000000 --- a/examples/baby-snark/README.md +++ /dev/null @@ -1,184 +0,0 @@ -# Lambdaworks Baby SNARK - -An implementation of [Baby SNARK](https://github.com/initc3/babySNARK/blob/bebb2948f8094a8d3949afe6d10b89a120a005be/babysnark.pdf) protocol. - -# Example - -Below is a simple example demonstrating the usage of BabySnark: - -**Step 1:** Construct Span Program (ssp): - -```rust - // Define Constraint Matrix - let u = vec![ - i64_vec_to_field(&[-1, 2, 0, 0]), - i64_vec_to_field(&[-1, 0, 2, 0]), - i64_vec_to_field(&[-1, 0, 0, 2]), - i64_vec_to_field(&[-1, 2, 2, -4]), - ]; - let witness = i64_vec_to_field(&[1, 1, 1]); - let public = i64_vec_to_field(&[1]); - let mut input = public.clone(); - input.extend(witness.clone()); - // Construct Span Program (ssp): - let ssp = SquareSpanProgram::from_scs(SquareConstraintSystem::from_matrix(u, public.len())); -``` - -*Note:* You must ensure that the first element of the `input` is 1. In the code above we can see how to build a Span Program for an And Gate. There, the variable `witness` is of the form `[input_1, input_2, output]` (which satisfy `input_1 ∧ input_2 = output`) and the `public` variable must be `[1]`. - -**Step 2:** Setup Proving and Verification Keys: -```rust - let (pk, vk) = setup(&ssp); -``` -**Step 3:** The prover generates a proof using the input, the SSP, and the proving key. -```rust - let proof = Prover::prove(&input, &ssp, &pk); -``` -**Step 4:** The verifier checks the validity of the proof using the verification key and the public input. -```rust - let verified = verify(&vk, &proof, &public); - assert!(verified); -``` - -# Working principle - -## Programs as relationships between polynomials - -BabySNARK is based on this [NIZK](https://eprint.iacr.org/2014/718) proposed in 2014. It works with square span programs, which are similar to, yet simpler than, quadratic arithmetic programs (used in [Pinocchio](https://eprint.iacr.org/2013/279)). The representation of the circuit is done with a matrix $U$ (belonging to $F^{m \times n}$) and a vector $z = (1 , u , w)^t$ (containing the instance $u$ and witness $w$), -$(U.z) \circ (U.z) = 1$ - -We can express any boolean circuit using these types of constraints. Let us rewrite the equations in a different form that will be convenient for later purposes: -$\left(\sum_j u_{ij} z_j \right)^2 = 1$ -which should be valid for every $i = 0, 1, 2, ...$. We can encode these equations using polynomials. Suppose that $m = 2^k$ for some $k$ and that we are working with a nice field $F$ containing a subgroup $D_i$ of size $2^k$. We can take $\omega$ as an $m$-th primitive root of unity ($\omega$ generates the whole subgroup) and find the polynomials $U_j (x)$ which satisfy -$U_j (\omega^i ) = u_{ij}$ -By doing this, we are encoding our equations as relations over polynomials. Thus, we can replace the problem equivalently, -$\left(\sum_j U_{j} (x) z_j \right)^2 - 1 = p(x)$ -If we evaluate the polynomials at $\omega^i$, then we get $U_j (\omega^i ) = u_{ij}$, and $p(\omega^i )$ evaluates to $0$ at every $\omega^i$. A theorem says that if $\omega^i$ is a root/zero of a polynomial $p(x)$, then $x - \omega^i$ divides $p(x)$. In other words, there is some $q (x)$ such that $p(x) = (x - \omega^i )q(x)$. - -If the polynomial has multiple zeros, then it must be divisible by each $x - \omega^i$. Let us define $Z(x)$ as the vanishing polynomial over $D_i$ -$Z(x) = \prod_j (x -\omega^j ) = x^m - 1$ -where we used in the last equality that $\omega$ is a primitive $m$-th root of unity (this trick is also used in [STARKs](https://blog.lambdaclass.com/diving-deep-fri/)). Therefore, if all the constraints hold, we have a polynomial $q(x)$ which fulfills this equality -$p(x) = Z(x) q(x)$ - -One way to show that the computation described by the system of equations is valid is by providing $p(x)$ and $q(x)$ and letting the verifier check the equality by himself. The problem is that we have to pass all the coefficients of both polynomials (which are as long as the computation) and let him compute the right-hand side and assert whether it equals the polynomial on the left-hand side. Besides, we also leak information on the witness! How can we turn this into something succinct and not leak information? - -## Polynomial commitment schemes - -A polynomial commitment scheme is given by four algorithms: setup, commit, open, and evaluate. The commitment allows us to bind ourselves to a given polynomial using short data and later be able to prove things about that polynomial. The commitment scheme must satisfy the following two properties: -1. Hiding: the commitment does not reveal anything about the committed polynomial. -2. Binding: given the commitment to $p(x)$, $\mathrm{cm} (p)$, it is infeasible to find another $q(x)$, such that $\mathrm{cm} (p) = \mathrm{cm} (q)$ - -One way to build a PCS is by using a pairing-friendly elliptic curve, such as BN254 or BLS12-381. We will work here with type-3 pairings, which are functions $e: G_1 \times G_2 \rightarrow G_t$ with the following properties: -1. Bilinearity: $e(a g_1 , b g_2) = e(g_1 , g_2 )^{ab}$. -2. Non-degeneracy: If $e(P,Q) = 1$, then either $P = \mathcal{O}$ or $Q = \mathcal{O}$. - -[KZG commitment scheme](https://blog.lambdaclass.com/mina-to-ethereum-bridge/) works in this setting, which is the tool we will use. Why are pairings useful? Because they provide us with a way of multiplying things hidden inside an elliptic curve group. - -We pick a random $s$ (which is unknown to both the prover and verifier), and we generate the following points in the elliptic curve -$\\{ P_0 , P_1 , ..., P_n \\} = \\{ g_1 , s g_1 , ..., s^n g_n \\}$ -These points contain the powers of $s$ hidden inside a group of the elliptic curve. Given any $P_k$, recovering $s$ is computationally intractable due to the hardness of the discrete log problem over elliptic curves. - -We commit to the polynomial by computing -$p(s) g_1 = \sum a_k (s^k g_1 ) = \sum a_k P_k$ -where $g_1$ is a generator of the group/subgroup of prime order $r$ of the elliptic curve. We could also commit using elements in $G_2$, where we have $g_2$ as a subgroup generator. - -Using pairings, we could prove the relationship between the polynomial $p(x)$ and the quotient $q(x)$ by computing two pairings and checking their equality: -$e( p(s) g_1 , g_2) = e(g_1 , g_2 )^{p(s)}$ -$e(q(s) g_1 , s^m g_2 - g_2) = e(g_1 , g_2 )^{ q(s)(s^m - 1)}$ -Since $s$ is chosen at random, if $p(s) = q(s) Z(s)$, then with overwhelming probability, we have that $p(x) = q(x) Z(x)$. - -With this construction, we do not need to supply the verifier with the coefficients of the polynomials, only their commitments. This solves part of the problem but not everything. - -## Intuition for the protocol - -The program/circuit that we want to prove is defined by the matrix $U$. When we define a particular instance/public input $u$ to the circuit, if $u$ is valid, we should be able to find some $w$ that solves the system of equations. To make the proof succinct, we should send much less information than the full witness (besides, if we want zero-knowledge, the witness should be kept secret). - -We have the polynomial $p(x)$ of the problem, the vanishing polynomial $Z(x)$, and the quotient $q(x)$. In the end we want to prove that -$p(x) = Z(x)q(x)$ -if the computation is valid. $Z(x)$ is known to both the prover and the verifier, and we could even commit to $Z(x)$ as part of the public information. We can reduce this check to just one point $x = s$ and verify this using pairings. However, this check alone would be insufficient since the prover could provide any polynomial $p(x)$. If we recall how we build $p(x)$, -$\left(\sum_j U_{j} (x) z_j \right)^2 - 1 = p(x)$ -Some terms in the summation can be computed by the verifier (since these are public). However, the verifier does not know the witness's terms, and we do not want to give him access to that data in total. The solution would be for the prover to give the summation, including only the values of the witness, -$$V_w (x) = \sum_{j \in w} w_j U_j(x)$$ -Moreover, we can provide a commitment to $V_w (x)$ using the commitment scheme we had before, $V_w (s) g_1$ and $V_w (s) g_2$ (we will show why we need both soon). The verifier can then compute -$$V_u (x) = \sum_{k \in u} u_j U_j(x)$$ -and get $V_u (s) g_1$ and $V_u (s) g_2$. The verifier can compute the pairing involving $e( p(s) g_1 , g_2)$ in an equivalent way, -$$e ( V_u (s) g_1 + V_w(s) g_1 , V_u (s) g_2 + V_w(s) g_2 ) e ( g_1 , g_2 )^{ - 1 } = e( p(s) g_1 , g_2)$$ -This looks odd, but if we take all the scalars to the exponent, we have $(V_u (s) + V_w (s))(V_u (s) + V_w (s)) - 1$, and the verifier can get the polynomial of the circuit. So, we get the first check, -$$e ( V_u (s) g_1 + V_w(s) g_1 , V_u (s) g_2 + V_w(s) g_2 ) e ( g_1 , g_2 )^{ - 1 } = e( q(s) g_1 , Z(s)g_2)$$ - -We have one problem, though. How do we know that the prover used the same $V_w (x)$ in both commitments? Luckily, we can solve this with another pairing check, -$e( V_w (s) g_1 , g_2 ) = e( g_1 , V_w(s) g_2 )$ - -We got another check. Finally, how do we know that the verifier computed $V_w (x)$ correctly and did not do some random linear combination that will cancel out with the public input and yield something nice? - -We could force the prover to provide the same linear combination, but with the points all shifted by some constant $\beta$, unknown to the parties. We define -$B_w (x) = \sum \beta w_j U_j (x) = \beta V_w (x)$ -We can do one final check for this relationship using pairings, -$e( B_w (s) g_1 , \gamma g_2 ) = e( \gamma \beta g_1 , V_w (s) g_2 )$ -where $\gamma$ is also unknown to the parties. This makes it impossible for the prover to build fake polynomials for $V_w (x)$. We can see that if this condition did not exist, we could create any $V_w (x) = C Z(x) - V_u (x) + 1$, which would pass all the other checks for any $C$ of our choice. In fact, -$V_w (x) + V_u (x) = C Z(x) + 1$ -But $p(x) = (V_w (x) + V_u (x))^2 - 1$, so -$p(x) = C^2 Z(x)Z(x) + C Z(x) = Z(x) (C^2 Z(x) + C)$ -and we find that $q(x) = (C^2 Z(x) + C)$, even though we do not know the witness. - -The proof $\pi$ will consist of: -1. The commitment to $V_w (x)$ using $g_1$. -2. The commitment to $V_w (x)$ using $g_2$. -3. The commitment to the quotient polynomial $q(x)$ using $g_1$. -4. The commitment to $B_w (x)$ using $g_1$ - -The verification involves six pairings (the pairing $e(g_1 , g_2)^{ - 1}$ can be precomputed since it is a constant), to check the three conditions we mentioned. - -To compute the commitments, we need parameters $s , \beta , \gamma$ to be unknown to both parties (hence, they are toxic waste). We need to generate a reference string, which will be circuit dependent (that is because we need to provide $\beta U_j(s) g_1$). With all this, we can jump into the implementation. - -# Implementation - -## Setup - -Prover and verifier agree on a pairing-friendly elliptic curve and generators of the groups $G_1$ and $G_2$, denoted by $g_1$ and $g_2$, respectively. In our case, we choose BLS12-381. The proving key consists of the following: -1. $\\{s^k g_1 \\}$ for $k = 0, 1, 2 , ... m$ -2. $\\{U_j (s) g_1 \\}$ for $j = l , l + 1 , ... m$ ($l$ being the number of public inputs). -3. $\\{U_j (s) g_2 \\}$ for $j = l , l + 1 , ... m$ -4. $\\{\beta U_j (s) g_1 \\}$ for $j = l , l + 1 , ... m$ - -The verifying key consists of the following: -1. $\\{U_j (s) g_1 \\}$ for $j = 0 , 1 , ... l - 1$ -2. $\\{U_j (s) g_2 \\}$ for $j = 0 , 1 , ... l - 1$ -3. $[Z^\prime ] = (s^m - 1)g_2$ (commitment to the vanishing polynomial) -4. $e(g_1 , g_2)^{ - 1}$ -5. $\beta \gamma g_1$ -6. $\gamma g_2$ - -## Prove - -The steps for the prover are as follows: -1. Compute $[V_w ] = V_w (s) g_1$, $[V_w^\prime ] = V_w (s) g_2$, and $[B_w ] = B_w (s) g_1$ using the proving key. -2. Compute the polynomial quotient polynomial $q(x)$ from the zerofier $Z(x)$, the vector of witness and instance, and the polynomials describing the circuit $U_j (x)$. -3. Compute $[q ] = q(s) g_1$ using the proving key. -4. Produce the proof $\pi = ( [q] , [V_w ] , [V_w^\prime ] , [B_w ])$ - -## Verify - -The verifier has the following steps: -1. Parse the proof $\pi$ as $[q] , [V_w ] , [V_w^\prime ] , [B_w ]$. -2. Check $e( [V_w ] , g_2 ) = e( g_1 , [V_w^\prime ])$ -3. Check $e( [B_w] , \gamma g_2) = e( \beta \gamma g_1 , [V_w^\prime ])$ -4. Compute $[V_u ] = V_u (s) g_1$, and $[V_u^\prime ] = V_u (s) g_2$ using the verifying key. -5. Check $e([V_u ] + [V_w ] , [V_u^\prime ] + [V_w^\prime ])e(g_1 , g_2)^{ - 1} = e( [q] , [Z^\prime])$ - -If all checks pass, the proof is valid. - -## Optimizations - -1. Interpolation is done using the Fast Fourier Transform (FFT). This is possible because BLS12-381's scalar field has $2^{32}$ as one of its factors. -2. The quotient is calculated in evaluation form, using the FFT. We need to evaluate the polynomials at $\mu \omega^k$, where $\mu$ is the offset (we want to evaluate on cosets because if we evaluate directly over $D_i$, we get $0/0$). -3. The evaluation of the vanishing polynomial is straightforward: $Z(\mu \omega^k ) = (\mu \omega^k )^m - 1 = \mu^m - 1$, because $\omega$ has order $m$. -4. Compute multiscalar multiplications using Pippenger's algorithm. - -## Turning the SNARK into a zk-SNARK. - -The protocol above is not zero-knowledge since $V_w (x)$ can be distinguished from a random-looking $V (x)$. To make it zero-knowledge, the prover has to sample a random value $\delta$ and make the following changes to the polynomials: -1. The polynomial $p(x) = \left(\sum_k z_j U_j(x) + \delta Z(x) \right)^2 - 1$. Note that adding $Z(x)$ does not change the main condition, which is that the constraints are satisfied if and only if $p(x)$ is divisible by $Z(x)$. -2. Compute $[V_w ] = (V_w (s) + \delta Z(s)) g_1$, $[V_w^\prime ] = (V_w (s) + \delta Z(s)) g_2$, and $[B_w ] = (B_w (s) + \beta \delta Z(s)) g_1$. - -The verifier's steps are unchanged. diff --git a/examples/baby-snark/example.json b/examples/baby-snark/example.json deleted file mode 100644 index 567954536..000000000 --- a/examples/baby-snark/example.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "u": [ - [1, 3, 2, 4, 5], - [-1, -2, 3, 4, -2], - [1, 2, 3, 2, 2], - [-3, -2, 0, 0, 0], - [0, 9, 2, -1, 3] - ] -} diff --git a/examples/baby-snark/src/common.rs b/examples/baby-snark/src/common.rs deleted file mode 100644 index c31d2fe39..000000000 --- a/examples/baby-snark/src/common.rs +++ /dev/null @@ -1,38 +0,0 @@ -use lambdaworks_math::{ - elliptic_curve::{ - short_weierstrass::curves::bls12_381::{ - curve::BLS12381Curve, default_types::FrElement as FE, default_types::FrField as FrF, - pairing::BLS12381AtePairing, twist::BLS12381TwistCurve, - }, - traits::{IsEllipticCurve, IsPairing}, - }, - field::element::FieldElement, - unsigned_integer::element::U256, -}; -use rand::{Rng, SeedableRng}; - -pub type Curve = BLS12381Curve; -pub type TwistedCurve = BLS12381TwistCurve; - -pub type FrElement = FE; -pub type FrField = FrF; - -pub type Pairing = BLS12381AtePairing; - -pub type G1Point = ::PointRepresentation; -pub type G2Point = ::PointRepresentation; -pub type PairingOutput = FieldElement<::OutputField>; - -pub const ORDER_R_MINUS_1_ROOT_UNITY: FrElement = FrElement::from_hex_unchecked("7"); - -pub fn sample_fr_elem() -> FrElement { - let mut rng = rand_chacha::ChaCha20Rng::seed_from_u64(9001); - FrElement::new(U256 { - limbs: [ - rng.gen::(), - rng.gen::(), - rng.gen::(), - rng.gen::(), - ], - }) -} diff --git a/examples/baby-snark/src/lib.rs b/examples/baby-snark/src/lib.rs deleted file mode 100644 index d9db1c57b..000000000 --- a/examples/baby-snark/src/lib.rs +++ /dev/null @@ -1,12 +0,0 @@ -pub mod common; -pub mod scs; -pub mod ssp; -pub mod utils; - -mod prover; -mod setup; -mod verifier; - -pub use prover::{Proof, Prover}; -pub use setup::{setup, ProvingKey, VerifyingKey}; -pub use verifier::verify; diff --git a/examples/baby-snark/src/prover.rs b/examples/baby-snark/src/prover.rs deleted file mode 100644 index 4f6d5edc4..000000000 --- a/examples/baby-snark/src/prover.rs +++ /dev/null @@ -1,65 +0,0 @@ -use crate::{common::*, setup::ProvingKey, ssp::SquareSpanProgram}; -use lambdaworks_math::{cyclic_group::IsGroup, msm::pippenger::msm}; -pub struct Proof { - pub h: G1Point, - pub v_w: G1Point, - pub v_w_prime: G2Point, - pub b_w: G1Point, -} - -#[derive(Debug)] -pub enum Error { - WrongWitness, - FirstInputElementIsNotOne, -} - -pub struct Prover; -impl Prover { - pub fn prove( - inputs: &[FrElement], - ssp: &SquareSpanProgram, - pk: &ProvingKey, - ) -> Result { - if inputs[0].ne(&FrElement::one()) { - return Err(Error::FirstInputElementIsNotOne); - } - if !ssp.check_valid(inputs) { - return Err(Error::WrongWitness); - } - - // Sample randomness for hiding - let delta = sample_fr_elem(); - - let h_coefficients = ssp - .calculate_h_coefficients(inputs, &delta) - .iter() - .map(|elem| elem.representative()) - .collect::>(); - - let h = msm(&h_coefficients, &pk.k_powers_of_tau_g1).unwrap(); - let w = inputs - .iter() - .skip(ssp.number_of_public_inputs) - .map(|elem| elem.representative()) - .collect::>(); - - let v_w = msm(&w, &pk.u_tau_g1) - .unwrap() - .operate_with(&pk.t_tau_g1.operate_with_self(delta.representative())); - - let v_w_prime = msm(&w, &pk.u_tau_g2) - .unwrap() - .operate_with(&pk.t_tau_g2.operate_with_self(delta.representative())); - - let b_w = msm(&w, &pk.beta_u_tau_g1) - .unwrap() - .operate_with(&pk.beta_t_tau_g1.operate_with_self(delta.representative())); - - Ok(Proof { - h, - v_w, - v_w_prime, - b_w, - }) - } -} diff --git a/examples/baby-snark/src/scs.rs b/examples/baby-snark/src/scs.rs deleted file mode 100644 index 1b7aef8db..000000000 --- a/examples/baby-snark/src/scs.rs +++ /dev/null @@ -1,26 +0,0 @@ -use crate::common::FrElement; - -pub type Constraint = Vec; - -#[derive(Clone, Debug, PartialEq, Eq)] -pub struct SquareConstraintSystem { - pub constraints: Vec, - pub number_of_public_inputs: usize, -} - -impl SquareConstraintSystem { - pub fn from_matrix(matrix: Vec>, number_of_public_inputs: usize) -> Self { - Self { - constraints: matrix, - number_of_public_inputs, - } - } - - pub fn number_of_constraints(&self) -> usize { - self.constraints.len() - } - - pub fn input_size(&self) -> usize { - self.constraints[0].len() - } -} diff --git a/examples/baby-snark/src/setup.rs b/examples/baby-snark/src/setup.rs deleted file mode 100644 index fe0bcf2d1..000000000 --- a/examples/baby-snark/src/setup.rs +++ /dev/null @@ -1,123 +0,0 @@ -use lambdaworks_math::{ - cyclic_group::IsGroup, - elliptic_curve::traits::{IsEllipticCurve, IsPairing}, -}; - -use crate::{ - common::{ - sample_fr_elem, Curve, FrElement, G1Point, G2Point, Pairing, PairingOutput, TwistedCurve, - }, - ssp::SquareSpanProgram, -}; - -pub struct VerifyingKey { - // Ui(τ) * g1, 0 <= i < l - pub u_tau_g1: Vec, - // Ui(τ) * g2, 0 <= i < l - pub u_tau_g2: Vec, - // t(τ) * g2 - pub t_tau_g2: G2Point, - // e(g1, g2)^-1 - pub inv_pairing_g1_g2: PairingOutput, - // β * γ * g1 - pub beta_gamma_g1: G1Point, - // γ * g2 - pub gamma_g2: G2Point, -} - -pub struct ProvingKey { - // (τ^k) * g1, 0 <= k < m - pub k_powers_of_tau_g1: Vec, - // Ui(τ) * g1, l <= i <= m - pub u_tau_g1: Vec, - // Ui(τ) * g2, l <= i <= m - pub u_tau_g2: Vec, - // β * Ui(τ) * g1, l <= i <= m - pub beta_u_tau_g1: Vec, - // t(τ) * g1 - pub t_tau_g1: G1Point, - // β * t(τ) * g1 - pub beta_t_tau_g1: G1Point, - // t(τ) * g2 - pub t_tau_g2: G2Point, -} - -struct ToxicWaste { - tau: FrElement, - beta: FrElement, - gamma: FrElement, -} - -impl ToxicWaste { - pub fn new() -> Self { - Self { - tau: sample_fr_elem(), - beta: sample_fr_elem(), - gamma: sample_fr_elem(), - } - } -} - -pub fn setup(u: &SquareSpanProgram) -> (ProvingKey, VerifyingKey) { - let g1: G1Point = Curve::generator(); - let g2: G2Point = TwistedCurve::generator(); - - let tw = ToxicWaste::new(); - - let u_tau = u.u_polynomials.iter().map(|p| p.evaluate(&tw.tau)); - - let vk = VerifyingKey { - u_tau_g1: u_tau - .clone() - .take(u.number_of_public_inputs) - .map(|ui| g1.operate_with_self(ui.representative())) - .collect(), - u_tau_g2: u_tau - .clone() - .take(u.number_of_public_inputs) - .map(|ui| g2.operate_with_self(ui.representative())) - .collect(), - t_tau_g2: g2.operate_with_self( - (tw.tau.pow(u.number_of_constraints) - FrElement::one()).representative(), - ), - inv_pairing_g1_g2: Pairing::compute(&g1, &g2).unwrap().inv().unwrap(), - beta_gamma_g1: g1.operate_with_self((&tw.beta * &tw.gamma).representative()), - gamma_g2: g2.operate_with_self(tw.gamma.representative()), - }; - - let pk = ProvingKey { - k_powers_of_tau_g1: (0..u.number_of_constraints + 1) - .map(|k| g1.operate_with_self(tw.tau.pow(k).representative())) - .collect(), - u_tau_g1: u_tau - .clone() - .take(u.number_of_constraints + 1) - .skip(u.number_of_public_inputs) - .map(|ui| g1.operate_with_self(ui.representative())) - .collect(), - u_tau_g2: u_tau - .clone() - .take(u.number_of_constraints + 1) - .skip(u.number_of_public_inputs) - .map(|ui| g2.operate_with_self(ui.representative())) - .collect(), - beta_u_tau_g1: u_tau - .clone() - .take(u.number_of_constraints + 1) - .skip(u.number_of_public_inputs) - .map(|ui| g1.operate_with_self((ui * (&tw.beta)).representative())) - .collect(), - t_tau_g1: g1.operate_with_self( - (tw.tau.pow(u.number_of_constraints) - FrElement::one()).representative(), - ), - beta_t_tau_g1: g1.operate_with_self( - ((&tw.beta) * (tw.tau.pow(u.number_of_constraints) - FrElement::one())) - .representative(), - ), - t_tau_g2: g2.operate_with_self( - (tw.tau.pow(u.number_of_constraints) - FrElement::one()).representative(), - ), - }; - - (pk, vk) -} diff --git a/examples/baby-snark/src/ssp.rs b/examples/baby-snark/src/ssp.rs deleted file mode 100644 index 95753c984..000000000 --- a/examples/baby-snark/src/ssp.rs +++ /dev/null @@ -1,153 +0,0 @@ -use lambdaworks_math::polynomial::Polynomial; - -use crate::{common::*, scs::SquareConstraintSystem}; - -#[derive(Debug)] -pub struct SquareSpanProgram { - pub number_of_public_inputs: usize, - pub number_of_constraints: usize, - pub u_polynomials: Vec>, - pub matrix: Vec>, -} - -impl SquareSpanProgram { - pub fn calculate_h_coefficients( - &self, - input: &[FrElement], - delta: &FrElement, - ) -> Vec { - let offset = &ORDER_R_MINUS_1_ROOT_UNITY; - let p_degree = 2 * self.number_of_constraints; - - let u_evaluated = self.evaluate_scaled_and_acumulated_u(input, p_degree, offset); - let (t_evaluated, t_inv_evaluated) = self.evaluate_t(p_degree, offset); - - let h_degree = self.number_of_constraints + 1; - - calculate_h_coefficients( - u_evaluated, - t_evaluated, - t_inv_evaluated, - h_degree, - offset, - delta, - ) - } - - fn evaluate_t(&self, degree: usize, offset: &FrElement) -> (Vec, Vec) { - let one_polynomial = Polynomial::new_monomial(FrElement::one(), self.number_of_constraints); - let t_polynomial = one_polynomial - FrElement::one(); - - let t_evaluated = - Polynomial::evaluate_offset_fft(&t_polynomial, 1, Some(degree), offset).unwrap(); - let mut t_inv_evaluated = t_evaluated.clone(); - FrElement::inplace_batch_inverse(&mut t_inv_evaluated).unwrap(); - - (t_evaluated, t_inv_evaluated) - } - - // Compute U.w by summing up polynomials U[0].w_0, U[1].w_1, ..., U[n].w_n - fn evaluate_scaled_and_acumulated_u( - &self, - w: &[FrElement], - degree: usize, - offset: &FrElement, - ) -> Vec { - let scaled_and_accumulated = self - .u_polynomials - .iter() - .zip(w) - .map(|(poly, coeff)| poly.mul_with_ref(&Polynomial::new_monomial(coeff.clone(), 0))) - .reduce(|poly1, poly2| poly1 + poly2) - .unwrap(); - - Polynomial::evaluate_offset_fft(&scaled_and_accumulated, 1, Some(degree), offset).unwrap() - } - - pub fn from_scs(scs: SquareConstraintSystem) -> Self { - let number_of_constraints = scs.number_of_constraints().next_power_of_two(); - - let u_polynomials = (0..scs.input_size()) - .map(|polynomial_index| { - get_u_polynomial_from_scs(&scs, polynomial_index, number_of_constraints) - }) - .collect(); - - let matrix = scs.constraints; - Self { - number_of_public_inputs: scs.number_of_public_inputs, - number_of_constraints, - u_polynomials, - matrix, - } - } - - pub fn number_of_private_inputs(&self) -> usize { - self.u_polynomials.len() - self.number_of_public_inputs - } - - pub fn check_valid(&self, input: &[FrElement]) -> bool { - for row in &self.matrix { - let coef = row - .iter() - .zip(input) - .map(|(a, b)| a * b) - .reduce(|a, b| a + b) - .unwrap(); - - if (&coef * &coef).ne(&FrElement::one()) { - return false; - } - } - true - } -} - -fn calculate_h_coefficients( - u_evaluated: Vec, - t_evaluated: Vec, - t_inv_evaluated: Vec, - degree: usize, - offset: &FrElement, - delta: &FrElement, -) -> Vec { - let h_evaluated: Vec<_> = u_evaluated - .iter() - .zip(&t_evaluated) - .zip(&t_inv_evaluated) - .map(|((ui, ti), ti_inv)| { - (ui * ui - FrElement::one()) * ti_inv - + FrElement::from(2) * delta * ui - + delta * delta * ti - }) - .collect(); - - let mut h_coefficients = Polynomial::interpolate_offset_fft(&h_evaluated, offset) - .unwrap() - .coefficients() - .to_vec(); - - let pad = vec![FrElement::zero(); degree - h_coefficients.len()]; - h_coefficients.extend(pad); - - h_coefficients -} - -#[inline] -fn get_u_polynomial_from_scs( - scs: &SquareConstraintSystem, - polynomial_index: usize, - number_of_constraints: usize, -) -> Polynomial { - let mut u_polynomial = if polynomial_index == 0 { - vec![FrElement::one(); number_of_constraints] - } else { - vec![FrElement::zero(); number_of_constraints] - }; - - for (constraint_index, constraint) in scs.constraints.iter().enumerate() { - u_polynomial[constraint_index] = constraint[polynomial_index].clone(); - } - - Polynomial::interpolate_fft::(&u_polynomial).unwrap() -} diff --git a/examples/baby-snark/src/utils.rs b/examples/baby-snark/src/utils.rs deleted file mode 100644 index 78f2a8942..000000000 --- a/examples/baby-snark/src/utils.rs +++ /dev/null @@ -1,24 +0,0 @@ -use std::ops::Neg; - -use crate::common::FrElement; - -pub fn i64_to_field(element: &i64) -> FrElement { - let mut fr_element = FrElement::from(element.unsigned_abs()); - if element.is_negative() { - fr_element = fr_element.neg() - } - - fr_element -} - -pub fn i64_vec_to_field(elements: &[i64]) -> Vec { - elements.iter().map(i64_to_field).collect() -} - -pub fn i64_matrix_to_field(elements: &[&[i64]]) -> Vec> { - let mut matrix = Vec::new(); - for f in elements { - matrix.push(i64_vec_to_field(f)); - } - matrix -} diff --git a/examples/baby-snark/src/verifier.rs b/examples/baby-snark/src/verifier.rs deleted file mode 100644 index 006f7176a..000000000 --- a/examples/baby-snark/src/verifier.rs +++ /dev/null @@ -1,43 +0,0 @@ -use std::ops::Mul; - -use lambdaworks_math::cyclic_group::IsGroup; -use lambdaworks_math::elliptic_curve::traits::IsEllipticCurve; -use lambdaworks_math::{elliptic_curve::traits::IsPairing, msm::pippenger::msm}; - -use crate::common::{Curve, FrElement, Pairing, TwistedCurve}; -use crate::prover::Proof; -use crate::setup::VerifyingKey; - -pub fn verify(vk: &VerifyingKey, proof: &Proof, pub_inputs: &[FrElement]) -> bool { - let v_w = &proof.v_w; - let v_w_prime = &proof.v_w_prime; - let h = &proof.h; - let b_w = &proof.b_w; - - let mut accept = true; - accept &= Pairing::compute(b_w, &vk.gamma_g2) == Pairing::compute(&vk.beta_gamma_g1, v_w_prime); - accept &= Pairing::compute(v_w, &TwistedCurve::generator()) - == Pairing::compute(&Curve::generator(), v_w_prime); - let v_u = msm( - &pub_inputs - .iter() - .map(|elem| elem.representative()) - .collect::>(), - &vk.u_tau_g1, - ) - .unwrap(); - let v_u_prime = msm( - &pub_inputs - .iter() - .map(|elem| elem.representative()) - .collect::>(), - &vk.u_tau_g2, - ) - .unwrap(); - - accept &= Pairing::compute(&v_u.operate_with(v_w), &v_u_prime.operate_with(v_w_prime)) - .unwrap() - .mul(&vk.inv_pairing_g1_g2) - == Pairing::compute(h, &vk.t_tau_g2).unwrap(); - accept -} diff --git a/examples/baby-snark/tests/integration_tests.rs b/examples/baby-snark/tests/integration_tests.rs deleted file mode 100644 index 7221bd3f0..000000000 --- a/examples/baby-snark/tests/integration_tests.rs +++ /dev/null @@ -1,127 +0,0 @@ -use baby_snark::common::FrElement; -use baby_snark::scs::SquareConstraintSystem; -use baby_snark::ssp::SquareSpanProgram; -use baby_snark::utils::{i64_matrix_to_field, i64_vec_to_field}; -use baby_snark::{setup, verify, Prover}; -#[test] -fn identity_matrix() { - let u = vec![i64_vec_to_field(&[1, 0]), i64_vec_to_field(&[0, 1])]; - let witness = i64_vec_to_field(&[1, 1]); - let public = i64_vec_to_field(&[]); - - test_integration(u, witness, public) -} - -#[test] -fn size_not_pow2() { - let u: &[&[i64]] = &[ - &[1, 3, 2, 4, 5], - &[-1, -2, 3, 4, -2], - &[1, 2, 3, 2, 2], - &[-3, -2, 0, 0, 0], - &[0, 9, 2, -1, 3], - ]; - let input: &[i64] = &[1, 2, 3, 4, 5]; - - let witness = i64_vec_to_field(&[3, 4, 5]); - let public = i64_vec_to_field(&[1, 2]); // Note that the first element must be 1. - let input_field = i64_vec_to_field(input); - let u_field = normalize(i64_matrix_to_field(u), &input_field); - - test_integration(u_field, witness, public); -} - -#[test] -fn and_gate() { - let u = vec![ - i64_vec_to_field(&[-1, 2, 0, 0]), - i64_vec_to_field(&[-1, 0, 2, 0]), - i64_vec_to_field(&[-1, 0, 0, 2]), - i64_vec_to_field(&[-1, 2, 2, -4]), - ]; - let witness = i64_vec_to_field(&[1, 1, 1]); // [input_1, input_2, output] - let public = i64_vec_to_field(&[1]); // This must be 1. - - test_integration(u, witness, public) -} - -#[test] -fn invalid_proof() { - let u: &[&[i64]] = &[ - &[1, 3, 2, 4, 5], - &[-1, 8, 3, 4, -2], - &[1, 2, 3, 2, 2], - &[-3, -2, 0, 0, 0], - &[0, 9, 2, -1, 3], - &[3, 9, 2, -1, 3], - ]; - let input: &[i64] = &[1, 4, 6, 0, 5]; - let witness = i64_vec_to_field(&[0, 5]); - let public = i64_vec_to_field(&[1, 4, 6]); - let mut input_field = i64_vec_to_field(input); - let u_field = normalize(i64_matrix_to_field(u), &input_field); - test_integration(u_field.clone(), witness.clone(), public.clone()); - - input_field = i64_vec_to_field(&[1, 4, 6, 0, 3]); - - let ssp = - SquareSpanProgram::from_scs(SquareConstraintSystem::from_matrix(u_field, public.len())); - let (proving_key, _verifying_key) = setup(&ssp); - let proof = Prover::prove(&input_field, &ssp, &proving_key); - assert!(proof.is_err()); -} - -#[test] -fn corrupted_proof() { - let u: &[&[i64]] = &[ - &[1, 3, 2, 4, 5], - &[-1, 8, 3, 4, -2], - &[1, 2, 3, 2, 2], - &[-3, -2, 0, 0, 0], - &[0, 9, 2, -1, 3], - &[3, 9, 2, -1, 3], - ]; - let input: &[i64] = &[1, 4, 6, 0, 5]; - let public = i64_vec_to_field(&[1, 4, 6]); - let input_field = i64_vec_to_field(input); - let u_field = normalize(i64_matrix_to_field(u), &input_field); - - let ssp = - SquareSpanProgram::from_scs(SquareConstraintSystem::from_matrix(u_field, public.len())); - let (proving_key, verifying_key) = setup(&ssp); - - let mut proof = Prover::prove(&input_field, &ssp, &proving_key).unwrap(); - proof.b_w = proof.b_w.double(); - let verified = verify(&verifying_key, &proof, &public); - assert!(!verified); -} - -fn normalize(matrix: Vec>, input: &Vec) -> Vec> { - let mut new_matrix = Vec::new(); - - for row in matrix { - let coef = row - .iter() - .zip(input) - .map(|(a, b)| a * b) - .reduce(|a, b| a + b) - .unwrap(); - let new_row: Vec = row.iter().map(|x| x * coef.inv().unwrap()).collect(); - new_matrix.push(new_row); - } - - new_matrix -} - -pub fn test_integration(u: Vec>, witness: Vec, public: Vec) { - let mut input = public.clone(); - input.extend(witness.clone()); - - let ssp = SquareSpanProgram::from_scs(SquareConstraintSystem::from_matrix(u, public.len())); - let (proving_key, verifying_key) = setup(&ssp); - - let proof = Prover::prove(&input, &ssp, &proving_key).unwrap(); - - let verified = verify(&verifying_key, &proof, &public); - assert!(verified); -} diff --git a/examples/merkle-tree-cli/.gitignore b/examples/merkle-tree-cli/.gitignore deleted file mode 100644 index a6c57f5fb..000000000 --- a/examples/merkle-tree-cli/.gitignore +++ /dev/null @@ -1 +0,0 @@ -*.json diff --git a/examples/merkle-tree-cli/Cargo.toml b/examples/merkle-tree-cli/Cargo.toml deleted file mode 100644 index 93f4c0f65..000000000 --- a/examples/merkle-tree-cli/Cargo.toml +++ /dev/null @@ -1,18 +0,0 @@ -[package] -name = "merkle-tree-cli" -version.workspace = true -edition.workspace = true -license.workspace = true -repository.workspace = true - -[[bin]] -name = "merkle-tree-cli" -path = "src/main.rs" - -[dependencies] -clap = { version = "4.4.6", features = ["derive"] } -lambdaworks-crypto = { workspace = true, features = ["serde"] } -lambdaworks-math = { workspace = true, features = ["lambdaworks-serde-string"] } -serde = { version = "1.0" } -serde_json = "1" -bincode = { version = "2.0.1", features = ["serde"] } diff --git a/examples/merkle-tree-cli/README.md b/examples/merkle-tree-cli/README.md deleted file mode 100644 index bd1d0a621..000000000 --- a/examples/merkle-tree-cli/README.md +++ /dev/null @@ -1,86 +0,0 @@ -
- -# Lambdaworks Merklee Tree CLI - -Simple Merkle Tree CLI that uses [Poseidon hash](https://www.poseidon-hash.info/). - -
- -### Usage: - -### To create a Merkle Tree and save it to a file you can use: - -```bash -cargo run --release generate-tree -``` - -The format of a tree `csv` file is as follows: -``` -elem1;elem2;...;elemN -``` -For example, the provided **`sample_tree.csv`** looks like this: -``` -0x12345;0x6789A;0xBCDEF -``` - -**`generate-tree` example:** - -```bash -cargo run --release generate-tree sample_tree.csv -``` -This will: -- Generate a `json` file with the tree structure and save it to the same directory as the `csv` file. - -- Save the root of the tree in a `txt` file named `_root.txt`. - - For example: - - ``` - sample_tree_root.txt - ``` - will contain - ``` - 0xa3bbbb9eac9f79d18862b802ea79f87e75efc37d4f4af4464976784c14a851b69c09aa04b1e8a8d1eb9825b713dc6ca - ``` - - -- Print the root of the tree in the terminal - - -### To generate proof for a Merkle Tree you can use: - -```bash -cargo run --release generate-proof -``` -This will: -- Generate a `json` file with the proof for the leaf at the specified position and save it to the same directory as the `csv` file. - -- Save the value of the leaf in a `txt` file named `_leaf_.txt`. - - -**`generate-proof` example:** - -```bash -cargo run --release generate-proof sample_tree.csv 0 -``` -This will generate: - -- `sample_tree_proof_0.json` will contain the proof for the leaf at position 0. - -- `sample_tree_leaf_0.txt` will contain the value of the leaf at position 0. For example: - ``` - 0x12345 - ``` - -### To verify a proof you can use: - -```bash -cargo run --release verify-proof -``` - - -**`verify-proof` example:** - -```bash -cargo run --release verify-proof sample_tree_root.txt 0 sample_tree_proof_0.json sample_tree_leaf_0.txt -``` diff --git a/examples/merkle-tree-cli/sample_tree.csv b/examples/merkle-tree-cli/sample_tree.csv deleted file mode 100644 index 6fb434f30..000000000 --- a/examples/merkle-tree-cli/sample_tree.csv +++ /dev/null @@ -1 +0,0 @@ -0x12345;0x6789A;0xBCDEF \ No newline at end of file diff --git a/examples/merkle-tree-cli/src/commands.rs b/examples/merkle-tree-cli/src/commands.rs deleted file mode 100644 index af22e904d..000000000 --- a/examples/merkle-tree-cli/src/commands.rs +++ /dev/null @@ -1,37 +0,0 @@ -use clap::{Args, Parser, Subcommand}; - -#[derive(Parser, Debug)] -#[command(author = "Lambdaworks", version, about)] -pub struct MerkleArgs { - #[clap(subcommand)] - pub entity: MerkleEntity, -} - -#[derive(Subcommand, Debug)] -pub enum MerkleEntity { - #[clap(about = "Generate a merkle tree")] - GenerateTree(GenerateTreeArgs), - #[clap(about = "Generate a merkle proof")] - GenerateProof(GenerateProofArgs), - #[clap(about = "Verify a merkle proof")] - VerifyProof(VerifyArgs), -} - -#[derive(Args, Debug)] -pub struct GenerateTreeArgs { - pub tree_path: String, -} - -#[derive(Args, Debug)] -pub struct GenerateProofArgs { - pub tree_path: String, - pub position: usize, -} - -#[derive(Args, Debug)] -pub struct VerifyArgs { - pub root_path: String, - pub index: usize, - pub proof_path: String, - pub leaf_path: String, -} diff --git a/examples/merkle-tree-cli/src/main.rs b/examples/merkle-tree-cli/src/main.rs deleted file mode 100644 index 22100ea5c..000000000 --- a/examples/merkle-tree-cli/src/main.rs +++ /dev/null @@ -1,114 +0,0 @@ -mod commands; -use clap::Parser; -use commands::{MerkleArgs, MerkleEntity}; -use lambdaworks_crypto::hash::poseidon::starknet::PoseidonCairoStark252; -use lambdaworks_crypto::merkle_tree::{ - backends::field_element::TreePoseidon, merkle::MerkleTree, proof::Proof, -}; -use lambdaworks_math::field::{ - element::FieldElement, fields::fft_friendly::stark_252_prime_field::Stark252PrimeField, -}; -use std::io::BufWriter; -use std::{ - fs::{self, File}, - io::{self, Write}, -}; - -type FE = FieldElement; - -fn load_fe_from_file(file_path: &String) -> Result { - FE::from_hex(&fs::read_to_string(file_path)?.replace('\n', "")) - .map_err(|e| io::Error::other(format!("{e:?}"))) -} - -fn load_tree_values(tree_path: &String) -> Result, io::Error> { - Ok(fs::read_to_string(tree_path)? - .split(';') - .map(FE::from_hex_unchecked) - .collect()) -} - -fn generate_merkle_tree(tree_path: String) -> Result<(), io::Error> { - let values: Vec = load_tree_values(&tree_path)?; - - let merkle_tree = MerkleTree::>::build(&values) - .ok_or_else(|| io::Error::other("requested empty tree"))?; - let root = merkle_tree.root.representative().to_string(); - println!("Generated merkle tree with root: {root:?}"); - - let generated_tree_path = tree_path.replace(".csv", ".json"); - let file = File::create(generated_tree_path)?; - let mut writer = BufWriter::new(file); - serde_json::to_writer_pretty(&mut writer, &merkle_tree)?; - println!("Saved tree to file"); - - let root_file_path = tree_path.replace(".csv", "_root.txt"); - let mut root_file = File::create(root_file_path)?; - root_file.write_all(root.as_bytes())?; - println!("Saved root file"); - - Ok(()) -} - -fn generate_merkle_proof(tree_path: String, pos: usize) -> Result<(), io::Error> { - let values: Vec = load_tree_values(&tree_path)?; - let merkle_tree = MerkleTree::>::build(&values) - .ok_or_else(|| io::Error::other("requested empty tree"))?; - - let Some(proof) = merkle_tree.get_proof_by_pos(pos) else { - return Err(io::Error::other("Index out of bounds")); - }; - - let proof_path = tree_path.replace(".csv", format!("_proof_{pos}.json").as_str()); - let file = File::create(&proof_path)?; - let mut writer = BufWriter::new(file); - serde_json::to_writer_pretty(&mut writer, &proof)?; - writer.flush()?; - - let leaf_value = values - .get(pos) - .ok_or_else(|| io::Error::other("Invalid position"))? - .representative() - .to_string(); - - let leaf_file_path = tree_path.replace(".csv", format!("_leaf_{pos}.txt").as_str()); - let mut leaf_file = File::create(&leaf_file_path)?; - leaf_file.write_all(leaf_value.as_bytes())?; - println!("Generated proof and saved to {proof_path}. Leaf saved to {leaf_file_path}"); - - Ok(()) -} - -fn verify_merkle_proof( - root_path: String, - index: usize, - proof_path: String, - leaf_path: String, -) -> Result<(), io::Error> { - let root_hash: FE = load_fe_from_file(&root_path)?; - - let leaf: FE = load_fe_from_file(&leaf_path)?; - - let file_str = fs::read_to_string(proof_path)?; - let proof: Proof = serde_json::from_str(&file_str)?; - - match proof.verify::>(&root_hash, index, &leaf) { - true => println!("\x1b[32mMerkle proof verified succesfully\x1b[0m"), - false => println!("\x1b[31mMerkle proof failed verifying\x1b[0m"), - } - - Ok(()) -} - -fn main() { - let args: MerkleArgs = MerkleArgs::parse(); - if let Err(e) = match args.entity { - MerkleEntity::GenerateTree(args) => generate_merkle_tree(args.tree_path), - MerkleEntity::GenerateProof(args) => generate_merkle_proof(args.tree_path, args.position), - MerkleEntity::VerifyProof(args) => { - verify_merkle_proof(args.root_path, args.index, args.proof_path, args.leaf_path) - } - } { - println!("Error while running command: {e:?}"); - } -} diff --git a/examples/pinocchio/Cargo.toml b/examples/pinocchio/Cargo.toml deleted file mode 100644 index 61751aa38..000000000 --- a/examples/pinocchio/Cargo.toml +++ /dev/null @@ -1,12 +0,0 @@ -[package] -name = "pinocchio" -version.workspace = true -edition.workspace = true -license.workspace = true -repository.workspace = true - -[dependencies] -lambdaworks-math = { workspace = true } -lambdaworks-crypto = { workspace = true } -rand_chacha = "0.3.1" -rand = "0.8.5" \ No newline at end of file diff --git a/examples/pinocchio/Readme.md b/examples/pinocchio/Readme.md deleted file mode 100644 index 4b422277b..000000000 --- a/examples/pinocchio/Readme.md +++ /dev/null @@ -1,36 +0,0 @@ -# Lambdaworks Pinocchio - -This is an implementation of [Pinocchio protocol](https://eprint.iacr.org/2013/279.pdf) using Lambdaworks. This source code is the companion of this [blog post](https://blog.lambdaclass.com/pinocchio-verifiable-computation-revisited) aimed at those who want to learn about SNARKs. - -# Understanding the code -We encourage to start by reading the [blog post](https://blog.lambdaclass.com/pinocchio-verifiable-computation-revisited/) to understand the code. - -Then, in the `tests/` module you will find integration tests that will guide you through the happy path: the setup, the prover and the verifier. Each of these components can be located in their own files: - -- `pinocchio/setup.rs`: generates the `VerificationKey` and the `EvaluationKey` with the relevant data to construct and verify proofs. This data comes from the structure of the program P encoded as `Polynomials` in a `QAP` (Quadratic arithmetic program). To hide this data, random `FieldElement`s are sampled as `ToxicWaste` and then mapped to `G1Point`'s and `G2Point`'s via repeated addition of the `generator()`'s of the curves. - -- `pinocchio/prover.rs`: takes the circuit encoded as a `QAP` and the trace of the program as `FieldElement`s. Then, it applies the `msm(...)` operation in order to generate the proof elements, in this case, `G1Point` and `G2Point` hidings. - -- `pinocchio/verifier.rs`: verifies the proof by checking the conditions mentioned in the paper. This involves computing `Pairing::compute(...)` operations between a `G1Point`and a `G2Point`. - -# Example - -**Step 1:** Construct a QAP - -```rust -let test_qap = new_test_r1cs().into(); -``` - -**Step 2**: Setup providing Evaluation and Verification keys. - -```rust -let (evaluation_key, verification_key) = setup(&test_qap, toxic_waste); -``` -**Step 3:** The prover constructs the proof using the evaluation key, the QAP and the inputs. -```rust -let proof = generate_proof(&evaluation_key, &test_qap, &c_vector); -``` -**Step 4:** The verifier checks the validity of the proof using the verification key and the public inputs. -```rust -let accepted = verify(&verification_key, &proof, &c_io_vector); -``` diff --git a/examples/pinocchio/src/common.rs b/examples/pinocchio/src/common.rs deleted file mode 100644 index 909ea3303..000000000 --- a/examples/pinocchio/src/common.rs +++ /dev/null @@ -1,36 +0,0 @@ -use lambdaworks_math::{ - elliptic_curve::{ - short_weierstrass::curves::bls12_381::{ - curve::BLS12381Curve, default_types::FrElement, default_types::FrField, - pairing::BLS12381AtePairing, twist::BLS12381TwistCurve, - }, - traits::IsEllipticCurve, - }, - unsigned_integer::element::U256, -}; -use rand::{Rng, SeedableRng}; - -pub type Curve = BLS12381Curve; -pub type TwistedCurve = BLS12381TwistCurve; - -pub type FE = FrElement; -pub type F = FrField; - -pub type Pairing = BLS12381AtePairing; - -pub type G1Point = ::PointRepresentation; -pub type G2Point = ::PointRepresentation; - -pub const ORDER_R_MINUS_1_ROOT_UNITY: FE = FE::from_hex_unchecked("7"); - -pub fn sample_fr_elem() -> FE { - let mut rng = rand_chacha::ChaCha20Rng::seed_from_u64(9001); - FE::new(U256 { - limbs: [ - rng.gen::(), - rng.gen::(), - rng.gen::(), - rng.gen::(), - ], - }) -} diff --git a/examples/pinocchio/src/lib.rs b/examples/pinocchio/src/lib.rs deleted file mode 100644 index 81402e18d..000000000 --- a/examples/pinocchio/src/lib.rs +++ /dev/null @@ -1,7 +0,0 @@ -pub mod common; -pub mod prover; -pub mod qap; -pub mod r1cs; -pub mod setup; -pub mod test_utils; -pub mod verifier; diff --git a/examples/pinocchio/src/prover.rs b/examples/pinocchio/src/prover.rs deleted file mode 100644 index 3ea58add1..000000000 --- a/examples/pinocchio/src/prover.rs +++ /dev/null @@ -1,51 +0,0 @@ -use crate::common::{G1Point, G2Point, FE}; -use crate::qap::QuadraticArithmeticProgram; -use crate::setup::EvaluationKey; -use lambdaworks_math::msm::pippenger::msm; - -pub struct Proof { - pub v: G1Point, - pub w1: G1Point, - pub w2: G2Point, - pub y: G1Point, - pub h: G2Point, - pub v_prime: G1Point, - pub w_prime: G1Point, - pub y_prime: G1Point, - pub z: G1Point, -} - -pub fn generate_proof( - evaluation_key: &EvaluationKey, - qap: &QuadraticArithmeticProgram, - qap_c_coefficients: &[FE], -) -> Proof { - let cmid = - &qap_c_coefficients[qap.number_of_inputs..qap_c_coefficients.len() - qap.number_of_outputs]; - // We transform each FieldElement of the cmid into an UnsignedInteger so we can multiply them to g1. - let c_mid = cmid - .iter() - .map(|elem| elem.representative()) - .collect::>(); - - let h_polynomial = qap.h_polynomial(qap_c_coefficients); - // We transform h_polynomial into UnsignedIntegers. - let h_coefficients = h_polynomial - .coefficients - .iter() - .map(|elem| elem.representative()) - .collect::>(); - let h_degree = h_polynomial.degree(); - - Proof { - v: msm(&c_mid, &evaluation_key.g1_vk).unwrap(), - w1: msm(&c_mid, &evaluation_key.g1_wk).unwrap(), - w2: msm(&c_mid, &evaluation_key.g2_wk).unwrap(), - y: msm(&c_mid, &evaluation_key.g1_yk).unwrap(), - v_prime: msm(&c_mid, &evaluation_key.g1_alpha_vk).unwrap(), - w_prime: msm(&c_mid, &evaluation_key.g1_alpha_wk).unwrap(), - y_prime: msm(&c_mid, &evaluation_key.g1_alpha_yk).unwrap(), - z: msm(&c_mid, &evaluation_key.g1_beta).unwrap(), - h: msm(&h_coefficients, &evaluation_key.g2_s_i[..h_degree]).unwrap(), - } -} diff --git a/examples/pinocchio/src/qap.rs b/examples/pinocchio/src/qap.rs deleted file mode 100644 index 35de8af41..000000000 --- a/examples/pinocchio/src/qap.rs +++ /dev/null @@ -1,365 +0,0 @@ -use crate::{common::*, r1cs::R1CS}; -use lambdaworks_math::polynomial::Polynomial; -use std::convert::From; - -#[derive(Clone, Debug, PartialEq, Eq)] -/// QAP Representation of the circuits -pub struct QuadraticArithmeticProgram { - pub vs: Vec>, - pub ws: Vec>, - pub ys: Vec>, - pub target: Polynomial, - pub number_of_inputs: usize, - pub number_of_outputs: usize, -} - -#[derive(Debug, PartialEq, Eq)] -pub enum CreationError { - PolynomialVectorsSizeMismatch, -} - -impl QuadraticArithmeticProgram { - /// Creates a new QAP - /// This expects vectors to be organized like: - /// v0,w0,y0 - /// inputs associated v,w,y polynomials - /// mid associated polynomials - /// outputs associated v,w,y polynomials - pub fn new( - vs: Vec>, - ws: Vec>, - ys: Vec>, - target: Polynomial, - number_of_inputs: usize, - number_of_outputs: usize, - ) -> Result { - if vs.len() != ws.len() - || vs.len() != ys.len() - || number_of_inputs + number_of_outputs > vs.len() - { - Err(CreationError::PolynomialVectorsSizeMismatch) - } else { - Ok(Self { - vs, - ws, - ys, - target, - number_of_inputs, - number_of_outputs, - }) - } - } - - pub fn h_polynomial(&self, c: &[FE]) -> Polynomial { - self.p_polynomial(c).div_with_ref(&self.target) - } - - /// Receives C elements of a solution of the circuit. - /// Returns p polynomial. - // This along the polynomial execution should be migrated with a better - // representation of the circuit. - pub fn p_polynomial(&self, cs: &[FE]) -> Polynomial { - let v: Polynomial = self.vs[0].clone() - + self.vs[1..] - .iter() - .zip(cs) - .map(|(v, c)| v.mul_with_ref(&Polynomial::new_monomial(c.clone(), 0))) - .reduce(|x, y| x + y) - .unwrap(); - - let w: Polynomial = self.ws[0].clone() - + self.ws[1..] - .iter() - .zip(cs) - .map(|(w, c)| w.mul_with_ref(&Polynomial::new_monomial(c.clone(), 0))) - .reduce(|x, y| x + y) - .unwrap(); - - let y: Polynomial = self.ys[0].clone() - + self.ys[1..] - .iter() - .zip(cs) - .map(|(y, c)| y.mul_with_ref(&Polynomial::new_monomial(c.clone(), 0))) - .reduce(|x, y| x + y) - .unwrap(); - - v * w - y - } - - pub fn v_mid(&'_ self) -> &[Polynomial] { - &self.vs[self.number_of_inputs + 1..(self.vs.len() - self.number_of_outputs)] - } - - pub fn w_mid(&'_ self) -> &[Polynomial] { - &self.ws[self.number_of_inputs + 1..(self.ws.len() - self.number_of_outputs)] - } - - pub fn y_mid(&'_ self) -> &[Polynomial] { - &self.ys[self.number_of_inputs + 1..(self.ys.len() - self.number_of_outputs)] - } - - pub fn v_input(&'_ self) -> &[Polynomial] { - &self.vs[1..self.number_of_inputs + 1] - } - - pub fn w_input(&'_ self) -> &[Polynomial] { - &self.ws[1..self.number_of_inputs + 1] - } - - pub fn y_input(&'_ self) -> &[Polynomial] { - &self.ys[1..self.number_of_inputs + 1] - } - - pub fn v0(&'_ self) -> &Polynomial { - &self.vs[0] - } - - pub fn w0(&'_ self) -> &Polynomial { - &self.ws[0] - } - - pub fn y0(&'_ self) -> &Polynomial { - &self.ys[0] - } - - pub fn v_output(&'_ self) -> &[Polynomial] { - &self.vs[(self.vs.len() - self.number_of_outputs)..] - } - pub fn w_output(&'_ self) -> &[Polynomial] { - &self.ws[(self.ws.len() - self.number_of_outputs)..] - } - - pub fn y_output(&'_ self) -> &[Polynomial] { - &self.ys[(self.ys.len() - self.number_of_outputs)..] - } -} - -impl From for QuadraticArithmeticProgram { - fn from(r1cs: R1CS) -> Self { - // The r values for the qap polynomial can each be any number, - // as long as there are the right amount of rs. - // In this case, it's set them to be 0,1,2..number_of_constraints(), - // number_of_constraints non inclusive. - let rs: Vec = (0..r1cs.number_of_constraints() as u128) - .map(|i| FE::new(i.into())) - .collect(); - - let mut vs: Vec> = Vec::with_capacity(r1cs.witness_size()); - let mut ws: Vec> = Vec::with_capacity(r1cs.witness_size()); - let mut ys: Vec> = Vec::with_capacity(r1cs.witness_size()); - let mut t: Polynomial = Polynomial::new_monomial(FE::from(1), 0); - - for r in &rs { - t = t * Polynomial::new(&[-r, FE::from(1)]); - } - - for i in 0..r1cs.witness_size() { - let v_ys: Vec = r1cs.constraints.iter().map(|c| c.a[i].clone()).collect(); - let w_ys: Vec = r1cs.constraints.iter().map(|c| c.b[i].clone()).collect(); - let y_ys: Vec = r1cs.constraints.iter().map(|c| c.c[i].clone()).collect(); - - vs.push(Polynomial::interpolate(&rs, &v_ys).expect("should interpolate")); - ws.push(Polynomial::interpolate(&rs, &w_ys).expect("should interpolate")); - ys.push(Polynomial::interpolate(&rs, &y_ys).expect("should interpolate")); - } - - QuadraticArithmeticProgram { - vs, - ws, - ys, - target: t, - number_of_inputs: r1cs.number_of_inputs, - number_of_outputs: r1cs.number_of_outputs, - } - } -} - -#[cfg(test)] -mod tests { - use crate::test_utils::{ - new_test_qap, new_test_r1cs, test_qap_r5, test_qap_r6, test_qap_solver, - }; - - use super::*; - - #[test] - fn qap_with_different_amount_of_polynomials_should_error() { - let v = &[ - Polynomial::new(&[FE::from(1), FE::from(2)]), - Polynomial::new(&[FE::from(2), FE::from(3)]), - ]; - - let u = v.clone(); - let w = &[Polynomial::new(&[FE::from(1), FE::from(2)])]; - let t = Polynomial::new(&[FE::from(3)]); - assert_eq!( - Err(CreationError::PolynomialVectorsSizeMismatch), - QuadraticArithmeticProgram::new(v.to_vec(), u.to_vec(), w.to_vec(), t, 2, 1) - ); - } - - #[test] - fn test_circuit_v_w_y_have_7_elements() { - let test_circuit = new_test_qap(); - assert_eq!(test_circuit.vs.len(), 7); - assert_eq!(test_circuit.ws.len(), 7); - assert_eq!(test_circuit.ys.len(), 7); - } - - //_mid polynomials of test circuit contains only one polynomial - #[test] - fn v_mid_test_circuit_on_r6_is_0() { - let test_circuit = new_test_qap(); - let r6 = test_qap_r6(); - assert_eq!(test_circuit.y_mid()[0].evaluate(&r6), FE::from(0)); - } - - #[test] - fn w_mid_test_circuit_has_one_element() { - let test_circuit = new_test_qap(); - assert_eq!(test_circuit.v_mid().len(), 1); - } - - #[test] - fn w_mid_test_circuit_on_r5_is_0() { - let test_circuit = new_test_qap(); - let r5 = test_qap_r5(); - assert_eq!(test_circuit.w_mid()[0].evaluate(&r5), FE::from(0)); - } - - #[test] - fn w_mid_test_circuit_on_r6_is_1() { - let test_circuit = new_test_qap(); - let r6 = test_qap_r6(); - assert_eq!(test_circuit.w_mid()[0].evaluate(&r6), FE::from(1)); - } - - #[test] - fn y_mid_test_circuit_on_r5_is_1() { - let test_circuit = new_test_qap(); - let r5 = test_qap_r5(); - assert_eq!(test_circuit.y_mid()[0].evaluate(&r5), FE::from(1)); - } - - #[test] - fn y_mid_test_circuit_on_r6_is_0() { - let test_circuit = new_test_qap(); - let r6 = test_qap_r6(); - assert_eq!(test_circuit.y_mid()[0].evaluate(&r6), FE::from(0)); - } - - #[test] - fn v_input_test_circuit_has_length_4() { - let test_circuit = new_test_qap(); - assert_eq!(test_circuit.v_input().len(), 4); - } - - #[test] - fn w_input_test_circuit_has_length_4() { - let test_circuit = new_test_qap(); - assert_eq!(test_circuit.w_input().len(), 4); - } - #[test] - fn y_input_test_circuit_has_length_4() { - let test_circuit = new_test_qap(); - assert_eq!(test_circuit.y_input().len(), 4); - } - - #[test] - fn v_output_test_circuit_has_length_1() { - let test_circuit = new_test_qap(); - assert_eq!(test_circuit.v_output().len(), 1); - } - - #[test] - fn w_output_test_circuit_has_length_1() { - let test_circuit = new_test_qap(); - assert_eq!(test_circuit.w_output().len(), 1); - } - - #[test] - fn y_output_test_circuit_has_length_1() { - let test_circuit = new_test_qap(); - assert_eq!(test_circuit.y_output().len(), 1); - } - - #[test] - /// This test runs multiple cases calculated in paper - /// t polynomial is tested implicitly by calculating h = p / t - fn test_polynomial_h_cases() { - let test_circuit = new_test_qap(); - - let inputs = [FE::from(1), FE::from(2), FE::from(3), FE::from(4)]; - - let (c5, c6) = test_qap_solver(inputs.clone()); - - let mut c_vector = inputs.to_vec(); - c_vector.append(&mut vec![c5, c6]); - - assert_eq!( - test_circuit.h_polynomial(&c_vector), - Polynomial::new_monomial(FE::from(0), 0) - ); - - let inputs = [FE::from(2), FE::from(2), FE::from(2), FE::from(2)]; - - let (c5, c6) = test_qap_solver(inputs.clone()); - - let mut c_vector = inputs.to_vec(); - c_vector.append(&mut vec![c5, c6]); - - assert_eq!( - test_circuit.h_polynomial(&c_vector), - Polynomial::new_monomial(FE::from(4), 0) - ); - - let inputs = [FE::from(3), FE::from(3), FE::from(3), FE::from(3)]; - - let (c5, c6) = test_qap_solver(inputs.clone()); - - let mut c_vector = inputs.to_vec(); - c_vector.append(&mut vec![c5, c6]); - - assert_eq!( - test_circuit.h_polynomial(&c_vector), - Polynomial::new_monomial(FE::from(18), 0) - ); - - let inputs = [FE::from(4), FE::from(3), FE::from(2), FE::from(1)]; - - let (c5, c6) = test_qap_solver(inputs.clone()); - - let mut c_vector = inputs.to_vec(); - c_vector.append(&mut vec![c5, c6]); - - assert_eq!( - test_circuit.h_polynomial(&c_vector), - Polynomial::new_monomial(FE::from(5), 0) - ); - } - - #[test] - fn test_circuit_solver_on_2_2_2_2_outputs_4_and_16() { - let inputs = [FE::from(2), FE::from(2), FE::from(2), FE::from(2)]; - - let (c5, c6) = test_qap_solver(inputs); - assert_eq!(c5, FE::from(4)); - assert_eq!(c6, FE::from(16)); - } - - #[test] - fn test_circuit_solver_on_1_2_3_4_outputs_12_and_36() { - let inputs = [FE::from(1), FE::from(2), FE::from(3), FE::from(4)]; - - let (c5, c6) = test_qap_solver(inputs); - assert_eq!(c5, FE::from(12)); - assert_eq!(c6, FE::from(36)); - } - #[test] - fn test_r1cs_into_qap_is_test_qap() { - let qap = new_test_qap(); - let r1cs = new_test_r1cs(); - let r1cs_as_qap: QuadraticArithmeticProgram = r1cs.into(); - assert_eq!(qap, r1cs_as_qap); - } -} diff --git a/examples/pinocchio/src/r1cs.rs b/examples/pinocchio/src/r1cs.rs deleted file mode 100644 index c9c3b4284..000000000 --- a/examples/pinocchio/src/r1cs.rs +++ /dev/null @@ -1,245 +0,0 @@ -use crate::common::FE; - -#[derive(Debug, PartialEq, Eq)] -pub enum CreationError { - VectorsSizeMismatch, - MatrixesSizeMismatch, - /// Number of IOs should be less than witness size - 1 - InputOutputTooBig, -} - -/// R1CS representation of an Arithmetic Program -#[derive(Clone, Debug, PartialEq, Eq)] -pub struct Constraint { - pub a: Vec, - pub b: Vec, - pub c: Vec, -} -#[derive(Clone, Debug, PartialEq, Eq)] -/// R1CS represented as a vector of constraints/gates -/// Noticing joining all the first vectors of the constraints results -/// in the first Matrix of the R1CS -/// joining the second vectors results on the second matrix, and so on -/// -pub struct R1CS { - pub constraints: Vec, - // These are not strictly part of a R1CS - // But they are data of the constraint system - // that is needed to generate the proof - pub number_of_inputs: usize, - pub number_of_outputs: usize, -} - -impl R1CS { - #[allow(dead_code)] - pub fn new( - constraints: Vec, - number_of_inputs: usize, - number_of_outputs: usize, - ) -> Result { - let witness_size = constraints[0].a.len(); - // Each constraints already has the correct dimensionality - // a check is needed before creatin the r1cs - // to be sure all constraints have the same dimension - let all_same_length = constraints - .iter() - .all(|v| v.a.len() == constraints[0].a.len()); - - if !all_same_length { - Err(CreationError::VectorsSizeMismatch) - } else if number_of_inputs + number_of_outputs > witness_size - 1 { - Err(CreationError::InputOutputTooBig) - } else { - Ok(Self { - constraints, - number_of_inputs, - number_of_outputs, - }) - } - } - - pub fn new_with_matrixes( - a: Vec>, - b: Vec>, - c: Vec>, - num_inputs: usize, - num_outputs: usize, - ) -> Result { - let mut constraints: Vec = Vec::with_capacity(a.len()); - // TO DO: - // - Check if sizes match - // - Remove clones - for i in 0..a.len() { - constraints.push(Constraint::new(a[i].clone(), b[i].clone(), c[i].clone()).unwrap()) - } - R1CS::new(constraints, num_inputs, num_outputs) - } - - #[allow(dead_code)] - pub fn verify_solution(self, s: &[FE]) -> bool { - for constraint in self.constraints { - if !constraint.verify_solution(s) { - return false; - } - } - true - } - - pub fn number_of_constraints(&self) -> usize { - self.constraints.len() - } - - /// Returns the size of the witness - /// This is the constant part, plus the of inputs + intermediate values + - /// outputs - pub fn witness_size(&self) -> usize { - // all constraints have the same size - // this is enforced in the creation - self.constraints[0].a.len() //gives the number of columns? - } -} - -impl Constraint { - /// Creates a new constraint for a,b,c vectors - #[allow(dead_code)] - pub fn new(a: Vec, b: Vec, c: Vec) -> Result { - if a.len() != b.len() || a.len() != c.len() || b.len() != c.len() { - Err(CreationError::VectorsSizeMismatch) - } else { - Ok(Self { a, b, c }) - } - } - - #[allow(dead_code)] - pub fn verify_solution(self, s: &[FE]) -> bool { - inner_product(&self.a, s) * inner_product(&self.b, s) == inner_product(&self.c, s) - } -} - -pub fn inner_product(v1: &[FE], v2: &[FE]) -> FE { - v1.iter() - .zip(v2) - .map(|(x, y)| x * y) - .fold(FE::from(0), |x, y| x + y) -} - -#[cfg(test)] -pub mod tests { - use crate::test_utils::{new_test_first_constraint, new_test_r1cs, new_test_second_constraint}; - - use super::*; - - #[test] - fn mul_vectors_2_2_3_3_equals_12() { - let v1 = &[FE::from(2), FE::from(2)]; - let v2 = &[FE::from(3), FE::from(3)]; - - assert_eq!(inner_product(v1, v2), FE::from(12)); - } - - #[test] - fn mul_vectors_3_5_equals_15() { - let v1 = &[FE::from(3)]; - let v2 = &[FE::from(5)]; - - assert_eq!(inner_product(v1, v2), FE::from(15)); - } - - #[test] - fn verify_solution_with_test_circuit_c5_constraints() { - assert!(new_test_second_constraint().verify_solution(&test_solution())); - } - - #[test] - fn verify_solution_with_test_circuit_c6_constraints() { - assert!(new_test_second_constraint().verify_solution(&test_solution())); - } - - #[test] - fn verify_bad_solution_with_test_circuit_c5_constraints() { - let solution = vec![ - FE::from(0), - FE::from(0), - FE::from(0), - FE::from(4), - FE::from(1), - FE::from(0), - FE::from(0), - ]; - assert!(!new_test_first_constraint().verify_solution(&solution)); - } - - #[test] - fn verify_bad_solution_with_test_circuit_c6_constraints() { - let solution = vec![ - FE::from(0), - FE::from(2), - FE::from(1), - FE::from(4), - FE::from(5), - FE::from(2), - FE::from(2), - ]; - assert!(!new_test_second_constraint().verify_solution(&solution)); - } - - #[test] - fn verify_solution_with_new_test_r1cs() { - assert!(new_test_r1cs().verify_solution(&test_solution())) - } - - #[test] - fn verify_bad_solution_with_new_test_r1cs() { - let solution = vec![ - FE::from(0), - FE::from(2), - FE::from(1), - FE::from(4), - FE::from(5), - FE::from(2), - FE::from(2), - ]; - - assert!(!new_test_r1cs().verify_solution(&solution)) - } - - #[test] - fn verify_bad_solution_because_of_second_constraint_with_new_test_r1cs() { - let solution = vec![ - FE::from(0), // c0 - FE::from(2), // c1 - FE::from(1), // c2 - FE::from(5), // c3 - FE::from(10), // c4 - FE::from(50), // c5 = c4 * c3 - FE::from(2), // c6 != c5 * (c1+c2), so this should fail - ]; - assert!(!new_test_r1cs().verify_solution(&solution)) - } - - #[test] - fn verify_bad_solution_because_of_first_constraint_with_new_test_r1cs() { - let solution = vec![ - FE::from(0), // c0 - FE::from(1), // c1 - FE::from(1), // c2 - FE::from(5), // c3 - FE::from(10), // c4 - FE::from(10), // c5 != c4 * c3 - FE::from(19), // c6 = c5 * (c1+c2), so this should fail - ]; - assert!(!new_test_r1cs().verify_solution(&solution)) - } - - fn test_solution() -> Vec { - vec![ - FE::from(0), - FE::from(1), - FE::from(2), - FE::from(3), - FE::from(4), - FE::from(12), - FE::from(36), - ] - } -} diff --git a/examples/pinocchio/src/setup.rs b/examples/pinocchio/src/setup.rs deleted file mode 100644 index 4b6c7b614..000000000 --- a/examples/pinocchio/src/setup.rs +++ /dev/null @@ -1,227 +0,0 @@ -use crate::common::{sample_fr_elem, Curve, G1Point, G2Point, TwistedCurve, FE}; -use lambdaworks_math::{cyclic_group::IsGroup, elliptic_curve::traits::IsEllipticCurve}; - -use crate::qap::QuadraticArithmeticProgram; - -pub struct EvaluationKey { - pub g1_vk: Vec, - pub g1_wk: Vec, - pub g2_wk: Vec, - pub g1_yk: Vec, - pub g1_alpha_vk: Vec, - pub g1_alpha_wk: Vec, - pub g1_alpha_yk: Vec, - pub g1_beta: Vec, - pub g2_s_i: Vec, -} -pub struct VerificationKey { - pub g2: G2Point, - pub g2_alpha_v: G2Point, - pub g2_alpha_w: G2Point, - pub g2_alpha_y: G2Point, - pub g2_gamma: G2Point, - pub g2_beta_gamma: G2Point, - pub g1y_t: G1Point, - pub g1_vk: Vec, - pub g2_wk: Vec, - pub g1_yk: Vec, -} -pub struct ToxicWaste { - rv: FE, - rw: FE, - s: FE, - alpha_v: FE, - alpha_y: FE, - alpha_w: FE, - beta: FE, - gamma: FE, -} -impl ToxicWaste { - pub fn ry(&self) -> FE { - &self.rv * &self.rw - } - - #[allow(clippy::too_many_arguments)] - pub fn new( - s: FE, - alpha_v: FE, - alpha_w: FE, - alpha_y: FE, - beta: FE, - rv: FE, - rw: FE, - gamma: FE, - ) -> Self { - Self { - s, - alpha_v, - alpha_w, - alpha_y, - beta, - rv, - rw, - gamma, - } - } - - pub fn sample() -> Self { - Self { - s: sample_fr_elem(), - alpha_v: sample_fr_elem(), - alpha_w: sample_fr_elem(), - alpha_y: sample_fr_elem(), - beta: sample_fr_elem(), - rv: sample_fr_elem(), - rw: sample_fr_elem(), - gamma: sample_fr_elem(), - } - } -} - -pub fn generate_verification_key( - qap: QuadraticArithmeticProgram, - toxic_waste: &ToxicWaste, -) -> VerificationKey { - let g1: G1Point = Curve::generator(); - let g2: G2Point = TwistedCurve::generator(); - let s = &toxic_waste.s; - let alpha_v = &toxic_waste.alpha_v; - let alpha_w = &toxic_waste.alpha_w; - let alpha_y = &toxic_waste.alpha_y; - let beta = &toxic_waste.beta; - let rv = &toxic_waste.rv; - let rw = &toxic_waste.rw; - let gamma = &toxic_waste.gamma; - let ry = toxic_waste.ry(); - let vector_capacity = qap.number_of_inputs + qap.number_of_outputs + 1; - - // We construct g1_vk. - let mut g1_vk_io: Vec = Vec::with_capacity(vector_capacity); - g1_vk_io.push(g1.operate_with_self((rv.clone() * qap.v0().evaluate(s)).representative())); - g1_vk_io.extend( - qap.v_input() - .iter() - .map(|vk| g1.operate_with_self((rv.clone() * vk.evaluate(s)).representative())), - ); - g1_vk_io.extend( - qap.v_output() - .iter() - .map(|vk| g1.operate_with_self((rv.clone() * &vk.evaluate(s)).representative())), - ); - - // We construct g2_wk. - let mut g2_wk_io: Vec = Vec::with_capacity(vector_capacity); - g2_wk_io.push(g2.operate_with_self((rw.clone() * qap.w0().evaluate(s)).representative())); - g2_wk_io.extend( - qap.w_input() - .iter() - .map(|wk| g2.operate_with_self((rw.clone() * wk.evaluate(s)).representative())), - ); - g2_wk_io.extend( - qap.w_output() - .iter() - .map(|wk| g2.operate_with_self((rw.clone() * wk.evaluate(s)).representative())), - ); - - // We construct g1_yk. - let mut g1_yk_io: Vec = Vec::with_capacity(vector_capacity); - g1_yk_io.push(g1.operate_with_self((ry.clone() * qap.y0().evaluate(s)).representative())); - g1_yk_io.extend( - qap.y_input() - .iter() - .map(|yk| g1.operate_with_self((ry.clone() * yk.evaluate(s)).representative())), - ); - g1_yk_io.extend( - qap.y_output() - .iter() - .map(|yk| g1.operate_with_self((ry.clone() * yk.evaluate(s)).representative())), - ); - - VerificationKey { - g2: g2.clone(), - g2_alpha_v: g2.operate_with_self(alpha_v.representative()), - g2_alpha_w: g2.operate_with_self(alpha_w.representative()), - g2_alpha_y: g2.operate_with_self(alpha_y.representative()), - g2_gamma: g2.operate_with_self(gamma.representative()), - g2_beta_gamma: g2.operate_with_self((beta * gamma).representative()), - g1y_t: g1.operate_with_self((ry * qap.target.evaluate(s)).representative()), - g1_vk: g1_vk_io, - g2_wk: g2_wk_io, - g1_yk: g1_yk_io, - } -} - -pub fn generate_evaluation_key( - qap: &QuadraticArithmeticProgram, - toxic_waste: &ToxicWaste, -) -> EvaluationKey { - let g1: G1Point = Curve::generator(); - let g2: G2Point = TwistedCurve::generator(); - let (v_mid, w_mid, y_mid) = (qap.v_mid(), qap.w_mid(), qap.y_mid()); - let s = &toxic_waste.s; - let alpha_v = &toxic_waste.alpha_v; - let alpha_w = &toxic_waste.alpha_w; - let alpha_y = &toxic_waste.alpha_y; - let beta = &toxic_waste.beta; - let rv = &toxic_waste.rv; - let rw = &toxic_waste.rw; - let ry = &toxic_waste.ry(); - let degree = qap.target.degree(); - - EvaluationKey { - g1_vk: v_mid - .iter() - .map(|vk| g1.operate_with_self((rv * vk.evaluate(s)).representative())) - .collect(), - g1_wk: w_mid - .iter() - .map(|wk| g1.operate_with_self((rw * wk.evaluate(s)).representative())) - .collect(), - g2_wk: w_mid - .iter() - .map(|wk| g2.operate_with_self((rw * wk.evaluate(s)).representative())) - .collect(), - g1_yk: y_mid - .iter() - .map(|yk| g1.operate_with_self((ry * yk.evaluate(s)).representative())) - .collect(), - g1_alpha_vk: v_mid - .iter() - .map(|vk| g1.operate_with_self((rv * alpha_v * vk.evaluate(s)).representative())) - .collect(), - g1_alpha_wk: w_mid - .iter() - .map(|wk| g1.operate_with_self((rw * alpha_w * wk.evaluate(s)).representative())) - .collect(), - g1_alpha_yk: y_mid - .iter() - .map(|yk| g1.operate_with_self((ry * alpha_y * yk.evaluate(s)).representative())) - .collect(), - g2_s_i: (0..degree) - .map(|i| g2.operate_with_self((s.pow(i)).representative())) - .collect(), - g1_beta: v_mid - .iter() - .zip(w_mid.iter()) - .zip(y_mid.iter()) - .map(|((vk, wk), yk)| { - g1.operate_with_self( - (rv * beta * vk.evaluate(s) - + rw * beta * wk.evaluate(s) - + ry * beta * yk.evaluate(s)) - .representative(), - ) - }) - .collect(), - } -} - -pub fn setup( - qap: &QuadraticArithmeticProgram, - toxic_waste: ToxicWaste, -) -> (EvaluationKey, VerificationKey) { - ( - generate_evaluation_key(qap, &toxic_waste), - generate_verification_key(qap.clone(), &toxic_waste), - ) -} diff --git a/examples/pinocchio/src/test_utils.rs b/examples/pinocchio/src/test_utils.rs deleted file mode 100644 index 01eb13746..000000000 --- a/examples/pinocchio/src/test_utils.rs +++ /dev/null @@ -1,144 +0,0 @@ -use super::common::FE; -use crate::{ - qap::QuadraticArithmeticProgram as QAP, - r1cs::{Constraint, R1CS}, -}; -use lambdaworks_math::polynomial::Polynomial; - -// r5 and r6 are exposed to help testing -pub fn test_qap_r5() -> FE { - FE::from(0) -} - -pub fn test_qap_r6() -> FE { - FE::from(1) -} - -/// This is a solver for the test qap -/// Inputs: c1,c2,c3,c4 circuit inputs -/// Outputs: c5 intermediate result, c6 result -pub fn test_qap_solver(inputs: [FE; 4]) -> (FE, FE) { - let c5 = &inputs[2] * &inputs[3]; - let c6 = (&inputs[0] + &inputs[1]) * c5.clone(); - (c5, c6) -} - -/// Test qap based on pinocchios paper example. -pub fn new_test_qap() -> QAP { - let r5: FE = test_qap_r5(); - let r6: FE = test_qap_r6(); - - let t: Polynomial = - Polynomial::new(&[-&r5, FE::from(1)]) * Polynomial::new(&[-&r6, FE::from(1)]); - - let vs: Vec> = vec![ - //v0 - Polynomial::interpolate(&[r5.clone(), r6.clone()], &[FE::from(0), FE::from(0)]).unwrap(), - //v1 - Polynomial::interpolate(&[r5.clone(), r6.clone()], &[FE::from(0), FE::from(1)]).unwrap(), - Polynomial::interpolate(&[r5.clone(), r6.clone()], &[FE::from(0), FE::from(1)]).unwrap(), - Polynomial::interpolate(&[r5.clone(), r6.clone()], &[FE::from(1), FE::from(0)]).unwrap(), - Polynomial::interpolate(&[r5.clone(), r6.clone()], &[FE::from(0), FE::from(0)]).unwrap(), - Polynomial::interpolate(&[r5.clone(), r6.clone()], &[FE::from(0), FE::from(0)]).unwrap(), - Polynomial::interpolate(&[r5.clone(), r6.clone()], &[FE::from(0), FE::from(0)]).unwrap(), - ]; - - let ws: Vec> = vec![ - //w0 - Polynomial::interpolate(&[r5.clone(), r6.clone()], &[FE::from(0), FE::from(0)]).unwrap(), - //w1 - Polynomial::interpolate(&[r5.clone(), r6.clone()], &[FE::from(0), FE::from(0)]).unwrap(), - Polynomial::interpolate(&[r5.clone(), r6.clone()], &[FE::from(0), FE::from(0)]).unwrap(), - Polynomial::interpolate(&[r5.clone(), r6.clone()], &[FE::from(0), FE::from(0)]).unwrap(), - Polynomial::interpolate(&[r5.clone(), r6.clone()], &[FE::from(1), FE::from(0)]).unwrap(), - Polynomial::interpolate(&[r5.clone(), r6.clone()], &[FE::from(0), FE::from(1)]).unwrap(), - Polynomial::interpolate(&[r5.clone(), r6.clone()], &[FE::from(0), FE::from(0)]).unwrap(), - ]; - - let ys: Vec> = vec![ - //y0 - Polynomial::interpolate(&[r5.clone(), r6.clone()], &[FE::from(0), FE::from(0)]).unwrap(), - //y1 - Polynomial::interpolate(&[r5.clone(), r6.clone()], &[FE::from(0), FE::from(0)]).unwrap(), - Polynomial::interpolate(&[r5.clone(), r6.clone()], &[FE::from(0), FE::from(0)]).unwrap(), - Polynomial::interpolate(&[r5.clone(), r6.clone()], &[FE::from(0), FE::from(0)]).unwrap(), - Polynomial::interpolate(&[r5.clone(), r6.clone()], &[FE::from(0), FE::from(0)]).unwrap(), - Polynomial::interpolate(&[r5.clone(), r6.clone()], &[FE::from(1), FE::from(0)]).unwrap(), - Polynomial::interpolate( - &[r5.clone(), r6.clone().clone()], - &[FE::from(0), FE::from(1)], - ) - .unwrap(), - ]; - - QAP::new(vs, ws, ys, t, 4, 1).unwrap() -} - -pub fn new_test_r1cs() -> R1CS { - let constraints = vec![new_test_first_constraint(), new_test_second_constraint()]; - R1CS::new(constraints, 4, 1).unwrap() -} - -pub fn new_test_first_constraint() -> Constraint { - Constraint { - a: vec![ - FE::from(0), - FE::from(0), - FE::from(0), - FE::from(1), - FE::from(0), - FE::from(0), - FE::from(0), - ], - b: vec![ - FE::from(0), - FE::from(0), - FE::from(0), - FE::from(0), - FE::from(1), - FE::from(0), - FE::from(0), - ], - c: vec![ - FE::from(0), - FE::from(0), - FE::from(0), - FE::from(0), - FE::from(0), - FE::from(1), - FE::from(0), - ], - } -} - -pub fn new_test_second_constraint() -> Constraint { - Constraint { - a: vec![ - FE::from(0), - FE::from(1), - FE::from(1), - FE::from(0), - FE::from(0), - FE::from(0), - FE::from(0), - ], - b: vec![ - FE::from(0), - FE::from(0), - FE::from(0), - FE::from(0), - FE::from(0), - FE::from(1), - FE::from(0), - ], - c: vec![ - FE::from(0), - FE::from(0), - FE::from(0), - FE::from(0), - FE::from(0), - FE::from(0), - FE::from(1), - ], - } -} diff --git a/examples/pinocchio/src/verifier.rs b/examples/pinocchio/src/verifier.rs deleted file mode 100644 index 5821c9239..000000000 --- a/examples/pinocchio/src/verifier.rs +++ /dev/null @@ -1,52 +0,0 @@ -use crate::common::{Pairing, FE}; -use crate::prover::Proof; -use crate::setup::VerificationKey; -use lambdaworks_math::{ - cyclic_group::IsGroup, elliptic_curve::traits::IsPairing, msm::pippenger::msm, -}; - -pub fn verify(verification_key: &VerificationKey, proof: &Proof, c_inputs_outputs: &[FE]) -> bool { - let b1 = check_divisibility(verification_key, proof, c_inputs_outputs); - let b2 = check_appropriate_spans(verification_key, proof); - let b3 = check_same_linear_combinations(verification_key, proof); - b1 && b2 && b3 -} - -pub fn check_divisibility(verification_key: &VerificationKey, proof: &Proof, c_io: &[FE]) -> bool { - // We transform c_io into UnsignedIntegers. - let c_io = c_io - .iter() - .map(|elem| elem.representative()) - .collect::>(); - - let v_io = - verification_key.g1_vk[0].operate_with(&msm(&c_io, &verification_key.g1_vk[1..]).unwrap()); - let w_io = - verification_key.g2_wk[0].operate_with(&msm(&c_io, &verification_key.g2_wk[1..]).unwrap()); - let y_io = - verification_key.g1_yk[0].operate_with(&msm(&c_io, &verification_key.g1_yk[1..]).unwrap()); - Pairing::compute(&v_io.operate_with(&proof.v), &w_io.operate_with(&proof.w2)).unwrap() - == Pairing::compute(&verification_key.g1y_t, &proof.h).unwrap() - * Pairing::compute(&y_io.operate_with(&proof.y), &verification_key.g2).unwrap() -} - -// We check that v (from the proof) is indeed g multpiplied by a linear combination of the g1_vk. -// The same with w and y. -pub fn check_appropriate_spans(verification_key: &VerificationKey, proof: &Proof) -> bool { - let b1 = Pairing::compute(&proof.v_prime, &verification_key.g2) - == Pairing::compute(&proof.v, &verification_key.g2_alpha_v); - let b2 = Pairing::compute(&proof.w_prime, &verification_key.g2) - == Pairing::compute(&proof.w1, &verification_key.g2_alpha_w); - let b3 = Pairing::compute(&proof.y_prime, &verification_key.g2) - == Pairing::compute(&proof.y, &verification_key.g2_alpha_y); - b1 && b2 && b3 -} - -// We check that the same coefficients were used for the linear combination of v, w and y. -pub fn check_same_linear_combinations(verification_key: &VerificationKey, proof: &Proof) -> bool { - Pairing::compute(&proof.z, &verification_key.g2_gamma) - == Pairing::compute( - &proof.v.operate_with(&proof.w1).operate_with(&proof.y), - &verification_key.g2_beta_gamma, - ) -} diff --git a/examples/pinocchio/tests/integration_test.rs b/examples/pinocchio/tests/integration_test.rs deleted file mode 100644 index cdfe6b1a0..000000000 --- a/examples/pinocchio/tests/integration_test.rs +++ /dev/null @@ -1,35 +0,0 @@ -use pinocchio::common::FE; -use pinocchio::prover::generate_proof; -use pinocchio::setup::{setup, ToxicWaste}; -use pinocchio::test_utils::{new_test_r1cs, test_qap_solver}; -use pinocchio::verifier::verify; - -fn test_pinocchio(toxic_waste: ToxicWaste) { - println!("Running Pinocchio test..."); - let test_qap = new_test_r1cs().into(); - // Setup - let (evaluation_key, verification_key) = setup(&test_qap, toxic_waste); - // Define inputs - let inputs = [FE::from(1), FE::from(2), FE::from(3), FE::from(4)]; - // Execute the QAP - let (c_mid, c_output) = test_qap_solver(inputs.clone()); - // Construct the full witness vector - let mut c_vector = inputs.to_vec(); - c_vector.push(c_mid); - c_vector.push(c_output.clone()); - // Generate proof - let proof = generate_proof(&evaluation_key, &test_qap, &c_vector); - let mut c_io_vector = inputs.to_vec(); - c_io_vector.push(c_output); - // Verify the proof - let accepted = verify(&verification_key, &proof, &c_io_vector); - assert!(accepted, "Proof verification failed"); - println!("Proof verified successfully!"); -} - -#[cfg(test)] -#[test] -fn test_pinocchio_random_toxic_waste() { - let toxic_waste = ToxicWaste::sample(); - test_pinocchio(toxic_waste); -} diff --git a/examples/pohlig-hellman-attack/Cargo.toml b/examples/pohlig-hellman-attack/Cargo.toml deleted file mode 100644 index 1cb35369e..000000000 --- a/examples/pohlig-hellman-attack/Cargo.toml +++ /dev/null @@ -1,10 +0,0 @@ -[package] -name = "pohlig-hellman-attack" -version.workspace = true -edition.workspace = true -license.workspace = true -repository.workspace = true - - -[dependencies] -lambdaworks-math = { workspace = true } diff --git a/examples/pohlig-hellman-attack/README.md b/examples/pohlig-hellman-attack/README.md deleted file mode 100644 index 1acc931a1..000000000 --- a/examples/pohlig-hellman-attack/README.md +++ /dev/null @@ -1,92 +0,0 @@ -# Pohlig-Hellman Attack Implementation - -This implementation demonstrates the Pohlig-Hellman algorithm for solving the discrete logarithm problem on elliptic curves. The algorithm is practical when the group order has small prime factors. - -This attack is significantly more efficient than attempting to solve the discrete logarithm problem using a brute-force search. In the group used for our implementation, the logarithm can be recovered in under one second with the Pohlig-Hellman attack, whereas a brute-force approach would take longer. This highlights the importance of working with groups of large prime order in cryptographic applications. For example, the group of the BLS12-381 elliptic curve over $\mathbb{F}_p$ has composite order $n$, and we need to ensure we are always working over a subgroup of large prime order $r$. - -## Discrete Logarithm Problem - -Given two points $g$ and $h$ on an elliptic curve, where $$h = g^x$$ for some unknown integer $x$, the discrete logarithm problem consists of finding the value of $x$. - -## Mathematical Foundations - -### Group Structure - -Let $G$ be a finite cyclic group of order $n$ with generator $g$. Let's say we want to find $x$ such that $h = g^x$. The Pohlig-Hellman algorithm exploits the structure of $G$ when $n$ has small prime factors. In other words, if the factorization of $n$ is: - -$$n = \prod_{i=1}^{k} p_i^{e_i} = p_1^{e_1} \ldots p_k^{e_k},$$ - -where $p_i$ are small distinct primes and $e_i$ are positive integers, then the algorithm can find $x$ efficiently. - -### Algorithm Steps - -1. **Factorization**: Decompose the group order $n$ into its prime power factors. -2. **Subgroup Resolution**: For each prime power $p_i^{e_i}$: - - Compute $g_i = g^{n/p_i^{e_i}}$. - - Compute $h_i = h^{n/p_i^{e_i}}$. - - Solve $h_i = g_i^{x_i}$ in the subgroup of order $p_i^{e_i}$, using the Baby Step Giant Step algorithm. This will give us $x_i$ such that $x \equiv x_i \text{ mod } p_i^{e_i}$. -3. **Chinese Remainder Theorem**: Combine the solutions $x_i$ to find $x$ modulus $n$. - -### Baby Step Giant Step Algorithm - -Given a generator point $g$ of a subgroup of order $n$ and another point $h$ such that -$$ h = g^x \text { mod } n$$ -the algorithm finds $x$, reducing the complexity of $O(n)$ (using brute force) to $O(\sqrt{n})$. - -#### Algorithm Steps - -1. Compute the step size $m = \lceil \sqrt{n} \rceil$. -2. Compute and store the list of points $\{g^0, g^1, \ldots, g^{m-1} \}$ -3. We compute each element of another list $\{hg^{-0m}, hg^{-1m}, hg^{-2m}, \ldots, hg^{-m^2}\}$, and look for a coincedence between both lists. -4. If a match $g^j = hg^{-im}$ is found then the descrete log is $x = im + j$ because: -$$ -\begin{aligned} -g^j &= hg^{-im} \\ -g^{im + j} &= h -\end{aligned} -$$ -5. If no match is found after $m$ iterations, then no solution exists within the given subgroup. - -### Chinese Remainder Theorem - -Once the subproblems are solved, the algorithm uses the [Chinese Remainder Theorem](https://en.wikipedia.org/wiki/Chinese_remainder_theorem) to find a solution that satisfies all congruences: - -$$x \equiv x_1 \pmod{n_1}$$ -$$x \equiv x_2 \pmod{n_2}$$ -$$\vdots$$ -$$x \equiv x_k \pmod{n_k}$$ - -The solution is given by: -$$x = \sum_{i=1}^{k} a_i M_i N_i \pmod{M}$$ -where $N = \prod_{i=1}^{k} n_i$, $N_i = N/n_i$, and $M_i$ is the modular inverse of $N_i$ modulus $n_i$. - -## Implementation - -You'll find two files in this folder. The file `chinese_remainder_theorem` contains all the algorithms needed to compute the final step. - -In the other file, called `pohlig_hellman`, we define a group vulnerable to this attack and the algorithms that compute it. - -We chose to use a subgroup $H$ of the BLS12-381 Elliptic Curve of order -$$s = 96192362849 = 11 \cdot 10177 \cdot 859267.$$ -This group is vulnerable to the attack because its factorization consists of small primes. - -To find the generator $h$ of this group we took $g$ the generator of a bigger group of order $n$ and computed $$h = g^{\frac{n}{s}}.$$ - -### Usage Example - -```rust -// Create the group -let group = PohligHellmanGroup::new(); -let generator = &group.generator; -let order = &group.order; - -// Define q = g^x. -let x = 7u64; -let q = generator.operate_with_self(x); - -// Find x. -let x_found = group.pohlig_hellman_attack(&q).unwrap(); - -// Check the result. -assert_eq(generator.operate_with_self(x_found), q); -``` diff --git a/examples/pohlig-hellman-attack/src/chinese_remainder_theorem.rs b/examples/pohlig-hellman-attack/src/chinese_remainder_theorem.rs deleted file mode 100644 index f4d668d10..000000000 --- a/examples/pohlig-hellman-attack/src/chinese_remainder_theorem.rs +++ /dev/null @@ -1,104 +0,0 @@ -#[derive(Debug)] -pub enum ChineseRemainderTheoremError { - ModuliNotCoprime, -} - -/// Given an array [(x_1, n_1), ... , (x_k, n_k)] it returns x such that -/// x ≡ x_1 mod n_1, -/// x ≡ x_2 mod n_2, -/// ... -/// x ≡ x_k mod n_k. -/// -/// The moduli n_i should be pairwise coprimes. -/// -/// The Chinese Remainder Theorem states that if the moduli n_i are pairwise coprimes, -/// then there is always a unique solution x to these equations in the range [0, N-1] -/// where N is the product of all moduli. -pub fn chinese_remainder_theorem( - equations: &[(i128, i128)], -) -> Result { - // Calculate the product of all moduli - let n: i128 = equations.iter().map(|&(_, m)| m).product(); - - // For each equation, compute: - // 1. n_i = n / m_i (product of all moduli except current). - // 2. x_i = inverse of n_i modulo m_i. - // 3. Add a_i * n_i * x_i to the result. - let mut result = 0; - for &(a, m) in equations { - let n_i = n / m; - - // Find x_i such that n_i * x_i ≡ 1 (mod m) - let x_i = mod_inverse(n_i, m).ok_or(ChineseRemainderTheoremError::ModuliNotCoprime)?; - - // Compute (result + a * n_i * x_i) % n - result = (result + a * n_i * x_i) % n; - } - - Ok((result % n + n) % n) -} - -/// Computes the multiplicative inverse of x modulo n. -pub fn mod_inverse(x: i128, n: i128) -> Option { - let (g, x, _) = extended_euclidean_algorithm(x, n); - if g == 1 { - Some((x % n + n) % n) - } else { - None - } -} - -/// Computes the extended Euclidean algorithm for two integers. -pub fn extended_euclidean_algorithm(a: i128, b: i128) -> (i128, i128, i128) { - let (mut s, mut old_s) = (0, 1); - let (mut t, mut old_t) = (1, 0); - let (mut r, mut old_r) = (b, a); - - while r != 0 { - let quotient = old_r / r; - - // Update remainders - let temp_r = r; - r = old_r - quotient * r; - old_r = temp_r; - - // Update coefficients - let temp_s = s; - s = old_s - quotient * s; - old_s = temp_s; - - let temp_t = t; - t = old_t - quotient * t; - old_t = temp_t; - } - - (old_r, old_s, old_t) -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_mod_inverse() { - assert_eq!(mod_inverse(3, 7), Some(5)); - assert_eq!(mod_inverse(17, 3120), Some(2753)); - assert_eq!(mod_inverse(2, 4), None); // gcd(2, 4) = 2, so '2' has no inverse modulus 4. - assert_eq!(mod_inverse(1, 5), Some(1)); - } - - #[test] - fn test_chinese_remainder_theorem() { - // x ≡ 2 (mod 3), x ≡ 3 (mod 5), x ≡ 2 (mod 7) - let equations = [(2, 3), (3, 5), (2, 7)]; - assert_eq!(chinese_remainder_theorem(&equations).unwrap(), 23); - - // x ≡ 1 (mod 4), x ≡ 2 (mod 27) - let equations = [(1, 4), (2, 27)]; - assert_eq!(chinese_remainder_theorem(&equations).unwrap(), 29); - - // x ≡ 1 (mod 3), x ≡ 2 (mod 4), x ≡ 3 (mod 5) - let equations = [(1, 3), (2, 4), (3, 5)]; - assert_eq!(chinese_remainder_theorem(&equations).unwrap(), 58); - } -} diff --git a/examples/pohlig-hellman-attack/src/lib.rs b/examples/pohlig-hellman-attack/src/lib.rs deleted file mode 100644 index 9d1fa7547..000000000 --- a/examples/pohlig-hellman-attack/src/lib.rs +++ /dev/null @@ -1,2 +0,0 @@ -pub mod chinese_remainder_theorem; -pub mod pohlig_hellman; diff --git a/examples/pohlig-hellman-attack/src/pohlig_hellman.rs b/examples/pohlig-hellman-attack/src/pohlig_hellman.rs deleted file mode 100644 index c0b59a366..000000000 --- a/examples/pohlig-hellman-attack/src/pohlig_hellman.rs +++ /dev/null @@ -1,234 +0,0 @@ -use lambdaworks_math::{ - cyclic_group::IsGroup, - elliptic_curve::{ - short_weierstrass::{ - curves::bls12_381::{curve::BLS12381Curve, field_extension::BLS12381PrimeField}, - point::ShortWeierstrassProjectivePoint, - }, - traits::FromAffine, - }, - field::element::FieldElement, - unsigned_integer::element::U384, -}; - -use crate::chinese_remainder_theorem::*; - -/// Errors that can occur during Pohlig-Hellman algorithm execution -#[derive(Debug)] -pub enum PohligHellmanError { - /// Error when no discrete logarithm solution could be found - DiscreteLogNotFound, - /// Error from Chinese Remainder Theorem calculation - ChineseRemainderTheoremError(ChineseRemainderTheoremError), -} - -/// Represents a group suitable for the Pohlig-Hellman algorithm -#[derive(Clone, Debug)] -pub struct PohligHellmanGroup { - pub order: u64, - pub order_factors: [u64; 3], - pub generator: ShortWeierstrassProjectivePoint, -} - -impl PohligHellmanGroup { - /// Creates a new subgroup of the BLS12-381 curve with a smooth order - /// that is suitable for the Pohlig-Hellman algorithm. - pub fn new() -> Self { - // Big subgroup of the BLS12-381 Elliptic Curve. - // Its order's factorization is: 11 * 10177 * 859267 * 52437899 * 52435875175126190479447740508185965837690552500527637822603658699938581184513. - // We'll take the first three factors to form the subgroup we are going to attack. - let big_group_order = U384::from_hex_unchecked( - "1FB322654A7CEF70462F7D205CF17F1D6B52ECA5FE8D9BBD809536AAD8A973FFF0AAAAAA5555AAAB", - ); - - // Generator of the big group. - // Constant taken from https://hackmd.io/@benjaminion/bls12-381#Generators. - let big_group_generator = ShortWeierstrassProjectivePoint::::from_affine( - FieldElement::::from_hex_unchecked("04"), - FieldElement::::from_hex_unchecked("0a989badd40d6212b33cffc3f3763e9bc760f988c9926b26da9dd85e928483446346b8ed00e1de5d5ea93e354abe706c"), - ).unwrap(); - - // We take the first three factors of the big group order, forming the order of a subgroup. - // 96192362849 = 11 * 10177 * 859267. - let subgroup_order = 96192362849u64; - - let subgroup_order_factors = [11u64, 10177u64, 859267u64]; - - // We construct the generator 'h' of the subgroup using: - // h = g^{n/s}, where 'g' is the big group generator, 'n' is its order and 's' the subgroup order. - let quotient = big_group_order.div_rem(&U384::from(subgroup_order)).0; - let subgroup_generator = big_group_generator.operate_with_self(quotient); - - Self { - order: subgroup_order, - order_factors: subgroup_order_factors, - generator: subgroup_generator, - } - } - - /// Performs the Pohlig-Hellman attack to find the discrete logarithm. - /// - /// Given a point q = h^x (where h is the smooth group generator) finds the value x using the Pohlig-Hellman algorithm. - /// Note That the result 'x' is given modulus the group order. - pub fn pohlig_hellman_attack( - &self, - q: &ShortWeierstrassProjectivePoint, - ) -> Result { - // In this vector we'll collect all the equations that we´ll use to find the exponent x using the Chinese Remainder Theorem. - // The elements of `equations` will be of the form (x', n), which represents that x satisfies x ≡ x' mod n. - let mut equations = Vec::new(); - - // For each factor n in the factorization of the order, we search for x' such that - // x ≡ x' mod n, using the Baby Step Giant Step algorithm. - for factor in self.order_factors { - let cofactor = self.order / factor; - - // We look for x' such that {h'}^{x'} = q' - let h_prime = self.generator.operate_with_self(cofactor); - let q_prime = q.operate_with_self(cofactor); - - // We use Baby Step Giant Step to find the discrete logarithm in the subgroup of order `factor`. - if let Some(x_prime) = Self::baby_step_giant_step(&h_prime, &q_prime, factor) { - // We add the congruence x ≡ x' mod factor - equations.push((x_prime as i128, factor as i128)); - } else { - // If we couldn't find the discrete logarithm in the subgroup of order `factor`, it - // means that there is no solution to the discrete logarithm equation proposed. - // That is, there is no 'x' such that h^x = q. - return Err(PohligHellmanError::DiscreteLogNotFound); - } - } - - // We combine the equations using the Chinese Remainder Theorem. - chinese_remainder_theorem(&equations) - .map(|x| x as u128) - .map_err(PohligHellmanError::ChineseRemainderTheoremError) - } - - /// Implementation of the Baby-Step Giant-Step algorithm. - /// - /// Finds the discrete logarithm x such that h^x = q in a cyclic group, - /// where 0 ≤ x < n. - pub fn baby_step_giant_step( - h: &ShortWeierstrassProjectivePoint, - q: &ShortWeierstrassProjectivePoint, - n: u64, - ) -> Option { - // Compute optimal step size m ≈ sqrt(n) - let m: u64 = (n as f64).sqrt().ceil() as u64 + 1; - - // We construct the list `baby_list`= [h^0, h^1, ..., h^{m-1}]. - let mut baby_list = Vec::with_capacity(m as usize); - for j in 0..m { - baby_list.push(h.operate_with_self(j)); - } - - // We want to compare the lists [h^0, h^1, ..., h^{m-1}] and [q * h^{-m}, q * h^{-2m}, q * h^{-3m}, ... , q * h^{-m*m}] - // to find a coincidence. - let hm = h.operate_with_self(m); - let minus_hm = hm.neg(); - - // y = q initially - let mut y = q.clone(); - - for i in 0..m { - // We check if y is in the baby_list. - for j in 0..m { - if baby_list[j as usize] == y { - let x = i * m + j; - return Some(x); - } - } - - // y = y * h^{-m} - y = y.operate_with(&minus_hm); - } - - // If there isn't any coincidence between the lists, then there is no result x such that h^x = q. - None - } -} - -impl Default for PohligHellmanGroup { - fn default() -> Self { - Self::new() - } -} - -#[cfg(test)] -mod tests { - use lambdaworks_math::elliptic_curve::short_weierstrass::curves::bls12_381::field_extension::BLS12381PrimeField; - use lambdaworks_math::{elliptic_curve::traits::FromAffine, unsigned_integer::element::U384}; - - use super::*; - - #[test] - fn check_group_generators_order() { - let big_group_generator = ShortWeierstrassProjectivePoint::::from_affine( - FieldElement::::from_hex_unchecked("04"), - FieldElement::::from_hex_unchecked("0a989badd40d6212b33cffc3f3763e9bc760f988c9926b26da9dd85e928483446346b8ed00e1de5d5ea93e354abe706c"), - ).unwrap(); - - let big_group_order = U384::from_hex_unchecked( - "1FB322654A7CEF70462F7D205CF17F1D6B52ECA5FE8D9BBD809536AAD8A973FFF0AAAAAA5555AAAB", - ); - - // `big_group_generator` should have order `big_group_order`. - assert_eq!( - big_group_generator.operate_with_self(big_group_order), - ShortWeierstrassProjectivePoint::::neutral_element() - ); - - // We construct a subgroup of the big groups of order 96192362849. - let subgroup = PohligHellmanGroup::default(); - let subgroup_generator = subgroup.generator; - let subgroup_order = subgroup.order; - - // `subgroup_generator` should have order 96192362849. - assert_eq!( - subgroup_generator.operate_with_self(subgroup_order), - ShortWeierstrassProjectivePoint::::neutral_element() - ); - } - - #[test] - fn test_pohlig_hellman_attack() { - let group = PohligHellmanGroup::default(); - let generator = &group.generator; - let order = &group.order; - - // We solve the descrete log for q = g^5. - let x1 = 5u64; - let q1 = generator.operate_with_self(x1); - let x1_found = group.pohlig_hellman_attack(&q1).unwrap(); - - // g^{x1_found} = q1. - assert_eq!(generator.operate_with_self(x1_found), q1); - // `x1_found` = x1 (mod order). - assert_eq!(x1_found, (x1 % order) as u128); - - // We solve the descrete log for q = g^{100000000000}. - let x2 = 100000000000; - let q2 = generator.operate_with_self(x2); - let x2_found = group.pohlig_hellman_attack(&q2).unwrap(); - - // g^{x2_found} = q2. - assert_eq!(generator.operate_with_self(x2_found), q2); - // `x2_found` = x2 (mod order). - assert_eq!(x2_found, (x2 % order) as u128); - } - - #[test] - fn test_pohlig_hellman_big_exponent() { - let group = PohligHellmanGroup::default(); - let generator = &group.generator; - let order = &group.order; - - let x = 14901161193847656250000000000000000000u128; - let q = generator.operate_with_self(x); - let x_found = group.pohlig_hellman_attack(&q).unwrap(); - - assert_eq!(generator.operate_with_self(x_found), q); - assert_eq!(x_found, (x % (*order as u128))); - } -} diff --git a/examples/prove-miden/.gitignore b/examples/prove-miden/.gitignore deleted file mode 100644 index a6c57f5fb..000000000 --- a/examples/prove-miden/.gitignore +++ /dev/null @@ -1 +0,0 @@ -*.json diff --git a/examples/prove-miden/Cargo.toml b/examples/prove-miden/Cargo.toml deleted file mode 100644 index 0be1cc47d..000000000 --- a/examples/prove-miden/Cargo.toml +++ /dev/null @@ -1,27 +0,0 @@ -[package] -name = "prove-miden" -version.workspace = true -edition.workspace = true -license.workspace = true -repository.workspace = true - -[[bin]] -name = "prove-miden" -path = "src/main.rs" - -[dependencies] -lambdaworks-crypto = { workspace = true } -lambdaworks-math = { workspace = true, features = ["lambdaworks-serde-string"] } -lambdaworks-winterfell-adapter = { workspace = true } -stark-platinum-prover = { git = "https://github.com/lambdaclass/lambdaworks", branch = "miden-version", features = [ - "winter_compatibility", -] } - -serde = { version = "1.0" } -serde_json = "1" -bincode = { version = "2.0.1", features = ["serde"] } -miden-core = { package = "miden-core", version = "0.7" } -miden-assembly = { package = "miden-assembly", version = "0.7" } -miden-processor = { package = "miden-processor", version = "0.7" } -miden-air = { package = "miden-air", version = "0.7" } -winter-prover = { package = "winter-prover", version = "0.6.4" } diff --git a/examples/prove-miden/README.md b/examples/prove-miden/README.md deleted file mode 100644 index 3e4fdf82d..000000000 --- a/examples/prove-miden/README.md +++ /dev/null @@ -1,8 +0,0 @@ -
- -# Lambdaworks Polygon Miden Proving example - -This programs compiles and executes a miden fibonacci programs, obtains the trace, and then proves it and verifies it with lambdaworks Stark Platinum Prover. - -Special thanks to Polygon Miden team for providing the open source VM used here, the prover, and documenting the project. - diff --git a/examples/prove-miden/src/main.rs b/examples/prove-miden/src/main.rs deleted file mode 100644 index 476735480..000000000 --- a/examples/prove-miden/src/main.rs +++ /dev/null @@ -1,107 +0,0 @@ -use std::time::Instant; - -use lambdaworks_winterfell_adapter::{ - adapter::{public_inputs::AirAdapterPublicInputs, QuadFeltTranscript}, - examples::miden_vm::MidenVMQuadFeltAir, -}; -use miden_air::{ProvingOptions, PublicInputs}; -use miden_assembly::Assembler; -use miden_core::{Felt, FieldElement, StackInputs, StarkField}; -use miden_processor::{self as processor}; -use processor::DefaultHost; -use stark_platinum_prover::{ - proof::options::{ProofOptions, SecurityLevel}, - prover::{IsStarkProver, Prover}, - verifier::{IsStarkVerifier, Verifier}, -}; -use winter_prover::Trace; - -fn compute_fibonacci(n: usize) -> Felt { - let mut t0 = Felt::ZERO; - let mut t1 = Felt::ONE; - - for _ in 0..n { - t1 = t0 + t1; - core::mem::swap(&mut t0, &mut t1); - } - t0 -} - -fn main() { - let fibonacci_number = 16; - - let program = format!( - "begin - repeat.{} - swap dup.1 add - end - end", - fibonacci_number - 1 - ); - - println!("\nCompiling miden fibonacci program"); - - let program = Assembler::default().compile(program).unwrap(); - let expected_result = vec![compute_fibonacci(fibonacci_number).as_int()]; - let stack_inputs = StackInputs::try_from_values([0, 1]).unwrap(); - - let mut lambda_proof_options = ProofOptions::new_secure(SecurityLevel::Conjecturable100Bits, 3); - lambda_proof_options.blowup_factor = 8; - - println!("\nExecuting program in Miden VM"); - let winter_trace = processor::execute( - &program, - stack_inputs.clone(), - DefaultHost::default(), - *ProvingOptions::default().execution_options(), - ) - .unwrap(); - let program_info = winter_trace.program_info().clone(); - let stack_outputs = winter_trace.stack_outputs().clone(); - - let pub_inputs = PublicInputs::new(program_info, stack_inputs, stack_outputs.clone()); - - assert_eq!( - expected_result, - stack_outputs.clone().stack_truncated(1), - "Program result was computed incorrectly" - ); - - let pub_inputs = AirAdapterPublicInputs::new( - pub_inputs, - vec![2; 182], - vec![0, 1], - winter_trace.get_info(), - winter_trace.clone().into(), - ); - - println!("\nImporting trace to lambdaworks"); - let trace = - MidenVMQuadFeltAir::convert_winterfell_trace_table(winter_trace.main_segment().clone()); - - println!("\nProving "); - - let timer0 = Instant::now(); - let proof = Prover::::prove( - &trace, - &pub_inputs, - &lambda_proof_options, - QuadFeltTranscript::new(&[]), - ) - .unwrap(); - let elapsed0 = timer0.elapsed(); - println!("Total time spent proving: {elapsed0:?}"); - - println!("\nVerifying "); - let timer0 = Instant::now(); - assert!(Verifier::::verify( - &proof, - &pub_inputs, - &lambda_proof_options, - QuadFeltTranscript::new(&[]), - )); - let elapsed0 = timer0.elapsed(); - println!("Total time spent verifying: {elapsed0:?}"); - - println!("\nDone!"); -} diff --git a/examples/prove-verify-circom/Cargo.toml b/examples/prove-verify-circom/Cargo.toml deleted file mode 100644 index 2b70b7dbc..000000000 --- a/examples/prove-verify-circom/Cargo.toml +++ /dev/null @@ -1,18 +0,0 @@ -[package] -name = "prove-verify-circom" -version.workspace = true -edition.workspace = true -license.workspace = true -repository.workspace = true - -[[bin]] -name = "prove-verify-circom" -path = "src/main.rs" - -[dependencies] -lambdaworks-crypto = { workspace = true } -lambdaworks-groth16 = { workspace = true } -lambdaworks-math = { workspace = true, features = ["lambdaworks-serde-string"] } -lambdaworks-circom-adapter = { workspace = true } -serde = { version = "1.0" } -serde_json = "1" diff --git a/examples/prove-verify-circom/README.md b/examples/prove-verify-circom/README.md deleted file mode 100644 index 327b9ed16..000000000 --- a/examples/prove-verify-circom/README.md +++ /dev/null @@ -1,5 +0,0 @@ -
- -# Lambdaworks Circom Proving & Verification Example - -This programs converts Circom & SnarkJS generated constraints and witnesses into Lambdaworks-compatible instances, performs trusted setup, generates proof, and finally verifies the integrity of witness assignments. diff --git a/examples/prove-verify-circom/circom_lambdaworks_tutorial.md b/examples/prove-verify-circom/circom_lambdaworks_tutorial.md deleted file mode 100644 index 56174c520..000000000 --- a/examples/prove-verify-circom/circom_lambdaworks_tutorial.md +++ /dev/null @@ -1,280 +0,0 @@ -# Groth16 from Circom to Lambdaworks Tutorial - -In this small tutorial, we will perform all the steps needed to prove a computation using Groth16. Our goal is to prove a computation. In this case we want to prove the Fibonacci sequence. - -> [!TIP] -> If you don´t know what Groth16 does, you can read our [post](https://blog.lambdaclass.com/groth16/). - -Let's begin by creating the circuit and the input for the program using [Circom2](https://docs.circom.io/getting-started/installation/). - -> [!IMPORTANT] -> This tutorial will work within `lambdaworks/provers/groth16` to make it easier to follow along. If you prefer, you can create a new Rust project, but you will need to import all the crates from the library. - -As we want to cover all the steps from creating a program to prove it using Groth 16, the first thing to do is to create the folder for our program. - -Create a file named `fibonacci` within [`test`](../../crates/provers/groth16/circom-adapter/tests/). This is where our program will be. -The directory should now look like this: - -```ml -provers/ -└── groth16/ - ├── arkworks-adapter/ - └── circom-adapter/ - ├── src/ - │ ├── lib.rs - │ └── README.md - ├── tests/ - │ ├── fibonacci/ (or any other name) - │ ├── poseidon/ - │ ├── vitalik_example/ - │ ├── poseidon_test.rs - │ └── vitalik_test.rs - └── Cargo.toml -``` - -### Understanding the Fibonacci Circuit - -The Fibonacci sequence is defined as follows: - -$$ -\begin{align*} -F(0) &= a \quad \text{(starting value 1)} \\ -F(1) &= b \quad \text{(starting value 2)} \\ -F(n) &= F(n-1) + F(n-2) \quad \text{for } n \geq 2 -\end{align*} -$$ - -In our circuit, we start with two initial numbers, $a$ and $b$, and compute the $n^\text{th}$ Fibonacci number. The circuit checks if the output is correctly computed based on these inputs - -```csharp -pragma circom 2.0.0; - -// Fibonacci with custom starting numbers -template Fibonacci(n) { - assert(n >= 2); - signal input in[2]; - signal output out; - - signal fib[n+1]; - fib[0] <== in[0]; - fib[1] <== in[1]; - for (var i = 2; i <= n; i++) { - fib[i] <== fib[i-2] + fib[i-1]; - } - - out <== fib[n]; -} - -// Instantiate Fibonacci as the main circuit with a specific n value -component main = Fibonacci(10); -``` - -In our case this will be named `fibonacci.circom`. - -In this circuit: -`in[0]` and `in[1]` are the initial values a and b - -`out` will hold F(10), the 10th number in the Fibonacci Sequence - -### Preparing Inputs - -Once we have defined the circuit, the next step is to provide the inputs needed for the proving process. In this case, our inputs consists of the starting numbers for the Fibonacci sequence. - -As we want to use $F(0) = 1$ and $F(1) = 1$, the input JSON file will look like this: - -```json -{ - "in": [1, 1] -} -``` - -The file should be named `input.json`. - -Inside the same directory where those files are, run the command: - -```bash -circom fibonacci.circom --r1cs --wasm -p bls12381 -``` - -The arguments provided here are: - -- `r1cs` to generate the R1CS file -- `wasm` to generate the WebAssembly code -- `-p bls12381` to set the elliptic curve - -That will create a `fibonacci_js` directory and a `fibonacci_r1cs `file - -> [!WARNING] -> Do not skip the `-p bls12381` flag, as this is the only field supported by the adapter right now. If not specified, the default is `bn128` and the proving will not work! - -To compute the `witness`, execute: - -```bash -node fibonacci_js/generate_witness.js fibonacci_js/fibonacci.wasm input.json witness.wtns -``` - -As our program inputs are json files we need to export the witness and r1cs files into the same format. To do that run: - -```bash -snarkjs wtns export json witness.wtns witness.wtns.json -``` - -and - -```bash -snarkjs r1cs export json fibonacci.r1cs fibonacci.r1cs.json -``` - -Now we have `witness.json` and `fibonacci.r1cs.json` - -The folder should look like: - -```ml -fibonacci/ -├── fibonacci_js/ -├── fibonacci.circom -├── fibonacci.r1cs -├── fibonacci.r1cs.json -├── input.json -├── witness.json -└── witness.wtns -``` - -We only need `fibonacci.r1cs.json` and `witness.json` so, if you want, you can delete the unnecessary files by running: - -```bash -rm -rf fibonacci_js fibonacci.circom fibonacci.r1cs witness.wtns input.json -``` - -All at once, you can copy-paste the following to the terminal in the same directory as your circuit: - -```bash -circom fibonacci.circom --r1cs --wasm -p bls12381; -node fibonacci_js/generate_witness.js fibonacci_js/fibonacci.wasm input.json witness.wtns; -snarkjs wtns export json witness.wtns witness.json; -snarkjs r1cs export json fibonacci.r1cs fibonacci.r1cs.json; -rm -rf fibonacci_js fibonacci.circom fibonacci.r1cs witness.wtns input.json; # deletes unnecessary artifacts -``` - -## Using Lambdaworks Circom Adapter - -As mentioned in the blog post, the main goal of Groth16 (or any other prover) is to prove computation. -The function `circom_to_lambda` will take the `fibonacci.r1cs.json` and the `witness.json` data and translate it into the format needed for Lambdaworks. - -The `circom_to_lambda` function is responsible for converting the R1CS constraints and witness generated by Circom into a format that Lambdaworks can use to construct a Quadratic Arithmetic Program (QAP). The QAP representation is essential for Groth16 proof generation, as it represents the arithmetic circuit in terms of polynomials. - -### How `circom_to_lambda` works - -The function `circom_to_lambda` does the following: - -1. **Parse JSON Data**: It parses the R1CS constraints and witness files from JSON format into Rust data structures using serde_json. - -2. **Build LRO Matrices**: It extracts the $L,R,O$ matrices which represent the variables involved in each constraint. - -3. **Construct QAP**: It uses the $L,R,O$ matrices to build a Quadratic Arithmetic Program (QAP). This QAP is used in the Groth16 proving process. - -## Generating the proof with Groth16 - -After converting the data using `circom_to_lambda`, you can use the resulting QAP and witness to generate a Groth16 proof. This process involves two key steps: creating the proving key and then using it to generate the proof based on the QAP. Here's a detailed explanation of the process: - -We will put all together inside `integration_test`. - -### Step 1: Read the R1CS and Witness Files - -First, you need to read the R1CS (Rank-1 Constraint System) file and the witness file generated by Circom. - -```rust -let circom_wtns = read_circom_witness("./tests/fibonacci/witness.json") - .expect("could not read witness"); -let circom_r1cs = - read_circom_r1cs("./tests/fibonacci/test.r1cs.json").expect("could not read r1cs"); - -let (qap, wtns, pubs) = circom_to_lambda(circom_r1cs, circom_wtns); -``` - -`test_dir`: Specifies the directory where the R1CS and witness files are stored. This allows for easy organization and reuse of test data. - -`circom_to_lambda` : Converts the content of the R1CS and witness files into a format compatible with Lambdaworks. This function parses the JSON content and produces a QAP, a corresponding witness vector and the public signals. - -### Step 2: Generate the Proving and Verifying Keys - -```rust -let (pk, vk) = setup(&qap); -``` - -- **Proving Key (pk)**: Used to create a proof that the computation was carried out correctly. -- **Verifying Key (vk)**: Used to verify the validity of the proof, ensuring that it matches the expected computation. - -### Step 3: Generate the Proof - -With the proving key in hand, you can generate a proof using the `Prover::prove` function: - -```rust -let proof = Prover::prove(&wtns, &qap, &pk); -``` - -`Prover::prove`: This function takes the witness, QAP, and proving key to generate a proof that certifies the correctness of the computation represented by the circuit. - -### Step 4: Verify the Proof - -To ensure the proof is valid, use the `verify` function: - -```rust -let accept = verify(&vk, &proof, &pubs); -``` - -Here, `verify` takes the verifying key, proof, and public inputs to check if the proof is valid. It ensures that the proof matches the expected output of the computation without revealing the private inputs. - -### Step 5: Assert the Verification Result - -Finally, check if the verification was successful: - -```rust -assert!(accept, "Proof verification failed."); -``` - -This line ensures that the proof verification returns `true`. If the verification fails, the test will produce an error, which helps catch issues during development. - -### Putting it all together - -Create a new file within [`test`](../../crates/provers/groth16/circom-adapter/tests/) named `fibonacci_test.rs` and paste the following code: - -```rust -use lambdaworks_circom_adapter::*; - -#[test] -fn fibonacci_verify() { - // Step 1: Read circuit files & process them - let circom_wtns = read_circom_witness("./tests/fibonacci/witness.json") - .expect("could not read witness"); - let circom_r1cs = - read_circom_r1cs("./tests/fibonacci/test.r1cs.json").expect("could not read r1cs"); - let (qap, wtns, pubs) = circom_to_lambda(circom_r1cs, circom_wtns); - - // Step 2: Generate the proving and verifying keys using the QAP - let (pk, vk) = setup(&qap); - - // Step 3: Generate the proof using the proving key and witness - let proof = Prover::prove(&wtns, &qap, &pk); - - // Step 4: Verify the proof using the verifying key and public inputs - let accept = verify(&vk, &proof, &pubs); - - // Step 5: Assert that the verification is successful - assert!(accept, "Proof verification failed."); - - println!("Proof verification succeeded. All steps completed."); -} -``` - -and run: - -```sh -cargo test -- --test fibonacci_verify -``` - -If everything is set up correctly, you should see a success the message "Proof verification succeeded. All steps completed.". - -## Summary - -Congratulations! You have successfully set up a Groth16 proof for a Fibonacci circuit using Circom and Lambdaworks. This tutorial walked you through the entire process—from defining the circuit in Circom, generating the R1CS and witness, converting the data for use in Rust, and finally, creating and verifying the proof using Lambdaworks. diff --git a/examples/prove-verify-circom/input_files/input.json b/examples/prove-verify-circom/input_files/input.json deleted file mode 100644 index 15d9f0c15..000000000 --- a/examples/prove-verify-circom/input_files/input.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "in": 3, - "k": 12331548239589023489032859403859043 -} diff --git a/examples/prove-verify-circom/input_files/test.circom b/examples/prove-verify-circom/input_files/test.circom deleted file mode 100644 index a713aed20..000000000 --- a/examples/prove-verify-circom/input_files/test.circom +++ /dev/null @@ -1,19 +0,0 @@ -pragma circom 2.0.0; - -include "circomlib/mimc.circom"; - -template Test() { - signal input in; - signal input k; - - signal output out; - - component hash = MiMC7(10); - - hash.x_in <== in; - hash.k <== k; - - out <== hash.out; -} - -component main = Test(); diff --git a/examples/prove-verify-circom/input_files/test.r1cs.json b/examples/prove-verify-circom/input_files/test.r1cs.json deleted file mode 100644 index 0b4ab46d8..000000000 --- a/examples/prove-verify-circom/input_files/test.r1cs.json +++ /dev/null @@ -1,560 +0,0 @@ -{ - "n8": 32, - "prime": "52435875175126190479447740508185965837690552500527637822603658699938581184513", - "nVars": 43, - "nOutputs": 1, - "nPubInputs": 0, - "nPrvInputs": 2, - "nLabels": 46, - "nConstraints": 40, - "useCustomGates": false, - "constraints": [ - [ - { - "2": "52435875175126190479447740508185965837690552500527637822603658699938581184512", - "3": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - }, - { - "2": "1", - "3": "1" - }, - { - "4": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - } - ], - [ - { - "4": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - }, - { - "4": "1" - }, - { - "14": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - } - ], - [ - { - "14": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - }, - { - "4": "1" - }, - { - "24": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - } - ], - [ - { - "2": "52435875175126190479447740508185965837690552500527637822603658699938581184512", - "3": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - }, - { - "24": "1" - }, - { - "34": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - } - ], - [ - { - "0": "31546913764184207022969313297519759288390047205751473155388718153343834613532", - "3": "52435875175126190479447740508185965837690552500527637822603658699938581184512", - "34": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - }, - { - "0": "20888961410941983456478427210666206549300505294776164667214940546594746570981", - "3": "1", - "34": "1" - }, - { - "5": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - } - ], - [ - { - "5": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - }, - { - "5": "1" - }, - { - "15": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - } - ], - [ - { - "15": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - }, - { - "5": "1" - }, - { - "25": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - } - ], - [ - { - "0": "31546913764184207022969313297519759288390047205751473155388718153343834613532", - "3": "52435875175126190479447740508185965837690552500527637822603658699938581184512", - "34": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - }, - { - "25": "1" - }, - { - "35": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - } - ], - [ - { - "0": "37170749061691167740887588596256925169098797041318237106136154014185835867320", - "3": "52435875175126190479447740508185965837690552500527637822603658699938581184512", - "35": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - }, - { - "0": "15265126113435022738560151911929040668591755459209400716467504685752745317193", - "3": "1", - "35": "1" - }, - { - "6": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - } - ], - [ - { - "6": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - }, - { - "6": "1" - }, - { - "16": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - } - ], - [ - { - "16": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - }, - { - "6": "1" - }, - { - "26": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - } - ], - [ - { - "0": "37170749061691167740887588596256925169098797041318237106136154014185835867320", - "3": "52435875175126190479447740508185965837690552500527637822603658699938581184512", - "35": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - }, - { - "26": "1" - }, - { - "36": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - } - ], - [ - { - "0": "44101697547633208494971236340683207528647340248885841624891974200292945474857", - "3": "52435875175126190479447740508185965837690552500527637822603658699938581184512", - "36": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - }, - { - "0": "8334177627492981984476504167502758309043212251641796197711684499645635709656", - "3": "1", - "36": "1" - }, - { - "7": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - } - ], - [ - { - "7": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - }, - { - "7": "1" - }, - { - "17": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - } - ], - [ - { - "17": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - }, - { - "7": "1" - }, - { - "27": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - } - ], - [ - { - "0": "44101697547633208494971236340683207528647340248885841624891974200292945474857", - "3": "52435875175126190479447740508185965837690552500527637822603658699938581184512", - "36": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - }, - { - "27": "1" - }, - { - "37": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - } - ], - [ - { - "0": "51061550955646024978576101143384273722293033235345833968426029372314447605109", - "3": "52435875175126190479447740508185965837690552500527637822603658699938581184512", - "37": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - }, - { - "0": "1374324219480165500871639364801692115397519265181803854177629327624133579404", - "3": "1", - "37": "1" - }, - { - "8": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - } - ], - [ - { - "8": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - }, - { - "8": "1" - }, - { - "18": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - } - ], - [ - { - "18": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - }, - { - "8": "1" - }, - { - "28": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - } - ], - [ - { - "0": "51061550955646024978576101143384273722293033235345833968426029372314447605109", - "3": "52435875175126190479447740508185965837690552500527637822603658699938581184512", - "37": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - }, - { - "28": "1" - }, - { - "38": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - } - ], - [ - { - "0": "40993286491461846084814174648925789391128665924565021489700464711187288192041", - "3": "52435875175126190479447740508185965837690552500527637822603658699938581184512", - "38": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - }, - { - "0": "11442588683664344394633565859260176446561886575962616332903193988751292992472", - "3": "1", - "38": "1" - }, - { - "9": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - } - ], - [ - { - "9": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - }, - { - "9": "1" - }, - { - "19": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - } - ], - [ - { - "19": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - }, - { - "9": "1" - }, - { - "29": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - } - ], - [ - { - "0": "40993286491461846084814174648925789391128665924565021489700464711187288192041", - "3": "52435875175126190479447740508185965837690552500527637822603658699938581184512", - "38": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - }, - { - "29": "1" - }, - { - "39": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - } - ], - [ - { - "0": "49876973986029631718998843838858879116687043869814669263555479608900735835368", - "3": "52435875175126190479447740508185965837690552500527637822603658699938581184512", - "39": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - }, - { - "0": "2558901189096558760448896669327086721003508630712968559048179091037845349145", - "3": "1", - "39": "1" - }, - { - "10": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - } - ], - [ - { - "10": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - }, - { - "10": "1" - }, - { - "20": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - } - ], - [ - { - "20": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - }, - { - "10": "1" - }, - { - "30": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - } - ], - [ - { - "0": "49876973986029631718998843838858879116687043869814669263555479608900735835368", - "3": "52435875175126190479447740508185965837690552500527637822603658699938581184512", - "39": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - }, - { - "30": "1" - }, - { - "40": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - } - ], - [ - { - "0": "41245896579833438124627598732587455686500593322610353024865913009811263108124", - "3": "52435875175126190479447740508185965837690552500527637822603658699938581184512", - "40": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - }, - { - "0": "11189978595292752354820141775598510151189959177917284797737745690127318076389", - "3": "1", - "40": "1" - }, - { - "11": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - } - ], - [ - { - "11": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - }, - { - "11": "1" - }, - { - "21": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - } - ], - [ - { - "21": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - }, - { - "11": "1" - }, - { - "31": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - } - ], - [ - { - "0": "41245896579833438124627598732587455686500593322610353024865913009811263108124", - "3": "52435875175126190479447740508185965837690552500527637822603658699938581184512", - "40": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - }, - { - "31": "1" - }, - { - "41": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - } - ], - [ - { - "0": "49172908601962629639762324594028110760479211924325701202071483671901834442759", - "3": "52435875175126190479447740508185965837690552500527637822603658699938581184512", - "41": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - }, - { - "0": "3262966573163560839685415914157855077211340576201936620532175028036746741754", - "3": "1", - "41": "1" - }, - { - "12": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - } - ], - [ - { - "12": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - }, - { - "12": "1" - }, - { - "22": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - } - ], - [ - { - "22": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - }, - { - "12": "1" - }, - { - "32": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - } - ], - [ - { - "0": "49172908601962629639762324594028110760479211924325701202071483671901834442759", - "3": "52435875175126190479447740508185965837690552500527637822603658699938581184512", - "41": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - }, - { - "32": "1" - }, - { - "42": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - } - ], - [ - { - "0": "35405960283582965178043908412305484106138721775160350841992479962234692012783", - "3": "52435875175126190479447740508185965837690552500527637822603658699938581184512", - "42": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - }, - { - "0": "17029914891543225301403832095880481731551830725367286980611178737703889171730", - "3": "1", - "42": "1" - }, - { - "13": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - } - ], - [ - { - "13": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - }, - { - "13": "1" - }, - { - "23": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - } - ], - [ - { - "23": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - }, - { - "13": "1" - }, - { - "33": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - } - ], - [ - { - "0": "35405960283582965178043908412305484106138721775160350841992479962234692012783", - "3": "52435875175126190479447740508185965837690552500527637822603658699938581184512", - "42": "52435875175126190479447740508185965837690552500527637822603658699938581184512" - }, - { - "33": "1" - }, - { - "1": "52435875175126190479447740508185965837690552500527637822603658699938581184512", - "3": "1" - } - ] - ], - "map": [ - 0, - 1, - 2, - 3, - 7, - 8, - 9, - 10, - 11, - 12, - 13, - 14, - 15, - 16, - 17, - 18, - 19, - 20, - 21, - 22, - 23, - 24, - 25, - 26, - 27, - 28, - 29, - 30, - 31, - 32, - 33, - 34, - 35, - 36, - 37, - 38, - 39, - 40, - 41, - 42, - 43, - 44, - 45 - ], - "customGates": [ - ], - "customGatesUses": [ - ] -} \ No newline at end of file diff --git a/examples/prove-verify-circom/input_files/witness.json b/examples/prove-verify-circom/input_files/witness.json deleted file mode 100644 index 4207715dc..000000000 --- a/examples/prove-verify-circom/input_files/witness.json +++ /dev/null @@ -1,45 +0,0 @@ -[ - "1", - "11073827213114255906957329086229857534997954238002191229153027681252578440908", - "100", - "12331548239589023947742773902311424", - "152067081985311155572379412291064145664100437650085545398190343202576", - "18516740700945257869280000978668015992452092678439170792524989272486120334118", - "7022662982230308648050335399232956948772963131418689887894252002987768546875", - "51956299443392382476770201768219770069593065321860795447566299716793859450837", - "4589832337077045695471924912184509963859780684590815544372399341002009614962", - "43151721263355612095272837571449207599792947163563037652152844379195293492604", - "13923518436926956033248001742457856978649324264284994908337535817691587891635", - "4843162906801503449905078294340993145380292462029620758174286369055383678623", - "10640954399009552536772298136370639347896451914994597493820043860910319045843", - "11291371871739639882110542053015739974835143639864153074617527945007984830419", - "23229375666573237224212401861297898243990213841110816744719630274246595529063", - "13222018352426448324603619710634291845769452930526768742982455911430474197902", - "42603836356642079878382034286045993013202665241505712437737535898375832995479", - "10995269502869734248523438640582785199000366778089562296821197719855018050405", - "40645749183742892759873169143186093331825511065838915139022830659075233297210", - "25618850680063679784636891699173923860604589216209294323272008078711267703840", - "15962905053399236510101999614716108643565959465166315792515757582279748982717", - "973052490867319169260388138475722587919634102742416681089859800463396932720", - "42807352589555389213401887801927095349998932794050126408119589917278925500531", - "46695708553157994100238971471717954933394796381183476828999886829039295221603", - "3626180647725717472054191658206022679537438575868185675506112763847162710449", - "25285980095154378776560516012978903055394632158541238218467652979685292726822", - "27816607785590160446327659697396340081191115924637584423924791369152684083116", - "35525072061136109241322499609324640537598190500428572531014069313824905329946", - "35563106371762815454741304660936732477470586695268953125592126490560288984508", - "45056739979471266569449674582587575213635745478010205909862676815215662974089", - "40358570328226569432826486161426406175042790798088701686126465254005569423560", - "48553457651005870236720726461954064723277080748347636615268856658497960886583", - "15733056697103112875048214017492962432689517825311068136995472144431109869193", - "12625278148081892918696176163869094613762763942891566221301165285408336779571", - "40039685832477979982835247828787760241465927326343165359633440817486155306585", - "47265254224399279457685970677598211706928140259384604497499811090850630191140", - "41481802742198877433199144299630080665026042721986180187526398831261498616178", - "3474751919335953185175349276390498700731214716705337653919711395671740710863", - "13005831308942873552288451182569049178693267833278245989386363118907948517691", - "26086009060096044275403218584294027752488245265982087335021303719996289950291", - "36046495468512496004693489463327404155473956559457160352359091807534328640775", - "33089447320298940485142764058538928211186227475820992627903342589733708196045", - "4173488563640681031480698170982092880592560785072979093381120411619075720669" -] \ No newline at end of file diff --git a/examples/prove-verify-circom/src/main.rs b/examples/prove-verify-circom/src/main.rs deleted file mode 100644 index b57a1c636..000000000 --- a/examples/prove-verify-circom/src/main.rs +++ /dev/null @@ -1,32 +0,0 @@ -use lambdaworks_circom_adapter::*; -use lambdaworks_groth16::*; - -/// This example demonstrates how to prove and verify a Circom circuit using the Groth16 prover. -/// -/// You can run with: -/// -/// ```sh -/// cargo run --package prove-verify-circom --bin prove-verify-circom -/// ``` -fn main() { - println!("Reading input files"); - let circom_r1cs = - read_circom_r1cs("./examples/prove-verify-circom/input_files/test.r1cs.json").unwrap(); - let circom_witness = - read_circom_witness("./examples/prove-verify-circom/input_files/witness.json").unwrap(); - - println!("Converting to Lambdaworks-compatible QAP and witness assignments"); - let (qap, witness, _) = circom_to_lambda(circom_r1cs, circom_witness); - - println!("Performing trusted setup"); - let (pk, vk) = setup(&qap); - - println!("Proving"); - let proof = Prover::prove(&witness, &qap, &pk); - - println!("Verifying"); - let accept = verify(&vk, &proof, &witness[..qap.num_of_public_inputs]); - - assert!(accept, "Proof verification failed!"); - println!("Proof verified!"); -} diff --git a/examples/rsa/Cargo.toml b/examples/rsa/Cargo.toml deleted file mode 100644 index 71ac4fbfc..000000000 --- a/examples/rsa/Cargo.toml +++ /dev/null @@ -1,18 +0,0 @@ -[package] -name = "rsa" -version.workspace = true -edition.workspace = true -license.workspace = true -repository.workspace = true - -[features] -default = ["alloc"] -alloc = ["lambdaworks-math/alloc"] - -[dependencies] -num-bigint = "0.4" -num-traits = "0.2" -num-integer = "0.1" -rand = "0.8" -lambdaworks-math = { workspace = true, features = ["alloc"] } -hex = "0.4" diff --git a/examples/rsa/README.md b/examples/rsa/README.md deleted file mode 100644 index 02b288f0c..000000000 --- a/examples/rsa/README.md +++ /dev/null @@ -1,90 +0,0 @@ -# RSA Implementation - -This is an implementation of the RSA cryptographic algorithm in Rust. RSA is one of the first public-key cryptosystems widely used for secure data transmission. - - -## ⚠️ Disclaimer - -This implementation is not cryptographically secure due to non-constant time operations and other considerations, so it must not be used in production. It is intended to be just an educational example. - -## Overview - -RSA is an asymmetric cryptographic algorithm that uses a pair of keys: -- **Public key**: Used for encryption and is shared openly -- **Private key**: Used for decryption and must be kept secret - -The security of RSA relies on the practical difficulty of factoring the product of two large prime numbers. - -## Mathematical Background - -### Key Generation - -1. Choose two distinct prime numbers $p$ and $q$ (these should be randomly generated, should be sufficiently large, and their magnitudes should not be similar) -2. Compute $n = p \cdot q$ -3. Calculate Euler's totient function: $\phi(n) = (p-1) \cdot (q-1)$ -4. Choose an integer $e$ such that $1 < e < \phi(n)$ and $\gcd(e, \phi(n)) = 1$ -5. Compute $d$ such that $d \cdot e \equiv 1 \pmod{\phi(n)}$ - -The public key is $(e, n)$, and the private key is $d$. In practice, $e$ is chosen as a number with low Hamming weight (such as Fermat primes) and $d$ is computed by finding the inverse. - -### Encryption and Decryption - -- **Encryption**: $c = m^e \pmod{n}$ where $m$ is the message and $c$ is the ciphertext -- **Decryption**: $m = c^d \pmod{n}$ - -### PKCS#1 v1.5 Padding - -For secure encryption of arbitrary byte data, we implement PKCS#1 v1.5 padding: - -``` -00 || 02 || PS || 00 || M -``` - -Where: -- `00`: First byte (block type) -- `02`: Second byte (block type for encryption) -- `PS`: Padding string of non-zero random bytes -- `00`: Separator -- `M`: Original message - -### Basic Example - -```rust -use rsa::RSA; -use lambdaworks_math::unsigned_integer::element::UnsignedInteger; - -// Create an RSA instance with primes that ensure e < φ(n) -let p = UnsignedInteger::<16>::from_u64(65539); -let q = UnsignedInteger::<16>::from_u64(65521); -let rsa = RSA::<16>::new(p, q)?; - -// Encrypt and decrypt a numeric message -let message = UnsignedInteger::<16>::from_u64(42); -let ciphertext = rsa.encrypt(&message)?; -let decrypted = rsa.decrypt(&ciphertext)?; - -assert_eq!(message, decrypted); -``` - -### Byte Data with Padding - -```rust -use rsa::RSA; -use lambdaworks_math::unsigned_integer::element::UnsignedInteger; - -// Create an RSA instance with primes that ensure e < φ(n) -let p = UnsignedInteger::<16>::from_u64(65539); -let q = UnsignedInteger::<16>::from_u64(65521); -let rsa = RSA::<16>::new(p, q)?; - -// Encrypt and decrypt byte data using PKCS#1 v1.5 padding -let msg_bytes = b"Hello RSA with padding!"; -let cipher_bytes = rsa.encrypt_bytes_pkcs1(msg_bytes)?; -let plain_bytes = rsa.decrypt_bytes_pkcs1(&cipher_bytes)?; - -assert_eq!(msg_bytes.to_vec(), plain_bytes); -``` - ---- - -**Note**: This implementation is for educational purposes. Production systems should use established cryptographic libraries that have undergone security audits. diff --git a/examples/rsa/src/lib.rs b/examples/rsa/src/lib.rs deleted file mode 100644 index 75513413a..000000000 --- a/examples/rsa/src/lib.rs +++ /dev/null @@ -1,3 +0,0 @@ -mod rsa; - -pub use rsa::{RSAError, RSA}; diff --git a/examples/rsa/src/rsa.rs b/examples/rsa/src/rsa.rs deleted file mode 100644 index ae04124ea..000000000 --- a/examples/rsa/src/rsa.rs +++ /dev/null @@ -1,402 +0,0 @@ -use lambdaworks_math::{traits::ByteConversion, unsigned_integer::element::UnsignedInteger}; -use std::error::Error; -use std::fmt; - -use rand::thread_rng; -use rand::Rng; - -pub const DEFAULT_LIMBS: usize = 16; - -#[derive(Debug)] -pub enum RSAError { - MessageTooLarge, - InvalidBytes, - InvalidCiphertext, - NonInvertible, - PaddingError, -} - -impl fmt::Display for RSAError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - RSAError::MessageTooLarge => write!(f, "Message must be less than n."), - RSAError::InvalidBytes => write!(f, "Invalid bytes for conversion."), - RSAError::InvalidCiphertext => write!(f, "Invalid ciphertext."), - RSAError::NonInvertible => write!(f, "e is not invertible modulo φ(n)."), - RSAError::PaddingError => write!(f, "Padding is invalid or corrupt."), - } - } -} - -impl Error for RSAError {} - -/// Basic implementation of the RSA algorithm -/// N is the number of limbs for UnsignedInteger. -pub struct RSA { - pub e: UnsignedInteger, - pub d: UnsignedInteger, - pub n: UnsignedInteger, -} - -impl RSA { - /// Generates an RSA instance from two primes `p` and `q`. - pub fn new(p: UnsignedInteger, q: UnsignedInteger) -> Result { - let n = p * q; - - // Compute φ(n) = (p - 1) * (q - 1) - let phi_n = - (p - UnsignedInteger::::from_u64(1)) * (q - UnsignedInteger::::from_u64(1)); - - // Public exponent e = 65537 (2^16 + 1) - let e = UnsignedInteger::::from_u64(65537); - - // Calculate d = e^(-1) mod φ(n) - let d = Self::modinv(&e, &phi_n).ok_or(RSAError::NonInvertible)?; - - Ok(RSA { e, d, n }) - } - - /// Returns the public key (e, n) - pub fn public_key(&self) -> (UnsignedInteger, UnsignedInteger) { - (self.e, self.n) - } - - /// Returns the private key d. - pub fn secret_key(&self) -> UnsignedInteger { - self.d - } - - /// Encrypts the message checking that message < n. - pub fn encrypt(&self, message: &UnsignedInteger) -> Result, RSAError> { - if message >= &self.n { - return Err(RSAError::MessageTooLarge); - } - - Ok(modpow(message, &self.e, &self.n)) - } - - /// Decrypts the ciphertext checking that ciphertext < n. - pub fn decrypt(&self, ciphertext: &UnsignedInteger) -> Result, RSAError> { - if ciphertext >= &self.n { - return Err(RSAError::InvalidCiphertext); - } - - Ok(modpow(ciphertext, &self.d, &self.n)) - } - - /// Encrypts a byte array without padding - #[cfg(feature = "alloc")] - pub fn encrypt_bytes_simple(&self, msg: &[u8]) -> Result, RSAError> { - // Create a fixed-size vector (N * 8 bytes) - let mut fixed_size_msg = vec![0; N * 8]; - let msg_len = msg.len(); - if msg_len > fixed_size_msg.len() { - return Err(RSAError::MessageTooLarge); - } - // Place the message at the end of the fixed-size buffer (right-aligned) - fixed_size_msg[N * 8 - msg_len..].copy_from_slice(msg); - let m = UnsignedInteger::::from_bytes_be(&fixed_size_msg) - .map_err(|_| RSAError::InvalidBytes)?; - let c = self.encrypt(&m)?; - Ok(c.to_bytes_be()) - } - - /// Decrypts a byte array that was encrypted without padding. - #[cfg(feature = "alloc")] - pub fn decrypt_bytes_simple(&self, cipher: &[u8]) -> Result, RSAError> { - // Create a fixed-size buffer (N * 8 bytes) - let mut fixed_size_cipher = vec![0; N * 8]; - let cipher_len = cipher.len(); - if cipher_len > fixed_size_cipher.len() { - return Err(RSAError::InvalidBytes); - } - // Place the cipher at the end of the fixed-size buffer (right-aligned) - fixed_size_cipher[N * 8 - cipher_len..].copy_from_slice(cipher); - let c = UnsignedInteger::::from_bytes_be(&fixed_size_cipher) - .map_err(|_| RSAError::InvalidBytes)?; - let m = self.decrypt(&c)?; - let decrypted = m.to_bytes_be(); - // Remove leading zeros to recover the original message - let first_nonzero = decrypted - .iter() - .position(|&x| x != 0) - .unwrap_or(decrypted.len()); - Ok(decrypted[first_nonzero..].to_vec()) - } - - /// Encrypt a byte array with PKCS#1 v1.5 padding. - /// Format: 00 || 02 || PS || 00 || M - /// PS is a random padding string of non-zero bytes. - /// For demonstration purposes, this implementation uses extremely minimal padding - #[cfg(feature = "alloc")] - pub fn encrypt_bytes_pkcs1(&self, msg: &[u8]) -> Result, RSAError> { - // Calculate the actual bit size of n - let n_bytes = self.n.to_bytes_be(); - - // Remove leading zeros to get the actual size - let first_nonzero = n_bytes.iter().position(|&x| x != 0).unwrap_or(0); - let actual_n_bytes = &n_bytes[first_nonzero..]; - let key_size = actual_n_bytes.len(); - - // For our tiny demonstration keys, we'll use extreme minimal padding - // Real PKCS#1 requires 11 bytes of overhead, but we'll use just 3 for demo - let min_padding = 3; // 02 PS 00 (where PS is at least 1 byte) - - if key_size <= min_padding { - return Err(RSAError::MessageTooLarge); - } - - let max_msg_len = key_size - min_padding; - - if msg.len() > max_msg_len { - return Err(RSAError::MessageTooLarge); - } - - // Create a buffer for the padded message (same size as actual n's byte representation) - let mut padded = vec![0; key_size]; - - // For our minimal version, we'll use: - // 02 || PS || 00 || M (skipping the initial 00) - - // Set the first byte to 02 (modified PKCS#1 format for demo) - padded[0] = 0x02; - - // Generate random non-zero bytes for padding - let padding_len = key_size - msg.len() - 2; // -2 for 02 00 - let mut rng = thread_rng(); - for i in 0..padding_len { - // Generate non-zero random bytes - let mut byte = 0; - while byte == 0 { - byte = rng.gen::(); - } - padded[1 + i] = byte; - } - - // Add 00 separator - padded[1 + padding_len] = 0x00; - - // Copy the message to the end - padded[key_size - msg.len()..].copy_from_slice(msg); - - // Convert to UnsignedInteger and encrypt - let m = UnsignedInteger::::from_bytes_be(&padded).map_err(|_| RSAError::InvalidBytes)?; - - // Check that m < n - if m >= self.n { - return Err(RSAError::MessageTooLarge); - } - - // Encrypt - let c = self.encrypt(&m)?; - - Ok(c.to_bytes_be()) - } - - /// Decrypts a ciphertext that was encrypted with PKCS#1 v1.5 padding. - /// Format: 00 || 02 || PS || 00 || M - #[cfg(feature = "alloc")] - pub fn decrypt_bytes_pkcs1(&self, cipher: &[u8]) -> Result, RSAError> { - // Create a fixed-size buffer - let mut fixed_size_cipher = vec![0; N * 8]; - let cipher_len = cipher.len(); - if cipher_len > fixed_size_cipher.len() { - return Err(RSAError::InvalidBytes); - } - - // Place the cipher at the end of the fixed-size buffer (right-aligned) - fixed_size_cipher[N * 8 - cipher_len..].copy_from_slice(cipher); - let c = UnsignedInteger::::from_bytes_be(&fixed_size_cipher) - .map_err(|_| RSAError::InvalidBytes)?; - - // Decrypt - let m = self.decrypt(&c)?; - let padded = m.to_bytes_be(); - - // Remove leading zeros to get the actual size - let first_nonzero = padded.iter().position(|&x| x != 0).unwrap_or(padded.len()); - let padded = &padded[first_nonzero..]; - - // Check for proper PKCS#1 format - // In our simplified version, the first byte should be 0x02 - if padded.is_empty() || padded[0] != 0x02 { - return Err(RSAError::PaddingError); - } - - // Find the 0x00 separator - let separator_pos = padded.iter().skip(1).position(|&x| x == 0); - - if let Some(pos) = separator_pos { - // The actual position in padded is pos+1 (we skipped the first byte) - let message_start = pos + 2; // Skip the 0x02, random padding, and 0x00 separator - - if message_start >= padded.len() { - return Err(RSAError::PaddingError); - } - - Ok(padded[message_start..].to_vec()) - } else { - Err(RSAError::PaddingError) - } - } - - /// Computes the modular multiplicative inverse of `a` modulo `m` using the extended Euclidean algorithm. - pub fn modinv(a: &UnsignedInteger, m: &UnsignedInteger) -> Option> { - // Initialize variables for the extended Euclidean algorithm - // Following the mathematical notation: - // r₀ = m, r₁ = a - // s₀ = 0, s₁ = 1 - let mut s_prev = UnsignedInteger::::from_u64(0); // s₀ - let mut s_curr = UnsignedInteger::::from_u64(1); // s₁ - let mut r_prev = *m; // r₀ - let mut r_curr = *a; // r₁ - - let zero = UnsignedInteger::::from_u64(0); - let one = UnsignedInteger::::from_u64(1); - - // Extended Euclidean algorithm - while r_curr != zero { - // Compute quotient q = r₀ ÷ r₁ - let (q, _) = r_prev.div_rem(&r_curr); - - // Update coefficients - let s_temp = s_prev; - s_prev = s_curr; - - // Compute new coefficient s₂ = s₀ - q * s₁ (mod m) - s_curr = if q * s_curr > s_temp { - let diff = q * s_curr - s_temp; - let (_, remainder) = diff.div_rem(m); - *m - remainder - } else { - let diff = s_temp - q * s_curr; - let (_, remainder) = diff.div_rem(m); - remainder - }; - - // Update remainders r₂ = r₀ - q * r₁ - let r_temp = r_prev; - r_prev = r_curr; - r_curr = r_temp - q * r_curr; - } - - // If r > 1, then a and m are not coprime, so no inverse exists - if r_prev > one { - return None; - } - - Some(s_prev) - } -} - -/// Computes (base^exponent) mod modulus using the square-and-multiply algorithm. -fn modpow( - base: &UnsignedInteger, - exponent: &UnsignedInteger, - modulus: &UnsignedInteger, -) -> UnsignedInteger { - let mut result = UnsignedInteger::::from_u64(1); - let mut base = *base; - let mut exponent = *exponent; - - // Process each bit of the exponent from right to left - while exponent != UnsignedInteger::::from_u64(0) { - // If the current bit is 1, multiply the result by the base - if exponent.limbs[N - 1] & 1 == 1 { - result = (result * base).div_rem(modulus).1; - } - // Square the base - base = (base * base).div_rem(modulus).1; - // Move to the next bit - exponent >>= 1; - } - - result -} - -#[cfg(test)] -mod tests { - use super::*; - use lambdaworks_math::unsigned_integer::element::UnsignedInteger; - - #[test] - fn test_rsa_encryption_decryption() { - const N: usize = 16; - // Using larger primes to ensure e (65537) < φ(n) - let p: UnsignedInteger = UnsignedInteger::from_u64(65539); - let q: UnsignedInteger = UnsignedInteger::from_u64(65521); - let rsa = RSA::new(p, q).unwrap(); - - let message: UnsignedInteger = UnsignedInteger::from_u64(42); - let ciphertext = rsa.encrypt(&message).unwrap(); - let decrypted = rsa.decrypt(&ciphertext).unwrap(); - - assert_eq!(message, decrypted); - } - - #[test] - fn test_rsa_bytes_encryption_decryption() { - const N: usize = 16; - // Using larger primes to ensure e (65537) < φ(n) - let p: UnsignedInteger = UnsignedInteger::from_u64(65539); - let q: UnsignedInteger = UnsignedInteger::from_u64(65521); - let rsa = RSA::new(p, q).unwrap(); - - let message = b"A"; // Use a single-byte message - let cipher = rsa.encrypt_bytes_simple(message).unwrap(); - let recovered = rsa.decrypt_bytes_simple(&cipher).unwrap(); - - assert_eq!(message, &recovered[..]); - } - - #[test] - fn test_rsa_message_too_large() { - const N: usize = 16; - // Using larger primes to ensure e (65537) < φ(n) - let p: UnsignedInteger = UnsignedInteger::from_u64(65539); - let q: UnsignedInteger = UnsignedInteger::from_u64(65521); - let rsa = RSA::new(p, q).unwrap(); - - // n = 65539 * 65521 = 4294343419 - let message: UnsignedInteger = UnsignedInteger::from_u64(4294343420); // Larger than n - let result = rsa.encrypt(&message); - assert!(matches!(result, Err(RSAError::MessageTooLarge))); - } - - #[test] - fn test_rsa_invalid_ciphertext() { - const N: usize = 16; - // Using larger primes to ensure e (65537) < φ(n) - let p: UnsignedInteger = UnsignedInteger::from_u64(65539); - let q: UnsignedInteger = UnsignedInteger::from_u64(65521); - let rsa = RSA::new(p, q).unwrap(); - - // n = 65539 * 65521 = 4294343419 - let ciphertext: UnsignedInteger = UnsignedInteger::from_u64(4294343420); // Larger than n - let result = rsa.decrypt(&ciphertext); - assert!(matches!(result, Err(RSAError::InvalidCiphertext))); - } - - #[test] - fn test_rsa_pkcs1_padding() { - // Use 32 limbs to get more space for padding - const LARGE_LIMBS: usize = 32; - - // Use larger primes to have enough space for padding - let p: UnsignedInteger = UnsignedInteger::from_u64(32749); - let q: UnsignedInteger = UnsignedInteger::from_u64(32719); - let rsa = RSA::::new(p, q).unwrap(); - - // Use a smaller message for the test - let message = b"A"; // Only a single byte to ensure it fits within the padding constraints - - let result = rsa.encrypt_bytes_pkcs1(message); - - // If we successfully encrypted, we should be able to decrypt - if let Ok(cipher) = result { - if let Ok(recovered) = rsa.decrypt_bytes_pkcs1(&cipher) { - assert_eq!(message, &recovered[..]); - } - } - } -} diff --git a/examples/schnorr-signature/Cargo.toml b/examples/schnorr-signature/Cargo.toml deleted file mode 100644 index 40d019f17..000000000 --- a/examples/schnorr-signature/Cargo.toml +++ /dev/null @@ -1,14 +0,0 @@ -[package] -name = "schnorr-siganture" -version.workspace = true -edition.workspace = true -license.workspace = true -repository.workspace = true - - -[dependencies] -lambdaworks-math = { workspace = true } -lambdaworks-crypto = { workspace = true } -sha3 = { version = "0.10", default-features = false } -rand = "0.8.5" -rand_chacha = "0.3.1" diff --git a/examples/schnorr-signature/README.md b/examples/schnorr-signature/README.md deleted file mode 100644 index f059bfe75..000000000 --- a/examples/schnorr-signature/README.md +++ /dev/null @@ -1,61 +0,0 @@ -# Schnorr Signature Scheme - -The Schnorr signature scheme is a simple and efficient digital signature algorithm whose security is based on the intractability of the discrete logarithm problem. This example demonstrates an implementation using elliptic curves. - -## ⚠️ Disclaimer -This implementation is not cryptographically secure due to non-constant time operations, so it must not be used in production. It is intended to be just an educational example. - -## What is a digital signature? - -A digital signature is a cryptographic mechanism for signing messages that provides three fundamental properties: - -- *Authentication:* Proves the identity of the signer. -- *Integrity:* Ensures the message has not been changed. -- *Non-repudiation:* The signer cannot deny having signed the message. - -For example, let's say Alice wants to send Bob a message, and Bob wants to be sure that this message came from Alice. First, Alice chooses a private key, which will produce a public key known by everybody. Then, she sends Bob the message, appending a signature computed from the message and her private key. Finally, Bob uses Alice's public key to verify the authenticity of the signed message. - -## Schnorr Protocol - -### Parameters known by the Signer and Verifier -- A group $G$ of prime order $p$ with generator $g$. -- A hash function $H$. - -### Signer -- Choose a private key $k \in \mathbb{F}_p$. -- Compute the public key $h = g^{-k}$. -- Sample a random $ \ell \in \mathbb{F}_p$. This element can't be reused and must be sampled every time a new message -- Compute $r = g^\ell \in G$ -- Compute $e = H(r || M) \in \mathbb{F}_p$, where $M$ is the message. -- Compute $s = \ell + k \cdot e \in \mathbb{F}_p$. -- Sends $M$ with the signature $(s, e)$. - -### Verifier -- Compute $r_v = g^s \cdot h^e \in G$. -- Compute $e_v = H(r_v || M) \in \mathbb{F}_p$. -- Check $e_v = e$. - -## Implementation -In `common.rs`, you'll find the elliptic curve we chose as the group. Other elliptic curves or even other types of groups could have been used. In this case, we use `BN254Curve` as the group $G$. Consequently, we must use `FrField` as $\mathbb{F}_p$, since `FrField` represents the field whose modulus is the order of $G$ (i.e. the number of elements in the curve). - -In `schnorr_signature.rs`, you'll find the protocol. To test it, see the example below: - -```rust -// Choose a private key. -let private_key = FE::from(5); - -// Compute the public key. -let public_key = get_public_key(&private_key); - -// Write the message -let message = "hello world"; - -// Sign the message -let message_signed = sign_message(&private_key, message); - -// Verify the signature -let is_the_signature_correct = verify_signature( - &public_key, - &message_signed -); -``` diff --git a/examples/schnorr-signature/src/common.rs b/examples/schnorr-signature/src/common.rs deleted file mode 100644 index 9ab1b803e..000000000 --- a/examples/schnorr-signature/src/common.rs +++ /dev/null @@ -1,34 +0,0 @@ -use lambdaworks_math::{ - elliptic_curve::{ - short_weierstrass::curves::bn_254::{ - curve::BN254Curve, - default_types::{FrElement, FrField}, - }, - traits::IsEllipticCurve, - }, - unsigned_integer::element::U256, -}; -use rand::Rng; -use rand_chacha::ChaCha20Rng; - -// We use the BN-254 Curve as the group. We could have used any other curve. -pub type Curve = BN254Curve; - -// We use the finite field Fr where r is the number of elements that the curve has, -// i.e. r is the order of the group G formed by the curve. -pub type F = FrField; - -pub type FE = FrElement; - -pub type CurvePoint = ::PointRepresentation; - -pub fn sample_field_elem(mut rng: ChaCha20Rng) -> FE { - FE::new(U256 { - limbs: [ - rng.gen::(), - rng.gen::(), - rng.gen::(), - rng.gen::(), - ], - }) -} diff --git a/examples/schnorr-signature/src/lib.rs b/examples/schnorr-signature/src/lib.rs deleted file mode 100644 index f757424af..000000000 --- a/examples/schnorr-signature/src/lib.rs +++ /dev/null @@ -1,2 +0,0 @@ -pub mod common; -pub mod schnorr_signature; diff --git a/examples/schnorr-signature/src/schnorr_signature.rs b/examples/schnorr-signature/src/schnorr_signature.rs deleted file mode 100644 index 107f08887..000000000 --- a/examples/schnorr-signature/src/schnorr_signature.rs +++ /dev/null @@ -1,130 +0,0 @@ -use crate::common::*; - -use lambdaworks_math::{ - cyclic_group::IsGroup, elliptic_curve::traits::IsEllipticCurve, traits::ByteConversion, -}; - -use sha3::{Digest, Keccak256}; - -use rand::SeedableRng; - -/// Schnorr Signature of a message using an elliptic curve as the group. -pub struct MessageSigned { - pub message: String, - pub signature: (FE, FE), -} - -/// Function that should use the the signer to obtain her public key. -pub fn get_public_key(private_key: &FE) -> CurvePoint { - let g = Curve::generator(); - // h = g^{-k}, where h = public_key and k = private_key. - g.operate_with_self(private_key.representative()).neg() -} - -/// Function that should use the signer to sign a message. -pub fn sign_message(private_key: &FE, message: &str) -> MessageSigned { - let g = Curve::generator(); - - // Choose l a random field element. This element should be different in each signature. - let rand = sample_field_elem(rand_chacha::ChaCha20Rng::from_entropy()); - - // r = g^l. - let r = g.operate_with_self(rand.representative()); - - // We want to compute e = H(r || message). - let mut hasher = Keccak256::new(); - - // We append r to the hasher. - let r_coordinate_x_bytes = &r.to_affine().x().to_bytes_be(); - let r_coordinate_y_bytes = &r.to_affine().y().to_bytes_be(); - hasher.update(r_coordinate_x_bytes); - hasher.update(r_coordinate_y_bytes); - - // We append the message to the hasher. - let message_bytes = message.as_bytes(); - hasher.update(message_bytes); - - // e = H(r || message) - let hashed_data = hasher.finalize().to_vec(); - let e = FE::from_bytes_be(&hashed_data).unwrap(); - - // s = l + private_key * e - let s = rand + &(private_key * &e); - - MessageSigned { - message: message.to_string(), - signature: (s, e), - } -} - -/// Function that should use the verifier to verify the message signed sent by the signer. -pub fn verify_signature(public_key: &CurvePoint, message_signed: &MessageSigned) -> bool { - let g = Curve::generator(); - let message = &message_signed.message; - let (s, e) = &message_signed.signature; - - // rv = g^s * h^e, with h = public_key and (s, e) = signature. - let rv = g - .operate_with_self(s.representative()) - .operate_with(&public_key.operate_with_self(e.representative())); - - // We want to compute ev = H(rv || M). - let mut hasher = Keccak256::new(); - - // We append rv to the hasher. - let rv_coordinate_x_bytes = &rv.to_affine().x().to_bytes_be(); - let rv_coordinate_y_bytes = &rv.to_affine().y().to_bytes_be(); - hasher.update(rv_coordinate_x_bytes); - hasher.update(rv_coordinate_y_bytes); - - // We append the message to the hasher. - let message_bytes = message.as_bytes(); - hasher.update(message_bytes); - - // ev = H(rv || M). - let hashed_data = hasher.finalize().to_vec(); - let ev = FE::from_bytes_be(&hashed_data).unwrap(); - - // Check if H(rv || M) = H(r || M) - ev == *e -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn verify_valid_signature() { - // The signer chooses a private key. - let private_key = FE::from(5); - // The signer calculates the public key that will be known by the verifier. - let public_key = get_public_key(&private_key); - // The signer signs the message. - let message = "hello world"; - let message_signed = sign_message(&private_key, message); - // The verifier verifies the message signed. - assert!(verify_signature(&public_key, &message_signed)); - } - - #[test] - // For security reasons, if the signer signs the same message twice (with the same private key), - // the signatures should be different each time. This difference is achieved by the random field - // element sampled for every message signed. - fn same_message_signed_twice_has_different_signatures_each_time() { - let private_key = - FE::from_hex_unchecked("73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff"); - let public_key = get_public_key(&private_key); - let message = "5f103b0bd4397d4df560eb559f38353f80eeb6"; - - let message_signed_1 = sign_message(&private_key, message); - let signatue_1 = &message_signed_1.signature; - assert!(verify_signature(&public_key, &message_signed_1)); - - let message_signed_2 = sign_message(&private_key, message); - let signatue_2 = &message_signed_2.signature; - assert!(verify_signature(&public_key, &message_signed_2)); - - // The message is the same, but the signatures are different. - assert_ne!(signatue_1, signatue_2); - } -} diff --git a/examples/shamir_secret_sharing/Cargo.toml b/examples/shamir_secret_sharing/Cargo.toml deleted file mode 100644 index 56cd2cb5f..000000000 --- a/examples/shamir_secret_sharing/Cargo.toml +++ /dev/null @@ -1,13 +0,0 @@ -[package] -name = "shamir_secret_sharing" -version.workspace = true -edition.workspace = true -license.workspace = true -repository.workspace = true - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] - -lambdaworks-math = { git = "https://github.com/lambdaclass/lambdaworks.git" } -rand = { version = "0.8", features = [ "std", "std_rng" ] } diff --git a/examples/shamir_secret_sharing/README.md b/examples/shamir_secret_sharing/README.md deleted file mode 100644 index 84fd1781a..000000000 --- a/examples/shamir_secret_sharing/README.md +++ /dev/null @@ -1,34 +0,0 @@ -# Shamir's Secret Sharing - -## Usage example - -```rust -// Definition of the secret -// Creation of 6 shares -// 3 shares will be used to recover the secret -let sss = ShamirSecretSharing { - secret: FE::new(1234), - n: 6, - k: 3, -}; - -// Polynomial calculation -let polynomial = sss.calculate_polynomial(); - -// Produce shares -let shares = sss.generating_shares(polynomial.clone()); - -// Specify the x and y coordinates of the shares to use -let shares_to_use_x = vec![shares.x[1], shares.x[3], shares.x[4]]; -let shares_to_use_y = vec![shares.y[1], shares.y[3], shares.y[4]]; - -// Interpolation -let poly_2 = sss.reconstructing(shares_to_use_x, shares_to_use_y); - -// Recover the free coefficient of the polynomial -let secret_recovered = sss.recover(&poly_2); - -// Verifications -assert_eq!(polynomial, poly_2); -assert_eq!(sss.secret, secret_recovered); -``` diff --git a/examples/shamir_secret_sharing/src/lib.rs b/examples/shamir_secret_sharing/src/lib.rs deleted file mode 100644 index 00e0a4210..000000000 --- a/examples/shamir_secret_sharing/src/lib.rs +++ /dev/null @@ -1 +0,0 @@ -pub mod shamir_secret_sharing; diff --git a/examples/shamir_secret_sharing/src/shamir_secret_sharing.rs b/examples/shamir_secret_sharing/src/shamir_secret_sharing.rs deleted file mode 100644 index 67a90d57a..000000000 --- a/examples/shamir_secret_sharing/src/shamir_secret_sharing.rs +++ /dev/null @@ -1,99 +0,0 @@ -use lambdaworks_math::{ - field::{element::FieldElement, traits::IsField}, - polynomial::Polynomial, -}; - -use rand::distributions::Standard; -use rand::prelude::Distribution; -use rand::random; - -#[allow(dead_code)] -pub struct ShamirSecretSharing { - secret: FieldElement, - n: usize, - k: usize, -} - -pub struct Shares { - pub x: Vec>, - pub y: Vec>, -} - -impl ShamirSecretSharing { - pub fn calculate_polynomial(&self) -> Polynomial> - where - Standard: Distribution<::BaseType>, - { - let mut coefficients = Vec::new(); - coefficients.push(self.secret.clone()); - for _ in 0..self.k - 1 { - coefficients.push(FieldElement::::new(random())); - } - - let polynomial = Polynomial::new(coefficients.as_slice()); - polynomial - } - - pub fn generating_shares(&self, polynomial: Polynomial>) -> Shares - where - Standard: Distribution<::BaseType>, - { - let mut shares_x: Vec> = Vec::new(); - let mut shares_y: Vec> = Vec::new(); - - for _ in 0..self.n { - let x = FieldElement::::new(random()); - let y = polynomial.evaluate(&x); - shares_x.push(x); - shares_y.push(y); - } - - Shares { - x: shares_x, - y: shares_y, - } - } - - pub fn reconstructing( - &self, - x: Vec>, - y: Vec>, - ) -> Polynomial> { - Polynomial::interpolate(&x, &y).unwrap() - } - - pub fn recover(&self, polynomial: &Polynomial>) -> FieldElement { - polynomial.coefficients()[0].clone() - } -} - -#[cfg(test)] -#[allow(non_snake_case)] -mod tests { - - use crate::shamir_secret_sharing::ShamirSecretSharing; - use lambdaworks_math::field::element::FieldElement; - use lambdaworks_math::field::fields::u64_prime_field::U64PrimeField; - - #[test] - fn shamir_secret_sharing_works() { - const ORDER: u64 = 1613; - type F = U64PrimeField; - type FE = FieldElement; - - let sss = ShamirSecretSharing { - secret: FE::new(1234), - n: 6, - k: 3, - }; - - let polynomial = sss.calculate_polynomial(); - let shares = sss.generating_shares(polynomial.clone()); - let shares_to_use_x = vec![shares.x[1], shares.x[3], shares.x[4]]; - let shares_to_use_y = vec![shares.y[1], shares.y[3], shares.y[4]]; - let poly_2 = sss.reconstructing(shares_to_use_x, shares_to_use_y); - let secret_recovered = sss.recover(&poly_2); - assert_eq!(polynomial, poly_2); - assert_eq!(sss.secret, secret_recovered); - } -} diff --git a/exercises/README.md b/exercises/README.md deleted file mode 100644 index c9cc91089..000000000 --- a/exercises/README.md +++ /dev/null @@ -1,7 +0,0 @@ -# Lambdaworks Exercises & Challenges - -Contains several examples and challenges to use Lambdaworks. - -Challenges 1, 2 and 3 appeared in [Ingonyama's CTF event](https://ingonyama.ctfd.io/) - -Challenges message, blind_trust and broken heart appeared in the first LambdaIngo ZK CTF diff --git a/exercises/blind_trust/Cargo.toml b/exercises/blind_trust/Cargo.toml deleted file mode 100644 index 9b30aca96..000000000 --- a/exercises/blind_trust/Cargo.toml +++ /dev/null @@ -1,15 +0,0 @@ -[package] -name = "blind_trust" -version = "0.1.0" -edition = "2021" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] -rand = "0.8.5" - - -lambdaworks-math = { git = "https://github.com/lambdaclass/lambdaworks", rev = "8fcd64f" } -lambdaworks-crypto = { git = "https://github.com/lambdaclass/lambdaworks", rev = "8fcd64f" } -lambdaworks-plonk = { git = "https://github.com/lambdaclass/lambdaworks_plonk_prover", rev = "6e39865"} - diff --git a/exercises/blind_trust/README.md b/exercises/blind_trust/README.md deleted file mode 100644 index 82956f0b3..000000000 --- a/exercises/blind_trust/README.md +++ /dev/null @@ -1,158 +0,0 @@ -# Obi-Wan's search for the Sith Foundry - -In his quest to stop the Sith’s menace, Obi-Wan Kenobi finds a (Sith) holocron, giving a zero-knowledge proof of the existence of the Sith’s galactic foundry (using galactic Plonk). This place is rumored to contain several artifacts that could aid the Galactic Republic in its war efforts. The position, given by (x,h,y), satisfies the equation y=x*h+b. After some study, Obi-Wan finds the values of y and b (which belong to Sith lore). The only problem is that, even with this knowledge, it may take him quite long to find the mysterious planet, and the situation in the Republic is desperate. He also finds, together with the holocron, a second item containing the SRS used to generate the proof, the prover, and a description of the circuit used. Will he be able to find the position of the foundry before it is too late? The flag consists of the x and h concatenated and written in hex (for example, x=0x123, h=0x789, the FLAG=123789) - -## Description -In this challenge the participants have to exploit a vulnerability in a PLONK implementation that's missing the blindings of the wire polynomials. - -The first round of PLONK reads as follows: - -``` -Compute polynomials a',b',c' as the interpolation polynomials of the columns of T at the domain H. -Sample random b_1, b_2, b_3, b_4, b_5, b_6 -Let - -a := (b_1X + b_2)Z_H + a' - -b := (b_3X + b_4)Z_H + b' - -c := (b_5X + b_6)Z_H + c' - -Compute [a]_1, [b]_1, [c]_1 and add them to the transcript. -``` - -The multiples of $Z_H$ that are added to $a', b', c'$ are the called the blindings. In subsequent rounds the polynomials $a, b, c$ are opened at a point chosen by the verifier. If the blindings are missing, information about the prover's private inputs can be leaked. - -In this challenge the participant is given a single proof of the following simple circuit, along with the corresponding values of $b$ and $y$: - -``` -PRIVATE INPUT: - x - h - -PUBLIC INPUT: - b - y - -OUTPUT: - ASSERT y == h * x + b -``` - -The flag is `x.representative() || h.representative()`. The objective of the challenge is to utilize the provided information in order to retrieve the private inputs. - -## Data provided to participants - -Participants get the following values: - -1. `y: "3610e39ce7acc430c1fa91efcec93722d77bc4e910ccb195fa4294b64ecb0d35"`, -1. `b: "1b0871ce73e72c599426228e37e7469be9f4fa0b7c9dae950bb77539ca9ebb0f"`. - -They also get access to the following files: - -1. `src/sith_generate_proof.rs` (this file has flags and toxic waste replaced by `???`) -1. `src/circuit.rs` -1. `srs` -1. `proof` - -The files `srs` and `proof` can be deserialized using Lambdaworks methods as follows. - -```rust -use std::{fs, io::{BufReader, Read}}; -use lambdaworks_plonk::prover::Proof; -use lambdaworks_crypto::commitments::kzg::StructuredReferenceString; -use lambdaworks_math::traits::{Deserializable, AsBytes}; -use crate::sith_generate_proof::{SithProof, SithSRS}; - -fn read_challenge_data_from_files() -> (SithSRS, SithProof) { - // Read proof from file - let f = fs::File::open("./proof").unwrap(); - let mut reader = BufReader::new(f); - let mut buffer = Vec::new(); - reader.read_to_end(&mut buffer).unwrap(); - let proof = Proof::deserialize(&buffer).unwrap(); - - // Read SRS from file - let f = fs::File::open("./srs").unwrap(); - let mut reader = BufReader::new(f); - let mut buffer = Vec::new(); - reader.read_to_end(&mut buffer).unwrap(); - let srs = StructuredReferenceString::deserialize(&buffer).unwrap(); - (srs, proof) -} -``` - -## Solution - -The solution for the coordinates is: - -1. `x: "2194826651b32ca1055614fc6e2f2de86eab941d2c55bd467268e9"`, -1. `h: "432904cca36659420aac29f8dc5e5bd0dd57283a58ab7a8ce4d1ca"`. - -The flag is the concatenation of the two: `FLAG: 2194826651b32ca1055614fc6e2f2de86eab941d2c55bd467268e9432904cca36659420aac29f8dc5e5bd0dd57283a58ab7a8ce4d1ca` - -## Solution description - -We'll use the notation of the `lambdaworks_plonk_prover` docs. - -By checking the code of the challenge the participants can find the following in `circuit.rs` - -```rust -/// Witness generator for the circuit `ASSERT y == x * h + b` -pub fn circuit_witness( - b: &FrElement, - y: &FrElement, - h: &FrElement, - x: &FrElement, -) -> Witness { - let z = x * h; - let w = &z + b; - let empty = b.clone(); - Witness { - a: vec![ - b.clone(), - y.clone(), - x.clone(), - b.clone(), - w.clone(), - empty.clone(), - empty.clone(), - empty.clone(), - ], - ... -``` - -This code reveals that the way prover constructs the $V$ matrix is - -| A | B | C | -| --- | --- | --- | -| b | - | - | -| y | - | - | -| x | h | z | -| b | z | w | -| w | y | - | -| - | - | - | -| - | - | - | -| - | - | - | - -Where `-` are empty values. The PLONK implementation of `lambdaworks-plonk` requires the empty values to be filled in with the first public input. So in this case the values `-` will be replaced by $b$. This can be seen directly from the code of the challenge - -Therefore, the polynomial $a'$, being the interpolation of the column `A` is - -$$a' = b L_1 + y L_2 + x L_3 + b L_4 + w L_5 + b L_6 + b L_7 + b L_8,$$ - -where $L_i$ is the $i$-th polynomial of the Lagrange basis. Also, the value $w$ is equal to $y$. That can be seen from the code and the fact that the last row of the $V$ matrix corresponds to the assertion of the actual output of the circuit being equal to the claimed output $y$. - -During the proof, the verifier sends a challenge $\zeta$ and the prover opens, among other things, the polynomial $a$ at $\zeta$. Since the implementation of the challenge does not include blindings, $a(\zeta) = a'(\zeta)$ and we get - -$$a(\zeta) = b L_1(\zeta) + y L_2(\zeta) + x L_3(\zeta) + b L_4(\zeta) + y L_5(\zeta) + b L_6(\zeta) + b L_7(\zeta) + b L_8(\zeta).$$ - -All the terms in this expression are known to the participants except for $x$, which can be cleared from the equation. To do so the participants need to know how to recover the challenges to get $\zeta$ and how to compute the Lagrange polynomials evaluated at it. The second private input $h$ can be computed as $h = (y - b) / x$. - -## Test - -A test with the above solution is given in `solution.rs`. To make it pass, lines 25 and 26 of `sith_generate_proof.rs` need to be replaced by the following - -```rust -pub const FLAG1: &str = "2194826651b32ca1055614fc6e2f2de86eab941d2c55bd467268e9"; -pub const FLAG2: &str = "432904cca36659420aac29f8dc5e5bd0dd57283a58ab7a8ce4d1ca"; -``` diff --git a/exercises/blind_trust/proof b/exercises/blind_trust/proof deleted file mode 100644 index ad5dc4f7e..000000000 Binary files a/exercises/blind_trust/proof and /dev/null differ diff --git a/exercises/blind_trust/src/circuit.rs b/exercises/blind_trust/src/circuit.rs deleted file mode 100644 index 562059593..000000000 --- a/exercises/blind_trust/src/circuit.rs +++ /dev/null @@ -1,187 +0,0 @@ -use lambdaworks_math::{ - elliptic_curve::short_weierstrass::curves::bls12_381::default_types::{FrElement, FrField}, - field::element::FieldElement, - polynomial::Polynomial, -}; -use lambdaworks_plonk::setup::{CommonPreprocessedInput, Witness}; - -use crate::sith_generate_proof::{ORDER_8_ROOT_UNITY, ORDER_R_MINUS_1_ROOT_UNITY}; - -/// Generates a domain to interpolate: 1, omega, omega², ..., omega^size -pub fn generate_domain(omega: &FrElement, size: usize) -> Vec { - (1..size).fold(vec![FieldElement::one()], |mut acc, _| { - acc.push(acc.last().unwrap() * omega); - acc - }) -} - -/// The identity permutation, auxiliary function to generate the copy constraints. -fn identity_permutation(w: &FrElement, n: usize) -> Vec { - let u = ORDER_R_MINUS_1_ROOT_UNITY; - let mut result: Vec = vec![]; - for index_column in 0..=2 { - for index_row in 0..n { - result.push(w.pow(index_row) * u.pow(index_column as u64)); - } - } - result -} - -/// Generates the permutation coefficients for the copy constraints. -/// polynomials S1, S2, S3. -pub fn generate_permutation_coefficients( - omega: &FrElement, - n: usize, - permutation: &[usize], -) -> Vec { - let identity = identity_permutation(omega, n); - let permuted: Vec = (0..n * 3) - .map(|i| identity[permutation[i]].clone()) - .collect(); - permuted -} - -/// Witness generator for the circuit `ASSERT y == x * h + b` -pub fn circuit_witness( - b: &FrElement, - y: &FrElement, - h: &FrElement, - x: &FrElement, -) -> Witness { - let z = x * h; - let w = &z + b; - let empty = b.clone(); - Witness { - a: vec![ - b.clone(), - y.clone(), - x.clone(), - b.clone(), - w.clone(), - empty.clone(), - empty.clone(), - empty.clone(), - ], - b: vec![ - empty.clone(), - empty.clone(), - h.clone(), - z.clone(), - y.clone(), - empty.clone(), - empty.clone(), - empty.clone(), - ], - c: vec![ - empty.clone(), - empty.clone(), - z.clone(), - w.clone(), - empty.clone(), - empty.clone(), - empty.clone(), - empty.clone(), - ], - } -} - -/// Common preprocessed input for the circuit `ASSERT y == x * h + b` -pub fn circuit_common_preprocessed_input() -> CommonPreprocessedInput { - let n: usize = 8; - let omega = ORDER_8_ROOT_UNITY; - let domain = generate_domain(&omega, n); - let permutation = &[ - 23, 12, 2, 0, 19, 3, 5, 6, 7, 8, 10, 18, 1, 9, 13, 14, 15, 16, 11, 4, 17, 20, 21, 22, - ]; - let permuted = generate_permutation_coefficients(&omega, n, permutation); - - let s1_lagrange: Vec = permuted[..8].to_vec(); - let s2_lagrange: Vec = permuted[8..16].to_vec(); - let s3_lagrange: Vec = permuted[16..].to_vec(); - - CommonPreprocessedInput { - n, - omega, - k1: ORDER_R_MINUS_1_ROOT_UNITY, - domain: domain.clone(), - - ql: Polynomial::interpolate( - &domain, - &[ - -FieldElement::one(), - -FieldElement::one(), - FieldElement::zero(), - FieldElement::one(), - FieldElement::one(), - FieldElement::zero(), - FieldElement::zero(), - FieldElement::zero(), - ], - ) - .unwrap(), - qr: Polynomial::interpolate( - &domain, - &[ - FieldElement::zero(), - FieldElement::zero(), - FieldElement::zero(), - FieldElement::one(), - -FieldElement::one(), - FieldElement::zero(), - FieldElement::zero(), - FieldElement::zero(), - ], - ) - .unwrap(), - qo: Polynomial::interpolate( - &domain, - &[ - FieldElement::zero(), - FieldElement::zero(), - -FieldElement::one(), - -FieldElement::one(), - FieldElement::zero(), - FieldElement::zero(), - FieldElement::zero(), - FieldElement::zero(), - ], - ) - .unwrap(), - qm: Polynomial::interpolate( - &domain, - &[ - FieldElement::zero(), - FieldElement::zero(), - FieldElement::one(), - FieldElement::zero(), - FieldElement::zero(), - FieldElement::zero(), - FieldElement::zero(), - FieldElement::zero(), - ], - ) - .unwrap(), - qc: Polynomial::interpolate( - &domain, - &[ - FieldElement::zero(), - FieldElement::zero(), - FieldElement::zero(), - FieldElement::zero(), - FieldElement::zero(), - FieldElement::zero(), - FieldElement::zero(), - FieldElement::zero(), - ], - ) - .unwrap(), - - s1: Polynomial::interpolate(&domain, &s1_lagrange).unwrap(), - s2: Polynomial::interpolate(&domain, &s2_lagrange).unwrap(), - s3: Polynomial::interpolate(&domain, &s3_lagrange).unwrap(), - - s1_lagrange, - s2_lagrange, - s3_lagrange, - } -} diff --git a/exercises/blind_trust/src/lib.rs b/exercises/blind_trust/src/lib.rs deleted file mode 100644 index 685512a0a..000000000 --- a/exercises/blind_trust/src/lib.rs +++ /dev/null @@ -1,3 +0,0 @@ -pub mod circuit; -pub mod sith_generate_proof; -pub mod solution; diff --git a/exercises/blind_trust/src/sith_generate_proof.rs b/exercises/blind_trust/src/sith_generate_proof.rs deleted file mode 100644 index 838f25d2a..000000000 --- a/exercises/blind_trust/src/sith_generate_proof.rs +++ /dev/null @@ -1,106 +0,0 @@ -use lambdaworks_crypto::commitments::{ - kzg::{KateZaveruchaGoldberg, StructuredReferenceString}, - traits::IsCommitmentScheme, -}; -use lambdaworks_math::{ - cyclic_group::IsGroup, - elliptic_curve::{ - short_weierstrass::curves::bls12_381::{ - curve::BLS12381Curve, - default_types::{FrElement, FrField}, - pairing::BLS12381AtePairing, - twist::BLS12381TwistCurve, - }, - traits::{IsEllipticCurve, IsPairing}, - }, - traits::IsRandomFieldElementGenerator, - unsigned_integer::element::U256, -}; -use lambdaworks_plonk::{ - prover::{Proof, Prover}, - setup::{setup, VerificationKey}, -}; - -use crate::circuit::{circuit_common_preprocessed_input, circuit_witness}; -use rand::Rng; - -pub const FLAG1: &str = "??????????????????????????????????????????????????????"; -pub const FLAG2: &str = "??????????????????????????????????????????????????????"; - -pub const X_COORDINATE: FrElement = FrElement::from_hex_unchecked(FLAG1); -pub const H_COORDINATE: FrElement = FrElement::from_hex_unchecked(FLAG2); - -pub type SithSRS = StructuredReferenceString< - ::G1Point, - ::G2Point, ->; - -pub const ORDER_8_ROOT_UNITY: FrElement = FrElement::from_hex_unchecked( - "345766f603fa66e78c0625cd70d77ce2b38b21c28713b7007228fd3397743f7a", -); // order 8 - -pub const ORDER_R_MINUS_1_ROOT_UNITY: FrElement = FrElement::from_hex_unchecked("7"); -pub type SithCS = KateZaveruchaGoldberg; -pub type SithVK = VerificationKey<>::Commitment>; -pub type SithProof = Proof; -pub type Pairing = BLS12381AtePairing; -pub type KZG = KateZaveruchaGoldberg; -type G1Point = ::PointRepresentation; -type G2Point = ::PointRepresentation; - -/// Generates a test SRS for the BLS12381 curve -/// n is the number of constraints in the system. -pub fn generate_srs(n: usize) -> StructuredReferenceString { - let mut rng = rand::thread_rng(); - let s = FrElement::new(U256 { - limbs: [ - rng.gen::(), - rng.gen::(), - rng.gen::(), - rng.gen::(), - ], - }); - let g1 = ::generator(); - let g2 = ::generator(); - - let powers_main_group: Vec = (0..n + 3) - .map(|exp| g1.operate_with_self(s.pow(exp as u64).representative())) - .collect(); - let powers_secondary_group = [g2.clone(), g2.operate_with_self(s.representative())]; - - StructuredReferenceString::new(&powers_main_group, &powers_secondary_group) -} - -pub struct SithRandomFieldGenerator; -impl IsRandomFieldElementGenerator for SithRandomFieldGenerator { - fn generate(&self) -> FrElement { - FrElement::zero() - } -} - -pub fn generate_proof(b: &FrElement) -> (FrElement, Proof, SithSRS) { - let common_preprocessed_input = circuit_common_preprocessed_input(); - let srs = generate_srs(common_preprocessed_input.n); - let kzg = KZG::new(srs.clone()); - let verifying_key = setup(&common_preprocessed_input.clone(), &kzg); - - let x = X_COORDINATE; - let h = H_COORDINATE; - - // Output - let y = &x * &h + b; - - // This is the circuit for y == x * h + b - let witness = circuit_witness(&b, &y, &h, &x); - let public_input = vec![b.clone(), y.clone()]; - - let random_generator = SithRandomFieldGenerator {}; - let prover = Prover::new(kzg.clone(), random_generator); - let proof = prover.prove( - &witness, - &public_input, - &common_preprocessed_input, - &verifying_key, - ); - (y, proof, srs) -} diff --git a/exercises/blind_trust/src/solution.rs b/exercises/blind_trust/src/solution.rs deleted file mode 100644 index 7feb52414..000000000 --- a/exercises/blind_trust/src/solution.rs +++ /dev/null @@ -1,165 +0,0 @@ -use lambdaworks_crypto::{ - commitments::traits::IsCommitmentScheme, fiat_shamir::transcript::Transcript, -}; -use lambdaworks_math::{ - field::{element::FieldElement, traits::IsField}, - traits::{ByteConversion, Serializable}, -}; -use lambdaworks_plonk::{ - prover::Proof, - setup::{new_strong_fiat_shamir_transcript, CommonPreprocessedInput, VerificationKey}, -}; - -fn compute_private_input( - proof: &Proof, - vk: &VerificationKey, - public_input: &[FieldElement], - common_preprocessed_input: &CommonPreprocessedInput, -) -> (FieldElement, FieldElement) -where - F: IsField, - CS: IsCommitmentScheme, - CS::Commitment: Serializable, - FieldElement: ByteConversion, -{ - // Replay interactions to recover challenges - let mut transcript = new_strong_fiat_shamir_transcript::(vk, public_input); - transcript.append(&proof.a_1.serialize()); - transcript.append(&proof.b_1.serialize()); - transcript.append(&proof.c_1.serialize()); - let _beta = FieldElement::from_bytes_be(&transcript.challenge()).unwrap(); - let _gamma = FieldElement::from_bytes_be(&transcript.challenge()).unwrap(); - - transcript.append(&proof.z_1.serialize()); - let _alpha = FieldElement::from_bytes_be(&transcript.challenge()).unwrap(); - - transcript.append(&proof.t_lo_1.serialize()); - transcript.append(&proof.t_mid_1.serialize()); - transcript.append(&proof.t_hi_1.serialize()); - let zeta = FieldElement::from_bytes_be(&transcript.challenge()).unwrap(); - - // Compute `x` and `h` - let [b, y] = [&public_input[0], &public_input[1]]; - let n = common_preprocessed_input.n as u64; - let omega = &common_preprocessed_input.omega; - let domain = &common_preprocessed_input.domain; - let l1_zeta = - (zeta.pow(n) - FieldElement::one()) / (&zeta - FieldElement::one()) / FieldElement::from(n); - - let mut li_zeta = l1_zeta; - let mut lagrange_basis_zeta = Vec::new(); - lagrange_basis_zeta.push(li_zeta.clone()); - for i in 1..domain.len() { - li_zeta = omega * &li_zeta * ((&zeta - &domain[i - 1]) / (&zeta - &domain[i])); - lagrange_basis_zeta.push(li_zeta.clone()); - } - - let x = (&proof.a_zeta - - b * &lagrange_basis_zeta[3] - - y * &lagrange_basis_zeta[4] - - b * &lagrange_basis_zeta[0] - - y * &lagrange_basis_zeta[1] - - b * &lagrange_basis_zeta[5] - - b * &lagrange_basis_zeta[6] - - b * &lagrange_basis_zeta[7]) - / &lagrange_basis_zeta[2]; - let h = (y - b) / &x; - (x, h) -} - -#[cfg(test)] -mod tests { - use std::{ - fs, - io::{BufReader, Read}, - }; - - use lambdaworks_crypto::commitments::kzg::StructuredReferenceString; - use lambdaworks_math::{ - elliptic_curve::short_weierstrass::curves::bls12_381::default_types::FrElement, - traits::{Deserializable, Serializable}, - }; - use lambdaworks_plonk::{prover::Proof, setup::setup, verifier::Verifier}; - - use crate::{ - circuit::circuit_common_preprocessed_input, - sith_generate_proof::{ - generate_proof, SithProof, SithSRS, H_COORDINATE, KZG, X_COORDINATE, - }, - solution::compute_private_input, - }; - - fn read_challenge_data_from_files() -> (SithSRS, SithProof) { - // Read proof from file - let f = fs::File::open("./proof").unwrap(); - let mut reader = BufReader::new(f); - let mut buffer = Vec::new(); - reader.read_to_end(&mut buffer).unwrap(); - let proof = Proof::deserialize(&buffer).unwrap(); - - // Read SRS from file - let f = fs::File::open("./srs").unwrap(); - let mut reader = BufReader::new(f); - let mut buffer = Vec::new(); - reader.read_to_end(&mut buffer).unwrap(); - let srs = StructuredReferenceString::deserialize(&buffer).unwrap(); - (srs, proof) - } - - #[test] - fn test_challenge_data() { - let b = - FrElement::from_hex("1b0871ce73e72c599426228e37e7469be9f4fa0b7c9dae950bb77539ca9ebb0f") - .unwrap(); - let y = - FrElement::from_hex("3610e39ce7acc430c1fa91efcec93722d77bc4e910ccb195fa4294b64ecb0d35") - .unwrap(); - let public_input = vec![b, y]; - - let (srs, proof) = read_challenge_data_from_files(); - let common_preprocessed_input = circuit_common_preprocessed_input(); - let kzg = KZG::new(srs.clone()); - let verifier = Verifier::new(kzg.clone()); - let vk = setup(&common_preprocessed_input, &kzg); - - assert!(verifier.verify(&proof, &public_input, &common_preprocessed_input, &vk)) - } - - fn export_challenge_data() { - use std::fs; - use std::io::Write; - - let b = - FrElement::from_hex("1b0871ce73e72c599426228e37e7469be9f4fa0b7c9dae950bb77539ca9ebb0f") - .unwrap(); - let (y, proof, srs) = generate_proof(&b); - - let mut srs_file = fs::File::create("./srs").unwrap(); - srs_file.write_all(&srs.serialize()).unwrap(); - let mut srs_file = fs::File::create("./proof").unwrap(); - srs_file.write_all(&proof.serialize()).unwrap(); - println!("{}", y); - } - - #[test] - fn test_solution() { - let b = - FrElement::from_hex("1b0871ce73e72c599426228e37e7469be9f4fa0b7c9dae950bb77539ca9ebb0f") - .unwrap(); - let y = - FrElement::from_hex("3610e39ce7acc430c1fa91efcec93722d77bc4e910ccb195fa4294b64ecb0d35") - .unwrap(); - let public_input = vec![b, y]; - - let (srs, proof) = read_challenge_data_from_files(); - let common_preprocessed_input = circuit_common_preprocessed_input(); - let kzg = KZG::new(srs.clone()); - - let vk = setup(&common_preprocessed_input, &kzg); - // Extract private input from proof, public input and public keys - let (x, h) = compute_private_input(&proof, &vk, &public_input, &common_preprocessed_input); - - assert_eq!(&X_COORDINATE, &x); - assert_eq!(&H_COORDINATE, &h); - } -} diff --git a/exercises/blind_trust/srs b/exercises/blind_trust/srs deleted file mode 100644 index 7662fe652..000000000 Binary files a/exercises/blind_trust/srs and /dev/null differ diff --git a/exercises/broken_heart/Cargo.toml b/exercises/broken_heart/Cargo.toml deleted file mode 100644 index 089261596..000000000 --- a/exercises/broken_heart/Cargo.toml +++ /dev/null @@ -1,12 +0,0 @@ -[package] -name = "irreducibull" -version = "0.1.0" -edition = "2021" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] -lambdaworks-math = { git = "https://github.com/lambdaclass/lambdaworks", rev = "d8f14cb"} -lambdaworks-crypto = { git = "https://github.com/lambdaclass/lambdaworks", rev = "d8f14cb"} -# lambdaworks-plonk = { git = "https://github.com/lambdaclass/lambdaworks_plonk_prover", rev="07e36bf"} -lambdaworks-plonk = { path = "path/to/local/lambdaworks_plonk_prover"} diff --git a/exercises/broken_heart/README.md b/exercises/broken_heart/README.md deleted file mode 100644 index 1ba59911c..000000000 --- a/exercises/broken_heart/README.md +++ /dev/null @@ -1,109 +0,0 @@ -# Loki’s broken heart - -After successfully breaking into Loki’s vault and getting access to some of his finest treasures and weapons, you spot a small trapdoor under a carpet. The trapdoor is locked and contains a device with a PLONK prover. It says: “Prove that the point $(1,y)$ belongs to the elliptic curve $y^2 = x^3 + 4$”. You see that, in order to prove this, you need that $y^2 - x^3 - 4$ is equal to zero, which corresponds to the circuit for the prover provided by Loki. Can you open the trapdoor? - -# Description - -This challenge is about exploiting a vulnerability in weak Fiat-Shamir implementations. - -The idea is to have a small server with an endpoint accepting proofs of executions of circuits. The plonk backend will have a bug in the initialization of the transcript and won't add the public inputs to the transcript. So as long as one public input is in control of the attacker, he can forge fake proofs. - -At the moment the circuit is: - -``` -PUBLIC INPUT: x -PUBLIC INPUT: y - -ASSERT 0 == y^2 - x^3 - 4 -``` -And it instantiated over the `BLS12 381` scalar field. -If the user achieves to send a proof for `x==1`, then they obtain the flag. Since $5$ is a quadratic non residue in the base field of the circuit, this can only be achieved by forging a fake proof. - -The vulnerability stems from a bug in the implementation of strong Fiat-Shamir. A correct implementation should add, among other things, all the public inputs to the transcript at initialization. If a public input is not added to the transcript and is in control of the attacker, they can forge a fake proof. Here, fixing `x=1` leaves `y` under control of the user. - -The attack is described in Section V of [Weak Fiat-Shamir Attacks on Modern Proof Systems](https://eprint.iacr.org/2023/691.pdf). - -Here is a description of the attack. - -![image](https://github.com/lambdaclass/challenges-ctf/assets/41742639/d2040ccd-17ad-4f0e-b910-a17ceda96ed4) - -Instead of taking random polynomials (steps (1) to (7)), the current solution takes a valid proof for the pair `x=0`, `y=2` and uses it to forge a `y'` for `x=1` that's compatible with the original proof. - -At the moment, the server endpoint is simulated with the following function. - -```rust -pub fn server_endpoint_verify( - srs: ChallengeSRS, - common_preprocessed_input: CommonPreprocessedInput, - vk: &ChallengeVK, - x: &FrElement, - y: &FrElement, - proof: &ChallengeProof, -) -> String { - let public_input = [x.clone(), y.clone()]; - let kzg = KZG::new(srs); - let verifier = Verifier::new(kzg); - let result = verifier.verify(proof, &public_input, &common_preprocessed_input, vk); - if !result { - "Invalid Proof".to_string() - } else if x != &FieldElement::one() { - "Valid Proof. Congrats!".to_string() - } else { - FLAG.to_string() - } -} -``` - -The attack can be found in `src/solution.rs` along with a test that showcases it. - -## Get it to work - -Currently `lambdaworks_plonk_prover` does not expose the weak Fiat-Shamir vulnerability. -So to make the challenge work we need to modify it. - -1. Clone `lambdaworks_plonk_prover` repo: `git clone git@github.com:lambdaclass/lambdaworks_plonk_prover.git` -1. `git checkout 07e36bf` -1. Make the following changes to it: - -```diff -diff --git a/Cargo.toml b/Cargo.toml -index 7f0e324..c36a00d 100644 ---- a/Cargo.toml -+++ b/Cargo.toml -@@ -7,8 +7,8 @@ edition = "2021" - - [dependencies] - serde = { version = "1.0", features = ["derive"]} --lambdaworks-math = { git = "https://github.com/lambdaclass/lambdaworks", rev = "943963c" } --lambdaworks-crypto = { git = "https://github.com/lambdaclass/lambdaworks", rev = "943963c" } -+lambdaworks-math = { git = "https://github.com/lambdaclass/lambdaworks", rev = "d8f14cb" } -+lambdaworks-crypto = { git = "https://github.com/lambdaclass/lambdaworks", rev = "d8f14cb" } - - thiserror = "1.0.38" - serde_json = "1.0" -diff --git a/src/setup.rs b/src/setup.rs -index 493278a..437bcc9 100644 ---- a/src/setup.rs -+++ b/src/setup.rs -@@ -69,7 +69,7 @@ pub fn setup>( - - pub fn new_strong_fiat_shamir_transcript( - vk: &VerificationKey, -- public_input: &[FieldElement], -+ _public_input: &[FieldElement], - ) -> DefaultTranscript - where - F: IsField, -@@ -88,9 +88,6 @@ where - transcript.append(&vk.qo_1.serialize()); - transcript.append(&vk.qc_1.serialize()); - -- for value in public_input.iter() { -- transcript.append(&value.to_bytes_be()); -- } - transcript - } -``` - -1. Clone this repo and modify its `Cargo.toml` to point the `lambdaworks-plonk` dependency to your local copy of `lambdaworks_plonk_prover`. -1. Run `cargo test` diff --git a/exercises/broken_heart/src/circuit.rs b/exercises/broken_heart/src/circuit.rs deleted file mode 100644 index 80fe94406..000000000 --- a/exercises/broken_heart/src/circuit.rs +++ /dev/null @@ -1,178 +0,0 @@ -use lambdaworks_math::{ - elliptic_curve::short_weierstrass::curves::bls12_381::default_types::{FrElement, FrField}, - field::element::FieldElement, - polynomial::Polynomial, -}; -use lambdaworks_plonk::setup::{CommonPreprocessedInput, Witness}; - -use crate::server::{ORDER_8_ROOT_UNITY, ORDER_R_MINUS_1_ROOT_UNITY}; - -/// Generates a domain to interpolate: 1, omega, omega², ..., omega^size -pub fn generate_domain(omega: &FrElement, size: usize) -> Vec { - (1..size).fold(vec![FieldElement::one()], |mut acc, _| { - acc.push(acc.last().unwrap() * omega); - acc - }) -} - -/// The identity permutation, auxiliary function to generate the copy constraints. -fn identity_permutation(w: &FrElement, n: usize) -> Vec { - let u = ORDER_R_MINUS_1_ROOT_UNITY; - let mut result: Vec = vec![]; - for index_column in 0..=2 { - for index_row in 0..n { - result.push(w.pow(index_row) * u.pow(index_column as u64)); - } - } - result -} - -/// Generates the permutation coefficients for the copy constraints. -/// polynomials S1, S2, S3. -pub fn generate_permutation_coefficients( - omega: &FrElement, - n: usize, - permutation: &[usize], -) -> Vec { - let identity = identity_permutation(omega, n); - let permuted: Vec = (0..n * 3) - .map(|i| identity[permutation[i]].clone()) - .collect(); - permuted -} - -/// Witness generator for the circuit `ASSERT 0 == y ** 2 - x ** 3 - 4` -pub fn circuit_witness(x: &FrElement, y: &FrElement) -> Witness { - let b = FieldElement::from(4); - let x2 = x * x; - let x3 = &x2 * x; - let u = &x3 + &b; - let y2 = y * y; - let w = &y2 - &u; - let empty = x.clone(); - Witness { - a: vec![ - x.clone(), - y.clone(), - x.clone(), - x2.clone(), - x3.clone(), - y.clone(), - u.clone(), - w.clone(), - ], - b: vec![ - empty.clone(), - empty.clone(), - x.clone(), - x.clone(), - empty.clone(), - y.clone(), - y2.clone(), - empty.clone(), - ], - c: vec![empty.clone(), empty.clone(), x2, x3, u, y2, w, empty], - } -} - -/// Common preprocessed input for the circuit `ASSERT 0 == y ** 2 - x ** 3 - 4` -pub fn circuit_common_preprocessed_input() -> CommonPreprocessedInput { - let n: usize = 8; - let omega = ORDER_8_ROOT_UNITY; - let domain = generate_domain(&omega, n); - - let permutation = &[ - 23, 13, 0, 18, 19, 1, 20, 22, 2, 8, 9, 10, 11, 5, 21, 12, 15, 16, 3, 4, 6, 14, 7, 17, - ]; - let permuted = generate_permutation_coefficients(&omega, n, permutation); - - let s1_lagrange: Vec = permuted[..8].to_vec(); - let s2_lagrange: Vec = permuted[8..16].to_vec(); - let s3_lagrange: Vec = permuted[16..].to_vec(); - - CommonPreprocessedInput { - n, - omega, - k1: ORDER_R_MINUS_1_ROOT_UNITY, - domain: domain.clone(), - - ql: Polynomial::interpolate( - &domain, - &[ - -FieldElement::one(), - -FieldElement::one(), - FieldElement::zero(), - FieldElement::zero(), - FieldElement::one(), - FieldElement::zero(), - -FieldElement::one(), - FieldElement::one(), - ], - ) - .unwrap(), - qr: Polynomial::interpolate( - &domain, - &[ - FieldElement::zero(), - FieldElement::zero(), - FieldElement::zero(), - FieldElement::zero(), - FieldElement::zero(), - FieldElement::zero(), - FieldElement::one(), - FieldElement::zero(), - ], - ) - .unwrap(), - qo: Polynomial::interpolate( - &domain, - &[ - FieldElement::zero(), - FieldElement::zero(), - -FieldElement::one(), - -FieldElement::one(), - -FieldElement::one(), - -FieldElement::one(), - -FieldElement::one(), - FieldElement::zero(), - ], - ) - .unwrap(), - qm: Polynomial::interpolate( - &domain, - &[ - FieldElement::zero(), - FieldElement::zero(), - FieldElement::one(), - FieldElement::one(), - FieldElement::zero(), - FieldElement::one(), - FieldElement::zero(), - FieldElement::zero(), - ], - ) - .unwrap(), - qc: Polynomial::interpolate( - &domain, - &[ - FieldElement::zero(), - FieldElement::zero(), - FieldElement::zero(), - FieldElement::zero(), - FieldElement::from(4), - FieldElement::zero(), - FieldElement::zero(), - FieldElement::zero(), - ], - ) - .unwrap(), - - s1: Polynomial::interpolate(&domain, &s1_lagrange).unwrap(), - s2: Polynomial::interpolate(&domain, &s2_lagrange).unwrap(), - s3: Polynomial::interpolate(&domain, &s3_lagrange).unwrap(), - - s1_lagrange, - s2_lagrange, - s3_lagrange, - } -} diff --git a/exercises/broken_heart/src/lib.rs b/exercises/broken_heart/src/lib.rs deleted file mode 100644 index 594e2d936..000000000 --- a/exercises/broken_heart/src/lib.rs +++ /dev/null @@ -1,3 +0,0 @@ -pub mod circuit; -pub mod server; -pub mod solution; diff --git a/exercises/broken_heart/src/server.rs b/exercises/broken_heart/src/server.rs deleted file mode 100644 index ca923ac62..000000000 --- a/exercises/broken_heart/src/server.rs +++ /dev/null @@ -1,96 +0,0 @@ -// -------- Irreduci-bull challenge --------- - -use lambdaworks_crypto::commitments::{ - kzg::{KateZaveruchaGoldberg, StructuredReferenceString}, - traits::IsCommitmentScheme, -}; -use lambdaworks_math::{ - cyclic_group::IsGroup, - elliptic_curve::{ - short_weierstrass::curves::bls12_381::{ - curve::BLS12381Curve, - default_types::{FrElement, FrField}, - pairing::BLS12381AtePairing, - twist::BLS12381TwistCurve, - }, - traits::{IsEllipticCurve, IsPairing}, - }, - field::element::FieldElement, - traits::IsRandomFieldElementGenerator, -}; -use lambdaworks_plonk::{ - prover::Proof, - setup::{CommonPreprocessedInput, VerificationKey}, - verifier::Verifier, -}; - -pub const FLAG: &str = "ZK{dummy_flag}"; - -type ChallengeSRS = StructuredReferenceString< - ::G1Point, - ::G2Point, ->; - -pub const ORDER_8_ROOT_UNITY: FrElement = FrElement::from_hex_unchecked( - "345766f603fa66e78c0625cd70d77ce2b38b21c28713b7007228fd3397743f7a", -); // order 8 - -pub const ORDER_R_MINUS_1_ROOT_UNITY: FrElement = FrElement::from_hex_unchecked("7"); -pub type ChallengeCS = KateZaveruchaGoldberg; -pub type ChallengeVK = VerificationKey<>::Commitment>; -pub type ChallengeProof = Proof; -pub type Pairing = BLS12381AtePairing; -pub type KZG = KateZaveruchaGoldberg; -pub type CPI = CommonPreprocessedInput; -type G1Point = ::PointRepresentation; -type G2Point = ::PointRepresentation; - -pub fn quadratic_non_residue() -> FrElement { - ORDER_R_MINUS_1_ROOT_UNITY -} - -/// Generates a test SRS for the BLS12381 curve -/// n is the number of constraints in the system. -pub fn generate_srs(n: usize) -> StructuredReferenceString { - let s = FrElement::from(2); - let g1 = ::generator(); - let g2 = ::generator(); - - let powers_main_group: Vec = (0..n + 3) - .map(|exp| g1.operate_with_self(s.pow(exp as u64).representative())) - .collect(); - let powers_secondary_group = [g2.clone(), g2.operate_with_self(s.representative())]; - - StructuredReferenceString::new(&powers_main_group, &powers_secondary_group) -} - -/// A mock of a random number generator, to have deterministic tests. -/// When set to zero, there is no zero knowledge applied, because it is used -/// to get random numbers to blind polynomials. -pub struct TestRandomFieldGenerator; -impl IsRandomFieldElementGenerator for TestRandomFieldGenerator { - fn generate(&self) -> FrElement { - FrElement::zero() - } -} - -pub fn server_endpoint_verify( - srs: ChallengeSRS, - common_preprocessed_input: CommonPreprocessedInput, - vk: &ChallengeVK, - x: &FrElement, - y: &FrElement, - proof: &ChallengeProof, -) -> String { - let public_input = [x.clone(), y.clone()]; - let kzg = KZG::new(srs); - let verifier = Verifier::new(kzg); - let result = verifier.verify(proof, &public_input, &common_preprocessed_input, vk); - if !result { - "Invalid Proof".to_string() - } else if x != &FieldElement::one() { - "Valid Proof. Congrats!".to_string() - } else { - FLAG.to_string() - } -} diff --git a/exercises/broken_heart/src/solution.rs b/exercises/broken_heart/src/solution.rs deleted file mode 100644 index 5c9e6d28d..000000000 --- a/exercises/broken_heart/src/solution.rs +++ /dev/null @@ -1,119 +0,0 @@ -use lambdaworks_crypto::{ - commitments::traits::IsCommitmentScheme, fiat_shamir::transcript::Transcript, -}; -use lambdaworks_math::{ - field::{element::FieldElement, traits::IsField}, - traits::{ByteConversion, Serializable}, -}; -use lambdaworks_plonk::{ - prover::Proof, - setup::{new_strong_fiat_shamir_transcript, CommonPreprocessedInput, VerificationKey}, -}; - -#[allow(unused)] -fn forge_y_for_valid_proof>( - proof: &Proof, - vk: &VerificationKey, - common_preprocessed_input: CommonPreprocessedInput, -) -> FieldElement -where - CS::Commitment: Serializable, - FieldElement: ByteConversion, -{ - // Replay interactions like the verifier - let mut transcript = new_strong_fiat_shamir_transcript::(vk, &[]); - - transcript.append(&proof.a_1.serialize()); - transcript.append(&proof.b_1.serialize()); - transcript.append(&proof.c_1.serialize()); - let beta = FieldElement::from_bytes_be(&transcript.challenge()).unwrap(); - let gamma = FieldElement::from_bytes_be(&transcript.challenge()).unwrap(); - - transcript.append(&proof.z_1.serialize()); - let alpha = FieldElement::from_bytes_be(&transcript.challenge()).unwrap(); - - transcript.append(&proof.t_lo_1.serialize()); - transcript.append(&proof.t_mid_1.serialize()); - transcript.append(&proof.t_hi_1.serialize()); - let zeta = &FieldElement::from_bytes_be(&transcript.challenge()).unwrap(); - - // Forge public input - let zh_zeta = zeta.pow(common_preprocessed_input.n) - FieldElement::one(); - - let omega = &common_preprocessed_input.omega; - let n = common_preprocessed_input.n as u64; - let one = &FieldElement::one(); - - let l1_zeta = ((zeta.pow(n) - one) / (zeta - one)) / FieldElement::from(n); - - let l2_zeta = omega * &l1_zeta * (zeta - one) / (zeta - omega); - - let mut p_constant_zeta = &alpha - * &proof.z_zeta_omega - * (&proof.c_zeta + &gamma) - * (&proof.a_zeta + &beta * &proof.s1_zeta + &gamma) - * (&proof.b_zeta + &beta * &proof.s2_zeta + &gamma); - p_constant_zeta = p_constant_zeta - &l1_zeta * &alpha * α - - let p_zeta = p_constant_zeta + &proof.p_non_constant_zeta; - -(p_zeta + l1_zeta * one - (&zh_zeta * &proof.t_zeta)) / l2_zeta -} - -#[cfg(test)] -mod tests { - use lambdaworks_math::field::element::FieldElement; - use lambdaworks_plonk::{prover::Prover, setup::setup}; - - use crate::{ - circuit::{circuit_common_preprocessed_input, circuit_witness}, - server::{generate_srs, server_endpoint_verify, TestRandomFieldGenerator, FLAG, KZG}, - solution::forge_y_for_valid_proof, - }; - - #[test] - fn test_challenge() { - // This is the circuit for `ASSERT 0 == y ** 2 - x ** 3 - 4` - let cpi = circuit_common_preprocessed_input(); - let srs = generate_srs(cpi.n); - let kzg = KZG::new(srs.clone()); - let verifying_key = setup(&cpi.clone(), &kzg); - - let x = FieldElement::from(0); - let y = FieldElement::from(2); - - let public_input = vec![x.clone(), y.clone()]; - let witness = circuit_witness(&x, &y); - - let random_generator = TestRandomFieldGenerator {}; - let prover = Prover::new(kzg.clone(), random_generator); - let proof = prover.prove(&witness, &public_input, &cpi, &verifying_key); - - let response_valid = - server_endpoint_verify(srs.clone(), cpi.clone(), &verifying_key, &x, &y, &proof); - assert_eq!("Valid Proof. Congrats!".to_string(), response_valid); - - let response_invalid = server_endpoint_verify( - srs.clone(), - cpi.clone(), - &verifying_key, - &FieldElement::one(), - &y, - &proof, - ); - assert_eq!("Invalid Proof".to_string(), response_invalid); - - // Use the real proof to modify the public input - // and make it pass for `x = 1` - let forged_y = forge_y_for_valid_proof(&proof, &verifying_key, cpi.clone()); - - let response_solution = server_endpoint_verify( - srs.clone(), - cpi.clone(), - &verifying_key, - &FieldElement::one(), - &forged_y, - &proof, - ); - assert_eq!(FLAG.to_string(), response_solution); - } -} diff --git a/exercises/challenge_1/Cargo.toml b/exercises/challenge_1/Cargo.toml deleted file mode 100644 index 669c6fec2..000000000 --- a/exercises/challenge_1/Cargo.toml +++ /dev/null @@ -1,10 +0,0 @@ -[package] -name = "block_cypher" -version = "0.1.0" -edition = "2021" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] -lambdaworks-math = { git = "https://github.com/lambdaclass/lambdaworks.git" } -rand = "0.8.5" diff --git a/exercises/challenge_1/README.md b/exercises/challenge_1/README.md deleted file mode 100644 index 8870b820f..000000000 --- a/exercises/challenge_1/README.md +++ /dev/null @@ -1,6 +0,0 @@ -# The Lost Relic - -During their quest to find the greatest treasure in the world, the One Piece, Luffy and his friends are wandering inside a subterranean maze. After many hours, they arrive at the door hiding an old relic, which can be instrumental to achieving their goal. The big problem is that it is made of sea stone and Luffy is unable to use his strength to break it. There are some inscriptions on the walls, which Nico Robin is able to translate. -It says: -"If you can find the secret hidden among these texts, the door will open." -There are many input plaintexts and their corresponding ciphertexts, all of them encrypted using a custom MiMC algorithm under the same key. There are also many skeletons around, of all the people who have so far failed this test. Luckily, Usopp brought his computing device and will try to break the secret. What can he do to recover the secret? \ No newline at end of file diff --git a/exercises/challenge_1/src/cypher.rs b/exercises/challenge_1/src/cypher.rs deleted file mode 100644 index 4d2e9ffcf..000000000 --- a/exercises/challenge_1/src/cypher.rs +++ /dev/null @@ -1,11 +0,0 @@ -use crate::field::ChallengeElement; - -const ROUNDS: usize = 2_usize.pow(24); - -pub fn evaluate(x: &ChallengeElement, key: &ChallengeElement) -> ChallengeElement { - (0..ROUNDS).fold(x.clone(), |acc, _| evaluate_round(&acc, key)) -} - -pub fn evaluate_round(x: &ChallengeElement, key: &ChallengeElement) -> ChallengeElement { - (x + key).pow(2_u64) -} diff --git a/exercises/challenge_1/src/data.rs b/exercises/challenge_1/src/data.rs deleted file mode 100644 index 0e14341ac..000000000 --- a/exercises/challenge_1/src/data.rs +++ /dev/null @@ -1,148 +0,0 @@ -use lambdaworks_math::unsigned_integer::element::UnsignedInteger; - -use crate::field::ChallengeElement; - -pub fn pairs() -> [(ChallengeElement, ChallengeElement); 10] { - [ - ( - ChallengeElement::new(UnsignedInteger::from_limbs([ - 183240637262039384, - 10134058328874369520, - 15648036570429036245, - 12373630818091389922, - ])), - ChallengeElement::new(UnsignedInteger::from_limbs([ - 58125280520191997, - 7563295584845025545, - 10505934299123791696, - 7243434167283202274, - ])), - ), - ( - ChallengeElement::new(UnsignedInteger::from_limbs([ - 53793503318333808, - 3207637959751464466, - 14494950274942836597, - 4136788124041118761, - ])), - ChallengeElement::new(UnsignedInteger::from_limbs([ - 311364612610444531, - 15966843845133580520, - 11733134576879619546, - 15728250733723614304, - ])), - ), - ( - ChallengeElement::new(UnsignedInteger::from_limbs([ - 568211605542321808, - 3665906368153986805, - 17170247225323812746, - 15915536692872977361, - ])), - ChallengeElement::new(UnsignedInteger::from_limbs([ - 217852115983473995, - 4166846794769956288, - 6317771942840139764, - 14625875306968385082, - ])), - ), - ( - ChallengeElement::new(UnsignedInteger::from_limbs([ - 207549010898490221, - 4243616016599017025, - 9848596493685366018, - 7918147356679190828, - ])), - ChallengeElement::new(UnsignedInteger::from_limbs([ - 477451920176997361, - 15490207318061191317, - 18290929028207201553, - 12313619366283486653, - ])), - ), - ( - ChallengeElement::new(UnsignedInteger::from_limbs([ - 373838775372616312, - 2673582186589028492, - 7759321698787748238, - 13844063650817543069, - ])), - ChallengeElement::new(UnsignedInteger::from_limbs([ - 501628185314588506, - 360101150955423272, - 9640428090924426801, - 4582961170966616797, - ])), - ), - ( - ChallengeElement::new(UnsignedInteger::from_limbs([ - 87489608385277507, - 7831811476568950391, - 2088690257692214216, - 5066699409404733431, - ])), - ChallengeElement::new(UnsignedInteger::from_limbs([ - 355875529635643805, - 13282387072996050664, - 8345773773609640016, - 16439847079178157230, - ])), - ), - ( - ChallengeElement::new(UnsignedInteger::from_limbs([ - 81907762606414085, - 13685489810001955596, - 1500287613219709574, - 2446883281376595718, - ])), - ChallengeElement::new(UnsignedInteger::from_limbs([ - 285826094074462510, - 2412521888121343816, - 17074562155301273597, - 743918196287760338, - ])), - ), - ( - ChallengeElement::new(UnsignedInteger::from_limbs([ - 539334648243077342, - 1241819602239411681, - 5927184179348544209, - 11640860157447976251, - ])), - ChallengeElement::new(UnsignedInteger::from_limbs([ - 502287359473451983, - 2105484562428168146, - 2070489610481465832, - 9074849931098206603, - ])), - ), - ( - ChallengeElement::new(UnsignedInteger::from_limbs([ - 536884484895507488, - 5347777065114328110, - 18395674441153693721, - 10552627961256589724, - ])), - ChallengeElement::new(UnsignedInteger::from_limbs([ - 55173036628470166, - 605478499650837527, - 3313559020999214570, - 17867174237670907305, - ])), - ), - ( - ChallengeElement::new(UnsignedInteger::from_limbs([ - 420067545220093896, - 2975148993333630768, - 15772310561964876975, - 5736425213799489626, - ])), - ChallengeElement::new(UnsignedInteger::from_limbs([ - 245745081194512045, - 17128497290282674116, - 12860542778099276299, - 11433466637600805681, - ])), - ), - ] -} diff --git a/exercises/challenge_1/src/field.rs b/exercises/challenge_1/src/field.rs deleted file mode 100644 index 718328ea2..000000000 --- a/exercises/challenge_1/src/field.rs +++ /dev/null @@ -1,5 +0,0 @@ -use lambdaworks_math::field::{ - element::FieldElement, fields::fft_friendly::stark_252_prime_field::Stark252PrimeField, -}; - -pub type ChallengeElement = FieldElement; diff --git a/exercises/challenge_1/src/main.rs b/exercises/challenge_1/src/main.rs deleted file mode 100644 index 6dd6dda95..000000000 --- a/exercises/challenge_1/src/main.rs +++ /dev/null @@ -1,18 +0,0 @@ -use data::pairs; -use solver::solve; - -use crate::cypher::evaluate; - -mod cypher; -mod data; -mod field; -mod solver; - -fn main() { - let key = solve(); - - let (p, c) = pairs()[0].clone(); - assert_eq!(evaluate(&p, &key), c); - - println!("Found Key! {}", &key); -} diff --git a/exercises/challenge_1/src/solver.rs b/exercises/challenge_1/src/solver.rs deleted file mode 100644 index 2e3efa67a..000000000 --- a/exercises/challenge_1/src/solver.rs +++ /dev/null @@ -1,6 +0,0 @@ -use crate::field::ChallengeElement; - -pub fn solve() -> ChallengeElement { - println!("Solving..."); - todo!(); -} diff --git a/exercises/challenge_2/Cargo.toml b/exercises/challenge_2/Cargo.toml deleted file mode 100644 index 5d1375d22..000000000 --- a/exercises/challenge_2/Cargo.toml +++ /dev/null @@ -1,10 +0,0 @@ -[package] -name = "template_solution_srs_1" -version = "0.1.0" -edition = "2021" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] -lambdaworks-crypto = { git = "https://github.com/lambdaclass/lambdaworks", rev = "366ac95" } -lambdaworks-math= { git = "https://github.com/lambdaclass/lambdaworks", rev = "366ac95" } diff --git a/exercises/challenge_2/README.md b/exercises/challenge_2/README.md deleted file mode 100644 index 9b821968d..000000000 --- a/exercises/challenge_2/README.md +++ /dev/null @@ -1,16 +0,0 @@ -# Breaking into the vault of Loki - -After years of careful investigation, you have reached the gate to Loki's vault in the icy mountains of Norway, where it is said that many great treasures and powerful weapons are hidden. The gate seems unbreakable, but you spot some ancient machinery with inscriptions in old runes. After some help from ChatGPT, you are able to translate the symbols and the whole message into modern English, and it reads: - -If you can prove that the polynomial - -$$ -\begin{aligned} -p(x) &= 69 +78x + 32x^2 + 65x^3 + 82x^4 + 71x^5 + 69x^6 + 78x^7 + 84x^8 + 73x^9 \newline &+78x^{10} + 65x^{11} + 32x^{12} + 78x^{13} + 65x^{14}+ 67x^{15} + 73x^{16} + 32x^{17} \newline -&+ 84x^{18} + 73x^{19} + 69x^{20} + 82x^{21} + 82x^{22} + 65 x^{23} -\end{aligned} -$$ - -is equal to $3$ at $x = 1$ modulo the BLS12-381 $r$ parameter, then the gate will open. - -Below is a long list of bytes representing the SRS that can be used to perform KZG commitments. The machinery, after careful examination, performs the KZG verification using pairings. There is only one open place where you can place a wooden tablet with your answer, comprising 48 bytes. You guess this should be the proof of the KZG scheme, providing the point in compressed form, following the ZCash standard. The other elements contain the commitment to $p(x)$, the desired value $3$, and the point $x=1$. You ask ChatGPT for enlightenment, but it suddenly collapses and only shows the message: fatal error. Is this just an impossible task? Perhaps there is some trick to get by Loki's challenge... \ No newline at end of file diff --git a/exercises/challenge_2/src/main.rs b/exercises/challenge_2/src/main.rs deleted file mode 100644 index 4f6d3ba94..000000000 --- a/exercises/challenge_2/src/main.rs +++ /dev/null @@ -1,90 +0,0 @@ -use lambdaworks_crypto::commitments::{ - kzg::{KateZaveruchaGoldberg, StructuredReferenceString}, - traits::IsCommitmentScheme, -}; -use lambdaworks_math::{ - elliptic_curve::{ - short_weierstrass::{ - curves::bls12_381::{ - curve::BLS12381Curve, - default_types::{FrConfig, FrElement}, - field_extension::BLS12381PrimeField, - pairing::BLS12381AtePairing, - twist::BLS12381TwistCurve, - }, - point::ShortWeierstrassProjectivePoint, - }, - traits::FromAffine, - }, - field::{ - element::FieldElement, fields::montgomery_backed_prime_fields::MontgomeryBackendPrimeField, - }, - polynomial::Polynomial, - unsigned_integer::element::UnsignedInteger, -}; - -type G1Point = ShortWeierstrassProjectivePoint; -type G2Point = ShortWeierstrassProjectivePoint; - -type KZG = KateZaveruchaGoldberg, BLS12381AtePairing>; -pub type Fq = FieldElement; - -fn challenge_polynomial() -> Polynomial { - Polynomial::::new(&[ - FieldElement::from(69), - FieldElement::from(78), - FieldElement::from(32), - FieldElement::from(65), - FieldElement::from(82), - FieldElement::from(71), - FieldElement::from(69), - FieldElement::from(78), - FieldElement::from(84), - FieldElement::from(73), - FieldElement::from(78), - FieldElement::from(65), - FieldElement::from(32), - FieldElement::from(78), - FieldElement::from(65), - FieldElement::from(67), - FieldElement::from(73), - FieldElement::from(32), - FieldElement::from(84), - FieldElement::from(73), - FieldElement::from(69), - FieldElement::from(82), - FieldElement::from(65), - ]) -} - -fn main() { - let base_dir = env!("CARGO_MANIFEST_DIR"); - let srs_path = base_dir.to_owned() + "/srs.bin"; - let srs = StructuredReferenceString::::from_file(&srs_path).unwrap(); - - let kzg = KZG::new(srs.clone()); - - let p = challenge_polynomial(); - - let p_commitment: G1Point = kzg.commit(&p); - - // If you need to write a bigger number, you can use - // If you are writing the solution in rust you shouldn't need this - let big_number = UnsignedInteger::<6>::from_limbs([0, 0, 0, 0, 0, 2]); - let y = Fq::new(big_number); - - // TO DO: Make your own fake proof - let fake_proof = - ShortWeierstrassProjectivePoint::::from_affine(Fq::from(0), y).unwrap(); - - println!("Fake proof for submission:"); - println!("{:?}", &fake_proof.to_affine().x().to_string()); - println!("{:?}", &fake_proof.to_affine().y().to_string()); - - assert!(kzg.verify( - &FrElement::from(1), - &FrElement::from(3), - &p_commitment, - &fake_proof - )); -} diff --git a/exercises/challenge_2/srs.bin b/exercises/challenge_2/srs.bin deleted file mode 100644 index ff79fcbf8..000000000 Binary files a/exercises/challenge_2/srs.bin and /dev/null differ diff --git a/exercises/challenge_3/Cargo.toml b/exercises/challenge_3/Cargo.toml deleted file mode 100644 index da231f811..000000000 --- a/exercises/challenge_3/Cargo.toml +++ /dev/null @@ -1,17 +0,0 @@ -[package] -name = "power" -version = "1.0.0" -edition = "2021" - -[dependencies] -json = "0.12" -serde = { version = "1.0", features = ["derive"] } -actix-web = "4.3" -env_logger = "0.10" -log = "0.4" -rand = "0.8" -serde_json = "1" -tokio = { version = "1.24.2", features = ["sync"] } -lambdaworks-crypto = { git = "https://github.com/lambdaclass/lambdaworks", rev = "366ac95" } -lambdaworks-math= { git = "https://github.com/lambdaclass/lambdaworks", rev = "366ac95" } -serde_cbor = "0.10" diff --git a/exercises/challenge_3/README.md b/exercises/challenge_3/README.md deleted file mode 100644 index 5edb1817f..000000000 --- a/exercises/challenge_3/README.md +++ /dev/null @@ -1,15 +0,0 @@ -# It is over 9000! - -The Saiyans have landed on planet earth. Our great defenders Krillin, Piccolo, Tien and Gohan -have to hold on till Goku arrives on the scene. - -Vegeta and Nappa have scouters that indicate our heroes power levels -and sadly we are not doing too well. - -Somehow, Gohan has raised his power level to `p_4(X) = 9000`, but it is not good enough. Piccolo `p_3(X)` can help but he is still regenerating, and Krillin `p_2(X)` and Tien `p_1(X)` are in bad shape. The total power of the team is computed as -``` -P = p_1(X) * 0 + p_2(X) * 0 + p_3(X) * 0 + p_4(X) -``` -At the current moment, the X is equal to `42`. - -Suddenly Gohan, and Piccolo recieve a message from Bulma that the scouters verify the sensed power level of individual enemies using KZG and for multiple enemies with batched KZG method. Vegeta knows for sure that the power level of Gohan is `p_4(X) = 9000`, so he will know if we change that. If only the team had a way to trick their opponents to believe that their total power level is `P > 9000` - then the enemies will surely flee. \ No newline at end of file diff --git a/exercises/challenge_3/src/main.rs b/exercises/challenge_3/src/main.rs deleted file mode 100644 index 9b7b67bc0..000000000 --- a/exercises/challenge_3/src/main.rs +++ /dev/null @@ -1,124 +0,0 @@ -use std::net::TcpStream; -use std::io::{Read, Write}; - -use lambdaworks_crypto::commitments::{ - kzg::{KateZaveruchaGoldberg, StructuredReferenceString}, - traits::IsCommitmentScheme, -}; -use lambdaworks_math::{ - elliptic_curve::{ - short_weierstrass::{ - curves::bls12_381::{ - curve::BLS12381Curve, - default_types::{FrElement, FrField}, - field_extension::BLS12381PrimeField, - pairing::BLS12381AtePairing, - twist::BLS12381TwistCurve, - }, - point::ShortWeierstrassProjectivePoint, - }, - }, - field::element::FieldElement, - polynomial::Polynomial, -}; -use serde::{Deserialize, Serialize}; - -const X: u64 = 42; -const NUM_POLYS: usize = 4; -#[allow(clippy::upper_case_acronyms)] -type KZG = KateZaveruchaGoldberg; - -pub type Fq = FieldElement; - -#[derive(Debug, Serialize, Deserialize)] -pub struct PowerProof { - pub proof_x_hex: String, - pub proof_y_hex: String, - pub u: String, - pub y: [String; NUM_POLYS], - pub commitments_x: [String; NUM_POLYS], - pub commitments_y: [String; NUM_POLYS], -} - -type G1Point = ShortWeierstrassProjectivePoint; -type G2Point = ShortWeierstrassProjectivePoint; - -fn load_srs() -> StructuredReferenceString:: { - let base_dir = env!("CARGO_MANIFEST_DIR"); - let srs_path = base_dir.to_owned() + "/srs.bin"; - StructuredReferenceString::::from_file(&srs_path).unwrap() -} - -fn upload_solution(proof: &PowerProof) { - let mut stream = TcpStream::connect("52.7.211.188:8000").unwrap(); - let proof_vec = serde_cbor::to_vec(&proof).expect("Failed serialization"); - - stream.write(&(proof_vec.len() as u64).to_be_bytes()).unwrap(); - stream.write(&proof_vec).unwrap(); - - let mut response = String::new(); - stream.read_to_string(&mut response).unwrap(); - println!("Received response: {}", response); -} - -fn main() { - let srs = load_srs(); - let kzg = KZG::new(srs); - let x = FieldElement::from(X); - - let p1_coeffs = [FieldElement::one(), FieldElement::one()]; - let p2_coeffs = [FieldElement::one(), FieldElement::one()]; - let p3_coeffs = [FieldElement::one(), FieldElement::one()]; - // This is Gohan power level, it can't be tampered with - let p4_coeffs = [FieldElement::from(9000)]; - - // Sample random u - let u = FieldElement::from(rand::random::()); - - let commit_and_open_at = |coeffs: &[FieldElement<_>]| -> ( - Polynomial<_>, - G1Point, - FieldElement<_> - ) { - let poly = Polynomial::::new(coeffs); - let commitment = kzg.commit(&poly); - let eval = poly.evaluate(&x); - - (poly, commitment, eval) - }; - - let (p1, - p1_comm, - y1) = commit_and_open_at(&p1_coeffs); - - let (p2, - p2_comm, - y2) = commit_and_open_at(&p2_coeffs); - - let (p3, - p3_comm, - y3) = commit_and_open_at(&p3_coeffs); - - let (p4, - p4_comm, - y4) = commit_and_open_at(&p4_coeffs); - - - let ys = [y1, y2, y3, y4]; - let ps = [p1, p2, p3 ,p4]; - let ps_c = [p1_comm, p2_comm, p3_comm, p4_comm]; - - let proof = kzg.open_batch(&x, &ys, &ps, &u); - assert!(kzg.verify_batch(&x, &ys, &ps_c, &proof, &u)); - - let power_proof = PowerProof { - proof_x_hex: proof.to_affine().x().to_string(), - proof_y_hex: proof.to_affine().y().to_string(), - u: u.to_string(), - y: ys.map(|y| y.to_string() ), - commitments_x: ps_c.clone().map(|c| c.to_affine().x().to_string()), - commitments_y: ps_c.map(|c| c.to_affine().y().to_string()), - }; - - upload_solution(&power_proof); -} \ No newline at end of file diff --git a/exercises/challenge_3/srs.bin b/exercises/challenge_3/srs.bin deleted file mode 100644 index 22550f08a..000000000 Binary files a/exercises/challenge_3/srs.bin and /dev/null differ diff --git a/favicon.png b/favicon.png new file mode 100644 index 000000000..a5b1aa16c Binary files /dev/null and b/favicon.png differ diff --git a/favicon.svg b/favicon.svg new file mode 100644 index 000000000..90e0ea58b --- /dev/null +++ b/favicon.svg @@ -0,0 +1,22 @@ + + + + + diff --git a/fft/benchmarks.html b/fft/benchmarks.html new file mode 100644 index 000000000..466a71f86 --- /dev/null +++ b/fft/benchmarks.html @@ -0,0 +1,216 @@ + + + + + + Benchmarks - docs + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + + + +
+
+ +

FFT Benchmarks

+

Polynomial interpolation methods comparison

+

Three methods of polynomial interpolation were benchmarked, with different input sizes each time:

+
    +
  • CPU Lagrange: Finding the Lagrange polynomial of a set of random points via a naive algorithm (see math/src/polynomial.rs:interpolate())
  • +
  • CPU FFT: Finding the lowest degree polynomial that interpolates pairs of twiddle factors and Fourier coefficients (the results of applying the Fourier transform to the coefficients of a polynomial) (see math/src/polynomial.rs:interpolate_fft()).
  • +
+

All values of time are in milliseconds. Those cases which were greater than 30 seconds were marked respectively as they're too slow and weren't worth to be benchmarked. The input size refers to d + 1 where d is the polynomial's degree (so size is amount of coefficients).

+ + + + + + + + + + +
Input sizeCPU LagrangeCPU FFT
2^42.2 ms0.2 ms
2^59.6 ms0.4 ms
2^642.6 ms0.8 ms
2^7200.8 ms1.7 ms
........
2^21>30000 ms28745 ms
2^22>30000 ms>30000 ms
2^23>30000 ms>30000 ms
2^24>30000 ms>30000 ms
+ +
+ + +
+
+ + + +
+ + + + + + + + + + + + + + + + + + + + diff --git a/LICENSE b/fonts/OPEN-SANS-LICENSE.txt similarity index 99% rename from LICENSE rename to fonts/OPEN-SANS-LICENSE.txt index 261eeb9e9..d64569567 100644 --- a/LICENSE +++ b/fonts/OPEN-SANS-LICENSE.txt @@ -1,3 +1,4 @@ + Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ diff --git a/fonts/SOURCE-CODE-PRO-LICENSE.txt b/fonts/SOURCE-CODE-PRO-LICENSE.txt new file mode 100644 index 000000000..366206f54 --- /dev/null +++ b/fonts/SOURCE-CODE-PRO-LICENSE.txt @@ -0,0 +1,93 @@ +Copyright 2010, 2012 Adobe Systems Incorporated (http://www.adobe.com/), with Reserved Font Name 'Source'. All Rights Reserved. Source is a trademark of Adobe Systems Incorporated in the United States and/or other countries. + +This Font Software is licensed under the SIL Open Font License, Version 1.1. +This license is copied below, and is also available with a FAQ at: +http://scripts.sil.org/OFL + + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font creation +efforts of academic and linguistic communities, and to provide a free and +open framework in which fonts may be shared and improved in partnership +with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply +to any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components as +distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software to a +new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, +in Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the corresponding +Copyright Holder. This restriction only applies to the primary font name as +presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created +using the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. diff --git a/fonts/fonts.css b/fonts/fonts.css new file mode 100644 index 000000000..858efa598 --- /dev/null +++ b/fonts/fonts.css @@ -0,0 +1,100 @@ +/* Open Sans is licensed under the Apache License, Version 2.0. See http://www.apache.org/licenses/LICENSE-2.0 */ +/* Source Code Pro is under the Open Font License. See https://scripts.sil.org/cms/scripts/page.php?site_id=nrsi&id=OFL */ + +/* open-sans-300 - latin_vietnamese_latin-ext_greek-ext_greek_cyrillic-ext_cyrillic */ +@font-face { + font-family: 'Open Sans'; + font-style: normal; + font-weight: 300; + src: local('Open Sans Light'), local('OpenSans-Light'), + url('open-sans-v17-all-charsets-300.woff2') format('woff2'); +} + +/* open-sans-300italic - latin_vietnamese_latin-ext_greek-ext_greek_cyrillic-ext_cyrillic */ +@font-face { + font-family: 'Open Sans'; + font-style: italic; + font-weight: 300; + src: local('Open Sans Light Italic'), local('OpenSans-LightItalic'), + url('open-sans-v17-all-charsets-300italic.woff2') format('woff2'); +} + +/* open-sans-regular - latin_vietnamese_latin-ext_greek-ext_greek_cyrillic-ext_cyrillic */ +@font-face { + font-family: 'Open Sans'; + font-style: normal; + font-weight: 400; + src: local('Open Sans Regular'), local('OpenSans-Regular'), + url('open-sans-v17-all-charsets-regular.woff2') format('woff2'); +} + +/* open-sans-italic - latin_vietnamese_latin-ext_greek-ext_greek_cyrillic-ext_cyrillic */ +@font-face { + font-family: 'Open Sans'; + font-style: italic; + font-weight: 400; + src: local('Open Sans Italic'), local('OpenSans-Italic'), + url('open-sans-v17-all-charsets-italic.woff2') format('woff2'); +} + +/* open-sans-600 - latin_vietnamese_latin-ext_greek-ext_greek_cyrillic-ext_cyrillic */ +@font-face { + font-family: 'Open Sans'; + font-style: normal; + font-weight: 600; + src: local('Open Sans SemiBold'), local('OpenSans-SemiBold'), + url('open-sans-v17-all-charsets-600.woff2') format('woff2'); +} + +/* open-sans-600italic - latin_vietnamese_latin-ext_greek-ext_greek_cyrillic-ext_cyrillic */ +@font-face { + font-family: 'Open Sans'; + font-style: italic; + font-weight: 600; + src: local('Open Sans SemiBold Italic'), local('OpenSans-SemiBoldItalic'), + url('open-sans-v17-all-charsets-600italic.woff2') format('woff2'); +} + +/* open-sans-700 - latin_vietnamese_latin-ext_greek-ext_greek_cyrillic-ext_cyrillic */ +@font-face { + font-family: 'Open Sans'; + font-style: normal; + font-weight: 700; + src: local('Open Sans Bold'), local('OpenSans-Bold'), + url('open-sans-v17-all-charsets-700.woff2') format('woff2'); +} + +/* open-sans-700italic - latin_vietnamese_latin-ext_greek-ext_greek_cyrillic-ext_cyrillic */ +@font-face { + font-family: 'Open Sans'; + font-style: italic; + font-weight: 700; + src: local('Open Sans Bold Italic'), local('OpenSans-BoldItalic'), + url('open-sans-v17-all-charsets-700italic.woff2') format('woff2'); +} + +/* open-sans-800 - latin_vietnamese_latin-ext_greek-ext_greek_cyrillic-ext_cyrillic */ +@font-face { + font-family: 'Open Sans'; + font-style: normal; + font-weight: 800; + src: local('Open Sans ExtraBold'), local('OpenSans-ExtraBold'), + url('open-sans-v17-all-charsets-800.woff2') format('woff2'); +} + +/* open-sans-800italic - latin_vietnamese_latin-ext_greek-ext_greek_cyrillic-ext_cyrillic */ +@font-face { + font-family: 'Open Sans'; + font-style: italic; + font-weight: 800; + src: local('Open Sans ExtraBold Italic'), local('OpenSans-ExtraBoldItalic'), + url('open-sans-v17-all-charsets-800italic.woff2') format('woff2'); +} + +/* source-code-pro-500 - latin_vietnamese_latin-ext_greek_cyrillic-ext_cyrillic */ +@font-face { + font-family: 'Source Code Pro'; + font-style: normal; + font-weight: 500; + src: url('source-code-pro-v11-all-charsets-500.woff2') format('woff2'); +} diff --git a/fonts/open-sans-v17-all-charsets-300.woff2 b/fonts/open-sans-v17-all-charsets-300.woff2 new file mode 100644 index 000000000..9f51be370 Binary files /dev/null and b/fonts/open-sans-v17-all-charsets-300.woff2 differ diff --git a/fonts/open-sans-v17-all-charsets-300italic.woff2 b/fonts/open-sans-v17-all-charsets-300italic.woff2 new file mode 100644 index 000000000..2f5454484 Binary files /dev/null and b/fonts/open-sans-v17-all-charsets-300italic.woff2 differ diff --git a/fonts/open-sans-v17-all-charsets-600.woff2 b/fonts/open-sans-v17-all-charsets-600.woff2 new file mode 100644 index 000000000..f503d558d Binary files /dev/null and b/fonts/open-sans-v17-all-charsets-600.woff2 differ diff --git a/fonts/open-sans-v17-all-charsets-600italic.woff2 b/fonts/open-sans-v17-all-charsets-600italic.woff2 new file mode 100644 index 000000000..c99aabe80 Binary files /dev/null and b/fonts/open-sans-v17-all-charsets-600italic.woff2 differ diff --git a/fonts/open-sans-v17-all-charsets-700.woff2 b/fonts/open-sans-v17-all-charsets-700.woff2 new file mode 100644 index 000000000..421a1ab25 Binary files /dev/null and b/fonts/open-sans-v17-all-charsets-700.woff2 differ diff --git a/fonts/open-sans-v17-all-charsets-700italic.woff2 b/fonts/open-sans-v17-all-charsets-700italic.woff2 new file mode 100644 index 000000000..12ce3d20d Binary files /dev/null and b/fonts/open-sans-v17-all-charsets-700italic.woff2 differ diff --git a/fonts/open-sans-v17-all-charsets-800.woff2 b/fonts/open-sans-v17-all-charsets-800.woff2 new file mode 100644 index 000000000..c94a223b0 Binary files /dev/null and b/fonts/open-sans-v17-all-charsets-800.woff2 differ diff --git a/fonts/open-sans-v17-all-charsets-800italic.woff2 b/fonts/open-sans-v17-all-charsets-800italic.woff2 new file mode 100644 index 000000000..eed7d3c63 Binary files /dev/null and b/fonts/open-sans-v17-all-charsets-800italic.woff2 differ diff --git a/fonts/open-sans-v17-all-charsets-italic.woff2 b/fonts/open-sans-v17-all-charsets-italic.woff2 new file mode 100644 index 000000000..398b68a08 Binary files /dev/null and b/fonts/open-sans-v17-all-charsets-italic.woff2 differ diff --git a/fonts/open-sans-v17-all-charsets-regular.woff2 b/fonts/open-sans-v17-all-charsets-regular.woff2 new file mode 100644 index 000000000..8383e94c6 Binary files /dev/null and b/fonts/open-sans-v17-all-charsets-regular.woff2 differ diff --git a/fonts/source-code-pro-v11-all-charsets-500.woff2 b/fonts/source-code-pro-v11-all-charsets-500.woff2 new file mode 100644 index 000000000..722245682 Binary files /dev/null and b/fonts/source-code-pro-v11-all-charsets-500.woff2 differ diff --git a/fuzz/Cargo.toml b/fuzz/Cargo.toml deleted file mode 100644 index 5fabb05e6..000000000 --- a/fuzz/Cargo.toml +++ /dev/null @@ -1,17 +0,0 @@ -[workspace] -members = ["no_gpu_fuzz", "cuda_fuzz"] -resolver = "2" - -[workspace.package] -edition = "2021" -publish = false -version = "0.1.1" - -[workspace.dependencies] -lambdaworks-math = { path = "../math" } -lambdaworks-gpu = { path = "../gpu" } -stark-platinum-prover = { path = "../provers/stark" } -libfuzzer-sys = "0.4" - -[profile.release] -debug = 1 diff --git a/fuzz/README.md b/fuzz/README.md deleted file mode 100644 index 11ddd6e67..000000000 --- a/fuzz/README.md +++ /dev/null @@ -1,27 +0,0 @@ -# Fuzzing -There are three types of fuzzers distributed on different workspaces depending on the features (metal/cuda) they need. So you should make sure you cded into the right folder before running any of the commands. -This directory contains three types of fuzzers distributed onto three different workspaces. `no_gpu_fuzz` and `metal_fuzz` both use `cargo-fuzz` and must be run with nightly, also the latter only runs on mac. `cuda_fuzz` runs on machines with nvida GPUs and uses `honggfuzz` which runs on most linux distros. - -## Setup -Run the following commands to get ready. - -### cargo-fuzz -* `cargo install cargo-fuzz` -* `rustup toolchain install nightly` - -### honggfuzz -* `cargo install honggfuzz` -* `apt install build-essential` -* `apt-get install binutils-dev` -* `sudo apt-get install libunwind-dev` -* `sudo apt-get install lldb` - -## Running the fuzzers -* no_gpu & metal: `cargo +nightly fuzz run --fuzz-dir . ` -* cuda: `cargo hfuzz run ` -The targets can be found in the `fuzz_targets` directory. Normally the name of the file without the extension should work, if it doesn't, look up the name for that binary target in `Cargo.toml`. - -## Debugging -If a crash is found, an `artifacts/` or `hfuzz_workspace/cuda_fuzz` folder will be added, inside it you'll find the different reports. To get an lldb dump, run -* no_gpu & metal: `cargo +nightly fuzz run --fuzz-dir . artifacts/` -* cuda: `cargo hfuzz run-debug hfuzz_workspace/cuda_fuzz/` diff --git a/fuzz/cuda_fuzz/Cargo.toml b/fuzz/cuda_fuzz/Cargo.toml deleted file mode 100644 index 5ee8125d3..000000000 --- a/fuzz/cuda_fuzz/Cargo.toml +++ /dev/null @@ -1,29 +0,0 @@ -[package] -name = "cuda_fuzz" -version.workspace = true -edition.workspace = true - -[package.metadata] -cargo-fuzz = true - -[dependencies] -lambdaworks-math = { workspace = true, features = ["cuda"] } -lambdaworks-gpu = { workspace = true, features = ["cuda"] } -honggfuzz = "0.5.55" - -[[bin]] -name = "cuda_fft_fuzzer" -path = "src/cuda_fft_fuzzer.rs" - -[[bin]] -name = "polynomial_fft_diff" -path = "src/polynomial_fft_diff.rs" -test = false -doc = false - -[[bin]] -name = "twiddles_generation_diff" -path = "src/twiddles_generation_diff.rs" -test = false -doc = false - diff --git a/fuzz/cuda_fuzz/src/cuda_fft_fuzzer.rs b/fuzz/cuda_fuzz/src/cuda_fft_fuzzer.rs deleted file mode 100644 index e493ec03f..000000000 --- a/fuzz/cuda_fuzz/src/cuda_fft_fuzzer.rs +++ /dev/null @@ -1,50 +0,0 @@ -#[macro_use] -extern crate honggfuzz; -use lambdaworks_math::{ - fft::{ - gpu::cuda::{ops::fft as fft_cuda, state::CudaState}, - cpu::{ - roots_of_unity::get_twiddles, - ops::fft as fft_cpu - } - }, - field::{ - traits::RootsConfig, - fields::fft_friendly::stark_252_prime_field::Stark252PrimeField, - element::FieldElement - }, -}; - - -fn main() { - loop { - fuzz!(|data: Vec| { - let mut input_raw = data; - let mut inputs = Vec::new(); - - if input_raw.len() == 0 { - input_raw.push(0u64); - } - - for i in 0..input_raw.len() { - let input_value = format!("{:x}", input_raw[i]); - inputs.push(FieldElement::::from_hex_unchecked(&input_value)) - } - - let twiddles = get_twiddles( - inputs.len().trailing_zeros() as u64, - RootsConfig::BitReverse, - ) - .unwrap(); - - let state = CudaState::new().unwrap(); - println!("inputs {:?}", &inputs); - println!("fft cpu{:?}", fft_cpu(&inputs, &twiddles)); - - match fft_cpu(&inputs, &twiddles) { - Ok(fft_result) => assert_eq!(fft_result, fft_cuda(&inputs, &twiddles, &state).unwrap()), - Err(_) => assert!(fft_cuda(&inputs, &twiddles, &state).is_err()) - } - }); - } -} diff --git a/fuzz/cuda_fuzz/src/polynomial_fft_diff.rs b/fuzz/cuda_fuzz/src/polynomial_fft_diff.rs deleted file mode 100644 index f87cf6c95..000000000 --- a/fuzz/cuda_fuzz/src/polynomial_fft_diff.rs +++ /dev/null @@ -1,49 +0,0 @@ -use honggfuzz::fuzz; -use lambdaworks_math::{ - fft::{ - gpu::cuda::polynomial::{evaluate_fft_cuda, interpolate_fft_cuda}, - polynomial::{evaluate_fft_cpu, interpolate_fft_cpu}, - }, - field::{ - fields::fft_friendly::stark_252_prime_field::Stark252PrimeField, - element::FieldElement, - } -}; - -fn main() { - loop { - fuzz!(|data: Vec| { - let mut inputs_raw = data; - let mut inputs = Vec::new(); - - if inputs_raw.len() == 0 { - inputs_raw.push(0u64); - } - - for i in 0..inputs_raw.len() { - let input_value = format!("{:x}", inputs_raw[i]); - inputs.push(FieldElement::::from_hex_unchecked(&input_value)) - } - - let (fft_eval_cuda, fft_eval_cpu) = match (evaluate_fft_cuda(&inputs), evaluate_fft_cpu(&inputs)) { - (Ok(fft_eval_cuda), Ok(fft_eval_cpu)) => { - assert_eq!(fft_eval_cuda, fft_eval_cpu); - (fft_eval_cuda.clone(), fft_eval_cpu.clone()) - }, - (Err(_), Err(_)) => { - (inputs.clone(), inputs) - }, - (cuda, cpu) => panic!("Evaluate results didn't match. cuda.is_err(): {}, cpu.is_err(): {}", cuda.is_err(), cpu.is_err()) - }; - - match (interpolate_fft_cuda(&fft_eval_cuda), interpolate_fft_cpu(&fft_eval_cpu)) { - (Ok(interpolated_cuda), Ok(interpolated_cpu)) => { - assert_eq!(interpolated_cuda, interpolated_cpu); - }, - (Err(_), Err(_)) => {}, - (cuda, cpu) => panic!("Interpolate results didn't match. cuda.is_err(): {}, cpu.is_err(): {}", cuda.is_err(), cpu.is_err()) - }; - }); - } -} - diff --git a/fuzz/cuda_fuzz/src/twiddles_generation_diff.rs b/fuzz/cuda_fuzz/src/twiddles_generation_diff.rs deleted file mode 100644 index 84301c467..000000000 --- a/fuzz/cuda_fuzz/src/twiddles_generation_diff.rs +++ /dev/null @@ -1,32 +0,0 @@ -use honggfuzz::fuzz; -use lambdaworks_math::{ - fft::{ - gpu::cuda::{ state::CudaState, ops::gen_twiddles }, - cpu::roots_of_unity::get_twiddles, - }, - field::{ - traits::RootsConfig, - fields::fft_friendly::stark_252_prime_field::Stark252PrimeField, - }, -}; - -fn main() { - loop { - fuzz!(|input: u64| { - let state = CudaState::new().unwrap(); - let roots_configurations = vec![RootsConfig::Natural, RootsConfig::BitReverseInversed, RootsConfig::BitReverse, RootsConfig::NaturalInversed]; - - for roots_config in roots_configurations { - let gen_twiddles_cuda = gen_twiddles::(input, roots_config, &state); - let get_twiddles_cpu = get_twiddles::(input, roots_config); - - match (gen_twiddles_cuda, get_twiddles_cpu) { - (Ok(twiddles_cuda), Ok(twiddles_cpu)) => assert_eq!(twiddles_cuda, twiddles_cpu), - (Err(_), Err(_)) => {}, - (cuda, cpu) => panic!("Evaluate results didn't match. cuda.is_err(): {}, cpu.is_err(): {}", cuda.is_err(), cpu.is_err()) - } - } - }); - } -} - diff --git a/fuzz/no_gpu_fuzz/Cargo.toml b/fuzz/no_gpu_fuzz/Cargo.toml deleted file mode 100644 index 0032023f7..000000000 --- a/fuzz/no_gpu_fuzz/Cargo.toml +++ /dev/null @@ -1,93 +0,0 @@ -[package] -name = "no_gpu_fuzz" -version.workspace = true -edition.workspace = true - -[package.metadata] -cargo-fuzz = true - -[dependencies] -lambdaworks-math = { workspace = true } -lambdaworks-gpu = { workspace = true } -libfuzzer-sys = { workspace = true } -stark-platinum-prover = { workspace = true } - -num-traits = "0.2" -ibig = "0.3.6" -p3-goldilocks = { git = "https://github.com/Plonky3/Plonky3", rev = "41cd843" } -p3-mersenne-31 = { git = "https://github.com/Plonky3/Plonky3", rev = "41cd843" } -p3-field = { git = "https://github.com/Plonky3/Plonky3" } -p3-baby-bear = { git = "https://github.com/Plonky3/Plonky3" } - -[[bin]] -name = "curve_bls12_381" -path = "fuzz_targets/curve/bls12_381.rs" -test = false -doc = false - -[[bin]] -name = "curve_bn254" -path = "fuzz_targets/curve/bn254.rs" -test = false -doc = false - -[[bin]] -name = "curve_grumpkin" -path = "fuzz_targets/curve/grumpkin.rs" -test = false -doc = false - -[[bin]] -name = "secp256k1" -path = "fuzz_targets/field/secp256k1.rs" -test = false -doc = false - -[[bin]] -name = "stark252" -path = "fuzz_targets/field/stark252.rs" -test = false -doc = false - -[[bin]] -name = "mersenne31" -path = "fuzz_targets/field/mersenne31.rs" -test = false -doc = false - -[[bin]] -name = "babybear" -path = "fuzz_targets/field/babybear.rs" -test = false -doc = false - -[[bin]] -name = "mini_goldilocks" -path = "fuzz_targets/field/mini_goldilocks.rs" -test = false -doc = false - -[[bin]] -name = "field_from_hex" -path = "fuzz_targets/field/from_hex.rs" -test = false -doc = false - -[[bin]] -name = "field_from_raw" -path = "fuzz_targets/field/from_raw.rs" -test = false -doc = false - -[[bin]] -name = "stark252_addition" -path = "fuzz_targets/field/stark_field_addition.rs" -test = false -doc = false - - -[[bin]] -name = "deserialize_stark_proof" -path = "fuzz_targets/deserialize_stark_proof.rs" -test = false -doc = false diff --git a/fuzz/no_gpu_fuzz/fuzz_targets/curve/curve_bls12_381.rs b/fuzz/no_gpu_fuzz/fuzz_targets/curve/curve_bls12_381.rs deleted file mode 100644 index b5f281463..000000000 --- a/fuzz/no_gpu_fuzz/fuzz_targets/curve/curve_bls12_381.rs +++ /dev/null @@ -1,101 +0,0 @@ -#![no_main] - -use libfuzzer_sys::fuzz_target; -use lambdaworks_math::{ - field::element::FieldElement, - cyclic_group::IsGroup, - elliptic_curve::{ - traits::{IsEllipticCurve, IsPairing}, - short_weierstrass::{ - curves::bls12_381::{ - curve::BLS12381Curve, - twist::BLS12381TwistCurve, - pairing::BLS12381AtePairing, - field_extension::Degree12ExtensionField, - }, - point::ShortWeierstrassProjectivePoint, - } - }, - unsigned_integer::element::U384, -}; - -type LambdaG1 = ShortWeierstrassProjectivePoint; -type LambdaG2 = ShortWeierstrassProjectivePoint; - -//TODO: Derive arbitrary for Affine and Projective or change this to use &[u8] as input to cover more cases. -//TODO: Use more advanced options to generate values over curve specifically given most inputs will fail curve check. -//TODO: Investigate normalization of projective coordinates in arkworks to allow for differential fuzzing. -fuzz_target!(|values: (u64, u64)| { - let (a_val, b_val) = values; - - let a_g1 = BLS12381Curve::generator().operate_with_self(a_val); - let b_g1 = BLS12381Curve::generator().operate_with_self(b_val); - - let a_g2 = BLS12381TwistCurve::generator().operate_with_self(a_val); - let b_g2 = BLS12381TwistCurve::generator().operate_with_self(b_val); - - // ***AXIOM SOUNDNESS*** - let g1_zero = LambdaG1::neutral_element(); - - let g2_zero = LambdaG2::neutral_element(); - - // G1 - // -O = O - assert_eq!(g1_zero.neg(), g1_zero, "Neutral mul element a failed"); - - // P + O = O - assert_eq!(a_g1.operate_with(&g1_zero), a_g1, "Neutral operate_with element a failed"); - assert_eq!(b_g1.operate_with(&g1_zero), b_g1, "Neutral operate_with element b failed"); - - // P + Q = Q + P - assert_eq!(a_g1.operate_with(&b_g1), b_g1.operate_with(&a_g1), "Commutative add property failed"); - - // (P + Q) + R = Q + (P + R) - let c_g1 = a_g1.operate_with(&b_g1); - assert_eq!((a_g1.operate_with(&b_g1)).operate_with(&c_g1), a_g1.operate_with(&b_g1.operate_with(&c_g1)), "Associative operate_with property failed"); - - // P + -P = O - assert_eq!(a_g1.operate_with(&a_g1.neg()), g1_zero, "Inverse add a failed"); - assert_eq!(b_g1.operate_with(&b_g1.neg()), g1_zero, "Inverse add b failed"); - - // G2 - // -O = O - assert_eq!(g2_zero.neg(), g2_zero, "Neutral mul element a failed"); - - // P + O = O - assert_eq!(a_g2.operate_with(&g2_zero), a_g2, "Neutral operate_with element a failed"); - assert_eq!(b_g2.operate_with(&g2_zero), b_g2, "Neutral operate_with element b failed"); - - // P + Q = Q + P - assert_eq!(a_g2.operate_with(&b_g2), b_g2.operate_with(&a_g2), "Commutative add property failed"); - - // (P + Q) + R = Q + (P + R) - let c_g2 = a_g2.operate_with(&b_g2); - assert_eq!((a_g2.operate_with(&b_g2)).operate_with(&c_g2), a_g2.operate_with(&b_g2.operate_with(&c_g2)), "Associative operate_with property failed"); - - // P + -P = O - assert_eq!(a_g2.operate_with(&a_g2.neg()), g2_zero, "Inverse add a failed"); - assert_eq!(b_g2.operate_with(&b_g2.neg()), g2_zero, "Inverse add b failed"); - - // Pairing Bilinearity - let a = U384::from_u64(a_val); - let b = U384::from_u64(b_val); - let result = BLS12381AtePairing::compute_batch(&[ - ( - &a_g1.operate_with_self(a).to_affine(), - &a_g2.operate_with_self(b).to_affine(), - ), - ( - &a_g1.operate_with_self(a * b).to_affine(), - &a_g2.neg().to_affine(), - ), - ]); - assert_eq!(result, FieldElement::::one()); - - // Ate Pairing returns one with one element is neutral element - let result = BLS12381AtePairing::compute_batch(&[(&a_g1.to_affine(), &LambdaG2::neutral_element())]); - assert_eq!(result, FieldElement::::one()); - - let result = BLS12381AtePairing::compute_batch(&[(&LambdaG1::neutral_element(), &a_g2.to_affine())]); - assert_eq!(result, FieldElement::::one()); -}); diff --git a/fuzz/no_gpu_fuzz/fuzz_targets/curve/curve_bn254.rs b/fuzz/no_gpu_fuzz/fuzz_targets/curve/curve_bn254.rs deleted file mode 100644 index 326ba0ad3..000000000 --- a/fuzz/no_gpu_fuzz/fuzz_targets/curve/curve_bn254.rs +++ /dev/null @@ -1,100 +0,0 @@ -#![no_main] - -use libfuzzer_sys::fuzz_target; -use lambdaworks_math::{ - cyclic_group::IsGroup, - elliptic_curve::{ - traits::{IsEllipticCurve, IsPairing}, - short_weierstrass::{ - curves::bn_254::{ - curve::BN254Curve, - twist::BN254TwistCurve, - field_extension::Degree12ExtensionField, - pairing::BN254AtePairing, - }, - point::ShortWeierstrassProjectivePoint, - } - }, - field::element::FieldElement, - unsigned_integer::element::U256, -}; - -type LambdaG1 = ShortWeierstrassProjectivePoint; -type LambdaG2 = ShortWeierstrassProjectivePoint; - -//TODO: derive arbitrary for Affine and Projective or change this to use &[u8] as input to cover more cases -fuzz_target!(|values: (u64, u64)| { - let (a_val, b_val) = values; - - let a_g1 = BN254Curve::generator().operate_with_self(a_val); - let b_g1 = BN254Curve::generator().operate_with_self(b_val); - - let a_g2 = BN254TwistCurve::generator().operate_with_self(a_val); - let b_g2 = BN254TwistCurve::generator().operate_with_self(b_val); - - // ***AXIOM SOUNDNESS*** - - let g1_zero = LambdaG1::neutral_element(); - - let g2_zero = LambdaG2::neutral_element(); - - // G1 - // -O = O - assert_eq!(g1_zero.neg(), g1_zero, "Neutral mul element a failed"); - - // P * O = O - assert_eq!(a_g1.operate_with(&g1_zero), a_g1, "Neutral operate_with element a failed"); - assert_eq!(b_g1.operate_with(&g1_zero), b_g1, "Neutral operate_with element b failed"); - - // P * Q = Q * P - assert_eq!(a_g1.operate_with(&b_g1), b_g1.operate_with(&a_g1), "Commutative add property failed"); - - // (P * Q) * R = Q * (P * R) - let c_g1 = a_g1.operate_with(&b_g1); - assert_eq!((a_g1.operate_with(&b_g1)).operate_with(&c_g1), a_g1.operate_with(&b_g1.operate_with(&c_g1)), "Associative operate_with property failed"); - - // P * -P = O - assert_eq!(a_g1.operate_with(&a_g1.neg()), g1_zero, "Inverse add a failed"); - assert_eq!(b_g1.operate_with(&b_g1.neg()), g1_zero, "Inverse add b failed"); - - // G2 - // -O = O - assert_eq!(g2_zero.neg(), g2_zero, "Neutral mul element a failed"); - - // P * O = O - assert_eq!(a_g2.operate_with(&g2_zero), a_g2, "Neutral operate_with element a failed"); - assert_eq!(b_g2.operate_with(&g2_zero), b_g2, "Neutral operate_with element b failed"); - - // P * Q = Q * P - assert_eq!(a_g2.operate_with(&b_g2), b_g2.operate_with(&a_g2), "Commutative add property failed"); - - // (P * Q) * R = Q * (P * R) - let c_g2 = a_g2.operate_with(&b_g2); - assert_eq!((a_g2.operate_with(&b_g2)).operate_with(&c_g2), a_g2.operate_with(&b_g2.operate_with(&c_g2)), "Associative operate_with property failed"); - - // P * -P = O - assert_eq!(a_g2.operate_with(&a_g2.neg()), g2_zero, "Inverse add a failed"); - assert_eq!(b_g2.operate_with(&b_g2.neg()), g2_zero, "Inverse add b failed"); - - // Pairing Bilinearity - let a = U256::from_u64(a_val); - let b = U256::from_u64(b_val); - let result = BN254AtePairing::compute_batch(&[ - ( - &a_g1.operate_with_self(a).to_affine(), - &a_g2.operate_with_self(b).to_affine(), - ), - ( - &a_g1.operate_with_self(a * b).to_affine(), - &a_g2.neg().to_affine(), - ), - ]); - assert_eq!(result, FieldElement::::one()); - - // Ate Pairing returns one with one element is neutral element - let result = BN254AtePairing::compute_batch(&[(&a_g1.to_affine(), &LambdaG2::neutral_element())]); - assert_eq!(result, FieldElement::::one()); - - let result = BN254AtePairing::compute_batch(&[(&LambdaG1::neutral_element(), &a_g2.to_affine())]); - assert_eq!(result, FieldElement::::one()); -}); diff --git a/fuzz/no_gpu_fuzz/fuzz_targets/curve/curve_grumpkin.rs b/fuzz/no_gpu_fuzz/fuzz_targets/curve/curve_grumpkin.rs deleted file mode 100644 index d1e5e3c94..000000000 --- a/fuzz/no_gpu_fuzz/fuzz_targets/curve/curve_grumpkin.rs +++ /dev/null @@ -1,45 +0,0 @@ -#![no_main] - -use libfuzzer_sys::fuzz_target; -use lambdaworks_math::{ - cyclic_group::IsGroup, - elliptic_curve::{ - traits::{IsEllipticCurve, IsPairing}, - short_weierstrass::{ - curves::grumpkin::curve::GrumpkinCurve, - point::ShortWeierstrassProjectivePoint, - } - }, - field::element::FieldElement, -}; - -type LambdaG1 = ShortWeierstrassProjectivePoint; - -//TODO: derive arbitrary for Affine and Projective or change this to use &[u8] as input to cover more cases -fuzz_target!(|values: (u64, u64)| { - let (a_val, b_val) = values; - - let a_g1 = GrumpkinCurve::generator().operate_with_self(a_val); - let b_g1 = GrumpkinCurve::generator().operate_with_self(b_val); - - // ***AXIOM SOUNDNESS*** - let g1_zero = LambdaG1::neutral_element(); - - // -O = O - assert_eq!(g1_zero.neg(), g1_zero, "Neutral mul element a failed"); - - // P * O = O - assert_eq!(a_g1.operate_with(&g1_zero), a_g1, "Neutral operate_with element a failed"); - assert_eq!(b_g1.operate_with(&g1_zero), b_g1, "Neutral operate_with element b failed"); - - // P * Q = Q * P - assert_eq!(a_g1.operate_with(&b_g1), b_g1.operate_with(&a_g1), "Commutative add property failed"); - - // (P * Q) * R = Q * (P * R) - let c_g1 = a_g1.operate_with(&b_g1); - assert_eq!((a_g1.operate_with(&b_g1)).operate_with(&c_g1), a_g1.operate_with(&b_g1.operate_with(&c_g1)), "Associative operate_with property failed"); - - // P * -P = O - assert_eq!(a_g1.operate_with(&a_g1.neg()), g1_zero, "Inverse add a failed"); - assert_eq!(b_g1.operate_with(&b_g1.neg()), g1_zero, "Inverse add b failed"); -}); diff --git a/fuzz/no_gpu_fuzz/fuzz_targets/deserialize_stark_proof.rs b/fuzz/no_gpu_fuzz/fuzz_targets/deserialize_stark_proof.rs deleted file mode 100644 index c6c945ee2..000000000 --- a/fuzz/no_gpu_fuzz/fuzz_targets/deserialize_stark_proof.rs +++ /dev/null @@ -1,12 +0,0 @@ -#![no_main] -use libfuzzer_sys::fuzz_target; -use stark_platinum_prover::proof::stark::StarkProof; -use lambdaworks_math::field::fields::fft_friendly::stark_252_prime_field::Stark252PrimeField; -use lambdaworks_math::traits::Deserializable; - - -fuzz_target!(|data: Vec| { - - let _proof = StarkProof::::deserialize(&data); - -}); diff --git a/fuzz/no_gpu_fuzz/fuzz_targets/field/babybear.rs b/fuzz/no_gpu_fuzz/fuzz_targets/field/babybear.rs deleted file mode 100644 index 9483781e3..000000000 --- a/fuzz/no_gpu_fuzz/fuzz_targets/field/babybear.rs +++ /dev/null @@ -1,79 +0,0 @@ -#![no_main] - -use lambdaworks_math::field::{ - element::FieldElement, - fields::u32_montgomery_backend_prime_field::U32MontgomeryBackendPrimeField, -}; -use libfuzzer_sys::fuzz_target; -use p3_baby_bear::BabyBear; -use p3_field::{Field, FieldAlgebra, PrimeField32}; - -pub type U32Babybear31PrimeField = U32MontgomeryBackendPrimeField<2013265921>; -pub type F = FieldElement; - -fuzz_target!(|values: (u32, u32)| { - // Note: we filter values outside of order as it triggers an assert within plonky3 disallowing values n >= Self::Order - let (value_u32_a, value_u32_b) = values; - - if value_u32_a >= 2013265921 || value_u32_b >= 2013265921 { - return; - } - let a = F::from(value_u32_a as u64); - let b = F::from(value_u32_b as u64); - - // Note: if we parse using from_canonical_u32 fails due to check that n < Self::Order - let a_expected = BabyBear::from_canonical_u32(value_u32_a); - let b_expected = BabyBear::from_canonical_u32(value_u32_b); - - let add_u32 = &a + &b; - let addition = a_expected + b_expected; - assert_eq!(add_u32.representative(), addition.as_canonical_u32()); - - let sub_u32 = &a - &b; - let substraction = a_expected - b_expected; - assert_eq!(sub_u32.representative(), substraction.as_canonical_u32()); - - let mul_u32 = &a * &b; - let multiplication = a_expected * b_expected; - assert_eq!(mul_u32.representative(), multiplication.as_canonical_u32()); - - // Axioms soundness - let one = F::one(); - let zero = F::zero(); - - assert_eq!(&a + &zero, a, "Neutral add element a failed"); - assert_eq!(&b + &zero, b, "Neutral mul element b failed"); - assert_eq!(&a * &one, a, "Neutral add element a failed"); - assert_eq!(&b * &one, b, "Neutral mul element b failed"); - - assert_eq!(&a + &b, &b + &a, "Commutative add property failed"); - assert_eq!(&a * &b, &b * &a, "Commutative mul property failed"); - - let c = &a * &b; - assert_eq!( - (&a + &b) + &c, - &a + (&b + &c), - "Associative add property failed" - ); - assert_eq!( - (&a * &b) * &c, - &a * (&b * &c), - "Associative mul property failed" - ); - - assert_eq!( - &a * (&b + &c), - &a * &b + &a * &c, - "Distributive property failed" - ); - - assert_eq!(&a - &a, zero, "Inverse add a failed"); - assert_eq!(&b - &b, zero, "Inverse add b failed"); - - if a != zero { - assert_eq!(&a * a.inv().unwrap(), one, "Inverse mul a failed"); - } - if b != zero { - assert_eq!(&b * b.inv().unwrap(), one, "Inverse mul b failed"); - } -}); diff --git a/fuzz/no_gpu_fuzz/fuzz_targets/field/from_hex.rs b/fuzz/no_gpu_fuzz/fuzz_targets/field/from_hex.rs deleted file mode 100644 index 0409cd6af..000000000 --- a/fuzz/no_gpu_fuzz/fuzz_targets/field/from_hex.rs +++ /dev/null @@ -1,94 +0,0 @@ -#![no_main] - -use libfuzzer_sys::fuzz_target; -use lambdaworks_math::field::{ - element::FieldElement, - fields::fft_friendly::stark_252_prime_field::Stark252PrimeField -}; -use ibig::{modular::ModuloRing, UBig}; - -fuzz_target!(|values: (String, String)| { - let (value_a, value_b) = values; - let cairo_prime = - UBig::from_str_radix("800000000000011000000000000000000000000000000000000000000000001", 16).unwrap(); - let a_parsed = UBig::from_str_radix(&value_a, 16); - let b_parsed = UBig::from_str_radix(&value_b, 16); - - if value_a.len() < 64 && value_b.len() < 64 && - a_parsed.is_ok() && b_parsed.is_ok() && - !value_a.chars().any(|c| matches!(c, '+')) && - !value_b.chars().any(|c| matches!(c, '+')) && - a_parsed.as_ref().unwrap() < &cairo_prime && b_parsed.as_ref().unwrap() < &cairo_prime - { - - let a = FieldElement::::from_hex_unchecked(&value_a); - let b = FieldElement::::from_hex_unchecked(&value_b); - - // Basic checks against ibig - let cairo_ring = ModuloRing::new(&cairo_prime); - let a_expected = cairo_ring.from(a_parsed.unwrap()); - let b_expected = cairo_ring.from(b_parsed.unwrap()); - - let add = &a + &b; - let expected_add = &a_expected + &b_expected; - assert_eq!(&(add.to_string())[2..], expected_add.residue().in_radix(16).to_string()); - - let sub = &a - &b; - let expected_sub = &a_expected - &b_expected; - assert_eq!(&(sub.to_string())[2..], expected_sub.residue().in_radix(16).to_string()); - - let mul = &a * &b; - let expected_mul = &a_expected * &b_expected; - assert_eq!(&(mul.to_string())[2..], expected_mul.residue().in_radix(16).to_string()); - - if !value_b.chars().all(|c| matches!(c, '0')) { - let div = &a / &b; - let expected_div = &a_expected / &b_expected; - assert_eq!(&(div.to_string())[2..], expected_div.residue().in_radix(16).to_string()); - } - - let pow = &a.pow(b.representative()); - let expected_pow = a_expected.pow(&b_expected.residue()); - assert_eq!(&(pow.to_string())[2..], expected_pow.residue().in_radix(16).to_string()); - - for n in [&a, &b] { - match n.sqrt() { - Some((fst_sqrt, snd_sqrt)) => { - assert_eq!(fst_sqrt.square(), snd_sqrt.square(), "Squared roots don't match each other"); - assert_eq!(n, &fst_sqrt.square(), "Squared roots don't match original number"); - } - None => {} - }; - } - - // Axioms soundness - - let one = FieldElement::::one(); - let zero = FieldElement::::zero(); - - assert_eq!(&a + &zero, a, "Neutral add element a failed"); - assert_eq!(&b + &zero, b, "Neutral mul element b failed"); - assert_eq!(&a * &one, a, "Neutral add element a failed"); - assert_eq!(&b * &one, b, "Neutral mul element b failed"); - - assert_eq!(&a + &b, &b + &a, "Commutative add property failed"); - assert_eq!(&a * &b, &b * &a, "Commutative mul property failed"); - - let c = &a * &b; - assert_eq!((&a + &b) + &c, &a + (&b + &c), "Associative add property failed"); - assert_eq!((&a * &b) * &c, &a * (&b * &c), "Associative mul property failed"); - - assert_eq!(&a * (&b + &c), &a * &b + &a * &c, "Distributive property failed"); - - assert_eq!(&a - &a, zero, "Inverse add a failed"); - assert_eq!(&b - &b, zero, "Inverse add b failed"); - - if a != zero { - assert_eq!(&a * a.inv().unwrap(), one, "Inverse mul a failed"); - } - if b != zero { - assert_eq!(&b * b.inv().unwrap(), one, "Inverse mul b failed"); - } - } -}); - diff --git a/fuzz/no_gpu_fuzz/fuzz_targets/field/from_raw.rs b/fuzz/no_gpu_fuzz/fuzz_targets/field/from_raw.rs deleted file mode 100644 index 972198158..000000000 --- a/fuzz/no_gpu_fuzz/fuzz_targets/field/from_raw.rs +++ /dev/null @@ -1,83 +0,0 @@ -#![no_main] - -use libfuzzer_sys::fuzz_target; -use lambdaworks_math::field::{ - element::FieldElement, - fields::fft_friendly::stark_252_prime_field::Stark252PrimeField -}; -use ibig::{modular::ModuloRing, UBig}; -use lambdaworks_math::unsigned_integer::element::UnsignedInteger; - -fuzz_target!(|values: (u64, u64)| { - - let (value_u64_a, value_u64_b) = values; - let prime = - UBig::from_str_radix("800000000000011000000000000000000000000000000000000000000000001", 16).unwrap(); - let ring = ModuloRing::new(&prime); - - let value_a = UnsignedInteger::from(value_u64_a); - let value_b = UnsignedInteger::from(value_u64_b); - - let a = FieldElement::::from_raw(&value_a); - let b = FieldElement::::from_raw(&value_b); - - let _a_expected = ring.from(value_u64_a); - let _b_expected = ring.from(value_u64_b); - - let _add_u64 = &a + &b; - - let _sub_u64 = &a - &b; - - let _mul_u64 = &a * &b; - - let _pow = &a.pow(b.representative()); - - if value_u64_b != 0 { - - let div = &a / &b; - assert_eq!(&div * &b, a.clone()); - } - - for n in [&a, &b] { - match n.sqrt() { - Some((fst_sqrt, snd_sqrt)) => { - assert_eq!(fst_sqrt.square(), snd_sqrt.square(), "Squared roots don't match each other"); - assert_eq!(n, &fst_sqrt.square(), "Squared roots don't match original number"); - } - None => {} - }; - } - - // Axioms soundness - - let one = FieldElement::::one(); - let zero = FieldElement::::zero(); - - assert_eq!(&a + &zero, a, "Neutral add element a failed"); - assert_eq!(&b + &zero, b, "Neutral mul element b failed"); - assert_eq!(&a * &one, a, "Neutral add element a failed"); - assert_eq!(&b * &one, b, "Neutral mul element b failed"); - - assert_eq!(&a + &b, &b + &a, "Commutative add property failed"); - assert_eq!(&a * &b, &b * &a, "Commutative mul property failed"); - - let c = &a * &b; - assert_eq!((&a + &b) + &c, &a + (&b + &c), "Associative add property failed"); - assert_eq!((&a * &b) * &c, &a * (&b * &c), "Associative mul property failed"); - - assert_eq!(&a * (&b + &c), &a * &b + &a * &c, "Distributive property failed"); - - assert_eq!(&a - &a, zero, "Inverse add a failed"); - assert_eq!(&b - &b, zero, "Inverse add b failed"); - - if a != zero { - assert_eq!(&a * a.inv().unwrap(), one, "Inverse mul a failed"); - } - if b != zero { - assert_eq!(&b * b.inv().unwrap(), one, "Inverse mul b failed"); - } - - -}); - - diff --git a/fuzz/no_gpu_fuzz/fuzz_targets/field/mersenne31.rs b/fuzz/no_gpu_fuzz/fuzz_targets/field/mersenne31.rs deleted file mode 100644 index 1a6de4d60..000000000 --- a/fuzz/no_gpu_fuzz/fuzz_targets/field/mersenne31.rs +++ /dev/null @@ -1,90 +0,0 @@ -#![no_main] - -use libfuzzer_sys::fuzz_target; -use lambdaworks_math::field::{ - element::FieldElement, - fields::{ - mersenne31::field::{Mersenne31Field, MERSENNE_31_PRIME_FIELD_ORDER}, - } -}; -use p3_mersenne_31::Mersenne31; -use p3_field::{Field, PrimeField32, PrimeField64, AbstractField}; - -fuzz_target!(|values: (u32, u32)| { - // Note: we filter values outside of order as it triggers an assert within plonky3 disallowing values n >= Self::Order - if values.0 >= MERSENNE_31_PRIME_FIELD_ORDER || values.1 >= MERSENNE_31_PRIME_FIELD_ORDER { - return - } - - let (value_u32_a, value_u32_b) = values; - - let a = FieldElement::::from(value_u32_a as u64); - let b = FieldElement::::from(value_u32_b as u64); - - // Note: if we parse using from_canonical_u32 fails due to check that n < Self::Order - let a_expected = Mersenne31::from_canonical_u32(value_u32_a); - let b_expected = Mersenne31::from_canonical_u32(value_u32_b); - - let add_u32 = &a + &b; - let addition = a_expected + b_expected; - - assert_eq!(add_u32.representative(), addition.as_canonical_u32()); - - let sub_u32 = &a - &b; - let substraction = a_expected - b_expected; - assert_eq!(sub_u32.representative(), substraction.as_canonical_u32()); - - let mul_u32 = &a * &b; - let multiplication = a_expected * b_expected; - assert_eq!(mul_u32.representative(), multiplication.as_canonical_u32()); - - let pow = &a.pow(b.representative()); - let expected_pow = a_expected.exp_u64(b_expected.as_canonical_u64()); - assert_eq!(pow.representative(), expected_pow.as_canonical_u32()); - - if value_u32_b != 0 && b.inv().is_ok() && b_expected.try_inverse().is_some() { - let div = &a / &b; - assert_eq!(&div * &b, a.clone()); - let expected_div = a_expected / b_expected; - assert_eq!(div.representative(), expected_div.as_canonical_u32()); - } - - for n in [&a, &b] { - match n.sqrt() { - Some((fst_sqrt, snd_sqrt)) => { - assert_eq!(fst_sqrt.square(), snd_sqrt.square(), "Squared roots don't match each other"); - assert_eq!(n, &fst_sqrt.square(), "Squared roots don't match original number"); - } - None => {} - }; - } - - // Axioms soundness - - let one = FieldElement::::one(); - let zero = FieldElement::::zero(); - - assert_eq!(&a + &zero, a, "Neutral add element a failed"); - assert_eq!(&b + &zero, b, "Neutral mul element b failed"); - assert_eq!(&a * &one, a, "Neutral add element a failed"); - assert_eq!(&b * &one, b, "Neutral mul element b failed"); - - assert_eq!(&a + &b, &b + &a, "Commutative add property failed"); - assert_eq!(&a * &b, &b * &a, "Commutative mul property failed"); - - let c = &a * &b; - assert_eq!((&a + &b) + &c, &a + (&b + &c), "Associative add property failed"); - assert_eq!((&a * &b) * &c, &a * (&b * &c), "Associative mul property failed"); - - assert_eq!(&a * (&b + &c), &a * &b + &a * &c, "Distributive property failed"); - - assert_eq!(&a - &a, zero, "Inverse add a failed"); - assert_eq!(&b - &b, zero, "Inverse add b failed"); - - if a != zero { - assert_eq!(&a * a.inv().unwrap(), one, "Inverse mul a failed"); - } - if b != zero { - assert_eq!(&b * b.inv().unwrap(), one, "Inverse mul b failed"); - } -}); diff --git a/fuzz/no_gpu_fuzz/fuzz_targets/field/mini_goldilocks.rs b/fuzz/no_gpu_fuzz/fuzz_targets/field/mini_goldilocks.rs deleted file mode 100644 index 9c9f551dd..000000000 --- a/fuzz/no_gpu_fuzz/fuzz_targets/field/mini_goldilocks.rs +++ /dev/null @@ -1,87 +0,0 @@ -#![no_main] - -use libfuzzer_sys::fuzz_target; -use lambdaworks_math::field::{ - element::FieldElement, - fields::{ - u64_prime_field::U64FieldElement, - u64_goldilocks_field::Goldilocks64Field, - }, -}; -use p3_goldilocks::Goldilocks; -use p3_field::{Field, PrimeField64, AbstractField}; - -fuzz_target!(|values: (u64, u64)| { - - let (value_u64_a, value_u64_b) = values; - - let a = FieldElement::::from(value_u64_a); - let b = FieldElement::::from(value_u64_b); - - let a_expected = Goldilocks::from_canonical_u64(value_u64_a); - let b_expected = Goldilocks::from_canonical_u64(value_u64_b); - - let add_u64 = &a + &b; - let addition = a_expected + b_expected; - - assert_eq!(add_u64.representative(), addition.as_canonical_u64()); - - let sub_u64 = &a - &b; - let substraction = a_expected - b_expected; - assert_eq!(sub_u64.representative(), substraction.as_canonical_u64()); - - let mul_u64 = &a * &b; - let multiplication = a_expected * b_expected; - assert_eq!(mul_u64.representative(), multiplication.as_canonical_u64()); - - let pow = &a.pow(b.representative()); - let expected_pow = a_expected.exp_u64(b_expected.as_canonical_u64()); - assert_eq!(pow.representative(), expected_pow.as_canonical_u64()); - - if value_u64_b != 0 && b.inv().is_ok() && b_expected.try_inverse().is_some() { - - let div = &a / &b; - assert_eq!(&div * &b, a.clone()); - let expected_div = a_expected / b_expected; - assert_eq!(div.representative(), expected_div.as_canonical_u64()); - } - - for n in [&a, &b] { - match n.sqrt() { - Some((fst_sqrt, snd_sqrt)) => { - assert_eq!(fst_sqrt.square(), snd_sqrt.square(), "Squared roots don't match each other"); - assert_eq!(n, &fst_sqrt.square(), "Squared roots don't match original number"); - } - None => {} - }; - } - - // Axioms soundness - - let one = FieldElement::::one(); - let zero = FieldElement::::zero(); - - assert_eq!(&a + &zero, a, "Neutral add element a failed"); - assert_eq!(&b + &zero, b, "Neutral mul element b failed"); - assert_eq!(&a * &one, a, "Neutral add element a failed"); - assert_eq!(&b * &one, b, "Neutral mul element b failed"); - - assert_eq!(&a + &b, &b + &a, "Commutative add property failed"); - assert_eq!(&a * &b, &b * &a, "Commutative mul property failed"); - - let c = &a * &b; - assert_eq!((&a + &b) + &c, &a + (&b + &c), "Associative add property failed"); - assert_eq!((&a * &b) * &c, &a * (&b * &c), "Associative mul property failed"); - - assert_eq!(&a * (&b + &c), &a * &b + &a * &c, "Distributive property failed"); - - assert_eq!(&a - &a, zero, "Inverse add a failed"); - assert_eq!(&b - &b, zero, "Inverse add b failed"); - - if a != zero { - assert_eq!(&a * a.inv().unwrap(), one, "Inverse mul a failed"); - } - if b != zero { - assert_eq!(&b * b.inv().unwrap(), one, "Inverse mul b failed"); - } -}); diff --git a/fuzz/no_gpu_fuzz/fuzz_targets/field/secp256k1.rs b/fuzz/no_gpu_fuzz/fuzz_targets/field/secp256k1.rs deleted file mode 100644 index b2d8d87dd..000000000 --- a/fuzz/no_gpu_fuzz/fuzz_targets/field/secp256k1.rs +++ /dev/null @@ -1,103 +0,0 @@ -#![no_main] - -use libfuzzer_sys::fuzz_target; -use lambdaworks_math::field::{ - element::FieldElement -}; -use ibig::{modular::ModuloRing, UBig}; -use lambdaworks_math::traits::ByteConversion; -use lambdaworks_math::field::fields::montgomery_backed_prime_fields::U256PrimeField; -use lambdaworks_math::unsigned_integer::element::U256; -use lambdaworks_math::field::fields::montgomery_backed_prime_fields::IsModulus; - -#[derive(Clone, Debug, Hash, Copy)] -pub struct MontgomeryConfigSecpPrimeField; - -impl IsModulus for MontgomeryConfigSecpPrimeField { - const MODULUS: U256 = - U256::from_hex_unchecked("0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F"); -} - -pub type SecpPrimeField = U256PrimeField; - -fuzz_target!(|bytes: ([u8;32], [u8;32])| { - - let secp256k1_prime = - UBig::from_str_radix("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F", 16).unwrap(); - - let secp256k1_ring_prime = ModuloRing::new(&secp256k1_prime); - - let (bytes_a, bytes_b) = bytes; - let a = FieldElement::::from_bytes_be(&bytes_a).unwrap(); - let b = FieldElement::::from_bytes_be(&bytes_b).unwrap(); - - let a_hex = a.to_string()[2..].to_string(); - let b_hex = b.to_string()[2..].to_string(); - - let a_ring = secp256k1_ring_prime.from(&UBig::from_str_radix(&a_hex, 16).unwrap()); - let b_ring = secp256k1_ring_prime.from(&UBig::from_str_radix(&b_hex, 16).unwrap()); - - let add = &a + &b; - let addition = &a_ring + &b_ring; - - assert_eq!(&(add.to_string())[2..], addition.residue().in_radix(16).to_string()); - - let sub = &a - &b; - let substraction = &a_ring - &b_ring; - assert_eq!(&(sub.to_string())[2..], substraction.residue().in_radix(16).to_string()); - - let mul = &a * &b; - let multiplication = &a_ring * &b_ring; - assert_eq!(&(mul.to_string())[2..], multiplication.residue().in_radix(16).to_string()); - - let pow = &a.pow(b.representative()); - let expected_pow = a_ring.pow(&b_ring.residue()); - assert_eq!(&(pow.to_string())[2..], expected_pow.residue().in_radix(16).to_string()); - - if b != FieldElement::zero() { - - let div = &a / &b; - assert_eq!(&div * &b, a.clone()); - let expected_div = &a_ring / &b_ring; - assert_eq!(&(div.to_string())[2..], expected_div.residue().in_radix(16).to_string()); - } - - for n in [&a, &b] { - match n.sqrt() { - Some((fst_sqrt, snd_sqrt)) => { - assert_eq!(fst_sqrt.square(), snd_sqrt.square(), "Squared roots don't match each other"); - assert_eq!(n, &fst_sqrt.square(), "Squared roots don't match original number"); - } - None => {} - }; - } - - // Axioms soundness - - let one = FieldElement::::one(); - let zero = FieldElement::::zero(); - - assert_eq!(&a + &zero, a, "Neutral add element a failed"); - assert_eq!(&b + &zero, b, "Neutral mul element b failed"); - assert_eq!(&a * &one, a, "Neutral add element a failed"); - assert_eq!(&b * &one, b, "Neutral mul element b failed"); - - assert_eq!(&a + &b, &b + &a, "Commutative add property failed"); - assert_eq!(&a * &b, &b * &a, "Commutative mul property failed"); - - let c = &a * &b; - assert_eq!((&a + &b) + &c, &a + (&b + &c), "Associative add property failed"); - assert_eq!((&a * &b) * &c, &a * (&b * &c), "Associative mul property failed"); - - assert_eq!(&a * (&b + &c), &a * &b + &a * &c, "Distributive property failed"); - - assert_eq!(&a - &a, zero, "Inverse add a failed"); - assert_eq!(&b - &b, zero, "Inverse add b failed"); - - if a != zero { - assert_eq!(&a * a.inv().unwrap(), one, "Inverse mul a failed"); - } - if b != zero { - assert_eq!(&b * b.inv().unwrap(), one, "Inverse mul b failed"); - } -}); diff --git a/fuzz/no_gpu_fuzz/fuzz_targets/field/stark252.rs b/fuzz/no_gpu_fuzz/fuzz_targets/field/stark252.rs deleted file mode 100644 index ca180cb0b..000000000 --- a/fuzz/no_gpu_fuzz/fuzz_targets/field/stark252.rs +++ /dev/null @@ -1,91 +0,0 @@ -#![no_main] - -use libfuzzer_sys::fuzz_target; -use lambdaworks_math::field::{ - element::FieldElement, - fields::fft_friendly::stark_252_prime_field::Stark252PrimeField -}; -use ibig::{modular::ModuloRing, UBig}; -use lambdaworks_math::traits::ByteConversion; - -fuzz_target!(|bytes: ([u8;32], [u8;32])| { - - let cairo_prime = - UBig::from_str_radix("800000000000011000000000000000000000000000000000000000000000001", 16).unwrap(); - - let stark252_ring_prime = ModuloRing::new(&cairo_prime); - - let (bytes_a, bytes_b) = bytes; - let a = FieldElement::from_bytes_be(&bytes_a).unwrap(); - let b = FieldElement::from_bytes_be(&bytes_b).unwrap(); - - let a_hex = a.to_string()[2..].to_string(); - let b_hex = b.to_string()[2..].to_string(); - - let a_ring = stark252_ring_prime.from(&UBig::from_str_radix(&a_hex, 16).unwrap()); - let b_ring = stark252_ring_prime.from(&UBig::from_str_radix(&b_hex, 16).unwrap()); - - let add = &a + &b; - let addition = &a_ring + &b_ring; - - assert_eq!(&(add.to_string())[2..], addition.residue().in_radix(16).to_string()); - - let sub = &a - &b; - let substraction = &a_ring - &b_ring; - assert_eq!(&(sub.to_string())[2..], substraction.residue().in_radix(16).to_string()); - - let mul = &a * &b; - let multiplication = &a_ring * &b_ring; - assert_eq!(&(mul.to_string())[2..], multiplication.residue().in_radix(16).to_string()); - - let pow = &a.pow(b.representative()); - let expected_pow = a_ring.pow(&b_ring.residue()); - assert_eq!(&(pow.to_string())[2..], expected_pow.residue().in_radix(16).to_string()); - - if b != FieldElement::zero() { - - let div = &a / &b; - assert_eq!(&div * &b, a.clone()); - let expected_div = &a_ring / &b_ring; - assert_eq!(&(div.to_string())[2..], expected_div.residue().in_radix(16).to_string()); - } - - for n in [&a, &b] { - match n.sqrt() { - Some((fst_sqrt, snd_sqrt)) => { - assert_eq!(fst_sqrt.square(), snd_sqrt.square(), "Squared roots don't match each other"); - assert_eq!(n, &fst_sqrt.square(), "Squared roots don't match original number"); - } - None => {} - }; - } - - // Axioms soundness - - let one = FieldElement::::one(); - let zero = FieldElement::::zero(); - - assert_eq!(&a + &zero, a, "Neutral add element a failed"); - assert_eq!(&b + &zero, b, "Neutral mul element b failed"); - assert_eq!(&a * &one, a, "Neutral add element a failed"); - assert_eq!(&b * &one, b, "Neutral mul element b failed"); - - assert_eq!(&a + &b, &b + &a, "Commutative add property failed"); - assert_eq!(&a * &b, &b * &a, "Commutative mul property failed"); - - let c = &a * &b; - assert_eq!((&a + &b) + &c, &a + (&b + &c), "Associative add property failed"); - assert_eq!((&a * &b) * &c, &a * (&b * &c), "Associative mul property failed"); - - assert_eq!(&a * (&b + &c), &a * &b + &a * &c, "Distributive property failed"); - - assert_eq!(&a - &a, zero, "Inverse add a failed"); - assert_eq!(&b - &b, zero, "Inverse add b failed"); - - if a != zero { - assert_eq!(&a * a.inv().unwrap(), one, "Inverse mul a failed"); - } - if b != zero { - assert_eq!(&b * b.inv().unwrap(), one, "Inverse mul b failed"); - } -}); diff --git a/fuzz/no_gpu_fuzz/fuzz_targets/field/stark_field_addition.rs b/fuzz/no_gpu_fuzz/fuzz_targets/field/stark_field_addition.rs deleted file mode 100644 index f2c35f682..000000000 --- a/fuzz/no_gpu_fuzz/fuzz_targets/field/stark_field_addition.rs +++ /dev/null @@ -1,37 +0,0 @@ -#![no_main] - -use libfuzzer_sys::fuzz_target; -use lambdaworks_math::field::{ - element::FieldElement, - fields::fft_friendly::stark_252_prime_field::Stark252PrimeField, -}; - -use lambdaworks_math::traits::ByteConversion; - -type FE = FieldElement; - -use ibig::{modular::ModuloRing, UBig}; - -fuzz_target!(|bytes: ([u8;32], [u8;32])| { - let (bytes_a, bytes_b) = bytes; - let a = FE::from_bytes_be(&bytes_a).unwrap(); - let b = FE::from_bytes_be(&bytes_b).unwrap(); - - let a_hex = a.representative().to_string()[2..].to_string(); - let b_hex = b.representative().to_string()[2..].to_string(); - - let c = a + &b; - let c_hex = c.representative().to_string()[2..].to_string(); - - let prime = - UBig::from_str_radix("800000000000011000000000000000000000000000000000000000000000001", 16).unwrap(); - let cairo_ring = ModuloRing::new(&prime); - - let a_ring = cairo_ring.from(&UBig::from_str_radix(&a_hex, 16).unwrap()); - let b_ring = cairo_ring.from(&UBig::from_str_radix(&b_hex, 16).unwrap()); - let expected_c = a_ring + &b_ring; - let expected_c_hex = expected_c.residue().in_radix(16).to_string(); - - assert_eq!(expected_c_hex, c_hex); -}); - diff --git a/highlight.css b/highlight.css new file mode 100644 index 000000000..c23432272 --- /dev/null +++ b/highlight.css @@ -0,0 +1,83 @@ +/* + * An increased contrast highlighting scheme loosely based on the + * "Base16 Atelier Dune Light" theme by Bram de Haan + * (http://atelierbram.github.io/syntax-highlighting/atelier-schemes/dune) + * Original Base16 color scheme by Chris Kempson + * (https://github.com/chriskempson/base16) + */ + +/* Comment */ +.hljs-comment, +.hljs-quote { + color: #575757; +} + +/* Red */ +.hljs-variable, +.hljs-template-variable, +.hljs-attribute, +.hljs-tag, +.hljs-name, +.hljs-regexp, +.hljs-link, +.hljs-name, +.hljs-selector-id, +.hljs-selector-class { + color: #d70025; +} + +/* Orange */ +.hljs-number, +.hljs-meta, +.hljs-built_in, +.hljs-builtin-name, +.hljs-literal, +.hljs-type, +.hljs-params { + color: #b21e00; +} + +/* Green */ +.hljs-string, +.hljs-symbol, +.hljs-bullet { + color: #008200; +} + +/* Blue */ +.hljs-title, +.hljs-section { + color: #0030f2; +} + +/* Purple */ +.hljs-keyword, +.hljs-selector-tag { + color: #9d00ec; +} + +.hljs { + display: block; + overflow-x: auto; + background: #f6f7f6; + color: #000; + padding: 0.5em; +} + +.hljs-emphasis { + font-style: italic; +} + +.hljs-strong { + font-weight: bold; +} + +.hljs-addition { + color: #22863a; + background-color: #f0fff4; +} + +.hljs-deletion { + color: #b31d28; + background-color: #ffeef0; +} diff --git a/highlight.js b/highlight.js new file mode 100644 index 000000000..180385b70 --- /dev/null +++ b/highlight.js @@ -0,0 +1,6 @@ +/* + Highlight.js 10.1.1 (93fd0d73) + License: BSD-3-Clause + Copyright (c) 2006-2020, Ivan Sagalaev +*/ +var hljs=function(){"use strict";function e(n){Object.freeze(n);var t="function"==typeof n;return Object.getOwnPropertyNames(n).forEach((function(r){!Object.hasOwnProperty.call(n,r)||null===n[r]||"object"!=typeof n[r]&&"function"!=typeof n[r]||t&&("caller"===r||"callee"===r||"arguments"===r)||Object.isFrozen(n[r])||e(n[r])})),n}class n{constructor(e){void 0===e.data&&(e.data={}),this.data=e.data}ignoreMatch(){this.ignore=!0}}function t(e){return e.replace(/&/g,"&").replace(//g,">").replace(/"/g,""").replace(/'/g,"'")}function r(e,...n){var t={};for(const n in e)t[n]=e[n];return n.forEach((function(e){for(const n in e)t[n]=e[n]})),t}function a(e){return e.nodeName.toLowerCase()}var i=Object.freeze({__proto__:null,escapeHTML:t,inherit:r,nodeStream:function(e){var n=[];return function e(t,r){for(var i=t.firstChild;i;i=i.nextSibling)3===i.nodeType?r+=i.nodeValue.length:1===i.nodeType&&(n.push({event:"start",offset:r,node:i}),r=e(i,r),a(i).match(/br|hr|img|input/)||n.push({event:"stop",offset:r,node:i}));return r}(e,0),n},mergeStreams:function(e,n,r){var i=0,s="",o=[];function l(){return e.length&&n.length?e[0].offset!==n[0].offset?e[0].offset"}function u(e){s+=""}function d(e){("start"===e.event?c:u)(e.node)}for(;e.length||n.length;){var g=l();if(s+=t(r.substring(i,g[0].offset)),i=g[0].offset,g===e){o.reverse().forEach(u);do{d(g.splice(0,1)[0]),g=l()}while(g===e&&g.length&&g[0].offset===i);o.reverse().forEach(c)}else"start"===g[0].event?o.push(g[0].node):o.pop(),d(g.splice(0,1)[0])}return s+t(r.substr(i))}});const s="",o=e=>!!e.kind;class l{constructor(e,n){this.buffer="",this.classPrefix=n.classPrefix,e.walk(this)}addText(e){this.buffer+=t(e)}openNode(e){if(!o(e))return;let n=e.kind;e.sublanguage||(n=`${this.classPrefix}${n}`),this.span(n)}closeNode(e){o(e)&&(this.buffer+=s)}value(){return this.buffer}span(e){this.buffer+=``}}class c{constructor(){this.rootNode={children:[]},this.stack=[this.rootNode]}get top(){return this.stack[this.stack.length-1]}get root(){return this.rootNode}add(e){this.top.children.push(e)}openNode(e){const n={kind:e,children:[]};this.add(n),this.stack.push(n)}closeNode(){if(this.stack.length>1)return this.stack.pop()}closeAllNodes(){for(;this.closeNode(););}toJSON(){return JSON.stringify(this.rootNode,null,4)}walk(e){return this.constructor._walk(e,this.rootNode)}static _walk(e,n){return"string"==typeof n?e.addText(n):n.children&&(e.openNode(n),n.children.forEach(n=>this._walk(e,n)),e.closeNode(n)),e}static _collapse(e){"string"!=typeof e&&e.children&&(e.children.every(e=>"string"==typeof e)?e.children=[e.children.join("")]:e.children.forEach(e=>{c._collapse(e)}))}}class u extends c{constructor(e){super(),this.options=e}addKeyword(e,n){""!==e&&(this.openNode(n),this.addText(e),this.closeNode())}addText(e){""!==e&&this.add(e)}addSublanguage(e,n){const t=e.root;t.kind=n,t.sublanguage=!0,this.add(t)}toHTML(){return new l(this,this.options).value()}finalize(){return!0}}function d(e){return e?"string"==typeof e?e:e.source:null}const g="(-?)(\\b0[xX][a-fA-F0-9]+|(\\b\\d+(\\.\\d*)?|\\.\\d+)([eE][-+]?\\d+)?)",h={begin:"\\\\[\\s\\S]",relevance:0},f={className:"string",begin:"'",end:"'",illegal:"\\n",contains:[h]},p={className:"string",begin:'"',end:'"',illegal:"\\n",contains:[h]},b={begin:/\b(a|an|the|are|I'm|isn't|don't|doesn't|won't|but|just|should|pretty|simply|enough|gonna|going|wtf|so|such|will|you|your|they|like|more)\b/},m=function(e,n,t={}){var a=r({className:"comment",begin:e,end:n,contains:[]},t);return a.contains.push(b),a.contains.push({className:"doctag",begin:"(?:TODO|FIXME|NOTE|BUG|OPTIMIZE|HACK|XXX):",relevance:0}),a},v=m("//","$"),x=m("/\\*","\\*/"),E=m("#","$");var _=Object.freeze({__proto__:null,IDENT_RE:"[a-zA-Z]\\w*",UNDERSCORE_IDENT_RE:"[a-zA-Z_]\\w*",NUMBER_RE:"\\b\\d+(\\.\\d+)?",C_NUMBER_RE:g,BINARY_NUMBER_RE:"\\b(0b[01]+)",RE_STARTERS_RE:"!|!=|!==|%|%=|&|&&|&=|\\*|\\*=|\\+|\\+=|,|-|-=|/=|/|:|;|<<|<<=|<=|<|===|==|=|>>>=|>>=|>=|>>>|>>|>|\\?|\\[|\\{|\\(|\\^|\\^=|\\||\\|=|\\|\\||~",SHEBANG:(e={})=>{const n=/^#![ ]*\//;return e.binary&&(e.begin=function(...e){return e.map(e=>d(e)).join("")}(n,/.*\b/,e.binary,/\b.*/)),r({className:"meta",begin:n,end:/$/,relevance:0,"on:begin":(e,n)=>{0!==e.index&&n.ignoreMatch()}},e)},BACKSLASH_ESCAPE:h,APOS_STRING_MODE:f,QUOTE_STRING_MODE:p,PHRASAL_WORDS_MODE:b,COMMENT:m,C_LINE_COMMENT_MODE:v,C_BLOCK_COMMENT_MODE:x,HASH_COMMENT_MODE:E,NUMBER_MODE:{className:"number",begin:"\\b\\d+(\\.\\d+)?",relevance:0},C_NUMBER_MODE:{className:"number",begin:g,relevance:0},BINARY_NUMBER_MODE:{className:"number",begin:"\\b(0b[01]+)",relevance:0},CSS_NUMBER_MODE:{className:"number",begin:"\\b\\d+(\\.\\d+)?(%|em|ex|ch|rem|vw|vh|vmin|vmax|cm|mm|in|pt|pc|px|deg|grad|rad|turn|s|ms|Hz|kHz|dpi|dpcm|dppx)?",relevance:0},REGEXP_MODE:{begin:/(?=\/[^/\n]*\/)/,contains:[{className:"regexp",begin:/\//,end:/\/[gimuy]*/,illegal:/\n/,contains:[h,{begin:/\[/,end:/\]/,relevance:0,contains:[h]}]}]},TITLE_MODE:{className:"title",begin:"[a-zA-Z]\\w*",relevance:0},UNDERSCORE_TITLE_MODE:{className:"title",begin:"[a-zA-Z_]\\w*",relevance:0},METHOD_GUARD:{begin:"\\.\\s*[a-zA-Z_]\\w*",relevance:0},END_SAME_AS_BEGIN:function(e){return Object.assign(e,{"on:begin":(e,n)=>{n.data._beginMatch=e[1]},"on:end":(e,n)=>{n.data._beginMatch!==e[1]&&n.ignoreMatch()}})}}),N="of and for in not or if then".split(" ");function w(e,n){return n?+n:function(e){return N.includes(e.toLowerCase())}(e)?0:1}const R=t,y=r,{nodeStream:k,mergeStreams:O}=i,M=Symbol("nomatch");return function(t){var a=[],i={},s={},o=[],l=!0,c=/(^(<[^>]+>|\t|)+|\n)/gm,g="Could not find the language '{}', did you forget to load/include a language module?";const h={disableAutodetect:!0,name:"Plain text",contains:[]};var f={noHighlightRe:/^(no-?highlight)$/i,languageDetectRe:/\blang(?:uage)?-([\w-]+)\b/i,classPrefix:"hljs-",tabReplace:null,useBR:!1,languages:null,__emitter:u};function p(e){return f.noHighlightRe.test(e)}function b(e,n,t,r){var a={code:n,language:e};S("before:highlight",a);var i=a.result?a.result:m(a.language,a.code,t,r);return i.code=a.code,S("after:highlight",i),i}function m(e,t,a,s){var o=t;function c(e,n){var t=E.case_insensitive?n[0].toLowerCase():n[0];return Object.prototype.hasOwnProperty.call(e.keywords,t)&&e.keywords[t]}function u(){null!=y.subLanguage?function(){if(""!==A){var e=null;if("string"==typeof y.subLanguage){if(!i[y.subLanguage])return void O.addText(A);e=m(y.subLanguage,A,!0,k[y.subLanguage]),k[y.subLanguage]=e.top}else e=v(A,y.subLanguage.length?y.subLanguage:null);y.relevance>0&&(I+=e.relevance),O.addSublanguage(e.emitter,e.language)}}():function(){if(!y.keywords)return void O.addText(A);let e=0;y.keywordPatternRe.lastIndex=0;let n=y.keywordPatternRe.exec(A),t="";for(;n;){t+=A.substring(e,n.index);const r=c(y,n);if(r){const[e,a]=r;O.addText(t),t="",I+=a,O.addKeyword(n[0],e)}else t+=n[0];e=y.keywordPatternRe.lastIndex,n=y.keywordPatternRe.exec(A)}t+=A.substr(e),O.addText(t)}(),A=""}function h(e){return e.className&&O.openNode(e.className),y=Object.create(e,{parent:{value:y}})}function p(e){return 0===y.matcher.regexIndex?(A+=e[0],1):(L=!0,0)}var b={};function x(t,r){var i=r&&r[0];if(A+=t,null==i)return u(),0;if("begin"===b.type&&"end"===r.type&&b.index===r.index&&""===i){if(A+=o.slice(r.index,r.index+1),!l){const n=Error("0 width match regex");throw n.languageName=e,n.badRule=b.rule,n}return 1}if(b=r,"begin"===r.type)return function(e){var t=e[0],r=e.rule;const a=new n(r),i=[r.__beforeBegin,r["on:begin"]];for(const n of i)if(n&&(n(e,a),a.ignore))return p(t);return r&&r.endSameAsBegin&&(r.endRe=RegExp(t.replace(/[-/\\^$*+?.()|[\]{}]/g,"\\$&"),"m")),r.skip?A+=t:(r.excludeBegin&&(A+=t),u(),r.returnBegin||r.excludeBegin||(A=t)),h(r),r.returnBegin?0:t.length}(r);if("illegal"===r.type&&!a){const e=Error('Illegal lexeme "'+i+'" for mode "'+(y.className||"")+'"');throw e.mode=y,e}if("end"===r.type){var s=function(e){var t=e[0],r=o.substr(e.index),a=function e(t,r,a){let i=function(e,n){var t=e&&e.exec(n);return t&&0===t.index}(t.endRe,a);if(i){if(t["on:end"]){const e=new n(t);t["on:end"](r,e),e.ignore&&(i=!1)}if(i){for(;t.endsParent&&t.parent;)t=t.parent;return t}}if(t.endsWithParent)return e(t.parent,r,a)}(y,e,r);if(!a)return M;var i=y;i.skip?A+=t:(i.returnEnd||i.excludeEnd||(A+=t),u(),i.excludeEnd&&(A=t));do{y.className&&O.closeNode(),y.skip||y.subLanguage||(I+=y.relevance),y=y.parent}while(y!==a.parent);return a.starts&&(a.endSameAsBegin&&(a.starts.endRe=a.endRe),h(a.starts)),i.returnEnd?0:t.length}(r);if(s!==M)return s}if("illegal"===r.type&&""===i)return 1;if(B>1e5&&B>3*r.index)throw Error("potential infinite loop, way more iterations than matches");return A+=i,i.length}var E=T(e);if(!E)throw console.error(g.replace("{}",e)),Error('Unknown language: "'+e+'"');var _=function(e){function n(n,t){return RegExp(d(n),"m"+(e.case_insensitive?"i":"")+(t?"g":""))}class t{constructor(){this.matchIndexes={},this.regexes=[],this.matchAt=1,this.position=0}addRule(e,n){n.position=this.position++,this.matchIndexes[this.matchAt]=n,this.regexes.push([n,e]),this.matchAt+=function(e){return RegExp(e.toString()+"|").exec("").length-1}(e)+1}compile(){0===this.regexes.length&&(this.exec=()=>null);const e=this.regexes.map(e=>e[1]);this.matcherRe=n(function(e,n="|"){for(var t=/\[(?:[^\\\]]|\\.)*\]|\(\??|\\([1-9][0-9]*)|\\./,r=0,a="",i=0;i0&&(a+=n),a+="(";o.length>0;){var l=t.exec(o);if(null==l){a+=o;break}a+=o.substring(0,l.index),o=o.substring(l.index+l[0].length),"\\"===l[0][0]&&l[1]?a+="\\"+(+l[1]+s):(a+=l[0],"("===l[0]&&r++)}a+=")"}return a}(e),!0),this.lastIndex=0}exec(e){this.matcherRe.lastIndex=this.lastIndex;const n=this.matcherRe.exec(e);if(!n)return null;const t=n.findIndex((e,n)=>n>0&&void 0!==e),r=this.matchIndexes[t];return n.splice(0,t),Object.assign(n,r)}}class a{constructor(){this.rules=[],this.multiRegexes=[],this.count=0,this.lastIndex=0,this.regexIndex=0}getMatcher(e){if(this.multiRegexes[e])return this.multiRegexes[e];const n=new t;return this.rules.slice(e).forEach(([e,t])=>n.addRule(e,t)),n.compile(),this.multiRegexes[e]=n,n}considerAll(){this.regexIndex=0}addRule(e,n){this.rules.push([e,n]),"begin"===n.type&&this.count++}exec(e){const n=this.getMatcher(this.regexIndex);n.lastIndex=this.lastIndex;const t=n.exec(e);return t&&(this.regexIndex+=t.position+1,this.regexIndex===this.count&&(this.regexIndex=0)),t}}function i(e,n){const t=e.input[e.index-1],r=e.input[e.index+e[0].length];"."!==t&&"."!==r||n.ignoreMatch()}if(e.contains&&e.contains.includes("self"))throw Error("ERR: contains `self` is not supported at the top-level of a language. See documentation.");return function t(s,o){const l=s;if(s.compiled)return l;s.compiled=!0,s.__beforeBegin=null,s.keywords=s.keywords||s.beginKeywords;let c=null;if("object"==typeof s.keywords&&(c=s.keywords.$pattern,delete s.keywords.$pattern),s.keywords&&(s.keywords=function(e,n){var t={};return"string"==typeof e?r("keyword",e):Object.keys(e).forEach((function(n){r(n,e[n])})),t;function r(e,r){n&&(r=r.toLowerCase()),r.split(" ").forEach((function(n){var r=n.split("|");t[r[0]]=[e,w(r[0],r[1])]}))}}(s.keywords,e.case_insensitive)),s.lexemes&&c)throw Error("ERR: Prefer `keywords.$pattern` to `mode.lexemes`, BOTH are not allowed. (see mode reference) ");return l.keywordPatternRe=n(s.lexemes||c||/\w+/,!0),o&&(s.beginKeywords&&(s.begin="\\b("+s.beginKeywords.split(" ").join("|")+")(?=\\b|\\s)",s.__beforeBegin=i),s.begin||(s.begin=/\B|\b/),l.beginRe=n(s.begin),s.endSameAsBegin&&(s.end=s.begin),s.end||s.endsWithParent||(s.end=/\B|\b/),s.end&&(l.endRe=n(s.end)),l.terminator_end=d(s.end)||"",s.endsWithParent&&o.terminator_end&&(l.terminator_end+=(s.end?"|":"")+o.terminator_end)),s.illegal&&(l.illegalRe=n(s.illegal)),void 0===s.relevance&&(s.relevance=1),s.contains||(s.contains=[]),s.contains=[].concat(...s.contains.map((function(e){return function(e){return e.variants&&!e.cached_variants&&(e.cached_variants=e.variants.map((function(n){return r(e,{variants:null},n)}))),e.cached_variants?e.cached_variants:function e(n){return!!n&&(n.endsWithParent||e(n.starts))}(e)?r(e,{starts:e.starts?r(e.starts):null}):Object.isFrozen(e)?r(e):e}("self"===e?s:e)}))),s.contains.forEach((function(e){t(e,l)})),s.starts&&t(s.starts,o),l.matcher=function(e){const n=new a;return e.contains.forEach(e=>n.addRule(e.begin,{rule:e,type:"begin"})),e.terminator_end&&n.addRule(e.terminator_end,{type:"end"}),e.illegal&&n.addRule(e.illegal,{type:"illegal"}),n}(l),l}(e)}(E),N="",y=s||_,k={},O=new f.__emitter(f);!function(){for(var e=[],n=y;n!==E;n=n.parent)n.className&&e.unshift(n.className);e.forEach(e=>O.openNode(e))}();var A="",I=0,S=0,B=0,L=!1;try{for(y.matcher.considerAll();;){B++,L?L=!1:(y.matcher.lastIndex=S,y.matcher.considerAll());const e=y.matcher.exec(o);if(!e)break;const n=x(o.substring(S,e.index),e);S=e.index+n}return x(o.substr(S)),O.closeAllNodes(),O.finalize(),N=O.toHTML(),{relevance:I,value:N,language:e,illegal:!1,emitter:O,top:y}}catch(n){if(n.message&&n.message.includes("Illegal"))return{illegal:!0,illegalBy:{msg:n.message,context:o.slice(S-100,S+100),mode:n.mode},sofar:N,relevance:0,value:R(o),emitter:O};if(l)return{illegal:!1,relevance:0,value:R(o),emitter:O,language:e,top:y,errorRaised:n};throw n}}function v(e,n){n=n||f.languages||Object.keys(i);var t=function(e){const n={relevance:0,emitter:new f.__emitter(f),value:R(e),illegal:!1,top:h};return n.emitter.addText(e),n}(e),r=t;return n.filter(T).filter(I).forEach((function(n){var a=m(n,e,!1);a.language=n,a.relevance>r.relevance&&(r=a),a.relevance>t.relevance&&(r=t,t=a)})),r.language&&(t.second_best=r),t}function x(e){return f.tabReplace||f.useBR?e.replace(c,e=>"\n"===e?f.useBR?"
":e:f.tabReplace?e.replace(/\t/g,f.tabReplace):e):e}function E(e){let n=null;const t=function(e){var n=e.className+" ";n+=e.parentNode?e.parentNode.className:"";const t=f.languageDetectRe.exec(n);if(t){var r=T(t[1]);return r||(console.warn(g.replace("{}",t[1])),console.warn("Falling back to no-highlight mode for this block.",e)),r?t[1]:"no-highlight"}return n.split(/\s+/).find(e=>p(e)||T(e))}(e);if(p(t))return;S("before:highlightBlock",{block:e,language:t}),f.useBR?(n=document.createElement("div")).innerHTML=e.innerHTML.replace(/\n/g,"").replace(//g,"\n"):n=e;const r=n.textContent,a=t?b(t,r,!0):v(r),i=k(n);if(i.length){const e=document.createElement("div");e.innerHTML=a.value,a.value=O(i,k(e),r)}a.value=x(a.value),S("after:highlightBlock",{block:e,result:a}),e.innerHTML=a.value,e.className=function(e,n,t){var r=n?s[n]:t,a=[e.trim()];return e.match(/\bhljs\b/)||a.push("hljs"),e.includes(r)||a.push(r),a.join(" ").trim()}(e.className,t,a.language),e.result={language:a.language,re:a.relevance,relavance:a.relevance},a.second_best&&(e.second_best={language:a.second_best.language,re:a.second_best.relevance,relavance:a.second_best.relevance})}const N=()=>{if(!N.called){N.called=!0;var e=document.querySelectorAll("pre code");a.forEach.call(e,E)}};function T(e){return e=(e||"").toLowerCase(),i[e]||i[s[e]]}function A(e,{languageName:n}){"string"==typeof e&&(e=[e]),e.forEach(e=>{s[e]=n})}function I(e){var n=T(e);return n&&!n.disableAutodetect}function S(e,n){var t=e;o.forEach((function(e){e[t]&&e[t](n)}))}Object.assign(t,{highlight:b,highlightAuto:v,fixMarkup:x,highlightBlock:E,configure:function(e){f=y(f,e)},initHighlighting:N,initHighlightingOnLoad:function(){window.addEventListener("DOMContentLoaded",N,!1)},registerLanguage:function(e,n){var r=null;try{r=n(t)}catch(n){if(console.error("Language definition for '{}' could not be registered.".replace("{}",e)),!l)throw n;console.error(n),r=h}r.name||(r.name=e),i[e]=r,r.rawDefinition=n.bind(null,t),r.aliases&&A(r.aliases,{languageName:e})},listLanguages:function(){return Object.keys(i)},getLanguage:T,registerAliases:A,requireLanguage:function(e){var n=T(e);if(n)return n;throw Error("The '{}' language is required, but not loaded.".replace("{}",e))},autoDetection:I,inherit:y,addPlugin:function(e){o.push(e)}}),t.debugMode=function(){l=!1},t.safeMode=function(){l=!0},t.versionString="10.1.1";for(const n in _)"object"==typeof _[n]&&e(_[n]);return Object.assign(t,_),t}({})}();"object"==typeof exports&&"undefined"!=typeof module&&(module.exports=hljs);hljs.registerLanguage("php",function(){"use strict";return function(e){var r={begin:"\\$+[a-zA-Z_-ÿ][a-zA-Z0-9_-ÿ]*"},t={className:"meta",variants:[{begin:/<\?php/,relevance:10},{begin:/<\?[=]?/},{begin:/\?>/}]},a={className:"string",contains:[e.BACKSLASH_ESCAPE,t],variants:[{begin:'b"',end:'"'},{begin:"b'",end:"'"},e.inherit(e.APOS_STRING_MODE,{illegal:null}),e.inherit(e.QUOTE_STRING_MODE,{illegal:null})]},n={variants:[e.BINARY_NUMBER_MODE,e.C_NUMBER_MODE]},i={keyword:"__CLASS__ __DIR__ __FILE__ __FUNCTION__ __LINE__ __METHOD__ __NAMESPACE__ __TRAIT__ die echo exit include include_once print require require_once array abstract and as binary bool boolean break callable case catch class clone const continue declare default do double else elseif empty enddeclare endfor endforeach endif endswitch endwhile eval extends final finally float for foreach from global goto if implements instanceof insteadof int integer interface isset iterable list new object or private protected public real return string switch throw trait try unset use var void while xor yield",literal:"false null true",built_in:"Error|0 AppendIterator ArgumentCountError ArithmeticError ArrayIterator ArrayObject AssertionError BadFunctionCallException BadMethodCallException CachingIterator CallbackFilterIterator CompileError Countable DirectoryIterator DivisionByZeroError DomainException EmptyIterator ErrorException Exception FilesystemIterator FilterIterator GlobIterator InfiniteIterator InvalidArgumentException IteratorIterator LengthException LimitIterator LogicException MultipleIterator NoRewindIterator OutOfBoundsException OutOfRangeException OuterIterator OverflowException ParentIterator ParseError RangeException RecursiveArrayIterator RecursiveCachingIterator RecursiveCallbackFilterIterator RecursiveDirectoryIterator RecursiveFilterIterator RecursiveIterator RecursiveIteratorIterator RecursiveRegexIterator RecursiveTreeIterator RegexIterator RuntimeException SeekableIterator SplDoublyLinkedList SplFileInfo SplFileObject SplFixedArray SplHeap SplMaxHeap SplMinHeap SplObjectStorage SplObserver SplObserver SplPriorityQueue SplQueue SplStack SplSubject SplSubject SplTempFileObject TypeError UnderflowException UnexpectedValueException ArrayAccess Closure Generator Iterator IteratorAggregate Serializable Throwable Traversable WeakReference Directory __PHP_Incomplete_Class parent php_user_filter self static stdClass"};return{aliases:["php","php3","php4","php5","php6","php7"],case_insensitive:!0,keywords:i,contains:[e.HASH_COMMENT_MODE,e.COMMENT("//","$",{contains:[t]}),e.COMMENT("/\\*","\\*/",{contains:[{className:"doctag",begin:"@[A-Za-z]+"}]}),e.COMMENT("__halt_compiler.+?;",!1,{endsWithParent:!0,keywords:"__halt_compiler"}),{className:"string",begin:/<<<['"]?\w+['"]?$/,end:/^\w+;?$/,contains:[e.BACKSLASH_ESCAPE,{className:"subst",variants:[{begin:/\$\w+/},{begin:/\{\$/,end:/\}/}]}]},t,{className:"keyword",begin:/\$this\b/},r,{begin:/(::|->)+[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*/},{className:"function",beginKeywords:"fn function",end:/[;{]/,excludeEnd:!0,illegal:"[$%\\[]",contains:[e.UNDERSCORE_TITLE_MODE,{className:"params",begin:"\\(",end:"\\)",excludeBegin:!0,excludeEnd:!0,keywords:i,contains:["self",r,e.C_BLOCK_COMMENT_MODE,a,n]}]},{className:"class",beginKeywords:"class interface",end:"{",excludeEnd:!0,illegal:/[:\(\$"]/,contains:[{beginKeywords:"extends implements"},e.UNDERSCORE_TITLE_MODE]},{beginKeywords:"namespace",end:";",illegal:/[\.']/,contains:[e.UNDERSCORE_TITLE_MODE]},{beginKeywords:"use",end:";",contains:[e.UNDERSCORE_TITLE_MODE]},{begin:"=>"},a,n]}}}());hljs.registerLanguage("nginx",function(){"use strict";return function(e){var n={className:"variable",variants:[{begin:/\$\d+/},{begin:/\$\{/,end:/}/},{begin:"[\\$\\@]"+e.UNDERSCORE_IDENT_RE}]},a={endsWithParent:!0,keywords:{$pattern:"[a-z/_]+",literal:"on off yes no true false none blocked debug info notice warn error crit select break last permanent redirect kqueue rtsig epoll poll /dev/poll"},relevance:0,illegal:"=>",contains:[e.HASH_COMMENT_MODE,{className:"string",contains:[e.BACKSLASH_ESCAPE,n],variants:[{begin:/"/,end:/"/},{begin:/'/,end:/'/}]},{begin:"([a-z]+):/",end:"\\s",endsWithParent:!0,excludeEnd:!0,contains:[n]},{className:"regexp",contains:[e.BACKSLASH_ESCAPE,n],variants:[{begin:"\\s\\^",end:"\\s|{|;",returnEnd:!0},{begin:"~\\*?\\s+",end:"\\s|{|;",returnEnd:!0},{begin:"\\*(\\.[a-z\\-]+)+"},{begin:"([a-z\\-]+\\.)+\\*"}]},{className:"number",begin:"\\b\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}(:\\d{1,5})?\\b"},{className:"number",begin:"\\b\\d+[kKmMgGdshdwy]*\\b",relevance:0},n]};return{name:"Nginx config",aliases:["nginxconf"],contains:[e.HASH_COMMENT_MODE,{begin:e.UNDERSCORE_IDENT_RE+"\\s+{",returnBegin:!0,end:"{",contains:[{className:"section",begin:e.UNDERSCORE_IDENT_RE}],relevance:0},{begin:e.UNDERSCORE_IDENT_RE+"\\s",end:";|{",returnBegin:!0,contains:[{className:"attribute",begin:e.UNDERSCORE_IDENT_RE,starts:a}],relevance:0}],illegal:"[^\\s\\}]"}}}());hljs.registerLanguage("csharp",function(){"use strict";return function(e){var n={keyword:"abstract as base bool break byte case catch char checked const continue decimal default delegate do double enum event explicit extern finally fixed float for foreach goto if implicit in int interface internal is lock long object operator out override params private protected public readonly ref sbyte sealed short sizeof stackalloc static string struct switch this try typeof uint ulong unchecked unsafe ushort using virtual void volatile while add alias ascending async await by descending dynamic equals from get global group into join let nameof on orderby partial remove select set value var when where yield",literal:"null false true"},i=e.inherit(e.TITLE_MODE,{begin:"[a-zA-Z](\\.?\\w)*"}),a={className:"number",variants:[{begin:"\\b(0b[01']+)"},{begin:"(-?)\\b([\\d']+(\\.[\\d']*)?|\\.[\\d']+)(u|U|l|L|ul|UL|f|F|b|B)"},{begin:"(-?)(\\b0[xX][a-fA-F0-9']+|(\\b[\\d']+(\\.[\\d']*)?|\\.[\\d']+)([eE][-+]?[\\d']+)?)"}],relevance:0},s={className:"string",begin:'@"',end:'"',contains:[{begin:'""'}]},t=e.inherit(s,{illegal:/\n/}),l={className:"subst",begin:"{",end:"}",keywords:n},r=e.inherit(l,{illegal:/\n/}),c={className:"string",begin:/\$"/,end:'"',illegal:/\n/,contains:[{begin:"{{"},{begin:"}}"},e.BACKSLASH_ESCAPE,r]},o={className:"string",begin:/\$@"/,end:'"',contains:[{begin:"{{"},{begin:"}}"},{begin:'""'},l]},g=e.inherit(o,{illegal:/\n/,contains:[{begin:"{{"},{begin:"}}"},{begin:'""'},r]});l.contains=[o,c,s,e.APOS_STRING_MODE,e.QUOTE_STRING_MODE,a,e.C_BLOCK_COMMENT_MODE],r.contains=[g,c,t,e.APOS_STRING_MODE,e.QUOTE_STRING_MODE,a,e.inherit(e.C_BLOCK_COMMENT_MODE,{illegal:/\n/})];var d={variants:[o,c,s,e.APOS_STRING_MODE,e.QUOTE_STRING_MODE]},E={begin:"<",end:">",contains:[{beginKeywords:"in out"},i]},_=e.IDENT_RE+"(<"+e.IDENT_RE+"(\\s*,\\s*"+e.IDENT_RE+")*>)?(\\[\\])?",b={begin:"@"+e.IDENT_RE,relevance:0};return{name:"C#",aliases:["cs","c#"],keywords:n,illegal:/::/,contains:[e.COMMENT("///","$",{returnBegin:!0,contains:[{className:"doctag",variants:[{begin:"///",relevance:0},{begin:"\x3c!--|--\x3e"},{begin:""}]}]}),e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,{className:"meta",begin:"#",end:"$",keywords:{"meta-keyword":"if else elif endif define undef warning error line region endregion pragma checksum"}},d,a,{beginKeywords:"class interface",end:/[{;=]/,illegal:/[^\s:,]/,contains:[{beginKeywords:"where class"},i,E,e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE]},{beginKeywords:"namespace",end:/[{;=]/,illegal:/[^\s:]/,contains:[i,e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE]},{className:"meta",begin:"^\\s*\\[",excludeBegin:!0,end:"\\]",excludeEnd:!0,contains:[{className:"meta-string",begin:/"/,end:/"/}]},{beginKeywords:"new return throw await else",relevance:0},{className:"function",begin:"("+_+"\\s+)+"+e.IDENT_RE+"\\s*(\\<.+\\>)?\\s*\\(",returnBegin:!0,end:/\s*[{;=]/,excludeEnd:!0,keywords:n,contains:[{begin:e.IDENT_RE+"\\s*(\\<.+\\>)?\\s*\\(",returnBegin:!0,contains:[e.TITLE_MODE,E],relevance:0},{className:"params",begin:/\(/,end:/\)/,excludeBegin:!0,excludeEnd:!0,keywords:n,relevance:0,contains:[d,a,e.C_BLOCK_COMMENT_MODE]},e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE]},b]}}}());hljs.registerLanguage("perl",function(){"use strict";return function(e){var n={$pattern:/[\w.]+/,keyword:"getpwent getservent quotemeta msgrcv scalar kill dbmclose undef lc ma syswrite tr send umask sysopen shmwrite vec qx utime local oct semctl localtime readpipe do return format read sprintf dbmopen pop getpgrp not getpwnam rewinddir qq fileno qw endprotoent wait sethostent bless s|0 opendir continue each sleep endgrent shutdown dump chomp connect getsockname die socketpair close flock exists index shmget sub for endpwent redo lstat msgctl setpgrp abs exit select print ref gethostbyaddr unshift fcntl syscall goto getnetbyaddr join gmtime symlink semget splice x|0 getpeername recv log setsockopt cos last reverse gethostbyname getgrnam study formline endhostent times chop length gethostent getnetent pack getprotoent getservbyname rand mkdir pos chmod y|0 substr endnetent printf next open msgsnd readdir use unlink getsockopt getpriority rindex wantarray hex system getservbyport endservent int chr untie rmdir prototype tell listen fork shmread ucfirst setprotoent else sysseek link getgrgid shmctl waitpid unpack getnetbyname reset chdir grep split require caller lcfirst until warn while values shift telldir getpwuid my getprotobynumber delete and sort uc defined srand accept package seekdir getprotobyname semop our rename seek if q|0 chroot sysread setpwent no crypt getc chown sqrt write setnetent setpriority foreach tie sin msgget map stat getlogin unless elsif truncate exec keys glob tied closedir ioctl socket readlink eval xor readline binmode setservent eof ord bind alarm pipe atan2 getgrent exp time push setgrent gt lt or ne m|0 break given say state when"},t={className:"subst",begin:"[$@]\\{",end:"\\}",keywords:n},s={begin:"->{",end:"}"},r={variants:[{begin:/\$\d/},{begin:/[\$%@](\^\w\b|#\w+(::\w+)*|{\w+}|\w+(::\w*)*)/},{begin:/[\$%@][^\s\w{]/,relevance:0}]},i=[e.BACKSLASH_ESCAPE,t,r],a=[r,e.HASH_COMMENT_MODE,e.COMMENT("^\\=\\w","\\=cut",{endsWithParent:!0}),s,{className:"string",contains:i,variants:[{begin:"q[qwxr]?\\s*\\(",end:"\\)",relevance:5},{begin:"q[qwxr]?\\s*\\[",end:"\\]",relevance:5},{begin:"q[qwxr]?\\s*\\{",end:"\\}",relevance:5},{begin:"q[qwxr]?\\s*\\|",end:"\\|",relevance:5},{begin:"q[qwxr]?\\s*\\<",end:"\\>",relevance:5},{begin:"qw\\s+q",end:"q",relevance:5},{begin:"'",end:"'",contains:[e.BACKSLASH_ESCAPE]},{begin:'"',end:'"'},{begin:"`",end:"`",contains:[e.BACKSLASH_ESCAPE]},{begin:"{\\w+}",contains:[],relevance:0},{begin:"-?\\w+\\s*\\=\\>",contains:[],relevance:0}]},{className:"number",begin:"(\\b0[0-7_]+)|(\\b0x[0-9a-fA-F_]+)|(\\b[1-9][0-9_]*(\\.[0-9_]+)?)|[0_]\\b",relevance:0},{begin:"(\\/\\/|"+e.RE_STARTERS_RE+"|\\b(split|return|print|reverse|grep)\\b)\\s*",keywords:"split return print reverse grep",relevance:0,contains:[e.HASH_COMMENT_MODE,{className:"regexp",begin:"(s|tr|y)/(\\\\.|[^/])*/(\\\\.|[^/])*/[a-z]*",relevance:10},{className:"regexp",begin:"(m|qr)?/",end:"/[a-z]*",contains:[e.BACKSLASH_ESCAPE],relevance:0}]},{className:"function",beginKeywords:"sub",end:"(\\s*\\(.*?\\))?[;{]",excludeEnd:!0,relevance:5,contains:[e.TITLE_MODE]},{begin:"-\\w\\b",relevance:0},{begin:"^__DATA__$",end:"^__END__$",subLanguage:"mojolicious",contains:[{begin:"^@@.*",end:"$",className:"comment"}]}];return t.contains=a,s.contains=a,{name:"Perl",aliases:["pl","pm"],keywords:n,contains:a}}}());hljs.registerLanguage("swift",function(){"use strict";return function(e){var i={keyword:"#available #colorLiteral #column #else #elseif #endif #file #fileLiteral #function #if #imageLiteral #line #selector #sourceLocation _ __COLUMN__ __FILE__ __FUNCTION__ __LINE__ Any as as! as? associatedtype associativity break case catch class continue convenience default defer deinit didSet do dynamic dynamicType else enum extension fallthrough false fileprivate final for func get guard if import in indirect infix init inout internal is lazy left let mutating nil none nonmutating open operator optional override postfix precedence prefix private protocol Protocol public repeat required rethrows return right self Self set static struct subscript super switch throw throws true try try! try? Type typealias unowned var weak where while willSet",literal:"true false nil",built_in:"abs advance alignof alignofValue anyGenerator assert assertionFailure bridgeFromObjectiveC bridgeFromObjectiveCUnconditional bridgeToObjectiveC bridgeToObjectiveCUnconditional c compactMap contains count countElements countLeadingZeros debugPrint debugPrintln distance dropFirst dropLast dump encodeBitsAsWords enumerate equal fatalError filter find getBridgedObjectiveCType getVaList indices insertionSort isBridgedToObjectiveC isBridgedVerbatimToObjectiveC isUniquelyReferenced isUniquelyReferencedNonObjC join lazy lexicographicalCompare map max maxElement min minElement numericCast overlaps partition posix precondition preconditionFailure print println quickSort readLine reduce reflect reinterpretCast reverse roundUpToAlignment sizeof sizeofValue sort split startsWith stride strideof strideofValue swap toString transcode underestimateCount unsafeAddressOf unsafeBitCast unsafeDowncast unsafeUnwrap unsafeReflect withExtendedLifetime withObjectAtPlusZero withUnsafePointer withUnsafePointerToObject withUnsafeMutablePointer withUnsafeMutablePointers withUnsafePointer withUnsafePointers withVaList zip"},n=e.COMMENT("/\\*","\\*/",{contains:["self"]}),t={className:"subst",begin:/\\\(/,end:"\\)",keywords:i,contains:[]},a={className:"string",contains:[e.BACKSLASH_ESCAPE,t],variants:[{begin:/"""/,end:/"""/},{begin:/"/,end:/"/}]},r={className:"number",begin:"\\b([\\d_]+(\\.[\\deE_]+)?|0x[a-fA-F0-9_]+(\\.[a-fA-F0-9p_]+)?|0b[01_]+|0o[0-7_]+)\\b",relevance:0};return t.contains=[r],{name:"Swift",keywords:i,contains:[a,e.C_LINE_COMMENT_MODE,n,{className:"type",begin:"\\b[A-Z][\\wÀ-ʸ']*[!?]"},{className:"type",begin:"\\b[A-Z][\\wÀ-ʸ']*",relevance:0},r,{className:"function",beginKeywords:"func",end:"{",excludeEnd:!0,contains:[e.inherit(e.TITLE_MODE,{begin:/[A-Za-z$_][0-9A-Za-z$_]*/}),{begin://},{className:"params",begin:/\(/,end:/\)/,endsParent:!0,keywords:i,contains:["self",r,a,e.C_BLOCK_COMMENT_MODE,{begin:":"}],illegal:/["']/}],illegal:/\[|%/},{className:"class",beginKeywords:"struct protocol class extension enum",keywords:i,end:"\\{",excludeEnd:!0,contains:[e.inherit(e.TITLE_MODE,{begin:/[A-Za-z$_][\u00C0-\u02B80-9A-Za-z$_]*/})]},{className:"meta",begin:"(@discardableResult|@warn_unused_result|@exported|@lazy|@noescape|@NSCopying|@NSManaged|@objc|@objcMembers|@convention|@required|@noreturn|@IBAction|@IBDesignable|@IBInspectable|@IBOutlet|@infix|@prefix|@postfix|@autoclosure|@testable|@available|@nonobjc|@NSApplicationMain|@UIApplicationMain|@dynamicMemberLookup|@propertyWrapper)\\b"},{beginKeywords:"import",end:/$/,contains:[e.C_LINE_COMMENT_MODE,n]}]}}}());hljs.registerLanguage("makefile",function(){"use strict";return function(e){var i={className:"variable",variants:[{begin:"\\$\\("+e.UNDERSCORE_IDENT_RE+"\\)",contains:[e.BACKSLASH_ESCAPE]},{begin:/\$[@%`]+/}]}]}]};return{name:"HTML, XML",aliases:["html","xhtml","rss","atom","xjb","xsd","xsl","plist","wsf","svg"],case_insensitive:!0,contains:[{className:"meta",begin:"",relevance:10,contains:[a,i,t,s,{begin:"\\[",end:"\\]",contains:[{className:"meta",begin:"",contains:[a,s,i,t]}]}]},e.COMMENT("\x3c!--","--\x3e",{relevance:10}),{begin:"<\\!\\[CDATA\\[",end:"\\]\\]>",relevance:10},n,{className:"meta",begin:/<\?xml/,end:/\?>/,relevance:10},{className:"tag",begin:")",end:">",keywords:{name:"style"},contains:[c],starts:{end:"",returnEnd:!0,subLanguage:["css","xml"]}},{className:"tag",begin:")",end:">",keywords:{name:"script"},contains:[c],starts:{end:"<\/script>",returnEnd:!0,subLanguage:["javascript","handlebars","xml"]}},{className:"tag",begin:"",contains:[{className:"name",begin:/[^\/><\s]+/,relevance:0},c]}]}}}());hljs.registerLanguage("bash",function(){"use strict";return function(e){const s={};Object.assign(s,{className:"variable",variants:[{begin:/\$[\w\d#@][\w\d_]*/},{begin:/\$\{/,end:/\}/,contains:[{begin:/:-/,contains:[s]}]}]});const t={className:"subst",begin:/\$\(/,end:/\)/,contains:[e.BACKSLASH_ESCAPE]},n={className:"string",begin:/"/,end:/"/,contains:[e.BACKSLASH_ESCAPE,s,t]};t.contains.push(n);const a={begin:/\$\(\(/,end:/\)\)/,contains:[{begin:/\d+#[0-9a-f]+/,className:"number"},e.NUMBER_MODE,s]},i=e.SHEBANG({binary:"(fish|bash|zsh|sh|csh|ksh|tcsh|dash|scsh)",relevance:10}),c={className:"function",begin:/\w[\w\d_]*\s*\(\s*\)\s*\{/,returnBegin:!0,contains:[e.inherit(e.TITLE_MODE,{begin:/\w[\w\d_]*/})],relevance:0};return{name:"Bash",aliases:["sh","zsh"],keywords:{$pattern:/\b-?[a-z\._]+\b/,keyword:"if then else elif fi for while in do done case esac function",literal:"true false",built_in:"break cd continue eval exec exit export getopts hash pwd readonly return shift test times trap umask unset alias bind builtin caller command declare echo enable help let local logout mapfile printf read readarray source type typeset ulimit unalias set shopt autoload bg bindkey bye cap chdir clone comparguments compcall compctl compdescribe compfiles compgroups compquote comptags comptry compvalues dirs disable disown echotc echoti emulate fc fg float functions getcap getln history integer jobs kill limit log noglob popd print pushd pushln rehash sched setcap setopt stat suspend ttyctl unfunction unhash unlimit unsetopt vared wait whence where which zcompile zformat zftp zle zmodload zparseopts zprof zpty zregexparse zsocket zstyle ztcp",_:"-ne -eq -lt -gt -f -d -e -s -l -a"},contains:[i,e.SHEBANG(),c,a,e.HASH_COMMENT_MODE,n,{className:"",begin:/\\"/},{className:"string",begin:/'/,end:/'/},s]}}}());hljs.registerLanguage("c-like",function(){"use strict";return function(e){function t(e){return"(?:"+e+")?"}var n="(decltype\\(auto\\)|"+t("[a-zA-Z_]\\w*::")+"[a-zA-Z_]\\w*"+t("<.*?>")+")",r={className:"keyword",begin:"\\b[a-z\\d_]*_t\\b"},a={className:"string",variants:[{begin:'(u8?|U|L)?"',end:'"',illegal:"\\n",contains:[e.BACKSLASH_ESCAPE]},{begin:"(u8?|U|L)?'(\\\\(x[0-9A-Fa-f]{2}|u[0-9A-Fa-f]{4,8}|[0-7]{3}|\\S)|.)",end:"'",illegal:"."},e.END_SAME_AS_BEGIN({begin:/(?:u8?|U|L)?R"([^()\\ ]{0,16})\(/,end:/\)([^()\\ ]{0,16})"/})]},i={className:"number",variants:[{begin:"\\b(0b[01']+)"},{begin:"(-?)\\b([\\d']+(\\.[\\d']*)?|\\.[\\d']+)(u|U|l|L|ul|UL|f|F|b|B)"},{begin:"(-?)(\\b0[xX][a-fA-F0-9']+|(\\b[\\d']+(\\.[\\d']*)?|\\.[\\d']+)([eE][-+]?[\\d']+)?)"}],relevance:0},s={className:"meta",begin:/#\s*[a-z]+\b/,end:/$/,keywords:{"meta-keyword":"if else elif endif define undef warning error line pragma _Pragma ifdef ifndef include"},contains:[{begin:/\\\n/,relevance:0},e.inherit(a,{className:"meta-string"}),{className:"meta-string",begin:/<.*?>/,end:/$/,illegal:"\\n"},e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE]},o={className:"title",begin:t("[a-zA-Z_]\\w*::")+e.IDENT_RE,relevance:0},c=t("[a-zA-Z_]\\w*::")+e.IDENT_RE+"\\s*\\(",l={keyword:"int float while private char char8_t char16_t char32_t catch import module export virtual operator sizeof dynamic_cast|10 typedef const_cast|10 const for static_cast|10 union namespace unsigned long volatile static protected bool template mutable if public friend do goto auto void enum else break extern using asm case typeid wchar_t short reinterpret_cast|10 default double register explicit signed typename try this switch continue inline delete alignas alignof constexpr consteval constinit decltype concept co_await co_return co_yield requires noexcept static_assert thread_local restrict final override atomic_bool atomic_char atomic_schar atomic_uchar atomic_short atomic_ushort atomic_int atomic_uint atomic_long atomic_ulong atomic_llong atomic_ullong new throw return and and_eq bitand bitor compl not not_eq or or_eq xor xor_eq",built_in:"std string wstring cin cout cerr clog stdin stdout stderr stringstream istringstream ostringstream auto_ptr deque list queue stack vector map set pair bitset multiset multimap unordered_set unordered_map unordered_multiset unordered_multimap priority_queue make_pair array shared_ptr abort terminate abs acos asin atan2 atan calloc ceil cosh cos exit exp fabs floor fmod fprintf fputs free frexp fscanf future isalnum isalpha iscntrl isdigit isgraph islower isprint ispunct isspace isupper isxdigit tolower toupper labs ldexp log10 log malloc realloc memchr memcmp memcpy memset modf pow printf putchar puts scanf sinh sin snprintf sprintf sqrt sscanf strcat strchr strcmp strcpy strcspn strlen strncat strncmp strncpy strpbrk strrchr strspn strstr tanh tan vfprintf vprintf vsprintf endl initializer_list unique_ptr _Bool complex _Complex imaginary _Imaginary",literal:"true false nullptr NULL"},d=[r,e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,i,a],_={variants:[{begin:/=/,end:/;/},{begin:/\(/,end:/\)/},{beginKeywords:"new throw return else",end:/;/}],keywords:l,contains:d.concat([{begin:/\(/,end:/\)/,keywords:l,contains:d.concat(["self"]),relevance:0}]),relevance:0},u={className:"function",begin:"("+n+"[\\*&\\s]+)+"+c,returnBegin:!0,end:/[{;=]/,excludeEnd:!0,keywords:l,illegal:/[^\w\s\*&:<>]/,contains:[{begin:"decltype\\(auto\\)",keywords:l,relevance:0},{begin:c,returnBegin:!0,contains:[o],relevance:0},{className:"params",begin:/\(/,end:/\)/,keywords:l,relevance:0,contains:[e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,a,i,r,{begin:/\(/,end:/\)/,keywords:l,relevance:0,contains:["self",e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,a,i,r]}]},r,e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,s]};return{aliases:["c","cc","h","c++","h++","hpp","hh","hxx","cxx"],keywords:l,disableAutodetect:!0,illegal:"",keywords:l,contains:["self",r]},{begin:e.IDENT_RE+"::",keywords:l},{className:"class",beginKeywords:"class struct",end:/[{;:]/,contains:[{begin://,contains:["self"]},e.TITLE_MODE]}]),exports:{preprocessor:s,strings:a,keywords:l}}}}());hljs.registerLanguage("coffeescript",function(){"use strict";const e=["as","in","of","if","for","while","finally","var","new","function","do","return","void","else","break","catch","instanceof","with","throw","case","default","try","switch","continue","typeof","delete","let","yield","const","class","debugger","async","await","static","import","from","export","extends"],n=["true","false","null","undefined","NaN","Infinity"],a=[].concat(["setInterval","setTimeout","clearInterval","clearTimeout","require","exports","eval","isFinite","isNaN","parseFloat","parseInt","decodeURI","decodeURIComponent","encodeURI","encodeURIComponent","escape","unescape"],["arguments","this","super","console","window","document","localStorage","module","global"],["Intl","DataView","Number","Math","Date","String","RegExp","Object","Function","Boolean","Error","Symbol","Set","Map","WeakSet","WeakMap","Proxy","Reflect","JSON","Promise","Float64Array","Int16Array","Int32Array","Int8Array","Uint16Array","Uint32Array","Float32Array","Array","Uint8Array","Uint8ClampedArray","ArrayBuffer"],["EvalError","InternalError","RangeError","ReferenceError","SyntaxError","TypeError","URIError"]);return function(r){var t={keyword:e.concat(["then","unless","until","loop","by","when","and","or","is","isnt","not"]).filter((e=>n=>!e.includes(n))(["var","const","let","function","static"])).join(" "),literal:n.concat(["yes","no","on","off"]).join(" "),built_in:a.concat(["npm","print"]).join(" ")},i="[A-Za-z$_][0-9A-Za-z$_]*",s={className:"subst",begin:/#\{/,end:/}/,keywords:t},o=[r.BINARY_NUMBER_MODE,r.inherit(r.C_NUMBER_MODE,{starts:{end:"(\\s*/)?",relevance:0}}),{className:"string",variants:[{begin:/'''/,end:/'''/,contains:[r.BACKSLASH_ESCAPE]},{begin:/'/,end:/'/,contains:[r.BACKSLASH_ESCAPE]},{begin:/"""/,end:/"""/,contains:[r.BACKSLASH_ESCAPE,s]},{begin:/"/,end:/"/,contains:[r.BACKSLASH_ESCAPE,s]}]},{className:"regexp",variants:[{begin:"///",end:"///",contains:[s,r.HASH_COMMENT_MODE]},{begin:"//[gim]{0,3}(?=\\W)",relevance:0},{begin:/\/(?![ *]).*?(?![\\]).\/[gim]{0,3}(?=\W)/}]},{begin:"@"+i},{subLanguage:"javascript",excludeBegin:!0,excludeEnd:!0,variants:[{begin:"```",end:"```"},{begin:"`",end:"`"}]}];s.contains=o;var c=r.inherit(r.TITLE_MODE,{begin:i}),l={className:"params",begin:"\\([^\\(]",returnBegin:!0,contains:[{begin:/\(/,end:/\)/,keywords:t,contains:["self"].concat(o)}]};return{name:"CoffeeScript",aliases:["coffee","cson","iced"],keywords:t,illegal:/\/\*/,contains:o.concat([r.COMMENT("###","###"),r.HASH_COMMENT_MODE,{className:"function",begin:"^\\s*"+i+"\\s*=\\s*(\\(.*\\))?\\s*\\B[-=]>",end:"[-=]>",returnBegin:!0,contains:[c,l]},{begin:/[:\(,=]\s*/,relevance:0,contains:[{className:"function",begin:"(\\(.*\\))?\\s*\\B[-=]>",end:"[-=]>",returnBegin:!0,contains:[l]}]},{className:"class",beginKeywords:"class",end:"$",illegal:/[:="\[\]]/,contains:[{beginKeywords:"extends",endsWithParent:!0,illegal:/[:="\[\]]/,contains:[c]},c]},{begin:i+":",end:":",returnBegin:!0,returnEnd:!0,relevance:0}])}}}());hljs.registerLanguage("ruby",function(){"use strict";return function(e){var n="[a-zA-Z_]\\w*[!?=]?|[-+~]\\@|<<|>>|=~|===?|<=>|[<>]=?|\\*\\*|[-/+%^&*~`|]|\\[\\]=?",a={keyword:"and then defined module in return redo if BEGIN retry end for self when next until do begin unless END rescue else break undef not super class case require yield alias while ensure elsif or include attr_reader attr_writer attr_accessor",literal:"true false nil"},s={className:"doctag",begin:"@[A-Za-z]+"},i={begin:"#<",end:">"},r=[e.COMMENT("#","$",{contains:[s]}),e.COMMENT("^\\=begin","^\\=end",{contains:[s],relevance:10}),e.COMMENT("^__END__","\\n$")],c={className:"subst",begin:"#\\{",end:"}",keywords:a},t={className:"string",contains:[e.BACKSLASH_ESCAPE,c],variants:[{begin:/'/,end:/'/},{begin:/"/,end:/"/},{begin:/`/,end:/`/},{begin:"%[qQwWx]?\\(",end:"\\)"},{begin:"%[qQwWx]?\\[",end:"\\]"},{begin:"%[qQwWx]?{",end:"}"},{begin:"%[qQwWx]?<",end:">"},{begin:"%[qQwWx]?/",end:"/"},{begin:"%[qQwWx]?%",end:"%"},{begin:"%[qQwWx]?-",end:"-"},{begin:"%[qQwWx]?\\|",end:"\\|"},{begin:/\B\?(\\\d{1,3}|\\x[A-Fa-f0-9]{1,2}|\\u[A-Fa-f0-9]{4}|\\?\S)\b/},{begin:/<<[-~]?'?(\w+)(?:.|\n)*?\n\s*\1\b/,returnBegin:!0,contains:[{begin:/<<[-~]?'?/},e.END_SAME_AS_BEGIN({begin:/(\w+)/,end:/(\w+)/,contains:[e.BACKSLASH_ESCAPE,c]})]}]},b={className:"params",begin:"\\(",end:"\\)",endsParent:!0,keywords:a},d=[t,i,{className:"class",beginKeywords:"class module",end:"$|;",illegal:/=/,contains:[e.inherit(e.TITLE_MODE,{begin:"[A-Za-z_]\\w*(::\\w+)*(\\?|\\!)?"}),{begin:"<\\s*",contains:[{begin:"("+e.IDENT_RE+"::)?"+e.IDENT_RE}]}].concat(r)},{className:"function",beginKeywords:"def",end:"$|;",contains:[e.inherit(e.TITLE_MODE,{begin:n}),b].concat(r)},{begin:e.IDENT_RE+"::"},{className:"symbol",begin:e.UNDERSCORE_IDENT_RE+"(\\!|\\?)?:",relevance:0},{className:"symbol",begin:":(?!\\s)",contains:[t,{begin:n}],relevance:0},{className:"number",begin:"(\\b0[0-7_]+)|(\\b0x[0-9a-fA-F_]+)|(\\b[1-9][0-9_]*(\\.[0-9_]+)?)|[0_]\\b",relevance:0},{begin:"(\\$\\W)|((\\$|\\@\\@?)(\\w+))"},{className:"params",begin:/\|/,end:/\|/,keywords:a},{begin:"("+e.RE_STARTERS_RE+"|unless)\\s*",keywords:"unless",contains:[i,{className:"regexp",contains:[e.BACKSLASH_ESCAPE,c],illegal:/\n/,variants:[{begin:"/",end:"/[a-z]*"},{begin:"%r{",end:"}[a-z]*"},{begin:"%r\\(",end:"\\)[a-z]*"},{begin:"%r!",end:"![a-z]*"},{begin:"%r\\[",end:"\\][a-z]*"}]}].concat(r),relevance:0}].concat(r);c.contains=d,b.contains=d;var g=[{begin:/^\s*=>/,starts:{end:"$",contains:d}},{className:"meta",begin:"^([>?]>|[\\w#]+\\(\\w+\\):\\d+:\\d+>|(\\w+-)?\\d+\\.\\d+\\.\\d(p\\d+)?[^>]+>)",starts:{end:"$",contains:d}}];return{name:"Ruby",aliases:["rb","gemspec","podspec","thor","irb"],keywords:a,illegal:/\/\*/,contains:r.concat(g).concat(d)}}}());hljs.registerLanguage("yaml",function(){"use strict";return function(e){var n="true false yes no null",a="[\\w#;/?:@&=+$,.~*\\'()[\\]]+",s={className:"string",relevance:0,variants:[{begin:/'/,end:/'/},{begin:/"/,end:/"/},{begin:/\S+/}],contains:[e.BACKSLASH_ESCAPE,{className:"template-variable",variants:[{begin:"{{",end:"}}"},{begin:"%{",end:"}"}]}]},i=e.inherit(s,{variants:[{begin:/'/,end:/'/},{begin:/"/,end:/"/},{begin:/[^\s,{}[\]]+/}]}),l={end:",",endsWithParent:!0,excludeEnd:!0,contains:[],keywords:n,relevance:0},t={begin:"{",end:"}",contains:[l],illegal:"\\n",relevance:0},g={begin:"\\[",end:"\\]",contains:[l],illegal:"\\n",relevance:0},b=[{className:"attr",variants:[{begin:"\\w[\\w :\\/.-]*:(?=[ \t]|$)"},{begin:'"\\w[\\w :\\/.-]*":(?=[ \t]|$)'},{begin:"'\\w[\\w :\\/.-]*':(?=[ \t]|$)"}]},{className:"meta",begin:"^---s*$",relevance:10},{className:"string",begin:"[\\|>]([0-9]?[+-])?[ ]*\\n( *)[\\S ]+\\n(\\2[\\S ]+\\n?)*"},{begin:"<%[%=-]?",end:"[%-]?%>",subLanguage:"ruby",excludeBegin:!0,excludeEnd:!0,relevance:0},{className:"type",begin:"!\\w+!"+a},{className:"type",begin:"!<"+a+">"},{className:"type",begin:"!"+a},{className:"type",begin:"!!"+a},{className:"meta",begin:"&"+e.UNDERSCORE_IDENT_RE+"$"},{className:"meta",begin:"\\*"+e.UNDERSCORE_IDENT_RE+"$"},{className:"bullet",begin:"\\-(?=[ ]|$)",relevance:0},e.HASH_COMMENT_MODE,{beginKeywords:n,keywords:{literal:n}},{className:"number",begin:"\\b[0-9]{4}(-[0-9][0-9]){0,2}([Tt \\t][0-9][0-9]?(:[0-9][0-9]){2})?(\\.[0-9]*)?([ \\t])*(Z|[-+][0-9][0-9]?(:[0-9][0-9])?)?\\b"},{className:"number",begin:e.C_NUMBER_RE+"\\b"},t,g,s],c=[...b];return c.pop(),c.push(i),l.contains=c,{name:"YAML",case_insensitive:!0,aliases:["yml","YAML"],contains:b}}}());hljs.registerLanguage("d",function(){"use strict";return function(e){var a={$pattern:e.UNDERSCORE_IDENT_RE,keyword:"abstract alias align asm assert auto body break byte case cast catch class const continue debug default delete deprecated do else enum export extern final finally for foreach foreach_reverse|10 goto if immutable import in inout int interface invariant is lazy macro mixin module new nothrow out override package pragma private protected public pure ref return scope shared static struct super switch synchronized template this throw try typedef typeid typeof union unittest version void volatile while with __FILE__ __LINE__ __gshared|10 __thread __traits __DATE__ __EOF__ __TIME__ __TIMESTAMP__ __VENDOR__ __VERSION__",built_in:"bool cdouble cent cfloat char creal dchar delegate double dstring float function idouble ifloat ireal long real short string ubyte ucent uint ulong ushort wchar wstring",literal:"false null true"},d="((0|[1-9][\\d_]*)|0[bB][01_]+|0[xX]([\\da-fA-F][\\da-fA-F_]*|_[\\da-fA-F][\\da-fA-F_]*))",n="\\\\(['\"\\?\\\\abfnrtv]|u[\\dA-Fa-f]{4}|[0-7]{1,3}|x[\\dA-Fa-f]{2}|U[\\dA-Fa-f]{8})|&[a-zA-Z\\d]{2,};",t={className:"number",begin:"\\b"+d+"(L|u|U|Lu|LU|uL|UL)?",relevance:0},_={className:"number",begin:"\\b(((0[xX](([\\da-fA-F][\\da-fA-F_]*|_[\\da-fA-F][\\da-fA-F_]*)\\.([\\da-fA-F][\\da-fA-F_]*|_[\\da-fA-F][\\da-fA-F_]*)|\\.?([\\da-fA-F][\\da-fA-F_]*|_[\\da-fA-F][\\da-fA-F_]*))[pP][+-]?(0|[1-9][\\d_]*|\\d[\\d_]*|[\\d_]+?\\d))|((0|[1-9][\\d_]*|\\d[\\d_]*|[\\d_]+?\\d)(\\.\\d*|([eE][+-]?(0|[1-9][\\d_]*|\\d[\\d_]*|[\\d_]+?\\d)))|\\d+\\.(0|[1-9][\\d_]*|\\d[\\d_]*|[\\d_]+?\\d)(0|[1-9][\\d_]*|\\d[\\d_]*|[\\d_]+?\\d)|\\.(0|[1-9][\\d_]*)([eE][+-]?(0|[1-9][\\d_]*|\\d[\\d_]*|[\\d_]+?\\d))?))([fF]|L|i|[fF]i|Li)?|"+d+"(i|[fF]i|Li))",relevance:0},r={className:"string",begin:"'("+n+"|.)",end:"'",illegal:"."},i={className:"string",begin:'"',contains:[{begin:n,relevance:0}],end:'"[cwd]?'},s=e.COMMENT("\\/\\+","\\+\\/",{contains:["self"],relevance:10});return{name:"D",keywords:a,contains:[e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,s,{className:"string",begin:'x"[\\da-fA-F\\s\\n\\r]*"[cwd]?',relevance:10},i,{className:"string",begin:'[rq]"',end:'"[cwd]?',relevance:5},{className:"string",begin:"`",end:"`[cwd]?"},{className:"string",begin:'q"\\{',end:'\\}"'},_,t,r,{className:"meta",begin:"^#!",end:"$",relevance:5},{className:"meta",begin:"#(line)",end:"$",relevance:5},{className:"keyword",begin:"@[a-zA-Z_][a-zA-Z_\\d]*"}]}}}());hljs.registerLanguage("properties",function(){"use strict";return function(e){var n="[ \\t\\f]*",t="("+n+"[:=]"+n+"|[ \\t\\f]+)",a="([^\\\\:= \\t\\f\\n]|\\\\.)+",s={end:t,relevance:0,starts:{className:"string",end:/$/,relevance:0,contains:[{begin:"\\\\\\n"}]}};return{name:".properties",case_insensitive:!0,illegal:/\S/,contains:[e.COMMENT("^\\s*[!#]","$"),{begin:"([^\\\\\\W:= \\t\\f\\n]|\\\\.)+"+t,returnBegin:!0,contains:[{className:"attr",begin:"([^\\\\\\W:= \\t\\f\\n]|\\\\.)+",endsParent:!0,relevance:0}],starts:s},{begin:a+t,returnBegin:!0,relevance:0,contains:[{className:"meta",begin:a,endsParent:!0,relevance:0}],starts:s},{className:"attr",relevance:0,begin:a+n+"$"}]}}}());hljs.registerLanguage("http",function(){"use strict";return function(e){var n="HTTP/[0-9\\.]+";return{name:"HTTP",aliases:["https"],illegal:"\\S",contains:[{begin:"^"+n,end:"$",contains:[{className:"number",begin:"\\b\\d{3}\\b"}]},{begin:"^[A-Z]+ (.*?) "+n+"$",returnBegin:!0,end:"$",contains:[{className:"string",begin:" ",end:" ",excludeBegin:!0,excludeEnd:!0},{begin:n},{className:"keyword",begin:"[A-Z]+"}]},{className:"attribute",begin:"^\\w",end:": ",excludeEnd:!0,illegal:"\\n|\\s|=",starts:{end:"$",relevance:0}},{begin:"\\n\\n",starts:{subLanguage:[],endsWithParent:!0}}]}}}());hljs.registerLanguage("haskell",function(){"use strict";return function(e){var n={variants:[e.COMMENT("--","$"),e.COMMENT("{-","-}",{contains:["self"]})]},i={className:"meta",begin:"{-#",end:"#-}"},a={className:"meta",begin:"^#",end:"$"},s={className:"type",begin:"\\b[A-Z][\\w']*",relevance:0},l={begin:"\\(",end:"\\)",illegal:'"',contains:[i,a,{className:"type",begin:"\\b[A-Z][\\w]*(\\((\\.\\.|,|\\w+)\\))?"},e.inherit(e.TITLE_MODE,{begin:"[_a-z][\\w']*"}),n]};return{name:"Haskell",aliases:["hs"],keywords:"let in if then else case of where do module import hiding qualified type data newtype deriving class instance as default infix infixl infixr foreign export ccall stdcall cplusplus jvm dotnet safe unsafe family forall mdo proc rec",contains:[{beginKeywords:"module",end:"where",keywords:"module where",contains:[l,n],illegal:"\\W\\.|;"},{begin:"\\bimport\\b",end:"$",keywords:"import qualified as hiding",contains:[l,n],illegal:"\\W\\.|;"},{className:"class",begin:"^(\\s*)?(class|instance)\\b",end:"where",keywords:"class family instance where",contains:[s,l,n]},{className:"class",begin:"\\b(data|(new)?type)\\b",end:"$",keywords:"data family type newtype deriving",contains:[i,s,l,{begin:"{",end:"}",contains:l.contains},n]},{beginKeywords:"default",end:"$",contains:[s,l,n]},{beginKeywords:"infix infixl infixr",end:"$",contains:[e.C_NUMBER_MODE,n]},{begin:"\\bforeign\\b",end:"$",keywords:"foreign import export ccall stdcall cplusplus jvm dotnet safe unsafe",contains:[s,e.QUOTE_STRING_MODE,n]},{className:"meta",begin:"#!\\/usr\\/bin\\/env runhaskell",end:"$"},i,a,e.QUOTE_STRING_MODE,e.C_NUMBER_MODE,s,e.inherit(e.TITLE_MODE,{begin:"^[_a-z][\\w']*"}),n,{begin:"->|<-"}]}}}());hljs.registerLanguage("handlebars",function(){"use strict";function e(...e){return e.map(e=>(function(e){return e?"string"==typeof e?e:e.source:null})(e)).join("")}return function(n){const a={"builtin-name":"action bindattr collection component concat debugger each each-in get hash if in input link-to loc log lookup mut outlet partial query-params render template textarea unbound unless view with yield"},t=/\[.*?\]/,s=/[^\s!"#%&'()*+,.\/;<=>@\[\\\]^`{|}~]+/,i=e("(",/'.*?'/,"|",/".*?"/,"|",t,"|",s,"|",/\.|\//,")+"),r=e("(",t,"|",s,")(?==)"),l={begin:i,lexemes:/[\w.\/]+/},c=n.inherit(l,{keywords:{literal:"true false undefined null"}}),o={begin:/\(/,end:/\)/},m={className:"attr",begin:r,relevance:0,starts:{begin:/=/,end:/=/,starts:{contains:[n.NUMBER_MODE,n.QUOTE_STRING_MODE,n.APOS_STRING_MODE,c,o]}}},d={contains:[n.NUMBER_MODE,n.QUOTE_STRING_MODE,n.APOS_STRING_MODE,{begin:/as\s+\|/,keywords:{keyword:"as"},end:/\|/,contains:[{begin:/\w+/}]},m,c,o],returnEnd:!0},g=n.inherit(l,{className:"name",keywords:a,starts:n.inherit(d,{end:/\)/})});o.contains=[g];const u=n.inherit(l,{keywords:a,className:"name",starts:n.inherit(d,{end:/}}/})}),b=n.inherit(l,{keywords:a,className:"name"}),h=n.inherit(l,{className:"name",keywords:a,starts:n.inherit(d,{end:/}}/})});return{name:"Handlebars",aliases:["hbs","html.hbs","html.handlebars","htmlbars"],case_insensitive:!0,subLanguage:"xml",contains:[{begin:/\\\{\{/,skip:!0},{begin:/\\\\(?=\{\{)/,skip:!0},n.COMMENT(/\{\{!--/,/--\}\}/),n.COMMENT(/\{\{!/,/\}\}/),{className:"template-tag",begin:/\{\{\{\{(?!\/)/,end:/\}\}\}\}/,contains:[u],starts:{end:/\{\{\{\{\//,returnEnd:!0,subLanguage:"xml"}},{className:"template-tag",begin:/\{\{\{\{\//,end:/\}\}\}\}/,contains:[b]},{className:"template-tag",begin:/\{\{#/,end:/\}\}/,contains:[u]},{className:"template-tag",begin:/\{\{(?=else\}\})/,end:/\}\}/,keywords:"else"},{className:"template-tag",begin:/\{\{\//,end:/\}\}/,contains:[b]},{className:"template-variable",begin:/\{\{\{/,end:/\}\}\}/,contains:[h]},{className:"template-variable",begin:/\{\{/,end:/\}\}/,contains:[h]}]}}}());hljs.registerLanguage("rust",function(){"use strict";return function(e){var n="([ui](8|16|32|64|128|size)|f(32|64))?",t="drop i8 i16 i32 i64 i128 isize u8 u16 u32 u64 u128 usize f32 f64 str char bool Box Option Result String Vec Copy Send Sized Sync Drop Fn FnMut FnOnce ToOwned Clone Debug PartialEq PartialOrd Eq Ord AsRef AsMut Into From Default Iterator Extend IntoIterator DoubleEndedIterator ExactSizeIterator SliceConcatExt ToString assert! assert_eq! bitflags! bytes! cfg! col! concat! concat_idents! debug_assert! debug_assert_eq! env! panic! file! format! format_args! include_bin! include_str! line! local_data_key! module_path! option_env! print! println! select! stringify! try! unimplemented! unreachable! vec! write! writeln! macro_rules! assert_ne! debug_assert_ne!";return{name:"Rust",aliases:["rs"],keywords:{$pattern:e.IDENT_RE+"!?",keyword:"abstract as async await become box break const continue crate do dyn else enum extern false final fn for if impl in let loop macro match mod move mut override priv pub ref return self Self static struct super trait true try type typeof unsafe unsized use virtual where while yield",literal:"true false Some None Ok Err",built_in:t},illegal:""}]}}}());hljs.registerLanguage("cpp",function(){"use strict";return function(e){var t=e.getLanguage("c-like").rawDefinition();return t.disableAutodetect=!1,t.name="C++",t.aliases=["cc","c++","h++","hpp","hh","hxx","cxx"],t}}());hljs.registerLanguage("ini",function(){"use strict";function e(e){return e?"string"==typeof e?e:e.source:null}function n(...n){return n.map(n=>e(n)).join("")}return function(a){var s={className:"number",relevance:0,variants:[{begin:/([\+\-]+)?[\d]+_[\d_]+/},{begin:a.NUMBER_RE}]},i=a.COMMENT();i.variants=[{begin:/;/,end:/$/},{begin:/#/,end:/$/}];var t={className:"variable",variants:[{begin:/\$[\w\d"][\w\d_]*/},{begin:/\$\{(.*?)}/}]},r={className:"literal",begin:/\bon|off|true|false|yes|no\b/},l={className:"string",contains:[a.BACKSLASH_ESCAPE],variants:[{begin:"'''",end:"'''",relevance:10},{begin:'"""',end:'"""',relevance:10},{begin:'"',end:'"'},{begin:"'",end:"'"}]},c={begin:/\[/,end:/\]/,contains:[i,r,t,l,s,"self"],relevance:0},g="("+[/[A-Za-z0-9_-]+/,/"(\\"|[^"])*"/,/'[^']*'/].map(n=>e(n)).join("|")+")";return{name:"TOML, also INI",aliases:["toml"],case_insensitive:!0,illegal:/\S/,contains:[i,{className:"section",begin:/\[+/,end:/\]+/},{begin:n(g,"(\\s*\\.\\s*",g,")*",n("(?=",/\s*=\s*[^#\s]/,")")),className:"attr",starts:{end:/$/,contains:[i,c,r,t,l,s]}}]}}}());hljs.registerLanguage("objectivec",function(){"use strict";return function(e){var n=/[a-zA-Z@][a-zA-Z0-9_]*/,_={$pattern:n,keyword:"@interface @class @protocol @implementation"};return{name:"Objective-C",aliases:["mm","objc","obj-c"],keywords:{$pattern:n,keyword:"int float while char export sizeof typedef const struct for union unsigned long volatile static bool mutable if do return goto void enum else break extern asm case short default double register explicit signed typename this switch continue wchar_t inline readonly assign readwrite self @synchronized id typeof nonatomic super unichar IBOutlet IBAction strong weak copy in out inout bycopy byref oneway __strong __weak __block __autoreleasing @private @protected @public @try @property @end @throw @catch @finally @autoreleasepool @synthesize @dynamic @selector @optional @required @encode @package @import @defs @compatibility_alias __bridge __bridge_transfer __bridge_retained __bridge_retain __covariant __contravariant __kindof _Nonnull _Nullable _Null_unspecified __FUNCTION__ __PRETTY_FUNCTION__ __attribute__ getter setter retain unsafe_unretained nonnull nullable null_unspecified null_resettable class instancetype NS_DESIGNATED_INITIALIZER NS_UNAVAILABLE NS_REQUIRES_SUPER NS_RETURNS_INNER_POINTER NS_INLINE NS_AVAILABLE NS_DEPRECATED NS_ENUM NS_OPTIONS NS_SWIFT_UNAVAILABLE NS_ASSUME_NONNULL_BEGIN NS_ASSUME_NONNULL_END NS_REFINED_FOR_SWIFT NS_SWIFT_NAME NS_SWIFT_NOTHROW NS_DURING NS_HANDLER NS_ENDHANDLER NS_VALUERETURN NS_VOIDRETURN",literal:"false true FALSE TRUE nil YES NO NULL",built_in:"BOOL dispatch_once_t dispatch_queue_t dispatch_sync dispatch_async dispatch_once"},illegal:"/,end:/$/,illegal:"\\n"},e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE]},{className:"class",begin:"("+_.keyword.split(" ").join("|")+")\\b",end:"({|$)",excludeEnd:!0,keywords:_,contains:[e.UNDERSCORE_TITLE_MODE]},{begin:"\\."+e.UNDERSCORE_IDENT_RE,relevance:0}]}}}());hljs.registerLanguage("apache",function(){"use strict";return function(e){var n={className:"number",begin:"\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}(:\\d{1,5})?"};return{name:"Apache config",aliases:["apacheconf"],case_insensitive:!0,contains:[e.HASH_COMMENT_MODE,{className:"section",begin:"",contains:[n,{className:"number",begin:":\\d{1,5}"},e.inherit(e.QUOTE_STRING_MODE,{relevance:0})]},{className:"attribute",begin:/\w+/,relevance:0,keywords:{nomarkup:"order deny allow setenv rewriterule rewriteengine rewritecond documentroot sethandler errordocument loadmodule options header listen serverroot servername"},starts:{end:/$/,relevance:0,keywords:{literal:"on off all deny allow"},contains:[{className:"meta",begin:"\\s\\[",end:"\\]$"},{className:"variable",begin:"[\\$%]\\{",end:"\\}",contains:["self",{className:"number",begin:"[\\$%]\\d+"}]},n,{className:"number",begin:"\\d+"},e.QUOTE_STRING_MODE]}}],illegal:/\S/}}}());hljs.registerLanguage("java",function(){"use strict";function e(e){return e?"string"==typeof e?e:e.source:null}function n(e){return a("(",e,")?")}function a(...n){return n.map(n=>e(n)).join("")}function s(...n){return"("+n.map(n=>e(n)).join("|")+")"}return function(e){var t="false synchronized int abstract float private char boolean var static null if const for true while long strictfp finally protected import native final void enum else break transient catch instanceof byte super volatile case assert short package default double public try this switch continue throws protected public private module requires exports do",i={className:"meta",begin:"@[À-ʸa-zA-Z_$][À-ʸa-zA-Z_$0-9]*",contains:[{begin:/\(/,end:/\)/,contains:["self"]}]},r=e=>a("[",e,"]+([",e,"_]*[",e,"]+)?"),c={className:"number",variants:[{begin:`\\b(0[bB]${r("01")})[lL]?`},{begin:`\\b(0${r("0-7")})[dDfFlL]?`},{begin:a(/\b0[xX]/,s(a(r("a-fA-F0-9"),/\./,r("a-fA-F0-9")),a(r("a-fA-F0-9"),/\.?/),a(/\./,r("a-fA-F0-9"))),/([pP][+-]?(\d+))?/,/[fFdDlL]?/)},{begin:a(/\b/,s(a(/\d*\./,r("\\d")),r("\\d")),/[eE][+-]?[\d]+[dDfF]?/)},{begin:a(/\b/,r(/\d/),n(/\.?/),n(r(/\d/)),/[dDfFlL]?/)}],relevance:0};return{name:"Java",aliases:["jsp"],keywords:t,illegal:/<\/|#/,contains:[e.COMMENT("/\\*\\*","\\*/",{relevance:0,contains:[{begin:/\w+@/,relevance:0},{className:"doctag",begin:"@[A-Za-z]+"}]}),e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,e.APOS_STRING_MODE,e.QUOTE_STRING_MODE,{className:"class",beginKeywords:"class interface",end:/[{;=]/,excludeEnd:!0,keywords:"class interface",illegal:/[:"\[\]]/,contains:[{beginKeywords:"extends implements"},e.UNDERSCORE_TITLE_MODE]},{beginKeywords:"new throw return else",relevance:0},{className:"function",begin:"([À-ʸa-zA-Z_$][À-ʸa-zA-Z_$0-9]*(<[À-ʸa-zA-Z_$][À-ʸa-zA-Z_$0-9]*(\\s*,\\s*[À-ʸa-zA-Z_$][À-ʸa-zA-Z_$0-9]*)*>)?\\s+)+"+e.UNDERSCORE_IDENT_RE+"\\s*\\(",returnBegin:!0,end:/[{;=]/,excludeEnd:!0,keywords:t,contains:[{begin:e.UNDERSCORE_IDENT_RE+"\\s*\\(",returnBegin:!0,relevance:0,contains:[e.UNDERSCORE_TITLE_MODE]},{className:"params",begin:/\(/,end:/\)/,keywords:t,relevance:0,contains:[i,e.APOS_STRING_MODE,e.QUOTE_STRING_MODE,e.C_NUMBER_MODE,e.C_BLOCK_COMMENT_MODE]},e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE]},c,i]}}}());hljs.registerLanguage("x86asm",function(){"use strict";return function(s){return{name:"Intel x86 Assembly",case_insensitive:!0,keywords:{$pattern:"[.%]?"+s.IDENT_RE,keyword:"lock rep repe repz repne repnz xaquire xrelease bnd nobnd aaa aad aam aas adc add and arpl bb0_reset bb1_reset bound bsf bsr bswap bt btc btr bts call cbw cdq cdqe clc cld cli clts cmc cmp cmpsb cmpsd cmpsq cmpsw cmpxchg cmpxchg486 cmpxchg8b cmpxchg16b cpuid cpu_read cpu_write cqo cwd cwde daa das dec div dmint emms enter equ f2xm1 fabs fadd faddp fbld fbstp fchs fclex fcmovb fcmovbe fcmove fcmovnb fcmovnbe fcmovne fcmovnu fcmovu fcom fcomi fcomip fcomp fcompp fcos fdecstp fdisi fdiv fdivp fdivr fdivrp femms feni ffree ffreep fiadd ficom ficomp fidiv fidivr fild fimul fincstp finit fist fistp fisttp fisub fisubr fld fld1 fldcw fldenv fldl2e fldl2t fldlg2 fldln2 fldpi fldz fmul fmulp fnclex fndisi fneni fninit fnop fnsave fnstcw fnstenv fnstsw fpatan fprem fprem1 fptan frndint frstor fsave fscale fsetpm fsin fsincos fsqrt fst fstcw fstenv fstp fstsw fsub fsubp fsubr fsubrp ftst fucom fucomi fucomip fucomp fucompp fxam fxch fxtract fyl2x fyl2xp1 hlt ibts icebp idiv imul in inc incbin insb insd insw int int01 int1 int03 int3 into invd invpcid invlpg invlpga iret iretd iretq iretw jcxz jecxz jrcxz jmp jmpe lahf lar lds lea leave les lfence lfs lgdt lgs lidt lldt lmsw loadall loadall286 lodsb lodsd lodsq lodsw loop loope loopne loopnz loopz lsl lss ltr mfence monitor mov movd movq movsb movsd movsq movsw movsx movsxd movzx mul mwait neg nop not or out outsb outsd outsw packssdw packsswb packuswb paddb paddd paddsb paddsiw paddsw paddusb paddusw paddw pand pandn pause paveb pavgusb pcmpeqb pcmpeqd pcmpeqw pcmpgtb pcmpgtd pcmpgtw pdistib pf2id pfacc pfadd pfcmpeq pfcmpge pfcmpgt pfmax pfmin pfmul pfrcp pfrcpit1 pfrcpit2 pfrsqit1 pfrsqrt pfsub pfsubr pi2fd pmachriw pmaddwd pmagw pmulhriw pmulhrwa pmulhrwc pmulhw pmullw pmvgezb pmvlzb pmvnzb pmvzb pop popa popad popaw popf popfd popfq popfw por prefetch prefetchw pslld psllq psllw psrad psraw psrld psrlq psrlw psubb psubd psubsb psubsiw psubsw psubusb psubusw psubw punpckhbw punpckhdq punpckhwd punpcklbw punpckldq punpcklwd push pusha pushad pushaw pushf pushfd pushfq pushfw pxor rcl rcr rdshr rdmsr rdpmc rdtsc rdtscp ret retf retn rol ror rdm rsdc rsldt rsm rsts sahf sal salc sar sbb scasb scasd scasq scasw sfence sgdt shl shld shr shrd sidt sldt skinit smi smint smintold smsw stc std sti stosb stosd stosq stosw str sub svdc svldt svts swapgs syscall sysenter sysexit sysret test ud0 ud1 ud2b ud2 ud2a umov verr verw fwait wbinvd wrshr wrmsr xadd xbts xchg xlatb xlat xor cmove cmovz cmovne cmovnz cmova cmovnbe cmovae cmovnb cmovb cmovnae cmovbe cmovna cmovg cmovnle cmovge cmovnl cmovl cmovnge cmovle cmovng cmovc cmovnc cmovo cmovno cmovs cmovns cmovp cmovpe cmovnp cmovpo je jz jne jnz ja jnbe jae jnb jb jnae jbe jna jg jnle jge jnl jl jnge jle jng jc jnc jo jno js jns jpo jnp jpe jp sete setz setne setnz seta setnbe setae setnb setnc setb setnae setcset setbe setna setg setnle setge setnl setl setnge setle setng sets setns seto setno setpe setp setpo setnp addps addss andnps andps cmpeqps cmpeqss cmpleps cmpless cmpltps cmpltss cmpneqps cmpneqss cmpnleps cmpnless cmpnltps cmpnltss cmpordps cmpordss cmpunordps cmpunordss cmpps cmpss comiss cvtpi2ps cvtps2pi cvtsi2ss cvtss2si cvttps2pi cvttss2si divps divss ldmxcsr maxps maxss minps minss movaps movhps movlhps movlps movhlps movmskps movntps movss movups mulps mulss orps rcpps rcpss rsqrtps rsqrtss shufps sqrtps sqrtss stmxcsr subps subss ucomiss unpckhps unpcklps xorps fxrstor fxrstor64 fxsave fxsave64 xgetbv xsetbv xsave xsave64 xsaveopt xsaveopt64 xrstor xrstor64 prefetchnta prefetcht0 prefetcht1 prefetcht2 maskmovq movntq pavgb pavgw pextrw pinsrw pmaxsw pmaxub pminsw pminub pmovmskb pmulhuw psadbw pshufw pf2iw pfnacc pfpnacc pi2fw pswapd maskmovdqu clflush movntdq movnti movntpd movdqa movdqu movdq2q movq2dq paddq pmuludq pshufd pshufhw pshuflw pslldq psrldq psubq punpckhqdq punpcklqdq addpd addsd andnpd andpd cmpeqpd cmpeqsd cmplepd cmplesd cmpltpd cmpltsd cmpneqpd cmpneqsd cmpnlepd cmpnlesd cmpnltpd cmpnltsd cmpordpd cmpordsd cmpunordpd cmpunordsd cmppd comisd cvtdq2pd cvtdq2ps cvtpd2dq cvtpd2pi cvtpd2ps cvtpi2pd cvtps2dq cvtps2pd cvtsd2si cvtsd2ss cvtsi2sd cvtss2sd cvttpd2pi cvttpd2dq cvttps2dq cvttsd2si divpd divsd maxpd maxsd minpd minsd movapd movhpd movlpd movmskpd movupd mulpd mulsd orpd shufpd sqrtpd sqrtsd subpd subsd ucomisd unpckhpd unpcklpd xorpd addsubpd addsubps haddpd haddps hsubpd hsubps lddqu movddup movshdup movsldup clgi stgi vmcall vmclear vmfunc vmlaunch vmload vmmcall vmptrld vmptrst vmread vmresume vmrun vmsave vmwrite vmxoff vmxon invept invvpid pabsb pabsw pabsd palignr phaddw phaddd phaddsw phsubw phsubd phsubsw pmaddubsw pmulhrsw pshufb psignb psignw psignd extrq insertq movntsd movntss lzcnt blendpd blendps blendvpd blendvps dppd dpps extractps insertps movntdqa mpsadbw packusdw pblendvb pblendw pcmpeqq pextrb pextrd pextrq phminposuw pinsrb pinsrd pinsrq pmaxsb pmaxsd pmaxud pmaxuw pminsb pminsd pminud pminuw pmovsxbw pmovsxbd pmovsxbq pmovsxwd pmovsxwq pmovsxdq pmovzxbw pmovzxbd pmovzxbq pmovzxwd pmovzxwq pmovzxdq pmuldq pmulld ptest roundpd roundps roundsd roundss crc32 pcmpestri pcmpestrm pcmpistri pcmpistrm pcmpgtq popcnt getsec pfrcpv pfrsqrtv movbe aesenc aesenclast aesdec aesdeclast aesimc aeskeygenassist vaesenc vaesenclast vaesdec vaesdeclast vaesimc vaeskeygenassist vaddpd vaddps vaddsd vaddss vaddsubpd vaddsubps vandpd vandps vandnpd vandnps vblendpd vblendps vblendvpd vblendvps vbroadcastss vbroadcastsd vbroadcastf128 vcmpeq_ospd vcmpeqpd vcmplt_ospd vcmpltpd vcmple_ospd vcmplepd vcmpunord_qpd vcmpunordpd vcmpneq_uqpd vcmpneqpd vcmpnlt_uspd vcmpnltpd vcmpnle_uspd vcmpnlepd vcmpord_qpd vcmpordpd vcmpeq_uqpd vcmpnge_uspd vcmpngepd vcmpngt_uspd vcmpngtpd vcmpfalse_oqpd vcmpfalsepd vcmpneq_oqpd vcmpge_ospd vcmpgepd vcmpgt_ospd vcmpgtpd vcmptrue_uqpd vcmptruepd vcmplt_oqpd vcmple_oqpd vcmpunord_spd vcmpneq_uspd vcmpnlt_uqpd vcmpnle_uqpd vcmpord_spd vcmpeq_uspd vcmpnge_uqpd vcmpngt_uqpd vcmpfalse_ospd vcmpneq_ospd vcmpge_oqpd vcmpgt_oqpd vcmptrue_uspd vcmppd vcmpeq_osps vcmpeqps vcmplt_osps vcmpltps vcmple_osps vcmpleps vcmpunord_qps vcmpunordps vcmpneq_uqps vcmpneqps vcmpnlt_usps vcmpnltps vcmpnle_usps vcmpnleps vcmpord_qps vcmpordps vcmpeq_uqps vcmpnge_usps vcmpngeps vcmpngt_usps vcmpngtps vcmpfalse_oqps vcmpfalseps vcmpneq_oqps vcmpge_osps vcmpgeps vcmpgt_osps vcmpgtps vcmptrue_uqps vcmptrueps vcmplt_oqps vcmple_oqps vcmpunord_sps vcmpneq_usps vcmpnlt_uqps vcmpnle_uqps vcmpord_sps vcmpeq_usps vcmpnge_uqps vcmpngt_uqps vcmpfalse_osps vcmpneq_osps vcmpge_oqps vcmpgt_oqps vcmptrue_usps vcmpps vcmpeq_ossd vcmpeqsd vcmplt_ossd vcmpltsd vcmple_ossd vcmplesd vcmpunord_qsd vcmpunordsd vcmpneq_uqsd vcmpneqsd vcmpnlt_ussd vcmpnltsd vcmpnle_ussd vcmpnlesd vcmpord_qsd vcmpordsd vcmpeq_uqsd vcmpnge_ussd vcmpngesd vcmpngt_ussd vcmpngtsd vcmpfalse_oqsd vcmpfalsesd vcmpneq_oqsd vcmpge_ossd vcmpgesd vcmpgt_ossd vcmpgtsd vcmptrue_uqsd vcmptruesd vcmplt_oqsd vcmple_oqsd vcmpunord_ssd vcmpneq_ussd vcmpnlt_uqsd vcmpnle_uqsd vcmpord_ssd vcmpeq_ussd vcmpnge_uqsd vcmpngt_uqsd vcmpfalse_ossd vcmpneq_ossd vcmpge_oqsd vcmpgt_oqsd vcmptrue_ussd vcmpsd vcmpeq_osss vcmpeqss vcmplt_osss vcmpltss vcmple_osss vcmpless vcmpunord_qss vcmpunordss vcmpneq_uqss vcmpneqss vcmpnlt_usss vcmpnltss vcmpnle_usss vcmpnless vcmpord_qss vcmpordss vcmpeq_uqss vcmpnge_usss vcmpngess vcmpngt_usss vcmpngtss vcmpfalse_oqss vcmpfalsess vcmpneq_oqss vcmpge_osss vcmpgess vcmpgt_osss vcmpgtss vcmptrue_uqss vcmptruess vcmplt_oqss vcmple_oqss vcmpunord_sss vcmpneq_usss vcmpnlt_uqss vcmpnle_uqss vcmpord_sss vcmpeq_usss vcmpnge_uqss vcmpngt_uqss vcmpfalse_osss vcmpneq_osss vcmpge_oqss vcmpgt_oqss vcmptrue_usss vcmpss vcomisd vcomiss vcvtdq2pd vcvtdq2ps vcvtpd2dq vcvtpd2ps vcvtps2dq vcvtps2pd vcvtsd2si vcvtsd2ss vcvtsi2sd vcvtsi2ss vcvtss2sd vcvtss2si vcvttpd2dq vcvttps2dq vcvttsd2si vcvttss2si vdivpd vdivps vdivsd vdivss vdppd vdpps vextractf128 vextractps vhaddpd vhaddps vhsubpd vhsubps vinsertf128 vinsertps vlddqu vldqqu vldmxcsr vmaskmovdqu vmaskmovps vmaskmovpd vmaxpd vmaxps vmaxsd vmaxss vminpd vminps vminsd vminss vmovapd vmovaps vmovd vmovq vmovddup vmovdqa vmovqqa vmovdqu vmovqqu vmovhlps vmovhpd vmovhps vmovlhps vmovlpd vmovlps vmovmskpd vmovmskps vmovntdq vmovntqq vmovntdqa vmovntpd vmovntps vmovsd vmovshdup vmovsldup vmovss vmovupd vmovups vmpsadbw vmulpd vmulps vmulsd vmulss vorpd vorps vpabsb vpabsw vpabsd vpacksswb vpackssdw vpackuswb vpackusdw vpaddb vpaddw vpaddd vpaddq vpaddsb vpaddsw vpaddusb vpaddusw vpalignr vpand vpandn vpavgb vpavgw vpblendvb vpblendw vpcmpestri vpcmpestrm vpcmpistri vpcmpistrm vpcmpeqb vpcmpeqw vpcmpeqd vpcmpeqq vpcmpgtb vpcmpgtw vpcmpgtd vpcmpgtq vpermilpd vpermilps vperm2f128 vpextrb vpextrw vpextrd vpextrq vphaddw vphaddd vphaddsw vphminposuw vphsubw vphsubd vphsubsw vpinsrb vpinsrw vpinsrd vpinsrq vpmaddwd vpmaddubsw vpmaxsb vpmaxsw vpmaxsd vpmaxub vpmaxuw vpmaxud vpminsb vpminsw vpminsd vpminub vpminuw vpminud vpmovmskb vpmovsxbw vpmovsxbd vpmovsxbq vpmovsxwd vpmovsxwq vpmovsxdq vpmovzxbw vpmovzxbd vpmovzxbq vpmovzxwd vpmovzxwq vpmovzxdq vpmulhuw vpmulhrsw vpmulhw vpmullw vpmulld vpmuludq vpmuldq vpor vpsadbw vpshufb vpshufd vpshufhw vpshuflw vpsignb vpsignw vpsignd vpslldq vpsrldq vpsllw vpslld vpsllq vpsraw vpsrad vpsrlw vpsrld vpsrlq vptest vpsubb vpsubw vpsubd vpsubq vpsubsb vpsubsw vpsubusb vpsubusw vpunpckhbw vpunpckhwd vpunpckhdq vpunpckhqdq vpunpcklbw vpunpcklwd vpunpckldq vpunpcklqdq vpxor vrcpps vrcpss vrsqrtps vrsqrtss vroundpd vroundps vroundsd vroundss vshufpd vshufps vsqrtpd vsqrtps vsqrtsd vsqrtss vstmxcsr vsubpd vsubps vsubsd vsubss vtestps vtestpd vucomisd vucomiss vunpckhpd vunpckhps vunpcklpd vunpcklps vxorpd vxorps vzeroall vzeroupper pclmullqlqdq pclmulhqlqdq pclmullqhqdq pclmulhqhqdq pclmulqdq vpclmullqlqdq vpclmulhqlqdq vpclmullqhqdq vpclmulhqhqdq vpclmulqdq vfmadd132ps vfmadd132pd vfmadd312ps vfmadd312pd vfmadd213ps vfmadd213pd vfmadd123ps vfmadd123pd vfmadd231ps vfmadd231pd vfmadd321ps vfmadd321pd vfmaddsub132ps vfmaddsub132pd vfmaddsub312ps vfmaddsub312pd vfmaddsub213ps vfmaddsub213pd vfmaddsub123ps vfmaddsub123pd vfmaddsub231ps vfmaddsub231pd vfmaddsub321ps vfmaddsub321pd vfmsub132ps vfmsub132pd vfmsub312ps vfmsub312pd vfmsub213ps vfmsub213pd vfmsub123ps vfmsub123pd vfmsub231ps vfmsub231pd vfmsub321ps vfmsub321pd vfmsubadd132ps vfmsubadd132pd vfmsubadd312ps vfmsubadd312pd vfmsubadd213ps vfmsubadd213pd vfmsubadd123ps vfmsubadd123pd vfmsubadd231ps vfmsubadd231pd vfmsubadd321ps vfmsubadd321pd vfnmadd132ps vfnmadd132pd vfnmadd312ps vfnmadd312pd vfnmadd213ps vfnmadd213pd vfnmadd123ps vfnmadd123pd vfnmadd231ps vfnmadd231pd vfnmadd321ps vfnmadd321pd vfnmsub132ps vfnmsub132pd vfnmsub312ps vfnmsub312pd vfnmsub213ps vfnmsub213pd vfnmsub123ps vfnmsub123pd vfnmsub231ps vfnmsub231pd vfnmsub321ps vfnmsub321pd vfmadd132ss vfmadd132sd vfmadd312ss vfmadd312sd vfmadd213ss vfmadd213sd vfmadd123ss vfmadd123sd vfmadd231ss vfmadd231sd vfmadd321ss vfmadd321sd vfmsub132ss vfmsub132sd vfmsub312ss vfmsub312sd vfmsub213ss vfmsub213sd vfmsub123ss vfmsub123sd vfmsub231ss vfmsub231sd vfmsub321ss vfmsub321sd vfnmadd132ss vfnmadd132sd vfnmadd312ss vfnmadd312sd vfnmadd213ss vfnmadd213sd vfnmadd123ss vfnmadd123sd vfnmadd231ss vfnmadd231sd vfnmadd321ss vfnmadd321sd vfnmsub132ss vfnmsub132sd vfnmsub312ss vfnmsub312sd vfnmsub213ss vfnmsub213sd vfnmsub123ss vfnmsub123sd vfnmsub231ss vfnmsub231sd vfnmsub321ss vfnmsub321sd rdfsbase rdgsbase rdrand wrfsbase wrgsbase vcvtph2ps vcvtps2ph adcx adox rdseed clac stac xstore xcryptecb xcryptcbc xcryptctr xcryptcfb xcryptofb montmul xsha1 xsha256 llwpcb slwpcb lwpval lwpins vfmaddpd vfmaddps vfmaddsd vfmaddss vfmaddsubpd vfmaddsubps vfmsubaddpd vfmsubaddps vfmsubpd vfmsubps vfmsubsd vfmsubss vfnmaddpd vfnmaddps vfnmaddsd vfnmaddss vfnmsubpd vfnmsubps vfnmsubsd vfnmsubss vfrczpd vfrczps vfrczsd vfrczss vpcmov vpcomb vpcomd vpcomq vpcomub vpcomud vpcomuq vpcomuw vpcomw vphaddbd vphaddbq vphaddbw vphadddq vphaddubd vphaddubq vphaddubw vphaddudq vphadduwd vphadduwq vphaddwd vphaddwq vphsubbw vphsubdq vphsubwd vpmacsdd vpmacsdqh vpmacsdql vpmacssdd vpmacssdqh vpmacssdql vpmacsswd vpmacssww vpmacswd vpmacsww vpmadcsswd vpmadcswd vpperm vprotb vprotd vprotq vprotw vpshab vpshad vpshaq vpshaw vpshlb vpshld vpshlq vpshlw vbroadcasti128 vpblendd vpbroadcastb vpbroadcastw vpbroadcastd vpbroadcastq vpermd vpermpd vpermps vpermq vperm2i128 vextracti128 vinserti128 vpmaskmovd vpmaskmovq vpsllvd vpsllvq vpsravd vpsrlvd vpsrlvq vgatherdpd vgatherqpd vgatherdps vgatherqps vpgatherdd vpgatherqd vpgatherdq vpgatherqq xabort xbegin xend xtest andn bextr blci blcic blsi blsic blcfill blsfill blcmsk blsmsk blsr blcs bzhi mulx pdep pext rorx sarx shlx shrx tzcnt tzmsk t1mskc valignd valignq vblendmpd vblendmps vbroadcastf32x4 vbroadcastf64x4 vbroadcasti32x4 vbroadcasti64x4 vcompresspd vcompressps vcvtpd2udq vcvtps2udq vcvtsd2usi vcvtss2usi vcvttpd2udq vcvttps2udq vcvttsd2usi vcvttss2usi vcvtudq2pd vcvtudq2ps vcvtusi2sd vcvtusi2ss vexpandpd vexpandps vextractf32x4 vextractf64x4 vextracti32x4 vextracti64x4 vfixupimmpd vfixupimmps vfixupimmsd vfixupimmss vgetexppd vgetexpps vgetexpsd vgetexpss vgetmantpd vgetmantps vgetmantsd vgetmantss vinsertf32x4 vinsertf64x4 vinserti32x4 vinserti64x4 vmovdqa32 vmovdqa64 vmovdqu32 vmovdqu64 vpabsq vpandd vpandnd vpandnq vpandq vpblendmd vpblendmq vpcmpltd vpcmpled vpcmpneqd vpcmpnltd vpcmpnled vpcmpd vpcmpltq vpcmpleq vpcmpneqq vpcmpnltq vpcmpnleq vpcmpq vpcmpequd vpcmpltud vpcmpleud vpcmpnequd vpcmpnltud vpcmpnleud vpcmpud vpcmpequq vpcmpltuq vpcmpleuq vpcmpnequq vpcmpnltuq vpcmpnleuq vpcmpuq vpcompressd vpcompressq vpermi2d vpermi2pd vpermi2ps vpermi2q vpermt2d vpermt2pd vpermt2ps vpermt2q vpexpandd vpexpandq vpmaxsq vpmaxuq vpminsq vpminuq vpmovdb vpmovdw vpmovqb vpmovqd vpmovqw vpmovsdb vpmovsdw vpmovsqb vpmovsqd vpmovsqw vpmovusdb vpmovusdw vpmovusqb vpmovusqd vpmovusqw vpord vporq vprold vprolq vprolvd vprolvq vprord vprorq vprorvd vprorvq vpscatterdd vpscatterdq vpscatterqd vpscatterqq vpsraq vpsravq vpternlogd vpternlogq vptestmd vptestmq vptestnmd vptestnmq vpxord vpxorq vrcp14pd vrcp14ps vrcp14sd vrcp14ss vrndscalepd vrndscaleps vrndscalesd vrndscaless vrsqrt14pd vrsqrt14ps vrsqrt14sd vrsqrt14ss vscalefpd vscalefps vscalefsd vscalefss vscatterdpd vscatterdps vscatterqpd vscatterqps vshuff32x4 vshuff64x2 vshufi32x4 vshufi64x2 kandnw kandw kmovw knotw kortestw korw kshiftlw kshiftrw kunpckbw kxnorw kxorw vpbroadcastmb2q vpbroadcastmw2d vpconflictd vpconflictq vplzcntd vplzcntq vexp2pd vexp2ps vrcp28pd vrcp28ps vrcp28sd vrcp28ss vrsqrt28pd vrsqrt28ps vrsqrt28sd vrsqrt28ss vgatherpf0dpd vgatherpf0dps vgatherpf0qpd vgatherpf0qps vgatherpf1dpd vgatherpf1dps vgatherpf1qpd vgatherpf1qps vscatterpf0dpd vscatterpf0dps vscatterpf0qpd vscatterpf0qps vscatterpf1dpd vscatterpf1dps vscatterpf1qpd vscatterpf1qps prefetchwt1 bndmk bndcl bndcu bndcn bndmov bndldx bndstx sha1rnds4 sha1nexte sha1msg1 sha1msg2 sha256rnds2 sha256msg1 sha256msg2 hint_nop0 hint_nop1 hint_nop2 hint_nop3 hint_nop4 hint_nop5 hint_nop6 hint_nop7 hint_nop8 hint_nop9 hint_nop10 hint_nop11 hint_nop12 hint_nop13 hint_nop14 hint_nop15 hint_nop16 hint_nop17 hint_nop18 hint_nop19 hint_nop20 hint_nop21 hint_nop22 hint_nop23 hint_nop24 hint_nop25 hint_nop26 hint_nop27 hint_nop28 hint_nop29 hint_nop30 hint_nop31 hint_nop32 hint_nop33 hint_nop34 hint_nop35 hint_nop36 hint_nop37 hint_nop38 hint_nop39 hint_nop40 hint_nop41 hint_nop42 hint_nop43 hint_nop44 hint_nop45 hint_nop46 hint_nop47 hint_nop48 hint_nop49 hint_nop50 hint_nop51 hint_nop52 hint_nop53 hint_nop54 hint_nop55 hint_nop56 hint_nop57 hint_nop58 hint_nop59 hint_nop60 hint_nop61 hint_nop62 hint_nop63",built_in:"ip eip rip al ah bl bh cl ch dl dh sil dil bpl spl r8b r9b r10b r11b r12b r13b r14b r15b ax bx cx dx si di bp sp r8w r9w r10w r11w r12w r13w r14w r15w eax ebx ecx edx esi edi ebp esp eip r8d r9d r10d r11d r12d r13d r14d r15d rax rbx rcx rdx rsi rdi rbp rsp r8 r9 r10 r11 r12 r13 r14 r15 cs ds es fs gs ss st st0 st1 st2 st3 st4 st5 st6 st7 mm0 mm1 mm2 mm3 mm4 mm5 mm6 mm7 xmm0 xmm1 xmm2 xmm3 xmm4 xmm5 xmm6 xmm7 xmm8 xmm9 xmm10 xmm11 xmm12 xmm13 xmm14 xmm15 xmm16 xmm17 xmm18 xmm19 xmm20 xmm21 xmm22 xmm23 xmm24 xmm25 xmm26 xmm27 xmm28 xmm29 xmm30 xmm31 ymm0 ymm1 ymm2 ymm3 ymm4 ymm5 ymm6 ymm7 ymm8 ymm9 ymm10 ymm11 ymm12 ymm13 ymm14 ymm15 ymm16 ymm17 ymm18 ymm19 ymm20 ymm21 ymm22 ymm23 ymm24 ymm25 ymm26 ymm27 ymm28 ymm29 ymm30 ymm31 zmm0 zmm1 zmm2 zmm3 zmm4 zmm5 zmm6 zmm7 zmm8 zmm9 zmm10 zmm11 zmm12 zmm13 zmm14 zmm15 zmm16 zmm17 zmm18 zmm19 zmm20 zmm21 zmm22 zmm23 zmm24 zmm25 zmm26 zmm27 zmm28 zmm29 zmm30 zmm31 k0 k1 k2 k3 k4 k5 k6 k7 bnd0 bnd1 bnd2 bnd3 cr0 cr1 cr2 cr3 cr4 cr8 dr0 dr1 dr2 dr3 dr8 tr3 tr4 tr5 tr6 tr7 r0 r1 r2 r3 r4 r5 r6 r7 r0b r1b r2b r3b r4b r5b r6b r7b r0w r1w r2w r3w r4w r5w r6w r7w r0d r1d r2d r3d r4d r5d r6d r7d r0h r1h r2h r3h r0l r1l r2l r3l r4l r5l r6l r7l r8l r9l r10l r11l r12l r13l r14l r15l db dw dd dq dt ddq do dy dz resb resw resd resq rest resdq reso resy resz incbin equ times byte word dword qword nosplit rel abs seg wrt strict near far a32 ptr",meta:"%define %xdefine %+ %undef %defstr %deftok %assign %strcat %strlen %substr %rotate %elif %else %endif %if %ifmacro %ifctx %ifidn %ifidni %ifid %ifnum %ifstr %iftoken %ifempty %ifenv %error %warning %fatal %rep %endrep %include %push %pop %repl %pathsearch %depend %use %arg %stacksize %local %line %comment %endcomment .nolist __FILE__ __LINE__ __SECT__ __BITS__ __OUTPUT_FORMAT__ __DATE__ __TIME__ __DATE_NUM__ __TIME_NUM__ __UTC_DATE__ __UTC_TIME__ __UTC_DATE_NUM__ __UTC_TIME_NUM__ __PASS__ struc endstruc istruc at iend align alignb sectalign daz nodaz up down zero default option assume public bits use16 use32 use64 default section segment absolute extern global common cpu float __utf16__ __utf16le__ __utf16be__ __utf32__ __utf32le__ __utf32be__ __float8__ __float16__ __float32__ __float64__ __float80m__ __float80e__ __float128l__ __float128h__ __Infinity__ __QNaN__ __SNaN__ Inf NaN QNaN SNaN float8 float16 float32 float64 float80m float80e float128l float128h __FLOAT_DAZ__ __FLOAT_ROUND__ __FLOAT__"},contains:[s.COMMENT(";","$",{relevance:0}),{className:"number",variants:[{begin:"\\b(?:([0-9][0-9_]*)?\\.[0-9_]*(?:[eE][+-]?[0-9_]+)?|(0[Xx])?[0-9][0-9_]*\\.?[0-9_]*(?:[pP](?:[+-]?[0-9_]+)?)?)\\b",relevance:0},{begin:"\\$[0-9][0-9A-Fa-f]*",relevance:0},{begin:"\\b(?:[0-9A-Fa-f][0-9A-Fa-f_]*[Hh]|[0-9][0-9_]*[DdTt]?|[0-7][0-7_]*[QqOo]|[0-1][0-1_]*[BbYy])\\b"},{begin:"\\b(?:0[Xx][0-9A-Fa-f_]+|0[DdTt][0-9_]+|0[QqOo][0-7_]+|0[BbYy][0-1_]+)\\b"}]},s.QUOTE_STRING_MODE,{className:"string",variants:[{begin:"'",end:"[^\\\\]'"},{begin:"`",end:"[^\\\\]`"}],relevance:0},{className:"symbol",variants:[{begin:"^\\s*[A-Za-z._?][A-Za-z0-9_$#@~.?]*(:|\\s+label)"},{begin:"^\\s*%%[A-Za-z0-9_$#@~.?]*:"}],relevance:0},{className:"subst",begin:"%[0-9]+",relevance:0},{className:"subst",begin:"%!S+",relevance:0},{className:"meta",begin:/^\s*\.[\w_-]+/}]}}}());hljs.registerLanguage("kotlin",function(){"use strict";return function(e){var n={keyword:"abstract as val var vararg get set class object open private protected public noinline crossinline dynamic final enum if else do while for when throw try catch finally import package is in fun override companion reified inline lateinit init interface annotation data sealed internal infix operator out by constructor super tailrec where const inner suspend typealias external expect actual trait volatile transient native default",built_in:"Byte Short Char Int Long Boolean Float Double Void Unit Nothing",literal:"true false null"},a={className:"symbol",begin:e.UNDERSCORE_IDENT_RE+"@"},i={className:"subst",begin:"\\${",end:"}",contains:[e.C_NUMBER_MODE]},s={className:"variable",begin:"\\$"+e.UNDERSCORE_IDENT_RE},t={className:"string",variants:[{begin:'"""',end:'"""(?=[^"])',contains:[s,i]},{begin:"'",end:"'",illegal:/\n/,contains:[e.BACKSLASH_ESCAPE]},{begin:'"',end:'"',illegal:/\n/,contains:[e.BACKSLASH_ESCAPE,s,i]}]};i.contains.push(t);var r={className:"meta",begin:"@(?:file|property|field|get|set|receiver|param|setparam|delegate)\\s*:(?:\\s*"+e.UNDERSCORE_IDENT_RE+")?"},l={className:"meta",begin:"@"+e.UNDERSCORE_IDENT_RE,contains:[{begin:/\(/,end:/\)/,contains:[e.inherit(t,{className:"meta-string"})]}]},c=e.COMMENT("/\\*","\\*/",{contains:[e.C_BLOCK_COMMENT_MODE]}),o={variants:[{className:"type",begin:e.UNDERSCORE_IDENT_RE},{begin:/\(/,end:/\)/,contains:[]}]},d=o;return d.variants[1].contains=[o],o.variants[1].contains=[d],{name:"Kotlin",aliases:["kt"],keywords:n,contains:[e.COMMENT("/\\*\\*","\\*/",{relevance:0,contains:[{className:"doctag",begin:"@[A-Za-z]+"}]}),e.C_LINE_COMMENT_MODE,c,{className:"keyword",begin:/\b(break|continue|return|this)\b/,starts:{contains:[{className:"symbol",begin:/@\w+/}]}},a,r,l,{className:"function",beginKeywords:"fun",end:"[(]|$",returnBegin:!0,excludeEnd:!0,keywords:n,illegal:/fun\s+(<.*>)?[^\s\(]+(\s+[^\s\(]+)\s*=/,relevance:5,contains:[{begin:e.UNDERSCORE_IDENT_RE+"\\s*\\(",returnBegin:!0,relevance:0,contains:[e.UNDERSCORE_TITLE_MODE]},{className:"type",begin://,keywords:"reified",relevance:0},{className:"params",begin:/\(/,end:/\)/,endsParent:!0,keywords:n,relevance:0,contains:[{begin:/:/,end:/[=,\/]/,endsWithParent:!0,contains:[o,e.C_LINE_COMMENT_MODE,c],relevance:0},e.C_LINE_COMMENT_MODE,c,r,l,t,e.C_NUMBER_MODE]},c]},{className:"class",beginKeywords:"class interface trait",end:/[:\{(]|$/,excludeEnd:!0,illegal:"extends implements",contains:[{beginKeywords:"public protected internal private constructor"},e.UNDERSCORE_TITLE_MODE,{className:"type",begin://,excludeBegin:!0,excludeEnd:!0,relevance:0},{className:"type",begin:/[,:]\s*/,end:/[<\(,]|$/,excludeBegin:!0,returnEnd:!0},r,l]},t,{className:"meta",begin:"^#!/usr/bin/env",end:"$",illegal:"\n"},{className:"number",begin:"\\b(0[bB]([01]+[01_]+[01]+|[01]+)|0[xX]([a-fA-F0-9]+[a-fA-F0-9_]+[a-fA-F0-9]+|[a-fA-F0-9]+)|(([\\d]+[\\d_]+[\\d]+|[\\d]+)(\\.([\\d]+[\\d_]+[\\d]+|[\\d]+))?|\\.([\\d]+[\\d_]+[\\d]+|[\\d]+))([eE][-+]?\\d+)?)[lLfF]?",relevance:0}]}}}());hljs.registerLanguage("armasm",function(){"use strict";return function(s){const e={variants:[s.COMMENT("^[ \\t]*(?=#)","$",{relevance:0,excludeBegin:!0}),s.COMMENT("[;@]","$",{relevance:0}),s.C_LINE_COMMENT_MODE,s.C_BLOCK_COMMENT_MODE]};return{name:"ARM Assembly",case_insensitive:!0,aliases:["arm"],keywords:{$pattern:"\\.?"+s.IDENT_RE,meta:".2byte .4byte .align .ascii .asciz .balign .byte .code .data .else .end .endif .endm .endr .equ .err .exitm .extern .global .hword .if .ifdef .ifndef .include .irp .long .macro .rept .req .section .set .skip .space .text .word .arm .thumb .code16 .code32 .force_thumb .thumb_func .ltorg ALIAS ALIGN ARM AREA ASSERT ATTR CN CODE CODE16 CODE32 COMMON CP DATA DCB DCD DCDU DCDO DCFD DCFDU DCI DCQ DCQU DCW DCWU DN ELIF ELSE END ENDFUNC ENDIF ENDP ENTRY EQU EXPORT EXPORTAS EXTERN FIELD FILL FUNCTION GBLA GBLL GBLS GET GLOBAL IF IMPORT INCBIN INCLUDE INFO KEEP LCLA LCLL LCLS LTORG MACRO MAP MEND MEXIT NOFP OPT PRESERVE8 PROC QN READONLY RELOC REQUIRE REQUIRE8 RLIST FN ROUT SETA SETL SETS SN SPACE SUBT THUMB THUMBX TTL WHILE WEND ",built_in:"r0 r1 r2 r3 r4 r5 r6 r7 r8 r9 r10 r11 r12 r13 r14 r15 pc lr sp ip sl sb fp a1 a2 a3 a4 v1 v2 v3 v4 v5 v6 v7 v8 f0 f1 f2 f3 f4 f5 f6 f7 p0 p1 p2 p3 p4 p5 p6 p7 p8 p9 p10 p11 p12 p13 p14 p15 c0 c1 c2 c3 c4 c5 c6 c7 c8 c9 c10 c11 c12 c13 c14 c15 q0 q1 q2 q3 q4 q5 q6 q7 q8 q9 q10 q11 q12 q13 q14 q15 cpsr_c cpsr_x cpsr_s cpsr_f cpsr_cx cpsr_cxs cpsr_xs cpsr_xsf cpsr_sf cpsr_cxsf spsr_c spsr_x spsr_s spsr_f spsr_cx spsr_cxs spsr_xs spsr_xsf spsr_sf spsr_cxsf s0 s1 s2 s3 s4 s5 s6 s7 s8 s9 s10 s11 s12 s13 s14 s15 s16 s17 s18 s19 s20 s21 s22 s23 s24 s25 s26 s27 s28 s29 s30 s31 d0 d1 d2 d3 d4 d5 d6 d7 d8 d9 d10 d11 d12 d13 d14 d15 d16 d17 d18 d19 d20 d21 d22 d23 d24 d25 d26 d27 d28 d29 d30 d31 {PC} {VAR} {TRUE} {FALSE} {OPT} {CONFIG} {ENDIAN} {CODESIZE} {CPU} {FPU} {ARCHITECTURE} {PCSTOREOFFSET} {ARMASM_VERSION} {INTER} {ROPI} {RWPI} {SWST} {NOSWST} . @"},contains:[{className:"keyword",begin:"\\b(adc|(qd?|sh?|u[qh]?)?add(8|16)?|usada?8|(q|sh?|u[qh]?)?(as|sa)x|and|adrl?|sbc|rs[bc]|asr|b[lx]?|blx|bxj|cbn?z|tb[bh]|bic|bfc|bfi|[su]bfx|bkpt|cdp2?|clz|clrex|cmp|cmn|cpsi[ed]|cps|setend|dbg|dmb|dsb|eor|isb|it[te]{0,3}|lsl|lsr|ror|rrx|ldm(([id][ab])|f[ds])?|ldr((s|ex)?[bhd])?|movt?|mvn|mra|mar|mul|[us]mull|smul[bwt][bt]|smu[as]d|smmul|smmla|mla|umlaal|smlal?([wbt][bt]|d)|mls|smlsl?[ds]|smc|svc|sev|mia([bt]{2}|ph)?|mrr?c2?|mcrr2?|mrs|msr|orr|orn|pkh(tb|bt)|rbit|rev(16|sh)?|sel|[su]sat(16)?|nop|pop|push|rfe([id][ab])?|stm([id][ab])?|str(ex)?[bhd]?|(qd?)?sub|(sh?|q|u[qh]?)?sub(8|16)|[su]xt(a?h|a?b(16)?)|srs([id][ab])?|swpb?|swi|smi|tst|teq|wfe|wfi|yield)(eq|ne|cs|cc|mi|pl|vs|vc|hi|ls|ge|lt|gt|le|al|hs|lo)?[sptrx]?(?=\\s)"},e,s.QUOTE_STRING_MODE,{className:"string",begin:"'",end:"[^\\\\]'",relevance:0},{className:"title",begin:"\\|",end:"\\|",illegal:"\\n",relevance:0},{className:"number",variants:[{begin:"[#$=]?0x[0-9a-f]+"},{begin:"[#$=]?0b[01]+"},{begin:"[#$=]\\d+"},{begin:"\\b\\d+"}],relevance:0},{className:"symbol",variants:[{begin:"^[ \\t]*[a-z_\\.\\$][a-z0-9_\\.\\$]+:"},{begin:"^[a-z_\\.\\$][a-z0-9_\\.\\$]+"},{begin:"[=#]\\w+"}],relevance:0}]}}}());hljs.registerLanguage("go",function(){"use strict";return function(e){var n={keyword:"break default func interface select case map struct chan else goto package switch const fallthrough if range type continue for import return var go defer bool byte complex64 complex128 float32 float64 int8 int16 int32 int64 string uint8 uint16 uint32 uint64 int uint uintptr rune",literal:"true false iota nil",built_in:"append cap close complex copy imag len make new panic print println real recover delete"};return{name:"Go",aliases:["golang"],keywords:n,illegal:">>|\.\.\.) /},i={className:"subst",begin:/\{/,end:/\}/,keywords:n,illegal:/#/},s={begin:/\{\{/,relevance:0},r={className:"string",contains:[e.BACKSLASH_ESCAPE],variants:[{begin:/(u|b)?r?'''/,end:/'''/,contains:[e.BACKSLASH_ESCAPE,a],relevance:10},{begin:/(u|b)?r?"""/,end:/"""/,contains:[e.BACKSLASH_ESCAPE,a],relevance:10},{begin:/(fr|rf|f)'''/,end:/'''/,contains:[e.BACKSLASH_ESCAPE,a,s,i]},{begin:/(fr|rf|f)"""/,end:/"""/,contains:[e.BACKSLASH_ESCAPE,a,s,i]},{begin:/(u|r|ur)'/,end:/'/,relevance:10},{begin:/(u|r|ur)"/,end:/"/,relevance:10},{begin:/(b|br)'/,end:/'/},{begin:/(b|br)"/,end:/"/},{begin:/(fr|rf|f)'/,end:/'/,contains:[e.BACKSLASH_ESCAPE,s,i]},{begin:/(fr|rf|f)"/,end:/"/,contains:[e.BACKSLASH_ESCAPE,s,i]},e.APOS_STRING_MODE,e.QUOTE_STRING_MODE]},l={className:"number",relevance:0,variants:[{begin:e.BINARY_NUMBER_RE+"[lLjJ]?"},{begin:"\\b(0o[0-7]+)[lLjJ]?"},{begin:e.C_NUMBER_RE+"[lLjJ]?"}]},t={className:"params",variants:[{begin:/\(\s*\)/,skip:!0,className:null},{begin:/\(/,end:/\)/,excludeBegin:!0,excludeEnd:!0,contains:["self",a,l,r,e.HASH_COMMENT_MODE]}]};return i.contains=[r,l,a],{name:"Python",aliases:["py","gyp","ipython"],keywords:n,illegal:/(<\/|->|\?)|=>/,contains:[a,l,{beginKeywords:"if",relevance:0},r,e.HASH_COMMENT_MODE,{variants:[{className:"function",beginKeywords:"def"},{className:"class",beginKeywords:"class"}],end:/:/,illegal:/[${=;\n,]/,contains:[e.UNDERSCORE_TITLE_MODE,t,{begin:/->/,endsWithParent:!0,keywords:"None"}]},{className:"meta",begin:/^[\t ]*@/,end:/$/},{begin:/\b(print|exec)\(/}]}}}());hljs.registerLanguage("shell",function(){"use strict";return function(s){return{name:"Shell Session",aliases:["console"],contains:[{className:"meta",begin:"^\\s{0,3}[/\\w\\d\\[\\]()@-]*[>%$#]",starts:{end:"$",subLanguage:"bash"}}]}}}());hljs.registerLanguage("scala",function(){"use strict";return function(e){var n={className:"subst",variants:[{begin:"\\$[A-Za-z0-9_]+"},{begin:"\\${",end:"}"}]},a={className:"string",variants:[{begin:'"',end:'"',illegal:"\\n",contains:[e.BACKSLASH_ESCAPE]},{begin:'"""',end:'"""',relevance:10},{begin:'[a-z]+"',end:'"',illegal:"\\n",contains:[e.BACKSLASH_ESCAPE,n]},{className:"string",begin:'[a-z]+"""',end:'"""',contains:[n],relevance:10}]},s={className:"type",begin:"\\b[A-Z][A-Za-z0-9_]*",relevance:0},t={className:"title",begin:/[^0-9\n\t "'(),.`{}\[\]:;][^\n\t "'(),.`{}\[\]:;]+|[^0-9\n\t "'(),.`{}\[\]:;=]/,relevance:0},i={className:"class",beginKeywords:"class object trait type",end:/[:={\[\n;]/,excludeEnd:!0,contains:[{beginKeywords:"extends with",relevance:10},{begin:/\[/,end:/\]/,excludeBegin:!0,excludeEnd:!0,relevance:0,contains:[s]},{className:"params",begin:/\(/,end:/\)/,excludeBegin:!0,excludeEnd:!0,relevance:0,contains:[s]},t]},l={className:"function",beginKeywords:"def",end:/[:={\[(\n;]/,excludeEnd:!0,contains:[t]};return{name:"Scala",keywords:{literal:"true false null",keyword:"type yield lazy override def with val var sealed abstract private trait object if forSome for while throw finally protected extends import final return else break new catch super class case package default try this match continue throws implicit"},contains:[e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,a,{className:"symbol",begin:"'\\w[\\w\\d_]*(?!')"},s,l,i,e.C_NUMBER_MODE,{className:"meta",begin:"@[A-Za-z]+"}]}}}());hljs.registerLanguage("julia",function(){"use strict";return function(e){var r="[A-Za-z_\\u00A1-\\uFFFF][A-Za-z_0-9\\u00A1-\\uFFFF]*",t={$pattern:r,keyword:"in isa where baremodule begin break catch ccall const continue do else elseif end export false finally for function global if import importall let local macro module quote return true try using while type immutable abstract bitstype typealias ",literal:"true false ARGS C_NULL DevNull ENDIAN_BOM ENV I Inf Inf16 Inf32 Inf64 InsertionSort JULIA_HOME LOAD_PATH MergeSort NaN NaN16 NaN32 NaN64 PROGRAM_FILE QuickSort RoundDown RoundFromZero RoundNearest RoundNearestTiesAway RoundNearestTiesUp RoundToZero RoundUp STDERR STDIN STDOUT VERSION catalan e|0 eu|0 eulergamma golden im nothing pi γ π φ ",built_in:"ANY AbstractArray AbstractChannel AbstractFloat AbstractMatrix AbstractRNG AbstractSerializer AbstractSet AbstractSparseArray AbstractSparseMatrix AbstractSparseVector AbstractString AbstractUnitRange AbstractVecOrMat AbstractVector Any ArgumentError Array AssertionError Associative Base64DecodePipe Base64EncodePipe Bidiagonal BigFloat BigInt BitArray BitMatrix BitVector Bool BoundsError BufferStream CachingPool CapturedException CartesianIndex CartesianRange Cchar Cdouble Cfloat Channel Char Cint Cintmax_t Clong Clonglong ClusterManager Cmd CodeInfo Colon Complex Complex128 Complex32 Complex64 CompositeException Condition ConjArray ConjMatrix ConjVector Cptrdiff_t Cshort Csize_t Cssize_t Cstring Cuchar Cuint Cuintmax_t Culong Culonglong Cushort Cwchar_t Cwstring DataType Date DateFormat DateTime DenseArray DenseMatrix DenseVecOrMat DenseVector Diagonal Dict DimensionMismatch Dims DirectIndexString Display DivideError DomainError EOFError EachLine Enum Enumerate ErrorException Exception ExponentialBackOff Expr Factorization FileMonitor Float16 Float32 Float64 Function Future GlobalRef GotoNode HTML Hermitian IO IOBuffer IOContext IOStream IPAddr IPv4 IPv6 IndexCartesian IndexLinear IndexStyle InexactError InitError Int Int128 Int16 Int32 Int64 Int8 IntSet Integer InterruptException InvalidStateException Irrational KeyError LabelNode LinSpace LineNumberNode LoadError LowerTriangular MIME Matrix MersenneTwister Method MethodError MethodTable Module NTuple NewvarNode NullException Nullable Number ObjectIdDict OrdinalRange OutOfMemoryError OverflowError Pair ParseError PartialQuickSort PermutedDimsArray Pipe PollingFileWatcher ProcessExitedException Ptr QuoteNode RandomDevice Range RangeIndex Rational RawFD ReadOnlyMemoryError Real ReentrantLock Ref Regex RegexMatch RemoteChannel RemoteException RevString RoundingMode RowVector SSAValue SegmentationFault SerializationState Set SharedArray SharedMatrix SharedVector Signed SimpleVector Slot SlotNumber SparseMatrixCSC SparseVector StackFrame StackOverflowError StackTrace StepRange StepRangeLen StridedArray StridedMatrix StridedVecOrMat StridedVector String SubArray SubString SymTridiagonal Symbol Symmetric SystemError TCPSocket Task Text TextDisplay Timer Tridiagonal Tuple Type TypeError TypeMapEntry TypeMapLevel TypeName TypeVar TypedSlot UDPSocket UInt UInt128 UInt16 UInt32 UInt64 UInt8 UndefRefError UndefVarError UnicodeError UniformScaling Union UnionAll UnitRange Unsigned UpperTriangular Val Vararg VecElement VecOrMat Vector VersionNumber Void WeakKeyDict WeakRef WorkerConfig WorkerPool "},a={keywords:t,illegal:/<\//},n={className:"subst",begin:/\$\(/,end:/\)/,keywords:t},o={className:"variable",begin:"\\$"+r},i={className:"string",contains:[e.BACKSLASH_ESCAPE,n,o],variants:[{begin:/\w*"""/,end:/"""\w*/,relevance:10},{begin:/\w*"/,end:/"\w*/}]},l={className:"string",contains:[e.BACKSLASH_ESCAPE,n,o],begin:"`",end:"`"},s={className:"meta",begin:"@"+r};return a.name="Julia",a.contains=[{className:"number",begin:/(\b0x[\d_]*(\.[\d_]*)?|0x\.\d[\d_]*)p[-+]?\d+|\b0[box][a-fA-F0-9][a-fA-F0-9_]*|(\b\d[\d_]*(\.[\d_]*)?|\.\d[\d_]*)([eEfF][-+]?\d+)?/,relevance:0},{className:"string",begin:/'(.|\\[xXuU][a-zA-Z0-9]+)'/},i,l,s,{className:"comment",variants:[{begin:"#=",end:"=#",relevance:10},{begin:"#",end:"$"}]},e.HASH_COMMENT_MODE,{className:"keyword",begin:"\\b(((abstract|primitive)\\s+)type|(mutable\\s+)?struct)\\b"},{begin:/<:/}],n.contains=a.contains,a}}());hljs.registerLanguage("php-template",function(){"use strict";return function(n){return{name:"PHP template",subLanguage:"xml",contains:[{begin:/<\?(php|=)?/,end:/\?>/,subLanguage:"php",contains:[{begin:"/\\*",end:"\\*/",skip:!0},{begin:'b"',end:'"',skip:!0},{begin:"b'",end:"'",skip:!0},n.inherit(n.APOS_STRING_MODE,{illegal:null,className:null,contains:null,skip:!0}),n.inherit(n.QUOTE_STRING_MODE,{illegal:null,className:null,contains:null,skip:!0})]}]}}}());hljs.registerLanguage("scss",function(){"use strict";return function(e){var t={className:"variable",begin:"(\\$[a-zA-Z-][a-zA-Z0-9_-]*)\\b"},i={className:"number",begin:"#[0-9A-Fa-f]+"};return e.CSS_NUMBER_MODE,e.QUOTE_STRING_MODE,e.APOS_STRING_MODE,e.C_BLOCK_COMMENT_MODE,{name:"SCSS",case_insensitive:!0,illegal:"[=/|']",contains:[e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,{className:"selector-id",begin:"\\#[A-Za-z0-9_-]+",relevance:0},{className:"selector-class",begin:"\\.[A-Za-z0-9_-]+",relevance:0},{className:"selector-attr",begin:"\\[",end:"\\]",illegal:"$"},{className:"selector-tag",begin:"\\b(a|abbr|acronym|address|area|article|aside|audio|b|base|big|blockquote|body|br|button|canvas|caption|cite|code|col|colgroup|command|datalist|dd|del|details|dfn|div|dl|dt|em|embed|fieldset|figcaption|figure|footer|form|frame|frameset|(h[1-6])|head|header|hgroup|hr|html|i|iframe|img|input|ins|kbd|keygen|label|legend|li|link|map|mark|meta|meter|nav|noframes|noscript|object|ol|optgroup|option|output|p|param|pre|progress|q|rp|rt|ruby|samp|script|section|select|small|span|strike|strong|style|sub|sup|table|tbody|td|textarea|tfoot|th|thead|time|title|tr|tt|ul|var|video)\\b",relevance:0},{className:"selector-pseudo",begin:":(visited|valid|root|right|required|read-write|read-only|out-range|optional|only-of-type|only-child|nth-of-type|nth-last-of-type|nth-last-child|nth-child|not|link|left|last-of-type|last-child|lang|invalid|indeterminate|in-range|hover|focus|first-of-type|first-line|first-letter|first-child|first|enabled|empty|disabled|default|checked|before|after|active)"},{className:"selector-pseudo",begin:"::(after|before|choices|first-letter|first-line|repeat-index|repeat-item|selection|value)"},t,{className:"attribute",begin:"\\b(src|z-index|word-wrap|word-spacing|word-break|width|widows|white-space|visibility|vertical-align|unicode-bidi|transition-timing-function|transition-property|transition-duration|transition-delay|transition|transform-style|transform-origin|transform|top|text-underline-position|text-transform|text-shadow|text-rendering|text-overflow|text-indent|text-decoration-style|text-decoration-line|text-decoration-color|text-decoration|text-align-last|text-align|tab-size|table-layout|right|resize|quotes|position|pointer-events|perspective-origin|perspective|page-break-inside|page-break-before|page-break-after|padding-top|padding-right|padding-left|padding-bottom|padding|overflow-y|overflow-x|overflow-wrap|overflow|outline-width|outline-style|outline-offset|outline-color|outline|orphans|order|opacity|object-position|object-fit|normal|none|nav-up|nav-right|nav-left|nav-index|nav-down|min-width|min-height|max-width|max-height|mask|marks|margin-top|margin-right|margin-left|margin-bottom|margin|list-style-type|list-style-position|list-style-image|list-style|line-height|letter-spacing|left|justify-content|initial|inherit|ime-mode|image-orientation|image-resolution|image-rendering|icon|hyphens|height|font-weight|font-variant-ligatures|font-variant|font-style|font-stretch|font-size-adjust|font-size|font-language-override|font-kerning|font-feature-settings|font-family|font|float|flex-wrap|flex-shrink|flex-grow|flex-flow|flex-direction|flex-basis|flex|filter|empty-cells|display|direction|cursor|counter-reset|counter-increment|content|column-width|column-span|column-rule-width|column-rule-style|column-rule-color|column-rule|column-gap|column-fill|column-count|columns|color|clip-path|clip|clear|caption-side|break-inside|break-before|break-after|box-sizing|box-shadow|box-decoration-break|bottom|border-width|border-top-width|border-top-style|border-top-right-radius|border-top-left-radius|border-top-color|border-top|border-style|border-spacing|border-right-width|border-right-style|border-right-color|border-right|border-radius|border-left-width|border-left-style|border-left-color|border-left|border-image-width|border-image-source|border-image-slice|border-image-repeat|border-image-outset|border-image|border-color|border-collapse|border-bottom-width|border-bottom-style|border-bottom-right-radius|border-bottom-left-radius|border-bottom-color|border-bottom|border|background-size|background-repeat|background-position|background-origin|background-image|background-color|background-clip|background-attachment|background-blend-mode|background|backface-visibility|auto|animation-timing-function|animation-play-state|animation-name|animation-iteration-count|animation-fill-mode|animation-duration|animation-direction|animation-delay|animation|align-self|align-items|align-content)\\b",illegal:"[^\\s]"},{begin:"\\b(whitespace|wait|w-resize|visible|vertical-text|vertical-ideographic|uppercase|upper-roman|upper-alpha|underline|transparent|top|thin|thick|text|text-top|text-bottom|tb-rl|table-header-group|table-footer-group|sw-resize|super|strict|static|square|solid|small-caps|separate|se-resize|scroll|s-resize|rtl|row-resize|ridge|right|repeat|repeat-y|repeat-x|relative|progress|pointer|overline|outside|outset|oblique|nowrap|not-allowed|normal|none|nw-resize|no-repeat|no-drop|newspaper|ne-resize|n-resize|move|middle|medium|ltr|lr-tb|lowercase|lower-roman|lower-alpha|loose|list-item|line|line-through|line-edge|lighter|left|keep-all|justify|italic|inter-word|inter-ideograph|inside|inset|inline|inline-block|inherit|inactive|ideograph-space|ideograph-parenthesis|ideograph-numeric|ideograph-alpha|horizontal|hidden|help|hand|groove|fixed|ellipsis|e-resize|double|dotted|distribute|distribute-space|distribute-letter|distribute-all-lines|disc|disabled|default|decimal|dashed|crosshair|collapse|col-resize|circle|char|center|capitalize|break-word|break-all|bottom|both|bolder|bold|block|bidi-override|below|baseline|auto|always|all-scroll|absolute|table|table-cell)\\b"},{begin:":",end:";",contains:[t,i,e.CSS_NUMBER_MODE,e.QUOTE_STRING_MODE,e.APOS_STRING_MODE,{className:"meta",begin:"!important"}]},{begin:"@(page|font-face)",lexemes:"@[a-z-]+",keywords:"@page @font-face"},{begin:"@",end:"[{;]",returnBegin:!0,keywords:"and or not only",contains:[{begin:"@[a-z-]+",className:"keyword"},t,e.QUOTE_STRING_MODE,e.APOS_STRING_MODE,i,e.CSS_NUMBER_MODE]}]}}}());hljs.registerLanguage("r",function(){"use strict";return function(e){var n="([a-zA-Z]|\\.[a-zA-Z.])[a-zA-Z0-9._]*";return{name:"R",contains:[e.HASH_COMMENT_MODE,{begin:n,keywords:{$pattern:n,keyword:"function if in break next repeat else for return switch while try tryCatch stop warning require library attach detach source setMethod setGeneric setGroupGeneric setClass ...",literal:"NULL NA TRUE FALSE T F Inf NaN NA_integer_|10 NA_real_|10 NA_character_|10 NA_complex_|10"},relevance:0},{className:"number",begin:"0[xX][0-9a-fA-F]+[Li]?\\b",relevance:0},{className:"number",begin:"\\d+(?:[eE][+\\-]?\\d*)?L\\b",relevance:0},{className:"number",begin:"\\d+\\.(?!\\d)(?:i\\b)?",relevance:0},{className:"number",begin:"\\d+(?:\\.\\d*)?(?:[eE][+\\-]?\\d*)?i?\\b",relevance:0},{className:"number",begin:"\\.\\d+(?:[eE][+\\-]?\\d*)?i?\\b",relevance:0},{begin:"`",end:"`",relevance:0},{className:"string",contains:[e.BACKSLASH_ESCAPE],variants:[{begin:'"',end:'"'},{begin:"'",end:"'"}]}]}}}());hljs.registerLanguage("sql",function(){"use strict";return function(e){var t=e.COMMENT("--","$");return{name:"SQL",case_insensitive:!0,illegal:/[<>{}*]/,contains:[{beginKeywords:"begin end start commit rollback savepoint lock alter create drop rename call delete do handler insert load replace select truncate update set show pragma grant merge describe use explain help declare prepare execute deallocate release unlock purge reset change stop analyze cache flush optimize repair kill install uninstall checksum restore check backup revoke comment values with",end:/;/,endsWithParent:!0,keywords:{$pattern:/[\w\.]+/,keyword:"as abort abs absolute acc acce accep accept access accessed accessible account acos action activate add addtime admin administer advanced advise aes_decrypt aes_encrypt after agent aggregate ali alia alias all allocate allow alter always analyze ancillary and anti any anydata anydataset anyschema anytype apply archive archived archivelog are as asc ascii asin assembly assertion associate asynchronous at atan atn2 attr attri attrib attribu attribut attribute attributes audit authenticated authentication authid authors auto autoallocate autodblink autoextend automatic availability avg backup badfile basicfile before begin beginning benchmark between bfile bfile_base big bigfile bin binary_double binary_float binlog bit_and bit_count bit_length bit_or bit_xor bitmap blob_base block blocksize body both bound bucket buffer_cache buffer_pool build bulk by byte byteordermark bytes cache caching call calling cancel capacity cascade cascaded case cast catalog category ceil ceiling chain change changed char_base char_length character_length characters characterset charindex charset charsetform charsetid check checksum checksum_agg child choose chr chunk class cleanup clear client clob clob_base clone close cluster_id cluster_probability cluster_set clustering coalesce coercibility col collate collation collect colu colum column column_value columns columns_updated comment commit compact compatibility compiled complete composite_limit compound compress compute concat concat_ws concurrent confirm conn connec connect connect_by_iscycle connect_by_isleaf connect_by_root connect_time connection consider consistent constant constraint constraints constructor container content contents context contributors controlfile conv convert convert_tz corr corr_k corr_s corresponding corruption cos cost count count_big counted covar_pop covar_samp cpu_per_call cpu_per_session crc32 create creation critical cross cube cume_dist curdate current current_date current_time current_timestamp current_user cursor curtime customdatum cycle data database databases datafile datafiles datalength date_add date_cache date_format date_sub dateadd datediff datefromparts datename datepart datetime2fromparts day day_to_second dayname dayofmonth dayofweek dayofyear days db_role_change dbtimezone ddl deallocate declare decode decompose decrement decrypt deduplicate def defa defau defaul default defaults deferred defi defin define degrees delayed delegate delete delete_all delimited demand dense_rank depth dequeue des_decrypt des_encrypt des_key_file desc descr descri describ describe descriptor deterministic diagnostics difference dimension direct_load directory disable disable_all disallow disassociate discardfile disconnect diskgroup distinct distinctrow distribute distributed div do document domain dotnet double downgrade drop dumpfile duplicate duration each edition editionable editions element ellipsis else elsif elt empty enable enable_all enclosed encode encoding encrypt end end-exec endian enforced engine engines enqueue enterprise entityescaping eomonth error errors escaped evalname evaluate event eventdata events except exception exceptions exchange exclude excluding execu execut execute exempt exists exit exp expire explain explode export export_set extended extent external external_1 external_2 externally extract failed failed_login_attempts failover failure far fast feature_set feature_value fetch field fields file file_name_convert filesystem_like_logging final finish first first_value fixed flash_cache flashback floor flush following follows for forall force foreign form forma format found found_rows freelist freelists freepools fresh from from_base64 from_days ftp full function general generated get get_format get_lock getdate getutcdate global global_name globally go goto grant grants greatest group group_concat group_id grouping grouping_id groups gtid_subtract guarantee guard handler hash hashkeys having hea head headi headin heading heap help hex hierarchy high high_priority hosts hour hours http id ident_current ident_incr ident_seed identified identity idle_time if ifnull ignore iif ilike ilm immediate import in include including increment index indexes indexing indextype indicator indices inet6_aton inet6_ntoa inet_aton inet_ntoa infile initial initialized initially initrans inmemory inner innodb input insert install instance instantiable instr interface interleaved intersect into invalidate invisible is is_free_lock is_ipv4 is_ipv4_compat is_not is_not_null is_used_lock isdate isnull isolation iterate java join json json_exists keep keep_duplicates key keys kill language large last last_day last_insert_id last_value lateral lax lcase lead leading least leaves left len lenght length less level levels library like like2 like4 likec limit lines link list listagg little ln load load_file lob lobs local localtime localtimestamp locate locator lock locked log log10 log2 logfile logfiles logging logical logical_reads_per_call logoff logon logs long loop low low_priority lower lpad lrtrim ltrim main make_set makedate maketime managed management manual map mapping mask master master_pos_wait match matched materialized max maxextents maximize maxinstances maxlen maxlogfiles maxloghistory maxlogmembers maxsize maxtrans md5 measures median medium member memcompress memory merge microsecond mid migration min minextents minimum mining minus minute minutes minvalue missing mod mode model modification modify module monitoring month months mount move movement multiset mutex name name_const names nan national native natural nav nchar nclob nested never new newline next nextval no no_write_to_binlog noarchivelog noaudit nobadfile nocheck nocompress nocopy nocycle nodelay nodiscardfile noentityescaping noguarantee nokeep nologfile nomapping nomaxvalue nominimize nominvalue nomonitoring none noneditionable nonschema noorder nopr nopro noprom nopromp noprompt norely noresetlogs noreverse normal norowdependencies noschemacheck noswitch not nothing notice notnull notrim novalidate now nowait nth_value nullif nulls num numb numbe nvarchar nvarchar2 object ocicoll ocidate ocidatetime ociduration ociinterval ociloblocator ocinumber ociref ocirefcursor ocirowid ocistring ocitype oct octet_length of off offline offset oid oidindex old on online only opaque open operations operator optimal optimize option optionally or oracle oracle_date oradata ord ordaudio orddicom orddoc order ordimage ordinality ordvideo organization orlany orlvary out outer outfile outline output over overflow overriding package pad parallel parallel_enable parameters parent parse partial partition partitions pascal passing password password_grace_time password_lock_time password_reuse_max password_reuse_time password_verify_function patch path patindex pctincrease pctthreshold pctused pctversion percent percent_rank percentile_cont percentile_disc performance period period_add period_diff permanent physical pi pipe pipelined pivot pluggable plugin policy position post_transaction pow power pragma prebuilt precedes preceding precision prediction prediction_cost prediction_details prediction_probability prediction_set prepare present preserve prior priority private private_sga privileges procedural procedure procedure_analyze processlist profiles project prompt protection public publishingservername purge quarter query quick quiesce quota quotename radians raise rand range rank raw read reads readsize rebuild record records recover recovery recursive recycle redo reduced ref reference referenced references referencing refresh regexp_like register regr_avgx regr_avgy regr_count regr_intercept regr_r2 regr_slope regr_sxx regr_sxy reject rekey relational relative relaylog release release_lock relies_on relocate rely rem remainder rename repair repeat replace replicate replication required reset resetlogs resize resource respect restore restricted result result_cache resumable resume retention return returning returns reuse reverse revoke right rlike role roles rollback rolling rollup round row row_count rowdependencies rowid rownum rows rtrim rules safe salt sample save savepoint sb1 sb2 sb4 scan schema schemacheck scn scope scroll sdo_georaster sdo_topo_geometry search sec_to_time second seconds section securefile security seed segment select self semi sequence sequential serializable server servererror session session_user sessions_per_user set sets settings sha sha1 sha2 share shared shared_pool short show shrink shutdown si_averagecolor si_colorhistogram si_featurelist si_positionalcolor si_stillimage si_texture siblings sid sign sin size size_t sizes skip slave sleep smalldatetimefromparts smallfile snapshot some soname sort soundex source space sparse spfile split sql sql_big_result sql_buffer_result sql_cache sql_calc_found_rows sql_small_result sql_variant_property sqlcode sqldata sqlerror sqlname sqlstate sqrt square standalone standby start starting startup statement static statistics stats_binomial_test stats_crosstab stats_ks_test stats_mode stats_mw_test stats_one_way_anova stats_t_test_ stats_t_test_indep stats_t_test_one stats_t_test_paired stats_wsr_test status std stddev stddev_pop stddev_samp stdev stop storage store stored str str_to_date straight_join strcmp strict string struct stuff style subdate subpartition subpartitions substitutable substr substring subtime subtring_index subtype success sum suspend switch switchoffset switchover sync synchronous synonym sys sys_xmlagg sysasm sysaux sysdate sysdatetimeoffset sysdba sysoper system system_user sysutcdatetime table tables tablespace tablesample tan tdo template temporary terminated tertiary_weights test than then thread through tier ties time time_format time_zone timediff timefromparts timeout timestamp timestampadd timestampdiff timezone_abbr timezone_minute timezone_region to to_base64 to_date to_days to_seconds todatetimeoffset trace tracking transaction transactional translate translation treat trigger trigger_nestlevel triggers trim truncate try_cast try_convert try_parse type ub1 ub2 ub4 ucase unarchived unbounded uncompress under undo unhex unicode uniform uninstall union unique unix_timestamp unknown unlimited unlock unnest unpivot unrecoverable unsafe unsigned until untrusted unusable unused update updated upgrade upped upper upsert url urowid usable usage use use_stored_outlines user user_data user_resources users using utc_date utc_timestamp uuid uuid_short validate validate_password_strength validation valist value values var var_samp varcharc vari varia variab variabl variable variables variance varp varraw varrawc varray verify version versions view virtual visible void wait wallet warning warnings week weekday weekofyear wellformed when whene whenev wheneve whenever where while whitespace window with within without work wrapped xdb xml xmlagg xmlattributes xmlcast xmlcolattval xmlelement xmlexists xmlforest xmlindex xmlnamespaces xmlpi xmlquery xmlroot xmlschema xmlserialize xmltable xmltype xor year year_to_month years yearweek",literal:"true false null unknown",built_in:"array bigint binary bit blob bool boolean char character date dec decimal float int int8 integer interval number numeric real record serial serial8 smallint text time timestamp tinyint varchar varchar2 varying void"},contains:[{className:"string",begin:"'",end:"'",contains:[{begin:"''"}]},{className:"string",begin:'"',end:'"',contains:[{begin:'""'}]},{className:"string",begin:"`",end:"`"},e.C_NUMBER_MODE,e.C_BLOCK_COMMENT_MODE,t,e.HASH_COMMENT_MODE]},e.C_BLOCK_COMMENT_MODE,t,e.HASH_COMMENT_MODE]}}}());hljs.registerLanguage("c",function(){"use strict";return function(e){var n=e.getLanguage("c-like").rawDefinition();return n.name="C",n.aliases=["c","h"],n}}());hljs.registerLanguage("json",function(){"use strict";return function(n){var e={literal:"true false null"},i=[n.C_LINE_COMMENT_MODE,n.C_BLOCK_COMMENT_MODE],t=[n.QUOTE_STRING_MODE,n.C_NUMBER_MODE],a={end:",",endsWithParent:!0,excludeEnd:!0,contains:t,keywords:e},l={begin:"{",end:"}",contains:[{className:"attr",begin:/"/,end:/"/,contains:[n.BACKSLASH_ESCAPE],illegal:"\\n"},n.inherit(a,{begin:/:/})].concat(i),illegal:"\\S"},s={begin:"\\[",end:"\\]",contains:[n.inherit(a)],illegal:"\\S"};return t.push(l,s),i.forEach((function(n){t.push(n)})),{name:"JSON",contains:t,keywords:e,illegal:"\\S"}}}());hljs.registerLanguage("python-repl",function(){"use strict";return function(n){return{aliases:["pycon"],contains:[{className:"meta",starts:{end:/ |$/,starts:{end:"$",subLanguage:"python"}},variants:[{begin:/^>>>(?=[ ]|$)/},{begin:/^\.\.\.(?=[ ]|$)/}]}]}}}());hljs.registerLanguage("markdown",function(){"use strict";return function(n){const e={begin:"<",end:">",subLanguage:"xml",relevance:0},a={begin:"\\[.+?\\][\\(\\[].*?[\\)\\]]",returnBegin:!0,contains:[{className:"string",begin:"\\[",end:"\\]",excludeBegin:!0,returnEnd:!0,relevance:0},{className:"link",begin:"\\]\\(",end:"\\)",excludeBegin:!0,excludeEnd:!0},{className:"symbol",begin:"\\]\\[",end:"\\]",excludeBegin:!0,excludeEnd:!0}],relevance:10},i={className:"strong",contains:[],variants:[{begin:/_{2}/,end:/_{2}/},{begin:/\*{2}/,end:/\*{2}/}]},s={className:"emphasis",contains:[],variants:[{begin:/\*(?!\*)/,end:/\*/},{begin:/_(?!_)/,end:/_/,relevance:0}]};i.contains.push(s),s.contains.push(i);var c=[e,a];return i.contains=i.contains.concat(c),s.contains=s.contains.concat(c),{name:"Markdown",aliases:["md","mkdown","mkd"],contains:[{className:"section",variants:[{begin:"^#{1,6}",end:"$",contains:c=c.concat(i,s)},{begin:"(?=^.+?\\n[=-]{2,}$)",contains:[{begin:"^[=-]*$"},{begin:"^",end:"\\n",contains:c}]}]},e,{className:"bullet",begin:"^[ \t]*([*+-]|(\\d+\\.))(?=\\s+)",end:"\\s+",excludeEnd:!0},i,s,{className:"quote",begin:"^>\\s+",contains:c,end:"$"},{className:"code",variants:[{begin:"(`{3,})(.|\\n)*?\\1`*[ ]*"},{begin:"(~{3,})(.|\\n)*?\\1~*[ ]*"},{begin:"```",end:"```+[ ]*$"},{begin:"~~~",end:"~~~+[ ]*$"},{begin:"`.+?`"},{begin:"(?=^( {4}|\\t))",contains:[{begin:"^( {4}|\\t)",end:"(\\n)$"}],relevance:0}]},{begin:"^[-\\*]{3,}",end:"$"},a,{begin:/^\[[^\n]+\]:/,returnBegin:!0,contains:[{className:"symbol",begin:/\[/,end:/\]/,excludeBegin:!0,excludeEnd:!0},{className:"link",begin:/:\s*/,end:/$/,excludeBegin:!0}]}]}}}());hljs.registerLanguage("javascript",function(){"use strict";const e=["as","in","of","if","for","while","finally","var","new","function","do","return","void","else","break","catch","instanceof","with","throw","case","default","try","switch","continue","typeof","delete","let","yield","const","class","debugger","async","await","static","import","from","export","extends"],n=["true","false","null","undefined","NaN","Infinity"],a=[].concat(["setInterval","setTimeout","clearInterval","clearTimeout","require","exports","eval","isFinite","isNaN","parseFloat","parseInt","decodeURI","decodeURIComponent","encodeURI","encodeURIComponent","escape","unescape"],["arguments","this","super","console","window","document","localStorage","module","global"],["Intl","DataView","Number","Math","Date","String","RegExp","Object","Function","Boolean","Error","Symbol","Set","Map","WeakSet","WeakMap","Proxy","Reflect","JSON","Promise","Float64Array","Int16Array","Int32Array","Int8Array","Uint16Array","Uint32Array","Float32Array","Array","Uint8Array","Uint8ClampedArray","ArrayBuffer"],["EvalError","InternalError","RangeError","ReferenceError","SyntaxError","TypeError","URIError"]);function s(e){return r("(?=",e,")")}function r(...e){return e.map(e=>(function(e){return e?"string"==typeof e?e:e.source:null})(e)).join("")}return function(t){var i="[A-Za-z$_][0-9A-Za-z$_]*",c={begin:/<[A-Za-z0-9\\._:-]+/,end:/\/[A-Za-z0-9\\._:-]+>|\/>/},o={$pattern:"[A-Za-z$_][0-9A-Za-z$_]*",keyword:e.join(" "),literal:n.join(" "),built_in:a.join(" ")},l={className:"number",variants:[{begin:"\\b(0[bB][01]+)n?"},{begin:"\\b(0[oO][0-7]+)n?"},{begin:t.C_NUMBER_RE+"n?"}],relevance:0},E={className:"subst",begin:"\\$\\{",end:"\\}",keywords:o,contains:[]},d={begin:"html`",end:"",starts:{end:"`",returnEnd:!1,contains:[t.BACKSLASH_ESCAPE,E],subLanguage:"xml"}},g={begin:"css`",end:"",starts:{end:"`",returnEnd:!1,contains:[t.BACKSLASH_ESCAPE,E],subLanguage:"css"}},u={className:"string",begin:"`",end:"`",contains:[t.BACKSLASH_ESCAPE,E]};E.contains=[t.APOS_STRING_MODE,t.QUOTE_STRING_MODE,d,g,u,l,t.REGEXP_MODE];var b=E.contains.concat([{begin:/\(/,end:/\)/,contains:["self"].concat(E.contains,[t.C_BLOCK_COMMENT_MODE,t.C_LINE_COMMENT_MODE])},t.C_BLOCK_COMMENT_MODE,t.C_LINE_COMMENT_MODE]),_={className:"params",begin:/\(/,end:/\)/,excludeBegin:!0,excludeEnd:!0,contains:b};return{name:"JavaScript",aliases:["js","jsx","mjs","cjs"],keywords:o,contains:[t.SHEBANG({binary:"node",relevance:5}),{className:"meta",relevance:10,begin:/^\s*['"]use (strict|asm)['"]/},t.APOS_STRING_MODE,t.QUOTE_STRING_MODE,d,g,u,t.C_LINE_COMMENT_MODE,t.COMMENT("/\\*\\*","\\*/",{relevance:0,contains:[{className:"doctag",begin:"@[A-Za-z]+",contains:[{className:"type",begin:"\\{",end:"\\}",relevance:0},{className:"variable",begin:i+"(?=\\s*(-)|$)",endsParent:!0,relevance:0},{begin:/(?=[^\n])\s/,relevance:0}]}]}),t.C_BLOCK_COMMENT_MODE,l,{begin:r(/[{,\n]\s*/,s(r(/(((\/\/.*)|(\/\*(.|\n)*\*\/))\s*)*/,i+"\\s*:"))),relevance:0,contains:[{className:"attr",begin:i+s("\\s*:"),relevance:0}]},{begin:"("+t.RE_STARTERS_RE+"|\\b(case|return|throw)\\b)\\s*",keywords:"return throw case",contains:[t.C_LINE_COMMENT_MODE,t.C_BLOCK_COMMENT_MODE,t.REGEXP_MODE,{className:"function",begin:"(\\([^(]*(\\([^(]*(\\([^(]*\\))?\\))?\\)|"+t.UNDERSCORE_IDENT_RE+")\\s*=>",returnBegin:!0,end:"\\s*=>",contains:[{className:"params",variants:[{begin:t.UNDERSCORE_IDENT_RE},{className:null,begin:/\(\s*\)/,skip:!0},{begin:/\(/,end:/\)/,excludeBegin:!0,excludeEnd:!0,keywords:o,contains:b}]}]},{begin:/,/,relevance:0},{className:"",begin:/\s/,end:/\s*/,skip:!0},{variants:[{begin:"<>",end:""},{begin:c.begin,end:c.end}],subLanguage:"xml",contains:[{begin:c.begin,end:c.end,skip:!0,contains:["self"]}]}],relevance:0},{className:"function",beginKeywords:"function",end:/\{/,excludeEnd:!0,contains:[t.inherit(t.TITLE_MODE,{begin:i}),_],illegal:/\[|%/},{begin:/\$[(.]/},t.METHOD_GUARD,{className:"class",beginKeywords:"class",end:/[{;=]/,excludeEnd:!0,illegal:/[:"\[\]]/,contains:[{beginKeywords:"extends"},t.UNDERSCORE_TITLE_MODE]},{beginKeywords:"constructor",end:/\{/,excludeEnd:!0},{begin:"(get|set)\\s+(?="+i+"\\()",end:/{/,keywords:"get set",contains:[t.inherit(t.TITLE_MODE,{begin:i}),{begin:/\(\)/},_]}],illegal:/#(?!!)/}}}());hljs.registerLanguage("typescript",function(){"use strict";const e=["as","in","of","if","for","while","finally","var","new","function","do","return","void","else","break","catch","instanceof","with","throw","case","default","try","switch","continue","typeof","delete","let","yield","const","class","debugger","async","await","static","import","from","export","extends"],n=["true","false","null","undefined","NaN","Infinity"],a=[].concat(["setInterval","setTimeout","clearInterval","clearTimeout","require","exports","eval","isFinite","isNaN","parseFloat","parseInt","decodeURI","decodeURIComponent","encodeURI","encodeURIComponent","escape","unescape"],["arguments","this","super","console","window","document","localStorage","module","global"],["Intl","DataView","Number","Math","Date","String","RegExp","Object","Function","Boolean","Error","Symbol","Set","Map","WeakSet","WeakMap","Proxy","Reflect","JSON","Promise","Float64Array","Int16Array","Int32Array","Int8Array","Uint16Array","Uint32Array","Float32Array","Array","Uint8Array","Uint8ClampedArray","ArrayBuffer"],["EvalError","InternalError","RangeError","ReferenceError","SyntaxError","TypeError","URIError"]);return function(r){var t={$pattern:"[A-Za-z$_][0-9A-Za-z$_]*",keyword:e.concat(["type","namespace","typedef","interface","public","private","protected","implements","declare","abstract","readonly"]).join(" "),literal:n.join(" "),built_in:a.concat(["any","void","number","boolean","string","object","never","enum"]).join(" ")},s={className:"meta",begin:"@[A-Za-z$_][0-9A-Za-z$_]*"},i={className:"number",variants:[{begin:"\\b(0[bB][01]+)n?"},{begin:"\\b(0[oO][0-7]+)n?"},{begin:r.C_NUMBER_RE+"n?"}],relevance:0},o={className:"subst",begin:"\\$\\{",end:"\\}",keywords:t,contains:[]},c={begin:"html`",end:"",starts:{end:"`",returnEnd:!1,contains:[r.BACKSLASH_ESCAPE,o],subLanguage:"xml"}},l={begin:"css`",end:"",starts:{end:"`",returnEnd:!1,contains:[r.BACKSLASH_ESCAPE,o],subLanguage:"css"}},E={className:"string",begin:"`",end:"`",contains:[r.BACKSLASH_ESCAPE,o]};o.contains=[r.APOS_STRING_MODE,r.QUOTE_STRING_MODE,c,l,E,i,r.REGEXP_MODE];var d={begin:"\\(",end:/\)/,keywords:t,contains:["self",r.QUOTE_STRING_MODE,r.APOS_STRING_MODE,r.NUMBER_MODE]},u={className:"params",begin:/\(/,end:/\)/,excludeBegin:!0,excludeEnd:!0,keywords:t,contains:[r.C_LINE_COMMENT_MODE,r.C_BLOCK_COMMENT_MODE,s,d]};return{name:"TypeScript",aliases:["ts"],keywords:t,contains:[r.SHEBANG(),{className:"meta",begin:/^\s*['"]use strict['"]/},r.APOS_STRING_MODE,r.QUOTE_STRING_MODE,c,l,E,r.C_LINE_COMMENT_MODE,r.C_BLOCK_COMMENT_MODE,i,{begin:"("+r.RE_STARTERS_RE+"|\\b(case|return|throw)\\b)\\s*",keywords:"return throw case",contains:[r.C_LINE_COMMENT_MODE,r.C_BLOCK_COMMENT_MODE,r.REGEXP_MODE,{className:"function",begin:"(\\([^(]*(\\([^(]*(\\([^(]*\\))?\\))?\\)|"+r.UNDERSCORE_IDENT_RE+")\\s*=>",returnBegin:!0,end:"\\s*=>",contains:[{className:"params",variants:[{begin:r.UNDERSCORE_IDENT_RE},{className:null,begin:/\(\s*\)/,skip:!0},{begin:/\(/,end:/\)/,excludeBegin:!0,excludeEnd:!0,keywords:t,contains:d.contains}]}]}],relevance:0},{className:"function",beginKeywords:"function",end:/[\{;]/,excludeEnd:!0,keywords:t,contains:["self",r.inherit(r.TITLE_MODE,{begin:"[A-Za-z$_][0-9A-Za-z$_]*"}),u],illegal:/%/,relevance:0},{beginKeywords:"constructor",end:/[\{;]/,excludeEnd:!0,contains:["self",u]},{begin:/module\./,keywords:{built_in:"module"},relevance:0},{beginKeywords:"module",end:/\{/,excludeEnd:!0},{beginKeywords:"interface",end:/\{/,excludeEnd:!0,keywords:"interface extends"},{begin:/\$[(.]/},{begin:"\\."+r.IDENT_RE,relevance:0},s,d]}}}());hljs.registerLanguage("plaintext",function(){"use strict";return function(t){return{name:"Plain text",aliases:["text","txt"],disableAutodetect:!0}}}());hljs.registerLanguage("less",function(){"use strict";return function(e){var n="([\\w-]+|@{[\\w-]+})",a=[],s=[],t=function(e){return{className:"string",begin:"~?"+e+".*?"+e}},r=function(e,n,a){return{className:e,begin:n,relevance:a}},i={begin:"\\(",end:"\\)",contains:s,relevance:0};s.push(e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,t("'"),t('"'),e.CSS_NUMBER_MODE,{begin:"(url|data-uri)\\(",starts:{className:"string",end:"[\\)\\n]",excludeEnd:!0}},r("number","#[0-9A-Fa-f]+\\b"),i,r("variable","@@?[\\w-]+",10),r("variable","@{[\\w-]+}"),r("built_in","~?`[^`]*?`"),{className:"attribute",begin:"[\\w-]+\\s*:",end:":",returnBegin:!0,excludeEnd:!0},{className:"meta",begin:"!important"});var c=s.concat({begin:"{",end:"}",contains:a}),l={beginKeywords:"when",endsWithParent:!0,contains:[{beginKeywords:"and not"}].concat(s)},o={begin:n+"\\s*:",returnBegin:!0,end:"[;}]",relevance:0,contains:[{className:"attribute",begin:n,end:":",excludeEnd:!0,starts:{endsWithParent:!0,illegal:"[<=$]",relevance:0,contains:s}}]},g={className:"keyword",begin:"@(import|media|charset|font-face|(-[a-z]+-)?keyframes|supports|document|namespace|page|viewport|host)\\b",starts:{end:"[;{}]",returnEnd:!0,contains:s,relevance:0}},d={className:"variable",variants:[{begin:"@[\\w-]+\\s*:",relevance:15},{begin:"@[\\w-]+"}],starts:{end:"[;}]",returnEnd:!0,contains:c}},b={variants:[{begin:"[\\.#:&\\[>]",end:"[;{}]"},{begin:n,end:"{"}],returnBegin:!0,returnEnd:!0,illegal:"[<='$\"]",relevance:0,contains:[e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,l,r("keyword","all\\b"),r("variable","@{[\\w-]+}"),r("selector-tag",n+"%?",0),r("selector-id","#"+n),r("selector-class","\\."+n,0),r("selector-tag","&",0),{className:"selector-attr",begin:"\\[",end:"\\]"},{className:"selector-pseudo",begin:/:(:)?[a-zA-Z0-9\_\-\+\(\)"'.]+/},{begin:"\\(",end:"\\)",contains:c},{begin:"!important"}]};return a.push(e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,g,d,o,b),{name:"Less",case_insensitive:!0,illegal:"[=>'/<($\"]",contains:a}}}());hljs.registerLanguage("lua",function(){"use strict";return function(e){var t={begin:"\\[=*\\[",end:"\\]=*\\]",contains:["self"]},a=[e.COMMENT("--(?!\\[=*\\[)","$"),e.COMMENT("--\\[=*\\[","\\]=*\\]",{contains:[t],relevance:10})];return{name:"Lua",keywords:{$pattern:e.UNDERSCORE_IDENT_RE,literal:"true false nil",keyword:"and break do else elseif end for goto if in local not or repeat return then until while",built_in:"_G _ENV _VERSION __index __newindex __mode __call __metatable __tostring __len __gc __add __sub __mul __div __mod __pow __concat __unm __eq __lt __le assert collectgarbage dofile error getfenv getmetatable ipairs load loadfile loadstring module next pairs pcall print rawequal rawget rawset require select setfenv setmetatable tonumber tostring type unpack xpcall arg self coroutine resume yield status wrap create running debug getupvalue debug sethook getmetatable gethook setmetatable setlocal traceback setfenv getinfo setupvalue getlocal getregistry getfenv io lines write close flush open output type read stderr stdin input stdout popen tmpfile math log max acos huge ldexp pi cos tanh pow deg tan cosh sinh random randomseed frexp ceil floor rad abs sqrt modf asin min mod fmod log10 atan2 exp sin atan os exit setlocale date getenv difftime remove time clock tmpname rename execute package preload loadlib loaded loaders cpath config path seeall string sub upper len gfind rep find match char dump gmatch reverse byte format gsub lower table setn insert getn foreachi maxn foreach concat sort remove"},contains:a.concat([{className:"function",beginKeywords:"function",end:"\\)",contains:[e.inherit(e.TITLE_MODE,{begin:"([_a-zA-Z]\\w*\\.)*([_a-zA-Z]\\w*:)?[_a-zA-Z]\\w*"}),{className:"params",begin:"\\(",endsWithParent:!0,contains:a}].concat(a)},e.C_NUMBER_MODE,e.APOS_STRING_MODE,e.QUOTE_STRING_MODE,{className:"string",begin:"\\[=*\\[",end:"\\]=*\\]",contains:[t],relevance:5}])}}}()); diff --git a/index.html b/index.html new file mode 100644 index 000000000..1316228a9 --- /dev/null +++ b/index.html @@ -0,0 +1,199 @@ + + + + + + Introduction - docs + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + + + +
+
+ +

Introduction

+

This site hosts the main documentation for Lambdaworks as a whole. It is still a work in progress.

+

Crates

+ + +
+ + +
+
+ + + +
+ + + + + + + + + + + + + + + + + + + + diff --git a/introduction.html b/introduction.html new file mode 100644 index 000000000..fb12de6c8 --- /dev/null +++ b/introduction.html @@ -0,0 +1,199 @@ + + + + + + Introduction - docs + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + + + +
+
+ +

Introduction

+

This site hosts the main documentation for Lambdaworks as a whole. It is still a work in progress.

+

Crates

+ + +
+ + +
+
+ + + +
+ + + + + + + + + + + + + + + + + + + + diff --git a/mark.min.js b/mark.min.js new file mode 100644 index 000000000..163623188 --- /dev/null +++ b/mark.min.js @@ -0,0 +1,7 @@ +/*!*************************************************** +* mark.js v8.11.1 +* https://markjs.io/ +* Copyright (c) 2014–2018, Julian Kühnel +* Released under the MIT license https://git.io/vwTVl +*****************************************************/ +!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t():"function"==typeof define&&define.amd?define(t):e.Mark=t()}(this,function(){"use strict";var e="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},t=function(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")},n=function(){function e(e,t){for(var n=0;n1&&void 0!==arguments[1])||arguments[1],i=arguments.length>2&&void 0!==arguments[2]?arguments[2]:[],o=arguments.length>3&&void 0!==arguments[3]?arguments[3]:5e3;t(this,e),this.ctx=n,this.iframes=r,this.exclude=i,this.iframesTimeout=o}return n(e,[{key:"getContexts",value:function(){var e=[];return(void 0!==this.ctx&&this.ctx?NodeList.prototype.isPrototypeOf(this.ctx)?Array.prototype.slice.call(this.ctx):Array.isArray(this.ctx)?this.ctx:"string"==typeof this.ctx?Array.prototype.slice.call(document.querySelectorAll(this.ctx)):[this.ctx]:[]).forEach(function(t){var n=e.filter(function(e){return e.contains(t)}).length>0;-1!==e.indexOf(t)||n||e.push(t)}),e}},{key:"getIframeContents",value:function(e,t){var n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:function(){},r=void 0;try{var i=e.contentWindow;if(r=i.document,!i||!r)throw new Error("iframe inaccessible")}catch(e){n()}r&&t(r)}},{key:"isIframeBlank",value:function(e){var t="about:blank",n=e.getAttribute("src").trim();return e.contentWindow.location.href===t&&n!==t&&n}},{key:"observeIframeLoad",value:function(e,t,n){var r=this,i=!1,o=null,a=function a(){if(!i){i=!0,clearTimeout(o);try{r.isIframeBlank(e)||(e.removeEventListener("load",a),r.getIframeContents(e,t,n))}catch(e){n()}}};e.addEventListener("load",a),o=setTimeout(a,this.iframesTimeout)}},{key:"onIframeReady",value:function(e,t,n){try{"complete"===e.contentWindow.document.readyState?this.isIframeBlank(e)?this.observeIframeLoad(e,t,n):this.getIframeContents(e,t,n):this.observeIframeLoad(e,t,n)}catch(e){n()}}},{key:"waitForIframes",value:function(e,t){var n=this,r=0;this.forEachIframe(e,function(){return!0},function(e){r++,n.waitForIframes(e.querySelector("html"),function(){--r||t()})},function(e){e||t()})}},{key:"forEachIframe",value:function(t,n,r){var i=this,o=arguments.length>3&&void 0!==arguments[3]?arguments[3]:function(){},a=t.querySelectorAll("iframe"),s=a.length,c=0;a=Array.prototype.slice.call(a);var u=function(){--s<=0&&o(c)};s||u(),a.forEach(function(t){e.matches(t,i.exclude)?u():i.onIframeReady(t,function(e){n(t)&&(c++,r(e)),u()},u)})}},{key:"createIterator",value:function(e,t,n){return document.createNodeIterator(e,t,n,!1)}},{key:"createInstanceOnIframe",value:function(t){return new e(t.querySelector("html"),this.iframes)}},{key:"compareNodeIframe",value:function(e,t,n){if(e.compareDocumentPosition(n)&Node.DOCUMENT_POSITION_PRECEDING){if(null===t)return!0;if(t.compareDocumentPosition(n)&Node.DOCUMENT_POSITION_FOLLOWING)return!0}return!1}},{key:"getIteratorNode",value:function(e){var t=e.previousNode();return{prevNode:t,node:null===t?e.nextNode():e.nextNode()&&e.nextNode()}}},{key:"checkIframeFilter",value:function(e,t,n,r){var i=!1,o=!1;return r.forEach(function(e,t){e.val===n&&(i=t,o=e.handled)}),this.compareNodeIframe(e,t,n)?(!1!==i||o?!1===i||o||(r[i].handled=!0):r.push({val:n,handled:!0}),!0):(!1===i&&r.push({val:n,handled:!1}),!1)}},{key:"handleOpenIframes",value:function(e,t,n,r){var i=this;e.forEach(function(e){e.handled||i.getIframeContents(e.val,function(e){i.createInstanceOnIframe(e).forEachNode(t,n,r)})})}},{key:"iterateThroughNodes",value:function(e,t,n,r,i){for(var o,a=this,s=this.createIterator(t,e,r),c=[],u=[],l=void 0,h=void 0;void 0,o=a.getIteratorNode(s),h=o.prevNode,l=o.node;)this.iframes&&this.forEachIframe(t,function(e){return a.checkIframeFilter(l,h,e,c)},function(t){a.createInstanceOnIframe(t).forEachNode(e,function(e){return u.push(e)},r)}),u.push(l);u.forEach(function(e){n(e)}),this.iframes&&this.handleOpenIframes(c,e,n,r),i()}},{key:"forEachNode",value:function(e,t,n){var r=this,i=arguments.length>3&&void 0!==arguments[3]?arguments[3]:function(){},o=this.getContexts(),a=o.length;a||i(),o.forEach(function(o){var s=function(){r.iterateThroughNodes(e,o,t,n,function(){--a<=0&&i()})};r.iframes?r.waitForIframes(o,s):s()})}}],[{key:"matches",value:function(e,t){var n="string"==typeof t?[t]:t,r=e.matches||e.matchesSelector||e.msMatchesSelector||e.mozMatchesSelector||e.oMatchesSelector||e.webkitMatchesSelector;if(r){var i=!1;return n.every(function(t){return!r.call(e,t)||(i=!0,!1)}),i}return!1}}]),e}(),o=function(){function e(n){t(this,e),this.opt=r({},{diacritics:!0,synonyms:{},accuracy:"partially",caseSensitive:!1,ignoreJoiners:!1,ignorePunctuation:[],wildcards:"disabled"},n)}return n(e,[{key:"create",value:function(e){return"disabled"!==this.opt.wildcards&&(e=this.setupWildcardsRegExp(e)),e=this.escapeStr(e),Object.keys(this.opt.synonyms).length&&(e=this.createSynonymsRegExp(e)),(this.opt.ignoreJoiners||this.opt.ignorePunctuation.length)&&(e=this.setupIgnoreJoinersRegExp(e)),this.opt.diacritics&&(e=this.createDiacriticsRegExp(e)),e=this.createMergedBlanksRegExp(e),(this.opt.ignoreJoiners||this.opt.ignorePunctuation.length)&&(e=this.createJoinersRegExp(e)),"disabled"!==this.opt.wildcards&&(e=this.createWildcardsRegExp(e)),e=this.createAccuracyRegExp(e),new RegExp(e,"gm"+(this.opt.caseSensitive?"":"i"))}},{key:"escapeStr",value:function(e){return e.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g,"\\$&")}},{key:"createSynonymsRegExp",value:function(e){var t=this.opt.synonyms,n=this.opt.caseSensitive?"":"i",r=this.opt.ignoreJoiners||this.opt.ignorePunctuation.length?"\0":"";for(var i in t)if(t.hasOwnProperty(i)){var o=t[i],a="disabled"!==this.opt.wildcards?this.setupWildcardsRegExp(i):this.escapeStr(i),s="disabled"!==this.opt.wildcards?this.setupWildcardsRegExp(o):this.escapeStr(o);""!==a&&""!==s&&(e=e.replace(new RegExp("("+this.escapeStr(a)+"|"+this.escapeStr(s)+")","gm"+n),r+"("+this.processSynonyms(a)+"|"+this.processSynonyms(s)+")"+r))}return e}},{key:"processSynonyms",value:function(e){return(this.opt.ignoreJoiners||this.opt.ignorePunctuation.length)&&(e=this.setupIgnoreJoinersRegExp(e)),e}},{key:"setupWildcardsRegExp",value:function(e){return(e=e.replace(/(?:\\)*\?/g,function(e){return"\\"===e.charAt(0)?"?":""})).replace(/(?:\\)*\*/g,function(e){return"\\"===e.charAt(0)?"*":""})}},{key:"createWildcardsRegExp",value:function(e){var t="withSpaces"===this.opt.wildcards;return e.replace(/\u0001/g,t?"[\\S\\s]?":"\\S?").replace(/\u0002/g,t?"[\\S\\s]*?":"\\S*")}},{key:"setupIgnoreJoinersRegExp",value:function(e){return e.replace(/[^(|)\\]/g,function(e,t,n){var r=n.charAt(t+1);return/[(|)\\]/.test(r)||""===r?e:e+"\0"})}},{key:"createJoinersRegExp",value:function(e){var t=[],n=this.opt.ignorePunctuation;return Array.isArray(n)&&n.length&&t.push(this.escapeStr(n.join(""))),this.opt.ignoreJoiners&&t.push("\\u00ad\\u200b\\u200c\\u200d"),t.length?e.split(/\u0000+/).join("["+t.join("")+"]*"):e}},{key:"createDiacriticsRegExp",value:function(e){var t=this.opt.caseSensitive?"":"i",n=this.opt.caseSensitive?["aàáảãạăằắẳẵặâầấẩẫậäåāą","AÀÁẢÃẠĂẰẮẲẴẶÂẦẤẨẪẬÄÅĀĄ","cçćč","CÇĆČ","dđď","DĐĎ","eèéẻẽẹêềếểễệëěēę","EÈÉẺẼẸÊỀẾỂỄỆËĚĒĘ","iìíỉĩịîïī","IÌÍỈĨỊÎÏĪ","lł","LŁ","nñňń","NÑŇŃ","oòóỏõọôồốổỗộơởỡớờợöøō","OÒÓỎÕỌÔỒỐỔỖỘƠỞỠỚỜỢÖØŌ","rř","RŘ","sšśșş","SŠŚȘŞ","tťțţ","TŤȚŢ","uùúủũụưừứửữựûüůū","UÙÚỦŨỤƯỪỨỬỮỰÛÜŮŪ","yýỳỷỹỵÿ","YÝỲỶỸỴŸ","zžżź","ZŽŻŹ"]:["aàáảãạăằắẳẵặâầấẩẫậäåāąAÀÁẢÃẠĂẰẮẲẴẶÂẦẤẨẪẬÄÅĀĄ","cçćčCÇĆČ","dđďDĐĎ","eèéẻẽẹêềếểễệëěēęEÈÉẺẼẸÊỀẾỂỄỆËĚĒĘ","iìíỉĩịîïīIÌÍỈĨỊÎÏĪ","lłLŁ","nñňńNÑŇŃ","oòóỏõọôồốổỗộơởỡớờợöøōOÒÓỎÕỌÔỒỐỔỖỘƠỞỠỚỜỢÖØŌ","rřRŘ","sšśșşSŠŚȘŞ","tťțţTŤȚŢ","uùúủũụưừứửữựûüůūUÙÚỦŨỤƯỪỨỬỮỰÛÜŮŪ","yýỳỷỹỵÿYÝỲỶỸỴŸ","zžżźZŽŻŹ"],r=[];return e.split("").forEach(function(i){n.every(function(n){if(-1!==n.indexOf(i)){if(r.indexOf(n)>-1)return!1;e=e.replace(new RegExp("["+n+"]","gm"+t),"["+n+"]"),r.push(n)}return!0})}),e}},{key:"createMergedBlanksRegExp",value:function(e){return e.replace(/[\s]+/gim,"[\\s]+")}},{key:"createAccuracyRegExp",value:function(e){var t=this,n=this.opt.accuracy,r="string"==typeof n?n:n.value,i="";switch(("string"==typeof n?[]:n.limiters).forEach(function(e){i+="|"+t.escapeStr(e)}),r){case"partially":default:return"()("+e+")";case"complementary":return"()([^"+(i="\\s"+(i||this.escapeStr("!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~¡¿")))+"]*"+e+"[^"+i+"]*)";case"exactly":return"(^|\\s"+i+")("+e+")(?=$|\\s"+i+")"}}}]),e}(),a=function(){function a(e){t(this,a),this.ctx=e,this.ie=!1;var n=window.navigator.userAgent;(n.indexOf("MSIE")>-1||n.indexOf("Trident")>-1)&&(this.ie=!0)}return n(a,[{key:"log",value:function(t){var n=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"debug",r=this.opt.log;this.opt.debug&&"object"===(void 0===r?"undefined":e(r))&&"function"==typeof r[n]&&r[n]("mark.js: "+t)}},{key:"getSeparatedKeywords",value:function(e){var t=this,n=[];return e.forEach(function(e){t.opt.separateWordSearch?e.split(" ").forEach(function(e){e.trim()&&-1===n.indexOf(e)&&n.push(e)}):e.trim()&&-1===n.indexOf(e)&&n.push(e)}),{keywords:n.sort(function(e,t){return t.length-e.length}),length:n.length}}},{key:"isNumeric",value:function(e){return Number(parseFloat(e))==e}},{key:"checkRanges",value:function(e){var t=this;if(!Array.isArray(e)||"[object Object]"!==Object.prototype.toString.call(e[0]))return this.log("markRanges() will only accept an array of objects"),this.opt.noMatch(e),[];var n=[],r=0;return e.sort(function(e,t){return e.start-t.start}).forEach(function(e){var i=t.callNoMatchOnInvalidRanges(e,r),o=i.start,a=i.end;i.valid&&(e.start=o,e.length=a-o,n.push(e),r=a)}),n}},{key:"callNoMatchOnInvalidRanges",value:function(e,t){var n=void 0,r=void 0,i=!1;return e&&void 0!==e.start?(r=(n=parseInt(e.start,10))+parseInt(e.length,10),this.isNumeric(e.start)&&this.isNumeric(e.length)&&r-t>0&&r-n>0?i=!0:(this.log("Ignoring invalid or overlapping range: "+JSON.stringify(e)),this.opt.noMatch(e))):(this.log("Ignoring invalid range: "+JSON.stringify(e)),this.opt.noMatch(e)),{start:n,end:r,valid:i}}},{key:"checkWhitespaceRanges",value:function(e,t,n){var r=void 0,i=!0,o=n.length,a=t-o,s=parseInt(e.start,10)-a;return(r=(s=s>o?o:s)+parseInt(e.length,10))>o&&(r=o,this.log("End range automatically set to the max value of "+o)),s<0||r-s<0||s>o||r>o?(i=!1,this.log("Invalid range: "+JSON.stringify(e)),this.opt.noMatch(e)):""===n.substring(s,r).replace(/\s+/g,"")&&(i=!1,this.log("Skipping whitespace only range: "+JSON.stringify(e)),this.opt.noMatch(e)),{start:s,end:r,valid:i}}},{key:"getTextNodes",value:function(e){var t=this,n="",r=[];this.iterator.forEachNode(NodeFilter.SHOW_TEXT,function(e){r.push({start:n.length,end:(n+=e.textContent).length,node:e})},function(e){return t.matchesExclude(e.parentNode)?NodeFilter.FILTER_REJECT:NodeFilter.FILTER_ACCEPT},function(){e({value:n,nodes:r})})}},{key:"matchesExclude",value:function(e){return i.matches(e,this.opt.exclude.concat(["script","style","title","head","html"]))}},{key:"wrapRangeInTextNode",value:function(e,t,n){var r=this.opt.element?this.opt.element:"mark",i=e.splitText(t),o=i.splitText(n-t),a=document.createElement(r);return a.setAttribute("data-markjs","true"),this.opt.className&&a.setAttribute("class",this.opt.className),a.textContent=i.textContent,i.parentNode.replaceChild(a,i),o}},{key:"wrapRangeInMappedTextNode",value:function(e,t,n,r,i){var o=this;e.nodes.every(function(a,s){var c=e.nodes[s+1];if(void 0===c||c.start>t){if(!r(a.node))return!1;var u=t-a.start,l=(n>a.end?a.end:n)-a.start,h=e.value.substr(0,a.start),f=e.value.substr(l+a.start);if(a.node=o.wrapRangeInTextNode(a.node,u,l),e.value=h+f,e.nodes.forEach(function(t,n){n>=s&&(e.nodes[n].start>0&&n!==s&&(e.nodes[n].start-=l),e.nodes[n].end-=l)}),n-=l,i(a.node.previousSibling,a.start),!(n>a.end))return!1;t=a.end}return!0})}},{key:"wrapGroups",value:function(e,t,n,r){return r((e=this.wrapRangeInTextNode(e,t,t+n)).previousSibling),e}},{key:"separateGroups",value:function(e,t,n,r,i){for(var o=t.length,a=1;a-1&&r(t[a],e)&&(e=this.wrapGroups(e,s,t[a].length,i))}return e}},{key:"wrapMatches",value:function(e,t,n,r,i){var o=this,a=0===t?0:t+1;this.getTextNodes(function(t){t.nodes.forEach(function(t){t=t.node;for(var i=void 0;null!==(i=e.exec(t.textContent))&&""!==i[a];){if(o.opt.separateGroups)t=o.separateGroups(t,i,a,n,r);else{if(!n(i[a],t))continue;var s=i.index;if(0!==a)for(var c=1;c + + + + + Circuit API - docs + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + + + +
+
+ +

Circuit API

+

In this section, we'll discuss how to build your own constraint system to prove the execution of a particular program.

+

Simple Example

+

Let's take the following simple program as an example. We have two public inputs: x and y. We want to prove to a verifier that we know a private input e such that x * e = y. You can achieve this by building the following constraint system:

+
use lambdaworks_plonk::constraint_system::ConstraintSystem;
+use lambdaworks_math::elliptic_curve::short_weierstrass::curves::bls12_381::default_types::FrField;
+
+fn main() {
+    let system = &mut ConstraintSystem::<FrField>::new();
+    let x = system.new_public_input();
+    let y = system.new_public_input();
+    let e = system.new_variable();
+
+    let z = system.mul(&x, &e);
+    
+    // This constraint system asserts that x * e == y
+    system.assert_eq(&y, &z);
+}
+
+

This code creates a constraint system over the field of the BLS12381 curve. Then, it creates three variables: two public inputs x and y, and a private variable e. Note that every variable is private except for the public inputs. Finally, it adds the constraints that represent a multiplication and an assertion.

+

Before generating proofs for this system, we need to run a setup and obtain a verifying key:

+

+#![allow(unused)]
+fn main() {
+let common = CommonPreprocessedInput::from_constraint_system(&system, &ORDER_R_MINUS_1_ROOT_UNITY);
+let srs = test_srs(common.n);
+let kzg = KZG::new(srs); // The commitment scheme for plonk.
+let vk = setup(&common, &kzg);
+}
+
+

Now we can generate proofs for our system. We just need to specify the public inputs and obtain a witness that is a solution for our constraint system:

+

+#![allow(unused)]
+fn main() {
+let inputs = HashMap::from([(x, FieldElement::from(4)), (e, FieldElement::from(3))]);
+let assignments = system.solve(inputs).unwrap();
+let witness = Witness::new(assignments, &system);
+}
+
+

Once you have all these ingredients, you can call the prover:

+

+#![allow(unused)]
+fn main() {
+let public_inputs = system.public_input_values(&assignments);
+let prover = Prover::new(kzg.clone(), TestRandomFieldGenerator {});
+let proof = prover.prove(&witness, &public_inputs, &common, &vk);
+}
+
+

and verify:

+

+#![allow(unused)]
+fn main() {
+let verifier = Verifier::new(kzg);
+assert!(verifier.verify(&proof, &public_inputs, &common, &vk));
+}
+
+

Building Complex Systems

+

Some operations are common, and it makes sense to wrap the set of constraints that do these operations in a function and use it several times. Lambdaworks comes with a collection of functions to help you build your own constraint systems, such as conditionals, inverses, and hash functions.

+

However, if you have an operation that does not come with Lambdaworks, you can easily extend Lambdaworks functionality. Suppose that the exponentiation operation is something common in your program. You can write the square and multiply algorithm and put it inside a function:

+

+#![allow(unused)]
+fn main() {
+pub fn pow(
+    system: &mut ConstraintSystem<FrField>,
+    base: Variable,
+    exponent: Variable,
+) -> Variable {
+    let exponent_bits = system.new_u32(&exponent);
+    let mut result = system.new_constant(FieldElement::one());
+
+    for i in 0..32 {
+        if i != 0 {
+            result = system.mul(&result, &result);
+        }
+        let result_times_base = system.mul(&result, &base);
+        result = system.if_else(&exponent_bits[i], &result_times_base, &result);
+    }
+    result
+}
+}
+
+

This function can then be used to modify our simple program from the previous section. The following circuit checks that the prover knows e such that pow(x, e) = y:

+
use lambdaworks_plonk::constraint_system::ConstraintSystem;
+use lambdaworks_math::elliptic_curve::short_weierstrass::curves::bls12_381::default_types::FrField;
+
+fn main() {
+    let system = &mut ConstraintSystem::<FrField>::new();
+    let x = system.new_public_input();
+    let y = system.new_public_input();
+    let e = system.new_variable();
+
+    let z = pow(system, &x, &e);
+    system.assert_eq(&y, &z);
+}
+
+

You can keep composing these functions in order to create more complex systems.

+ +
+ + +
+
+ + + +
+ + + + + + + + + + + + + + + + + + + + diff --git a/plonk/implementation.html b/plonk/implementation.html new file mode 100644 index 000000000..c28e96956 --- /dev/null +++ b/plonk/implementation.html @@ -0,0 +1,493 @@ + + + + + + Implementation - docs + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + + + +
+
+ +

Implementation

+

In this section we discuss the implementation details of the PLONK algorithm. We use the notation and terminology of the protocol and recap sections.

+

At the moment our API supports the backend of PLONK, that is, all the setup, prove and verify algorithms. We temporarily rely on external sources for the definition of a circuit and the creation of the and matrices, as well as the execution of it to obtain the trace matrix . We mainly use gnark temporarily for that purpose.

+

To generate proofs and validate them, we need to feed the algorithms with precomputed values of the , and matrices, and the primitive root of unity .

+

Let's see our API on a test circuit that provides all these values. The program in this case is the one that takes an input , a private input and computes . As in the toy example of the recap, the output of the program is added to the public inputs and the circuit actually asserts that the output is the claimed value. So more precisely, the prover will generate a proof for the statement ASSERT(x*e+5==y), where both are public inputs.

+

Usage

+

Here is the happy path.

+

+#![allow(unused)]
+fn main() {
+// This is the common preprocessed input for
+// the test circuit ( ASSERT(x * e + 5 == y) )
+let common_preprocessed_input = test_common_preprocessed_input_2();
+
+// Input
+let x = FieldElement::from(2_u64);
+
+// Private input
+let e = FieldElement::from(3_u64);
+
+let y, witness = test_witness_2(x, e);
+
+let srs = test_srs(common_preprocessed_input.n);
+let kzg = KZG::new(srs);
+
+let verifying_key = setup(&common_preprocessed_input, &kzg);
+
+let random_generator = TestRandomFieldGenerator {};
+let prover = Prover::new(kzg.clone(), random_generator);
+
+let public_input = vec![x.clone(), y];
+
+let proof = prover.prove(
+    &witness,
+    &public_input,
+    &common_preprocessed_input,
+    &verifying_key,
+);
+
+let verifier = Verifier::new(kzg);
+assert!(verifier.verify(
+    &proof,
+    &public_input,
+    &common_preprocessed_input,
+    &verifying_key
+));
+}
+
+

Let's brake it down. The helper function test_common_preprocessed_input_2() returns an instance of the following struct for the particular test circuit:

+

+#![allow(unused)]
+fn main() {
+pub struct CommonPreprocessedInput<F: IsField> {
+    pub n: usize,
+    pub domain: Vec<FieldElement<F>>,
+    pub omega: FieldElement<F>,
+    pub k1: FieldElement<F>,
+
+    pub ql: Polynomial<FieldElement<F>>,
+    pub qr: Polynomial<FieldElement<F>>,
+    pub qo: Polynomial<FieldElement<F>>,
+    pub qm: Polynomial<FieldElement<F>>,
+    pub qc: Polynomial<FieldElement<F>>,
+
+    pub s1: Polynomial<FieldElement<F>>,
+    pub s2: Polynomial<FieldElement<F>>,
+    pub s3: Polynomial<FieldElement<F>>,
+
+    pub s1_lagrange: Vec<FieldElement<F>>,
+    pub s2_lagrange: Vec<FieldElement<F>>,
+    pub s3_lagrange: Vec<FieldElement<F>>,
+}
+}
+
+

Apart from the eight polynomials in the canonical basis, we store also here the number of constraints , the domain , the primitive -th of unity and the element . The element will be . For convenience, we also store the polynomials in Lagrange form.

+

The following lines define the particular values of the program input and the private input .

+

+#![allow(unused)]
+fn main() {
+// Input
+let x = FieldElement::from(2_u64);
+
+// Private input
+let e = FieldElement::from(3_u64);
+let y, witness = test_witness_2(x, e);
+}
+
+

The function test_witness_2(x, e) returns an instance of the following struct, that holds the polynomials that interpolate the columns of the trace matrix .

+

+#![allow(unused)]
+fn main() {
+pub struct Witness<F: IsField> {
+    pub a: Vec<FieldElement<F>>,
+    pub b: Vec<FieldElement<F>>,
+    pub c: Vec<FieldElement<F>>,
+}
+}
+
+

Next the commitment scheme KZG (Kate-Zaverucha-Goldberg) is instantiated.

+

+#![allow(unused)]
+fn main() {
+let srs = test_srs(common_preprocessed_input.n);
+let kzg = KZG::new(srs);
+}
+
+

The setup function performs the setup phase. It only needs the common preprocessed input and the commitment scheme.

+

+#![allow(unused)]
+fn main() {
+let verifying_key = setup(&common_preprocessed_input, &kzg);
+}
+
+

It outputs an instance of the struct VerificationKey:

+

+#![allow(unused)]
+fn main() {
+pub struct VerificationKey<G1Point> {
+    pub qm_1: G1Point,
+    pub ql_1: G1Point,
+    pub qr_1: G1Point,
+    pub qo_1: G1Point,
+    pub qc_1: G1Point,
+
+    pub s1_1: G1Point,
+    pub s2_1: G1Point,
+    pub s3_1: G1Point,
+}
+}
+
+

It stores the commitments of the eight polynomials of the common preprocessed input. The suffix _1 means it is a commitment. It comes from the notation , where is a polynomial.

+

Then a prover is instantiated

+

+#![allow(unused)]
+fn main() {
+let random_generator = TestRandomFieldGenerator {};
+let prover = Prover::new(kzg.clone(), random_generator);
+}
+
+

The prover is an instance of the struct Prover:

+

+#![allow(unused)]
+fn main() {
+pub struct Prover<F, CS, R>
+where
+  F:  IsField,
+  CS: IsCommitmentScheme<F>,
+  R:  IsRandomFieldElementGenerator<F>
+  {
+    commitment_scheme: CS,
+    random_generator: R,
+    phantom: PhantomData<F>,
+}
+}
+
+

It stores an instance of a commitment scheme and a random field element generator needed for blinding polynomials.

+

Then the public input is defined. As we mentioned in the recap, the public input contains the output of the program.

+

+#![allow(unused)]
+fn main() {
+let public_input = vec![x.clone(), y];
+}
+
+

We then generate a proof using the prover's method prove

+

+#![allow(unused)]
+fn main() {
+let proof = prover.prove(
+    &witness,
+    &public_input,
+    &common_preprocessed_input,
+    &verifying_key,
+);
+}
+
+

The output is an instance of the struct Proof.

+

+#![allow(unused)]
+fn main() {
+pub struct Proof<F: IsField, CS: IsCommitmentScheme<F>> {
+    // Round 1.
+    /// Commitment to the wire polynomial `a(x)`
+    pub a_1: CS::Commitment,
+    /// Commitment to the wire polynomial `b(x)`
+    pub b_1: CS::Commitment,
+    /// Commitment to the wire polynomial `c(x)`
+    pub c_1: CS::Commitment,
+
+    // Round 2.
+    /// Commitment to the copy constraints polynomial `z(x)`
+    pub z_1: CS::Commitment,
+
+    // Round 3.
+    /// Commitment to the low part of the quotient polynomial t(X)
+    pub t_lo_1: CS::Commitment,
+    /// Commitment to the middle part of the quotient polynomial t(X)
+    pub t_mid_1: CS::Commitment,
+    /// Commitment to the high part of the quotient polynomial t(X)
+    pub t_hi_1: CS::Commitment,
+
+    // Round 4.
+    /// Value of `a(ζ)`.
+    pub a_zeta: FieldElement<F>,
+    /// Value of `b(ζ)`.
+    pub b_zeta: FieldElement<F>,
+    /// Value of `c(ζ)`.
+    pub c_zeta: FieldElement<F>,
+    /// Value of `S_σ1(ζ)`.
+    pub s1_zeta: FieldElement<F>,
+    /// Value of `S_σ2(ζ)`.
+    pub s2_zeta: FieldElement<F>,
+    /// Value of `z(ζω)`.
+    pub z_zeta_omega: FieldElement<F>,
+
+    // Round 5
+    /// Value of `p_non_constant(ζ)`.
+    pub p_non_constant_zeta: FieldElement<F>,
+    ///  Value of `t(ζ)`.
+    pub t_zeta: FieldElement<F>,
+    /// Batch opening proof for all the evaluations at ζ
+    pub w_zeta_1: CS::Commitment,
+    /// Single opening proof for `z(ζω)`.
+    pub w_zeta_omega_1: CS::Commitment,
+}
+}
+
+

Finally, we instantiate a verifier.

+

+#![allow(unused)]
+fn main() {
+let verifier = Verifier::new(kzg);
+}
+
+

It's an instance of Verifier:

+

+#![allow(unused)]
+fn main() {
+struct Verifier<F: IsField, CS: IsCommitmentScheme<F>> {
+    commitment_scheme: CS,
+    phantom: PhantomData<F>,
+}
+}
+
+

Finally, we call the verifier's method verify that outputs a bool.

+

+#![allow(unused)]
+fn main() {
+assert!(verifier.verify(
+    &proof,
+    &public_input,
+    &common_preprocessed_input,
+    &verifying_key
+));
+}
+
+

Padding

+

All the matrices are padded with dummy rows so that their length is a power of two. To be able to interpolate their columns, we need a primitive root of unity of that order. Given the particular field used in our implementation, that means that the maximum possible size for a circuit is .

+

The entries of the dummy rows are filled in with zeroes in the , and matrices. The matrix needs to be consistent with the matrix. Therefore it is filled with the value of the variable with index .

+

Some other rows in the matrix have also dummy values. These are the rows corresponding to the and columns of the public input rows. In the recap we denoted them with the empty - symbol. They are filled in with the same logic as the padding rows, as well as the corresponding values in the matrix.

+

Implementation details

+

The implementation pretty much follows the rounds as are described in the protocol section. There are a few details that are worth mentioning.

+

Commitment Scheme

+

The commitment scheme we use is the Kate-Zaverucha-Goldberg scheme with the BLS 12 381 curve and the ate pairing. It can be found in the commitments module of the lambdaworks_crypto package.

+

The order of the cyclic subgroup is

+
0x73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001
+
+

The maximum power of two that divides is . Therefore, that is the maximum possible order for a primitive root of unity in with order a power of two.

+

Fiat-Shamir

+

Transcript strategy

+

Here we describe our implementation of the transcript used for the Fiat-Shamir heuristic.

+

A Transcript exposes two methods: append and challenge.

+

The method append adds a message to the transcript by updating the internal state of the hasher with the raw bytes of the message.

+

The method challenge returns the result of the hasher using the current internal state of the hasher. It subsequently resets the hasher and updates the internal state with the last result.

+

Here is an example of this process:

+
    +
  1. Start a fresh transcript.
  2. +
  3. Call append and pass message_1.
  4. +
  5. Call append and pass message_2.
  6. +
  7. The internal state of the hasher at this point is message_2 || message_1.
  8. +
  9. Call challenge. The output is Hash(message_2 || message_1).
  10. +
  11. Call append and pass message_3.
  12. +
  13. Call challenge. The output is Hash(message_3 || Hash(message_2 || message_1)).
  14. +
  15. Call append and pass message_4.
  16. +
+

The internal state of the hasher at the end of this exercise is message_4 || Hash(message_3 || Hash(message_2 || message_1))

+

The underlying hasher function we use is h=sha3.

+

Field elements

+

The result of every challenge is a -bit string, which is interpreted as an integer in big-endian order. A field element is constructed out of it by taking modulo the field order. The prime field used in this implementation has a -bit order. Therefore some field elements are more probable to occur than others because they have more representatives as 256-bit integers.

+

Strong Fiat-Shamir

+

The first messages added to the transcript are all commitments of the polynomials of the common preprocessed input and the values of the public inputs. This prevents a known vulnerability called "weak Fiat-Shamir". +Check out the following resources to learn more about it.

+ + +
+ + +
+
+ + + +
+ + + + + + + + + + + + + + + + + + + + diff --git a/plonk/protocol.html b/plonk/protocol.html new file mode 100644 index 000000000..b030b3172 --- /dev/null +++ b/plonk/protocol.html @@ -0,0 +1,323 @@ + + + + + + Protocol - docs + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + + + +
+
+ +

Protocol

+

Details and tricks

+

Polynomial commitment scheme

+

A polynomial commitment scheme (PCS) is a cryptographic tool that allows one party to commit to a polynomial, and later prove properties of that polynomial. +This commitment polynomial hides the original polynomial's coefficients and can be publicly shared without revealing any information about the original polynomial. +Later, the party can use the commitment to prove certain properties of the polynomial, such as that it satisfies certain constraints or that it evaluates to a certain value at a specific point.

+

In the implementation section we'll explain the inner workings of the Kate-Zaverucha-Goldberg scheme, a popular PCS chosen in Lambdaworks for PLONK.

+

For the moment we only need the following about it:

+

It consists of a finite group and the following algorithms:

+
    +
  • Commit(): This algorithm takes a polynomial and produces an element of the group . It is called the commitment of and is denoted by . It is homomorphic in the sense that . The former sum being addition of polynomials. The latter is addition in the group .
  • +
  • Open(, ): It takes a polynomial and a field element and produces an element of the group . This element is called an opening proof for . It is the proof that evaluated at gives .
  • +
  • Verify(, , , ): It takes group elements and , and also field elements and . With overwhelming probability it outputs Accept if and Reject otherwise.
  • +
+

Blindings

+

As you will see in the protocol, the prover reveals the value taken by a bunch of the polynomials at a random . In order for the protocol to be Honest Verifier Zero Knowledge, these polynomials need to be blinded. This is a process that makes the values of these polynomials at seemingly random by forcing them to be of certain degree. Here's how it works.

+

Let's take for example the polynomial the prover constructs. This is the interpolation of the first column of the trace matrix at the domain . +This matrix has all of the left operands of all the gates. The prover wishes to keep them secret. +Say the trace matrix has rows. is . The invariant that the prover cannot violate is that must take the value , for all . This is what the interpolation polynomial satisfies. And is the unique such polynomial of degree at most with such property. But for higher degrees, there are many such polynomials.

+

The blinding process takes and a desired degree , and produces a new polynomial of degree exactly . This new polynomial satisfies that for all . But outside differs from .

+

This may seem hard but it's actually very simple. Let be the polynomial . If , with , then sample random values and define +

+

The reason why this does the job is that for all . Therefore the added term vanishes at and leaves the values of at unchanged.

+

Linearization trick

+

This is an optimization in PLONK to reduce the number of checks of the verifier.

+

One of the main checks in PLONK boils down to check that , with some polynomial that looks like , and so on. In particular the verifier needs to get the value from somewhere.

+

For the sake of simplicity, in this section assume is exactly . Secret to the prover here are only . The polynomials and are known also to the verifier. The verifier will already have the commitments and . So the prover could send just , along with their opening proofs and let the verifier compute by himself and . Then with all these values the verifier could compute . And also use his commitments to validate the opening proofs of and .

+

This has the problem that computing and is expensive. The prover can instead save the verifier this by sending also along with opening proofs. Since the verifier will have the commitments and beforehand, he can check that the prover is not cheating and cheaply be convinced that the claimed values are actually and . This is much better. It involves the check of four opening proofs and the computation of off the values received from the prover. But it can be further improved as follows.

+

As before, the prover sends along with their opening proofs. She constructs the polynomial . She sends the value along with an opening proof of it. Notice that the value of is exactly . The verifier can compute by himself as . The verifier has everything to check all three openings and get convinced that the claimed value is true. And this value is actually . So this means no more work for the verifier. And the whole thing got reduced to three openings.

+

This is called the linearization trick. The polynomial is called the linearization of .

+

Setup

+

There's a one time setup phase to compute some values common to any execution and proof of the particular circuit. Precisely, the following commitments are computed and published. +

+

Proving algorithm

+

Next we describe the proving algorithm for a program of size . That includes public inputs. Let be a primitive -th root of unity. Let . Define .

+

Assume the eight polynomials of common preprocessed input are already given.

+

The prover computes the trace matrix as described in the first sections. That means, with the first rows corresponding to the public inputs. It should be a matrix.

+

Round 1

+

Add to the transcript the following: +

+

Compute polynomials as the interpolation polynomials of the columns of at the domain . +Sample random +Let

+

+

+

+

Compute and add them to the transcript.

+

Round 2

+

Sample from the transcript.

+

Let and define recursively for .

+

+

Compute the polynomial as the interpolation polynomial at the domain of the values .

+

Sample random values and let .

+

Compute and add it to the transcript.

+

Round 3

+

Sample from the transcript.

+

Let be the interpolation of the public input matrix at the domain .

+

Let

+

+

and define . Compute such that . Write with and polynomials of degree at most .

+

Sample random and define

+

+

Compute and add them to the transcript.

+

Round 4

+

Sample from the transcript.

+

Compute and add them to the transcript.

+

Round 5

+

Sample from the transcript.

+

Let

+

+

Define

+

+

The subscript stands for "non-constant", as is the part of the linearization of that has non-constant factors. The subscript "partial" indicates that it is a partial evaluation of at . Partial meaning that only some power of ar replaced by the powers of . So in particular .

+

Let be the opening proof at of the polynomial defined as +

+

Let be the opening proof at of the polynomial .

+

Compute and .

+

Proof

+

The proof is: +

+

Verification algorithm

+

Transcript initialization

+

The first step is to initialize the transcript in the same way the prover did, adding to it the following elements. +

+

Extraction of values and commitments

+

Challenges

+

Firstly, the verifier needs to compute all the challenges. For that, he follows these steps:

+
    +
  • Add to the transcript.
  • +
  • Sample two challenges .
  • +
  • Add to the transcript.
  • +
  • Sample a challenge .
  • +
  • Add to the transcript.
  • +
  • Sample a challenge .
  • +
  • Add to the transcript.
  • +
  • Sample a challenge .
  • +
+

Compute

+

Also he needs compute a few values off all these data. First, he computes the matrix with the public inputs and outputs. He needs to compute , where is the interpolation of at the domain . But he doesn't need to compute . He can instead compute as + +where is the number of public inputs and is the Lagrange basis at the domain .

+

Compute claimed values of and

+

He computes

+

This is the constant part of the linearization of . So adding it to what the prover claims to be , he obtains +

+

With respect to , this is actually already .

+

Compute and

+

He computes these off the commitments in the proof as follows +

+

For , first compute

+

+

Then .

+

Compute claimed value and

+

Compute as

+

+

Also, the commitment of the polynomial is +

+

Proof check

+

Now the verifier has all the necessary values to proceed with the checks.

+
    +
  • Check that equals .
  • +
  • Verify the opening of at . That is, check that outputs Accept.
  • +
  • Verify the opening of at . That is, check the validity of the proof using the commitment and the value . +That is, check that outputs Accept.
  • +
+

If all checks pass, he outputs Accept. Otherwise outputs Reject.

+ +
+ + +
+
+ + + +
+ + + + + + + + + + + + + + + + + + + + diff --git a/plonk/recap.html b/plonk/recap.html new file mode 100644 index 000000000..ae63f53c9 --- /dev/null +++ b/plonk/recap.html @@ -0,0 +1,495 @@ + + + + + + Recap - docs + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + + + +
+
+ +

PLONK

+

PLONK is a popular cryptographic proving system within the Zero Knowledge (ZK) community due to its efficiency and flexibility. It enables the verification of complex computations executed by untrusted parties through the transformation of programs into circuit representations. The system relies on a process called arithmetization, which converts logical circuits into polynomial representations. The main idea behind arithmetization is to express the computation as a set of polynomial equations. The solutions to these equations correspond to the outputs of the circuit. In this section, we will delve into the mechanics of how arithmetization works in PLONK, as well as the protocol used to generate and verify proofs.

+

The paper can be found here

+

Notation

+

We use the following notation.

+

The symbol denotes a finite field. It is fixed all along. The symbol denotes a primitive root of unity in .

+

All polynomials have coefficients in and the variable is usually denoted by . We denote polynomials by single letters like . We only denote them as when we want to emphasize the fact that it is a polynomial in , or we need that to explicitly define a polynomial from another one. For example when composing a polynomial with the polynomial , the result being denoted by . The symbol is not used to denote derivatives.

+

When interpolating at a domain , the symbols denote the Lagrange basis. That is is the polynomial such that for all , and that .

+

If is a matrix, then denotes the value at the row and column .

+

The ideas and components

+

Programs. Our toy example

+

For better clarity, we'll be using the following toy program throughout this recap.

+
INPUT:
+  x
+
+PRIVATE INPUT:
+  e
+
+OUTPUT:
+  e * x + x - 1
+
+

The observer would have noticed that this program could also be written as , which is more sensible. But the way it is written now serves us to better explain the arithmetization of PLONK. So we'll stick to it.

+

The idea here is that the verifier holds some value , say . He gives it to the prover. She executes the program using her own chosen value , and sends the output value, say , along with a proof demonstrating correct execution of the program and obtaining the correct output.

+

In the context of PLONK, both the inputs and outputs of the program are considered public inputs. This may sound odd, but it is because these are the inputs to the verification algorithm. This is the algorithm that takes, in this case, the tuple and outputs Accept if the toy program was executed with input , some private value not revealed to the verifier, and out came . Otherwise it outputs Reject.

+

PLONK can be used to delegate program executions to untrusted parties, but it can also be used as a proof of knowledge. Our program could be used by a prover to demostrate that she knows the multiplicative inverse of some value in the finite field without revealing it. She would do it by sending the verifier the tuple , where is the proof of the execution of our toy program.

+

In our toy example this is pointless because inverting field elements is easily performed by any verifier. But change our program to the following and you get proofs of knowledge of the preimage of SHA256 digests.

+
PRIVATE INPUT:
+  e
+
+OUTPUT:
+  SHA256(e)
+
+

Here there's no input aside from the prover's private input. As we mentioned, the output of the program is then part of the inputs to the verification algorithm. Which in this case just takes .

+

PLONK Arithmetization

+

This is the process that takes the circuit of a particular program and produces a set of mathematical tools that can be used to generate and verify proofs of execution. The end result will be a set of eight polynomials. To compute them we need first to define two matrices. We call them the matrix and the matrix. The polynomials and the matrices depend only on the program and not on any particular execution of it. So they can be computed once and used for every execution instance. To understand what they are useful for, we need to start from execution traces.

+

Circuits and execution traces

+

See the program as a sequence of gates that have left operand, a right operand and an output. The two most basic gates are multiplication and addition gates. In our example, one way of seeing our toy program is as a composition of three gates.

+

Gate 1: left: e, right: x, output: u = e * x +Gate 2: left: u, right: x, output: v = e + x +Gate 3: left: v, right: 1, output: w = v - 1

+

On executing the circuit, all these variables will take a concrete value. All that information can be put in table form. It will be a matrix with all left, right and output values of all the gates. One row per gate. We call the columns of this matrix . Let's build them for and . We get , and . So the first matrix is:

+ + + + +
ABC
236
639
9-8
+

The last gate subtracts a constant value that is part of the program and is not a variable. So it actually has only one input instead of two. And the output is the result of subtracting to it. That's why it is handled a bit different from the second gate. The symbol "-" in the column is a consequence of that. With that we mean "any value" because it won't change the result. In the next section we'll see how we implement that. Here we'll use this notation when any value can be put there. In case we have to choose some, we'll default to .

+

What we got is a valid execution trace. Not all matrices of that shape will be the trace of an execution. The matrices and will be the tool we need to distinguish between valid and invalid execution traces.

+

The matrix

+

As we said, it only depends on the program itself and not on any particular evaluation of it. It has one row for each gate and its columns are called . They encode the type of gate of the rows and are designed to satisfy the following.

+

Claim: if columns correspond to a valid evaluation of the circuit then for all the following equality holds

+

This is better seen with examples. A multiplication gate is represented by the row:

+ + +
001-10
+

And the row in the trace matrix that corresponds to the execution of that gate is

+ + +
ABC
236
+

The equation in the claim for that row is that , which equals . The next is an addition gate. This is represented by the row

+ + +
110-10
+

The corresponding row in the trace matrix its

+ + +
ABC
639
+

And the equation of the claim is , which adds up to . Our last row is the gate that adds a constant. Addition by constant C can be represented by the row

+ + +
100-1C
+

In our case . The corresponding row in the execution trace is

+ + +
ABC
9-8
+

And the equation of the claim is . This is also zero.

+

Putting it altogether, the full matrix is

+ + + + +
001-10
110-10
100-1-1
+

And we saw that the claim is true for our particular execution: + + +

+

Not important to our example, but multiplication by constant C can be represented by:

+ + +
C00-10
+

As you might have already noticed, there are several ways of representing the same gate in some cases. We'll exploit this in a moment.

+

The matrix

+

The claim in the previous section is clearly not an "if and only if" statement because the following trace columns do satisfy the equations but do not correspond to a valid execution:

+ + + + +
ABC
236
000
20-19
+

The matrix encodes the carry of the results from one gate to the right or left operand of a subsequent one. These are called wirings. Like the matrix, it's independent of the particular evaluation. It consists of indices for all input and intermediate variables. In this case that matrix is:

+ + + + +
LRO
012
213
3-4
+

Here is the index of , is the index of , is the index of , is the index of and is the index of the output . Now we can update the claim to have an "if and only if" statement.

+

Claim: Let be a matrix with columns . It correspond to a valid evaluation of the circuit if and only if a) for all the following equality holds b) for all such that we have .

+

So now our malformed example does not pass the second check.

+

Custom gates

+

Our matrices are fine now. But they can be optimized. Let's do that to showcase this flexibility of PLONK and also reduce the size of our example.

+

PLONK has the flexibility to construct more sophisticated gates as combinations of the five columns. And therefore the same program can be expressed in multiple ways. In our case all three gates can actually be merged into a single custom gate. The matrix ends up being a single row.

+ + +
011-11
+

and also the matrix

+ + +
LRO
012
+

The trace matrix for this representation is just

+ + +
ABC
238
+

And we check that it satisfies the equation

+

+

Of course, we can't always squash an entire program into a single gate.

+

Public inputs

+

Aside from the gates that execute the program operations, additional rows must be incorporated into these matrices. This is due to the fact that the prover must demonstrate not only that she executed the program, but also that she used the appropriate inputs. Furthermore, the proof must include an assertion of the output value. As a result, a few extra rows are necessary. In our case these are the first two and the last one. The original one sits now in the third row.

+ + + + + +
-10003
-10008
111-11
1-1000
+

And this is the updated matrix

+ + + + + +
LRO
0--
1--
203
13-
+

The first row is there to force the variable with index to take the value . Similarly the second row forces variable with index to take the value . These two will be the public inputs of the verifier. The last row checks that the output of the program is the claimed one.

+

And the trace matrix is now

+ + + + + +
ABC
3--
8--
238
88-
+

With these extra rows, equations add up to zero only for valid executions of the program with input and output .

+

An astute observer would notice that by incorporating these new rows, the matrix is no longer independent of the specific evaluation. This is because the first two rows of the column contain concrete values that are specific to a particular execution instance. To maintain independence, we can remove these values and consider them as part of an extra one-column matrix called (stands for Public Input). This column has zeros in all rows not related to public inputs. We put zeros in the columns. The responsibility of filling in the matrix is of the prover and verifier. In our example it is

+ + + + + +
3
8
0
0
+

And the final matrix is

+ + + + + +
-10000
-10000
111-11
1-1000
+

We ended up with two matrices that depend only on the program, and . And two matrices that depend on a particular evaluation, namely the and matrices. The updated version of the claim is the following:

+

Claim: Let be a matrix with columns . It corresponds to a evaluation of the circuit if and only if a) for all the following equality holds b) for all such that we have .

+

From matrices to polynomials

+

In the previous section we showed how the arithmetization process works in PLONK. For a program with public inputs and gates, we constructed two matrices and , of sizes and that satisfy the following. Let

+

Claim: Let be a matrix with columns and a matrix. They correspond to a valid execution instance with public input given by if and only if a) for all the following equality holds b) for all such that we have , c) for all .

+

Polynomials enter now to squash most of these equations. We will traduce the set of all equations in conditions (a) and (b) to just a few equations on polynomials.

+

Let be a primitive -th root of unity and let . Let be the polynomials of degree at most that interpolate the columns at the domain . This means for example that for all . And similarly for all the other columns.

+

With this, condition (a) of the claim is equivalent to for all in .This is just by definition of the polynomials. But in polynomials land this is also equivalent to (a) there exists a polynomial such that , where is the polynomial .

+

To reduce condition (b) to polynomial equations we need to introduce the concept of permutation. A permutation is a rearrangement of a set. Usually denoted . For finite sets it is a map from a set to itself that takes all values. In our case the set will be the set of all pairs + +The matrix induces a permutation of this set where is equal to the indices of the next occurrence of the value at position . If already at the last occurrence, go to the first one. By next we mean the following occurrence as if the columns were stacked on each other. Let's see how this works in the example circuit. Recall is

+ + + + + +
LRO
0--
1--
203
13-
+

The permutation in this case is the map , , , , , , . For the positions with - values doesn't really matter right now.

+

It's not hard to see that condition (b) is equivalent to: for all , .

+

A little less obvious is that this condition is in turn equivalent to checking whether the following sets and are equal + + +The proof this equivalence is straightforward. Give it a try!

+

In our example the sets in question are respectively + +and +

+

You can check these sets coincide by inspection. Recall our trace matrix is

+ + + + + +
ABC
3--
8--
238
88-
+

Checking equality of these sets is something that can be reduced to polynomial equations. It is a very nice method that PLONK uses. To understand it better let's start with a simpler case.

+

Equality of sets

+

Suppose we have two sets of two field elements in . And we are interested in checking whether they are equal.

+

One thing we could do is compute and and compare them. If the sets are equal, then those elements are necessarily equal.

+

But the converse is not true. For example the sets and both have as the result of the product of their elements. But they are not equal. So this is not good to check equality.

+

Polynomials come to rescue here. What we can do instead is consider the following sets of polynomials , . Sets and are equal if and only if sets and are equal. This is because equality of polynomials boils down to equality of their coefficients. But the difference with and is that now the approach of multiplying the elements works. That is, and are equal if and only if . This is not entirely evident but follows from a property that polynomials have, called unique factorization. Here the important fact is that linear polynomials act as sort of prime factors. Anyway, you can take that for granted. The last part of this trick is to use the Schwartz-Zippel lemma and go back to the land of field elements. That means, if for some random element we have , then with overwhelming probability the equality holds.

+

Putting this altogether, if for some random element we have , then the sets and are equal. Of course this also holds for sets with more than two elements. Let's write that down.

+

Fact: Let and be sets of field elements. If for some random the following equality holds + +then with overwhelming probability is equal to .

+

And here comes the trick that reduces this check to polynomial equations. Let + be a domain of the form for some primitive -th root of unity . Let and be respectively the polynomials that interpolate the following values at . + +

+

Then equals if and only if there exists a polynomial such that + + +for all .

+

Let's see why. Suppose that equals . Construct as the polynomial that interpolates the following values +in the same domain as and . That works. Conversely, suppose such a polynomial exists. By evaluating the equation at and using recursion we get that . Moreover, evaluating it at we obtain that +The second equality holds because since it is a -th root of unity. Expanding with the values of and one obtains that equals . Which is what we wanted.

+

In summary. We proved the following:

+

Fact: Let and be sets of field elements. Let be a random field element. Let be a primitive -th root of unity and . Let and be respectively the polynomials that interpolate the values and at . If there exists a polynomial such that + + +for all , then with overwhelming probability the sets and are equal.

+

Sets of tuples

+

In the previous section we saw how to check whether two sets of field elements are equal using polynomial equations. To be able to use it in our context we need to extend it to sets of tuples of field elements. This is pretty straightforward.

+

Let's start with the easy case. Let and be two sets of pairs of field elements. That is for all . The trick is very similar to the previous section. + +

+

Just as before, by looking at coefficients we can see that the sets and are equal if and only if and are equal. +And notice that these are sets of polynomials, we got rid of the tuples! And now the situation is very similar to the previous section. We have that and are equal if and only if the product of their elements coincide. This is true also because polynomials in two variables are a unique factorization domain. So as before, we can use the Schwartz-Zippel lemma. Precisely, if for random , the elements + +and + +coincide, then and are equal with overwhelming probability.

+

Here is the statement for sets of more than two pairs of field elements.

+

Fact: Let and be sets of pairs of field elements. So that and the same for . Let be a random field elements. Let be a -th root of unity and . Let and be respectively the polynomials that interpolate the values + +and + +at . If there exists a polynomial such that + + +for all , then with overwhelming probability the sets and are equal.

+

Going back to our case

+

Recall we want to rephrase condition (b) in terms of polynomials. We have already seen that condition (b) is equivalent to and being equal, where + +and +

+

We cannot directly use the facts of the previous sections because our sets are not sets of field elements. Nor are they sets of pairs of field elements. They are sets of pairs with some indexes in the first coordinate and a field element in the second one. So the solution is to convert them to sets of pairs of field elements and apply the result of the previous section. So how do we map an element of the form to something of the form with and field elements? The second coordinate is trivial, we can just leave as it is and take . For the indexes pair there are multiple ways. The important thing to achieve here is that different pairs get mapped to different field elements. Recall that ranges from to and ranges from to . One way is to take a -th primitive root of unity and define . Putting it altogether, we are mapping the pair to the pair , which is a pair of field elements. Now we can consider the sets + +and + +We have that condition (b) is equivalent to and being equal.

+

Applying the method of the previous section to these sets, we obtain the following.

+

Fact: Let be a -th root of unity and and random field elements. Let . Let and be the polynomials that interpolate, respectively, the following values at : + +and + +Suppose there exists a polynomial such that + + +for all . +Then the sets and are equal with overwhelming probability.

+

One last minute definitions. Notice that is a primitive -th root of unity. Let .

+

Define to be the interpolation at of + +Similarly define and to be the interpolation at of the sets of values + + +These will be useful during the protocol to work with such polynomials and the above equations.

+

A more compact form

+

The last fact is equivalent the following. There's no new idea here, just a more compact form of the same thing that allows the polynomial to be of degree at most .

+

Fact: Let be a -th root of unity. Let . Let and be two field elements such that for all . Let and be random field elements. Let and be the polynomials that interpolate, respectively, the following values at : + +and + +Suppose there exists a polynomial such that + + +for all . +Then the sets and are equal with overwhelming probability.

+

Common preprocessed input

+

We have arrived at the eight polynomials we mentioned at the beginning: +

+

These are what's called the common preprocessed input.

+

Wrapping up the whole thing

+

Let's try to wrap up what we have so far. We started from a program. We saw that it can be seen as a sequence of gates with left, right and output values. That's called a circuit. From this two matrices and can be computed that capture the gates logic.

+

Executing the circuit leaves us with matrices and , called the trace matrix and the public input matrix, respectively. Everything we want to prove boils down to check that such matrices are valid. And we have the following result.

+

Fact: Let be a matrix with columns and a matrix. They correspond to a valid execution instance with public input given by if and only if a) for all the following equality holds b) for all such that we have , c) for all .

+

Then we constructed polynomials , , off the matrices and . They are the result of interpolating at a domain for some -th primitive root of unity and a few random values. And also constructed polynomials off the matrices and . Loosely speaking, the above fact can be reformulated in terms of polynomial equations as follows.

+

Fact: Let . Let be a matrix with columns and a matrix. They correspond to a valid execution instance with public input given by if and only if

+

a) There is a polynomial such that the following equality holds

+

b) There are polynomials , such that and , where

+

You might be wondering where the polynomials came from. Recall that for a polynomial , we have for all if and only if for some polynomial .

+

Finally both conditions (a) and (b) are equivalent to a single equation (c) if we let more randomness to come into play. This is:

+

(c) Let be a random field element. There is a polynomial such that +

+

This last step is not obvious. You can check the paper to see the proof. Anyway, this is the equation you'll recognize below in the description of the protocol.

+

Randomness is a delicate matter and an important part of the protocol is where it comes from, who chooses it and when they choose it. Check out the protocol to see how it works.

+ +
+ + +
+
+ + + +
+ + + + + + + + + + + + + + + + + + + + diff --git a/print.html b/print.html new file mode 100644 index 000000000..f17c84a84 --- /dev/null +++ b/print.html @@ -0,0 +1,2390 @@ + + + + + + docs + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + + + +
+
+ +

Introduction

+

This site hosts the main documentation for Lambdaworks as a whole. It is still a work in progress.

+

Crates

+ +
+

FFT Benchmarks

+

Polynomial interpolation methods comparison

+

Three methods of polynomial interpolation were benchmarked, with different input sizes each time:

+
    +
  • CPU Lagrange: Finding the Lagrange polynomial of a set of random points via a naive algorithm (see math/src/polynomial.rs:interpolate())
  • +
  • CPU FFT: Finding the lowest degree polynomial that interpolates pairs of twiddle factors and Fourier coefficients (the results of applying the Fourier transform to the coefficients of a polynomial) (see math/src/polynomial.rs:interpolate_fft()).
  • +
+

All values of time are in milliseconds. Those cases which were greater than 30 seconds were marked respectively as they're too slow and weren't worth to be benchmarked. The input size refers to d + 1 where d is the polynomial's degree (so size is amount of coefficients).

+ + + + + + + + + + +
Input sizeCPU LagrangeCPU FFT
2^42.2 ms0.2 ms
2^59.6 ms0.4 ms
2^642.6 ms0.8 ms
2^7200.8 ms1.7 ms
........
2^21>30000 ms28745 ms
2^22>30000 ms>30000 ms
2^23>30000 ms>30000 ms
2^24>30000 ms>30000 ms
+
+

PLONK

+

PLONK is a popular cryptographic proving system within the Zero Knowledge (ZK) community due to its efficiency and flexibility. It enables the verification of complex computations executed by untrusted parties through the transformation of programs into circuit representations. The system relies on a process called arithmetization, which converts logical circuits into polynomial representations. The main idea behind arithmetization is to express the computation as a set of polynomial equations. The solutions to these equations correspond to the outputs of the circuit. In this section, we will delve into the mechanics of how arithmetization works in PLONK, as well as the protocol used to generate and verify proofs.

+

The paper can be found here

+

Notation

+

We use the following notation.

+

The symbol denotes a finite field. It is fixed all along. The symbol denotes a primitive root of unity in .

+

All polynomials have coefficients in and the variable is usually denoted by . We denote polynomials by single letters like . We only denote them as when we want to emphasize the fact that it is a polynomial in , or we need that to explicitly define a polynomial from another one. For example when composing a polynomial with the polynomial , the result being denoted by . The symbol is not used to denote derivatives.

+

When interpolating at a domain , the symbols denote the Lagrange basis. That is is the polynomial such that for all , and that .

+

If is a matrix, then denotes the value at the row and column .

+

The ideas and components

+

Programs. Our toy example

+

For better clarity, we'll be using the following toy program throughout this recap.

+
INPUT:
+  x
+
+PRIVATE INPUT:
+  e
+
+OUTPUT:
+  e * x + x - 1
+
+

The observer would have noticed that this program could also be written as , which is more sensible. But the way it is written now serves us to better explain the arithmetization of PLONK. So we'll stick to it.

+

The idea here is that the verifier holds some value , say . He gives it to the prover. She executes the program using her own chosen value , and sends the output value, say , along with a proof demonstrating correct execution of the program and obtaining the correct output.

+

In the context of PLONK, both the inputs and outputs of the program are considered public inputs. This may sound odd, but it is because these are the inputs to the verification algorithm. This is the algorithm that takes, in this case, the tuple and outputs Accept if the toy program was executed with input , some private value not revealed to the verifier, and out came . Otherwise it outputs Reject.

+

PLONK can be used to delegate program executions to untrusted parties, but it can also be used as a proof of knowledge. Our program could be used by a prover to demostrate that she knows the multiplicative inverse of some value in the finite field without revealing it. She would do it by sending the verifier the tuple , where is the proof of the execution of our toy program.

+

In our toy example this is pointless because inverting field elements is easily performed by any verifier. But change our program to the following and you get proofs of knowledge of the preimage of SHA256 digests.

+
PRIVATE INPUT:
+  e
+
+OUTPUT:
+  SHA256(e)
+
+

Here there's no input aside from the prover's private input. As we mentioned, the output of the program is then part of the inputs to the verification algorithm. Which in this case just takes .

+

PLONK Arithmetization

+

This is the process that takes the circuit of a particular program and produces a set of mathematical tools that can be used to generate and verify proofs of execution. The end result will be a set of eight polynomials. To compute them we need first to define two matrices. We call them the matrix and the matrix. The polynomials and the matrices depend only on the program and not on any particular execution of it. So they can be computed once and used for every execution instance. To understand what they are useful for, we need to start from execution traces.

+

Circuits and execution traces

+

See the program as a sequence of gates that have left operand, a right operand and an output. The two most basic gates are multiplication and addition gates. In our example, one way of seeing our toy program is as a composition of three gates.

+

Gate 1: left: e, right: x, output: u = e * x +Gate 2: left: u, right: x, output: v = e + x +Gate 3: left: v, right: 1, output: w = v - 1

+

On executing the circuit, all these variables will take a concrete value. All that information can be put in table form. It will be a matrix with all left, right and output values of all the gates. One row per gate. We call the columns of this matrix . Let's build them for and . We get , and . So the first matrix is:

+ + + + +
ABC
236
639
9-8
+

The last gate subtracts a constant value that is part of the program and is not a variable. So it actually has only one input instead of two. And the output is the result of subtracting to it. That's why it is handled a bit different from the second gate. The symbol "-" in the column is a consequence of that. With that we mean "any value" because it won't change the result. In the next section we'll see how we implement that. Here we'll use this notation when any value can be put there. In case we have to choose some, we'll default to .

+

What we got is a valid execution trace. Not all matrices of that shape will be the trace of an execution. The matrices and will be the tool we need to distinguish between valid and invalid execution traces.

+

The matrix

+

As we said, it only depends on the program itself and not on any particular evaluation of it. It has one row for each gate and its columns are called . They encode the type of gate of the rows and are designed to satisfy the following.

+

Claim: if columns correspond to a valid evaluation of the circuit then for all the following equality holds

+

This is better seen with examples. A multiplication gate is represented by the row:

+ + +
001-10
+

And the row in the trace matrix that corresponds to the execution of that gate is

+ + +
ABC
236
+

The equation in the claim for that row is that , which equals . The next is an addition gate. This is represented by the row

+ + +
110-10
+

The corresponding row in the trace matrix its

+ + +
ABC
639
+

And the equation of the claim is , which adds up to . Our last row is the gate that adds a constant. Addition by constant C can be represented by the row

+ + +
100-1C
+

In our case . The corresponding row in the execution trace is

+ + +
ABC
9-8
+

And the equation of the claim is . This is also zero.

+

Putting it altogether, the full matrix is

+ + + + +
001-10
110-10
100-1-1
+

And we saw that the claim is true for our particular execution: + + +

+

Not important to our example, but multiplication by constant C can be represented by:

+ + +
C00-10
+

As you might have already noticed, there are several ways of representing the same gate in some cases. We'll exploit this in a moment.

+

The matrix

+

The claim in the previous section is clearly not an "if and only if" statement because the following trace columns do satisfy the equations but do not correspond to a valid execution:

+ + + + +
ABC
236
000
20-19
+

The matrix encodes the carry of the results from one gate to the right or left operand of a subsequent one. These are called wirings. Like the matrix, it's independent of the particular evaluation. It consists of indices for all input and intermediate variables. In this case that matrix is:

+ + + + +
LRO
012
213
3-4
+

Here is the index of , is the index of , is the index of , is the index of and is the index of the output . Now we can update the claim to have an "if and only if" statement.

+

Claim: Let be a matrix with columns . It correspond to a valid evaluation of the circuit if and only if a) for all the following equality holds b) for all such that we have .

+

So now our malformed example does not pass the second check.

+

Custom gates

+

Our matrices are fine now. But they can be optimized. Let's do that to showcase this flexibility of PLONK and also reduce the size of our example.

+

PLONK has the flexibility to construct more sophisticated gates as combinations of the five columns. And therefore the same program can be expressed in multiple ways. In our case all three gates can actually be merged into a single custom gate. The matrix ends up being a single row.

+ + +
011-11
+

and also the matrix

+ + +
LRO
012
+

The trace matrix for this representation is just

+ + +
ABC
238
+

And we check that it satisfies the equation

+

+

Of course, we can't always squash an entire program into a single gate.

+

Public inputs

+

Aside from the gates that execute the program operations, additional rows must be incorporated into these matrices. This is due to the fact that the prover must demonstrate not only that she executed the program, but also that she used the appropriate inputs. Furthermore, the proof must include an assertion of the output value. As a result, a few extra rows are necessary. In our case these are the first two and the last one. The original one sits now in the third row.

+ + + + + +
-10003
-10008
111-11
1-1000
+

And this is the updated matrix

+ + + + + +
LRO
0--
1--
203
13-
+

The first row is there to force the variable with index to take the value . Similarly the second row forces variable with index to take the value . These two will be the public inputs of the verifier. The last row checks that the output of the program is the claimed one.

+

And the trace matrix is now

+ + + + + +
ABC
3--
8--
238
88-
+

With these extra rows, equations add up to zero only for valid executions of the program with input and output .

+

An astute observer would notice that by incorporating these new rows, the matrix is no longer independent of the specific evaluation. This is because the first two rows of the column contain concrete values that are specific to a particular execution instance. To maintain independence, we can remove these values and consider them as part of an extra one-column matrix called (stands for Public Input). This column has zeros in all rows not related to public inputs. We put zeros in the columns. The responsibility of filling in the matrix is of the prover and verifier. In our example it is

+ + + + + +
3
8
0
0
+

And the final matrix is

+ + + + + +
-10000
-10000
111-11
1-1000
+

We ended up with two matrices that depend only on the program, and . And two matrices that depend on a particular evaluation, namely the and matrices. The updated version of the claim is the following:

+

Claim: Let be a matrix with columns . It corresponds to a evaluation of the circuit if and only if a) for all the following equality holds b) for all such that we have .

+

From matrices to polynomials

+

In the previous section we showed how the arithmetization process works in PLONK. For a program with public inputs and gates, we constructed two matrices and , of sizes and that satisfy the following. Let

+

Claim: Let be a matrix with columns and a matrix. They correspond to a valid execution instance with public input given by if and only if a) for all the following equality holds b) for all such that we have , c) for all .

+

Polynomials enter now to squash most of these equations. We will traduce the set of all equations in conditions (a) and (b) to just a few equations on polynomials.

+

Let be a primitive -th root of unity and let . Let be the polynomials of degree at most that interpolate the columns at the domain . This means for example that for all . And similarly for all the other columns.

+

With this, condition (a) of the claim is equivalent to for all in .This is just by definition of the polynomials. But in polynomials land this is also equivalent to (a) there exists a polynomial such that , where is the polynomial .

+

To reduce condition (b) to polynomial equations we need to introduce the concept of permutation. A permutation is a rearrangement of a set. Usually denoted . For finite sets it is a map from a set to itself that takes all values. In our case the set will be the set of all pairs + +The matrix induces a permutation of this set where is equal to the indices of the next occurrence of the value at position . If already at the last occurrence, go to the first one. By next we mean the following occurrence as if the columns were stacked on each other. Let's see how this works in the example circuit. Recall is

+ + + + + +
LRO
0--
1--
203
13-
+

The permutation in this case is the map , , , , , , . For the positions with - values doesn't really matter right now.

+

It's not hard to see that condition (b) is equivalent to: for all , .

+

A little less obvious is that this condition is in turn equivalent to checking whether the following sets and are equal + + +The proof this equivalence is straightforward. Give it a try!

+

In our example the sets in question are respectively + +and +

+

You can check these sets coincide by inspection. Recall our trace matrix is

+ + + + + +
ABC
3--
8--
238
88-
+

Checking equality of these sets is something that can be reduced to polynomial equations. It is a very nice method that PLONK uses. To understand it better let's start with a simpler case.

+

Equality of sets

+

Suppose we have two sets of two field elements in . And we are interested in checking whether they are equal.

+

One thing we could do is compute and and compare them. If the sets are equal, then those elements are necessarily equal.

+

But the converse is not true. For example the sets and both have as the result of the product of their elements. But they are not equal. So this is not good to check equality.

+

Polynomials come to rescue here. What we can do instead is consider the following sets of polynomials , . Sets and are equal if and only if sets and are equal. This is because equality of polynomials boils down to equality of their coefficients. But the difference with and is that now the approach of multiplying the elements works. That is, and are equal if and only if . This is not entirely evident but follows from a property that polynomials have, called unique factorization. Here the important fact is that linear polynomials act as sort of prime factors. Anyway, you can take that for granted. The last part of this trick is to use the Schwartz-Zippel lemma and go back to the land of field elements. That means, if for some random element we have , then with overwhelming probability the equality holds.

+

Putting this altogether, if for some random element we have , then the sets and are equal. Of course this also holds for sets with more than two elements. Let's write that down.

+

Fact: Let and be sets of field elements. If for some random the following equality holds + +then with overwhelming probability is equal to .

+

And here comes the trick that reduces this check to polynomial equations. Let + be a domain of the form for some primitive -th root of unity . Let and be respectively the polynomials that interpolate the following values at . + +

+

Then equals if and only if there exists a polynomial such that + + +for all .

+

Let's see why. Suppose that equals . Construct as the polynomial that interpolates the following values +in the same domain as and . That works. Conversely, suppose such a polynomial exists. By evaluating the equation at and using recursion we get that . Moreover, evaluating it at we obtain that +The second equality holds because since it is a -th root of unity. Expanding with the values of and one obtains that equals . Which is what we wanted.

+

In summary. We proved the following:

+

Fact: Let and be sets of field elements. Let be a random field element. Let be a primitive -th root of unity and . Let and be respectively the polynomials that interpolate the values and at . If there exists a polynomial such that + + +for all , then with overwhelming probability the sets and are equal.

+

Sets of tuples

+

In the previous section we saw how to check whether two sets of field elements are equal using polynomial equations. To be able to use it in our context we need to extend it to sets of tuples of field elements. This is pretty straightforward.

+

Let's start with the easy case. Let and be two sets of pairs of field elements. That is for all . The trick is very similar to the previous section. + +

+

Just as before, by looking at coefficients we can see that the sets and are equal if and only if and are equal. +And notice that these are sets of polynomials, we got rid of the tuples! And now the situation is very similar to the previous section. We have that and are equal if and only if the product of their elements coincide. This is true also because polynomials in two variables are a unique factorization domain. So as before, we can use the Schwartz-Zippel lemma. Precisely, if for random , the elements + +and + +coincide, then and are equal with overwhelming probability.

+

Here is the statement for sets of more than two pairs of field elements.

+

Fact: Let and be sets of pairs of field elements. So that and the same for . Let be a random field elements. Let be a -th root of unity and . Let and be respectively the polynomials that interpolate the values + +and + +at . If there exists a polynomial such that + + +for all , then with overwhelming probability the sets and are equal.

+

Going back to our case

+

Recall we want to rephrase condition (b) in terms of polynomials. We have already seen that condition (b) is equivalent to and being equal, where + +and +

+

We cannot directly use the facts of the previous sections because our sets are not sets of field elements. Nor are they sets of pairs of field elements. They are sets of pairs with some indexes in the first coordinate and a field element in the second one. So the solution is to convert them to sets of pairs of field elements and apply the result of the previous section. So how do we map an element of the form to something of the form with and field elements? The second coordinate is trivial, we can just leave as it is and take . For the indexes pair there are multiple ways. The important thing to achieve here is that different pairs get mapped to different field elements. Recall that ranges from to and ranges from to . One way is to take a -th primitive root of unity and define . Putting it altogether, we are mapping the pair to the pair , which is a pair of field elements. Now we can consider the sets + +and + +We have that condition (b) is equivalent to and being equal.

+

Applying the method of the previous section to these sets, we obtain the following.

+

Fact: Let be a -th root of unity and and random field elements. Let . Let and be the polynomials that interpolate, respectively, the following values at : + +and + +Suppose there exists a polynomial such that + + +for all . +Then the sets and are equal with overwhelming probability.

+

One last minute definitions. Notice that is a primitive -th root of unity. Let .

+

Define to be the interpolation at of + +Similarly define and to be the interpolation at of the sets of values + + +These will be useful during the protocol to work with such polynomials and the above equations.

+

A more compact form

+

The last fact is equivalent the following. There's no new idea here, just a more compact form of the same thing that allows the polynomial to be of degree at most .

+

Fact: Let be a -th root of unity. Let . Let and be two field elements such that for all . Let and be random field elements. Let and be the polynomials that interpolate, respectively, the following values at : + +and + +Suppose there exists a polynomial such that + + +for all . +Then the sets and are equal with overwhelming probability.

+

Common preprocessed input

+

We have arrived at the eight polynomials we mentioned at the beginning: +

+

These are what's called the common preprocessed input.

+

Wrapping up the whole thing

+

Let's try to wrap up what we have so far. We started from a program. We saw that it can be seen as a sequence of gates with left, right and output values. That's called a circuit. From this two matrices and can be computed that capture the gates logic.

+

Executing the circuit leaves us with matrices and , called the trace matrix and the public input matrix, respectively. Everything we want to prove boils down to check that such matrices are valid. And we have the following result.

+

Fact: Let be a matrix with columns and a matrix. They correspond to a valid execution instance with public input given by if and only if a) for all the following equality holds b) for all such that we have , c) for all .

+

Then we constructed polynomials , , off the matrices and . They are the result of interpolating at a domain for some -th primitive root of unity and a few random values. And also constructed polynomials off the matrices and . Loosely speaking, the above fact can be reformulated in terms of polynomial equations as follows.

+

Fact: Let . Let be a matrix with columns and a matrix. They correspond to a valid execution instance with public input given by if and only if

+

a) There is a polynomial such that the following equality holds

+

b) There are polynomials , such that and , where

+

You might be wondering where the polynomials came from. Recall that for a polynomial , we have for all if and only if for some polynomial .

+

Finally both conditions (a) and (b) are equivalent to a single equation (c) if we let more randomness to come into play. This is:

+

(c) Let be a random field element. There is a polynomial such that +

+

This last step is not obvious. You can check the paper to see the proof. Anyway, this is the equation you'll recognize below in the description of the protocol.

+

Randomness is a delicate matter and an important part of the protocol is where it comes from, who chooses it and when they choose it. Check out the protocol to see how it works.

+
+

Protocol

+

Details and tricks

+

Polynomial commitment scheme

+

A polynomial commitment scheme (PCS) is a cryptographic tool that allows one party to commit to a polynomial, and later prove properties of that polynomial. +This commitment polynomial hides the original polynomial's coefficients and can be publicly shared without revealing any information about the original polynomial. +Later, the party can use the commitment to prove certain properties of the polynomial, such as that it satisfies certain constraints or that it evaluates to a certain value at a specific point.

+

In the implementation section we'll explain the inner workings of the Kate-Zaverucha-Goldberg scheme, a popular PCS chosen in Lambdaworks for PLONK.

+

For the moment we only need the following about it:

+

It consists of a finite group and the following algorithms:

+
    +
  • Commit(): This algorithm takes a polynomial and produces an element of the group . It is called the commitment of and is denoted by . It is homomorphic in the sense that . The former sum being addition of polynomials. The latter is addition in the group .
  • +
  • Open(, ): It takes a polynomial and a field element and produces an element of the group . This element is called an opening proof for . It is the proof that evaluated at gives .
  • +
  • Verify(, , , ): It takes group elements and , and also field elements and . With overwhelming probability it outputs Accept if and Reject otherwise.
  • +
+

Blindings

+

As you will see in the protocol, the prover reveals the value taken by a bunch of the polynomials at a random . In order for the protocol to be Honest Verifier Zero Knowledge, these polynomials need to be blinded. This is a process that makes the values of these polynomials at seemingly random by forcing them to be of certain degree. Here's how it works.

+

Let's take for example the polynomial the prover constructs. This is the interpolation of the first column of the trace matrix at the domain . +This matrix has all of the left operands of all the gates. The prover wishes to keep them secret. +Say the trace matrix has rows. is . The invariant that the prover cannot violate is that must take the value , for all . This is what the interpolation polynomial satisfies. And is the unique such polynomial of degree at most with such property. But for higher degrees, there are many such polynomials.

+

The blinding process takes and a desired degree , and produces a new polynomial of degree exactly . This new polynomial satisfies that for all . But outside differs from .

+

This may seem hard but it's actually very simple. Let be the polynomial . If , with , then sample random values and define +

+

The reason why this does the job is that for all . Therefore the added term vanishes at and leaves the values of at unchanged.

+

Linearization trick

+

This is an optimization in PLONK to reduce the number of checks of the verifier.

+

One of the main checks in PLONK boils down to check that , with some polynomial that looks like , and so on. In particular the verifier needs to get the value from somewhere.

+

For the sake of simplicity, in this section assume is exactly . Secret to the prover here are only . The polynomials and are known also to the verifier. The verifier will already have the commitments and . So the prover could send just , along with their opening proofs and let the verifier compute by himself and . Then with all these values the verifier could compute . And also use his commitments to validate the opening proofs of and .

+

This has the problem that computing and is expensive. The prover can instead save the verifier this by sending also along with opening proofs. Since the verifier will have the commitments and beforehand, he can check that the prover is not cheating and cheaply be convinced that the claimed values are actually and . This is much better. It involves the check of four opening proofs and the computation of off the values received from the prover. But it can be further improved as follows.

+

As before, the prover sends along with their opening proofs. She constructs the polynomial . She sends the value along with an opening proof of it. Notice that the value of is exactly . The verifier can compute by himself as . The verifier has everything to check all three openings and get convinced that the claimed value is true. And this value is actually . So this means no more work for the verifier. And the whole thing got reduced to three openings.

+

This is called the linearization trick. The polynomial is called the linearization of .

+

Setup

+

There's a one time setup phase to compute some values common to any execution and proof of the particular circuit. Precisely, the following commitments are computed and published. +

+

Proving algorithm

+

Next we describe the proving algorithm for a program of size . That includes public inputs. Let be a primitive -th root of unity. Let . Define .

+

Assume the eight polynomials of common preprocessed input are already given.

+

The prover computes the trace matrix as described in the first sections. That means, with the first rows corresponding to the public inputs. It should be a matrix.

+

Round 1

+

Add to the transcript the following: +

+

Compute polynomials as the interpolation polynomials of the columns of at the domain . +Sample random +Let

+

+

+

+

Compute and add them to the transcript.

+

Round 2

+

Sample from the transcript.

+

Let and define recursively for .

+

+

Compute the polynomial as the interpolation polynomial at the domain of the values .

+

Sample random values and let .

+

Compute and add it to the transcript.

+

Round 3

+

Sample from the transcript.

+

Let be the interpolation of the public input matrix at the domain .

+

Let

+

+

and define . Compute such that . Write with and polynomials of degree at most .

+

Sample random and define

+

+

Compute and add them to the transcript.

+

Round 4

+

Sample from the transcript.

+

Compute and add them to the transcript.

+

Round 5

+

Sample from the transcript.

+

Let

+

+

Define

+

+

The subscript stands for "non-constant", as is the part of the linearization of that has non-constant factors. The subscript "partial" indicates that it is a partial evaluation of at . Partial meaning that only some power of ar replaced by the powers of . So in particular .

+

Let be the opening proof at of the polynomial defined as +

+

Let be the opening proof at of the polynomial .

+

Compute and .

+

Proof

+

The proof is: +

+

Verification algorithm

+

Transcript initialization

+

The first step is to initialize the transcript in the same way the prover did, adding to it the following elements. +

+

Extraction of values and commitments

+

Challenges

+

Firstly, the verifier needs to compute all the challenges. For that, he follows these steps:

+
    +
  • Add to the transcript.
  • +
  • Sample two challenges .
  • +
  • Add to the transcript.
  • +
  • Sample a challenge .
  • +
  • Add to the transcript.
  • +
  • Sample a challenge .
  • +
  • Add to the transcript.
  • +
  • Sample a challenge .
  • +
+

Compute

+

Also he needs compute a few values off all these data. First, he computes the matrix with the public inputs and outputs. He needs to compute , where is the interpolation of at the domain . But he doesn't need to compute . He can instead compute as + +where is the number of public inputs and is the Lagrange basis at the domain .

+

Compute claimed values of and

+

He computes

+

This is the constant part of the linearization of . So adding it to what the prover claims to be , he obtains +

+

With respect to , this is actually already .

+

Compute and

+

He computes these off the commitments in the proof as follows +

+

For , first compute

+

+

Then .

+

Compute claimed value and

+

Compute as

+

+

Also, the commitment of the polynomial is +

+

Proof check

+

Now the verifier has all the necessary values to proceed with the checks.

+
    +
  • Check that equals .
  • +
  • Verify the opening of at . That is, check that outputs Accept.
  • +
  • Verify the opening of at . That is, check the validity of the proof using the commitment and the value . +That is, check that outputs Accept.
  • +
+

If all checks pass, he outputs Accept. Otherwise outputs Reject.

+
+

Implementation

+

In this section we discuss the implementation details of the PLONK algorithm. We use the notation and terminology of the protocol and recap sections.

+

At the moment our API supports the backend of PLONK, that is, all the setup, prove and verify algorithms. We temporarily rely on external sources for the definition of a circuit and the creation of the and matrices, as well as the execution of it to obtain the trace matrix . We mainly use gnark temporarily for that purpose.

+

To generate proofs and validate them, we need to feed the algorithms with precomputed values of the , and matrices, and the primitive root of unity .

+

Let's see our API on a test circuit that provides all these values. The program in this case is the one that takes an input , a private input and computes . As in the toy example of the recap, the output of the program is added to the public inputs and the circuit actually asserts that the output is the claimed value. So more precisely, the prover will generate a proof for the statement ASSERT(x*e+5==y), where both are public inputs.

+

Usage

+

Here is the happy path.

+

+#![allow(unused)]
+fn main() {
+// This is the common preprocessed input for
+// the test circuit ( ASSERT(x * e + 5 == y) )
+let common_preprocessed_input = test_common_preprocessed_input_2();
+
+// Input
+let x = FieldElement::from(2_u64);
+
+// Private input
+let e = FieldElement::from(3_u64);
+
+let y, witness = test_witness_2(x, e);
+
+let srs = test_srs(common_preprocessed_input.n);
+let kzg = KZG::new(srs);
+
+let verifying_key = setup(&common_preprocessed_input, &kzg);
+
+let random_generator = TestRandomFieldGenerator {};
+let prover = Prover::new(kzg.clone(), random_generator);
+
+let public_input = vec![x.clone(), y];
+
+let proof = prover.prove(
+    &witness,
+    &public_input,
+    &common_preprocessed_input,
+    &verifying_key,
+);
+
+let verifier = Verifier::new(kzg);
+assert!(verifier.verify(
+    &proof,
+    &public_input,
+    &common_preprocessed_input,
+    &verifying_key
+));
+}
+
+

Let's brake it down. The helper function test_common_preprocessed_input_2() returns an instance of the following struct for the particular test circuit:

+

+#![allow(unused)]
+fn main() {
+pub struct CommonPreprocessedInput<F: IsField> {
+    pub n: usize,
+    pub domain: Vec<FieldElement<F>>,
+    pub omega: FieldElement<F>,
+    pub k1: FieldElement<F>,
+
+    pub ql: Polynomial<FieldElement<F>>,
+    pub qr: Polynomial<FieldElement<F>>,
+    pub qo: Polynomial<FieldElement<F>>,
+    pub qm: Polynomial<FieldElement<F>>,
+    pub qc: Polynomial<FieldElement<F>>,
+
+    pub s1: Polynomial<FieldElement<F>>,
+    pub s2: Polynomial<FieldElement<F>>,
+    pub s3: Polynomial<FieldElement<F>>,
+
+    pub s1_lagrange: Vec<FieldElement<F>>,
+    pub s2_lagrange: Vec<FieldElement<F>>,
+    pub s3_lagrange: Vec<FieldElement<F>>,
+}
+}
+
+

Apart from the eight polynomials in the canonical basis, we store also here the number of constraints , the domain , the primitive -th of unity and the element . The element will be . For convenience, we also store the polynomials in Lagrange form.

+

The following lines define the particular values of the program input and the private input .

+

+#![allow(unused)]
+fn main() {
+// Input
+let x = FieldElement::from(2_u64);
+
+// Private input
+let e = FieldElement::from(3_u64);
+let y, witness = test_witness_2(x, e);
+}
+
+

The function test_witness_2(x, e) returns an instance of the following struct, that holds the polynomials that interpolate the columns of the trace matrix .

+

+#![allow(unused)]
+fn main() {
+pub struct Witness<F: IsField> {
+    pub a: Vec<FieldElement<F>>,
+    pub b: Vec<FieldElement<F>>,
+    pub c: Vec<FieldElement<F>>,
+}
+}
+
+

Next the commitment scheme KZG (Kate-Zaverucha-Goldberg) is instantiated.

+

+#![allow(unused)]
+fn main() {
+let srs = test_srs(common_preprocessed_input.n);
+let kzg = KZG::new(srs);
+}
+
+

The setup function performs the setup phase. It only needs the common preprocessed input and the commitment scheme.

+

+#![allow(unused)]
+fn main() {
+let verifying_key = setup(&common_preprocessed_input, &kzg);
+}
+
+

It outputs an instance of the struct VerificationKey:

+

+#![allow(unused)]
+fn main() {
+pub struct VerificationKey<G1Point> {
+    pub qm_1: G1Point,
+    pub ql_1: G1Point,
+    pub qr_1: G1Point,
+    pub qo_1: G1Point,
+    pub qc_1: G1Point,
+
+    pub s1_1: G1Point,
+    pub s2_1: G1Point,
+    pub s3_1: G1Point,
+}
+}
+
+

It stores the commitments of the eight polynomials of the common preprocessed input. The suffix _1 means it is a commitment. It comes from the notation , where is a polynomial.

+

Then a prover is instantiated

+

+#![allow(unused)]
+fn main() {
+let random_generator = TestRandomFieldGenerator {};
+let prover = Prover::new(kzg.clone(), random_generator);
+}
+
+

The prover is an instance of the struct Prover:

+

+#![allow(unused)]
+fn main() {
+pub struct Prover<F, CS, R>
+where
+  F:  IsField,
+  CS: IsCommitmentScheme<F>,
+  R:  IsRandomFieldElementGenerator<F>
+  {
+    commitment_scheme: CS,
+    random_generator: R,
+    phantom: PhantomData<F>,
+}
+}
+
+

It stores an instance of a commitment scheme and a random field element generator needed for blinding polynomials.

+

Then the public input is defined. As we mentioned in the recap, the public input contains the output of the program.

+

+#![allow(unused)]
+fn main() {
+let public_input = vec![x.clone(), y];
+}
+
+

We then generate a proof using the prover's method prove

+

+#![allow(unused)]
+fn main() {
+let proof = prover.prove(
+    &witness,
+    &public_input,
+    &common_preprocessed_input,
+    &verifying_key,
+);
+}
+
+

The output is an instance of the struct Proof.

+

+#![allow(unused)]
+fn main() {
+pub struct Proof<F: IsField, CS: IsCommitmentScheme<F>> {
+    // Round 1.
+    /// Commitment to the wire polynomial `a(x)`
+    pub a_1: CS::Commitment,
+    /// Commitment to the wire polynomial `b(x)`
+    pub b_1: CS::Commitment,
+    /// Commitment to the wire polynomial `c(x)`
+    pub c_1: CS::Commitment,
+
+    // Round 2.
+    /// Commitment to the copy constraints polynomial `z(x)`
+    pub z_1: CS::Commitment,
+
+    // Round 3.
+    /// Commitment to the low part of the quotient polynomial t(X)
+    pub t_lo_1: CS::Commitment,
+    /// Commitment to the middle part of the quotient polynomial t(X)
+    pub t_mid_1: CS::Commitment,
+    /// Commitment to the high part of the quotient polynomial t(X)
+    pub t_hi_1: CS::Commitment,
+
+    // Round 4.
+    /// Value of `a(ζ)`.
+    pub a_zeta: FieldElement<F>,
+    /// Value of `b(ζ)`.
+    pub b_zeta: FieldElement<F>,
+    /// Value of `c(ζ)`.
+    pub c_zeta: FieldElement<F>,
+    /// Value of `S_σ1(ζ)`.
+    pub s1_zeta: FieldElement<F>,
+    /// Value of `S_σ2(ζ)`.
+    pub s2_zeta: FieldElement<F>,
+    /// Value of `z(ζω)`.
+    pub z_zeta_omega: FieldElement<F>,
+
+    // Round 5
+    /// Value of `p_non_constant(ζ)`.
+    pub p_non_constant_zeta: FieldElement<F>,
+    ///  Value of `t(ζ)`.
+    pub t_zeta: FieldElement<F>,
+    /// Batch opening proof for all the evaluations at ζ
+    pub w_zeta_1: CS::Commitment,
+    /// Single opening proof for `z(ζω)`.
+    pub w_zeta_omega_1: CS::Commitment,
+}
+}
+
+

Finally, we instantiate a verifier.

+

+#![allow(unused)]
+fn main() {
+let verifier = Verifier::new(kzg);
+}
+
+

It's an instance of Verifier:

+

+#![allow(unused)]
+fn main() {
+struct Verifier<F: IsField, CS: IsCommitmentScheme<F>> {
+    commitment_scheme: CS,
+    phantom: PhantomData<F>,
+}
+}
+
+

Finally, we call the verifier's method verify that outputs a bool.

+

+#![allow(unused)]
+fn main() {
+assert!(verifier.verify(
+    &proof,
+    &public_input,
+    &common_preprocessed_input,
+    &verifying_key
+));
+}
+
+

Padding

+

All the matrices are padded with dummy rows so that their length is a power of two. To be able to interpolate their columns, we need a primitive root of unity of that order. Given the particular field used in our implementation, that means that the maximum possible size for a circuit is .

+

The entries of the dummy rows are filled in with zeroes in the , and matrices. The matrix needs to be consistent with the matrix. Therefore it is filled with the value of the variable with index .

+

Some other rows in the matrix have also dummy values. These are the rows corresponding to the and columns of the public input rows. In the recap we denoted them with the empty - symbol. They are filled in with the same logic as the padding rows, as well as the corresponding values in the matrix.

+

Implementation details

+

The implementation pretty much follows the rounds as are described in the protocol section. There are a few details that are worth mentioning.

+

Commitment Scheme

+

The commitment scheme we use is the Kate-Zaverucha-Goldberg scheme with the BLS 12 381 curve and the ate pairing. It can be found in the commitments module of the lambdaworks_crypto package.

+

The order of the cyclic subgroup is

+
0x73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001
+
+

The maximum power of two that divides is . Therefore, that is the maximum possible order for a primitive root of unity in with order a power of two.

+

Fiat-Shamir

+

Transcript strategy

+

Here we describe our implementation of the transcript used for the Fiat-Shamir heuristic.

+

A Transcript exposes two methods: append and challenge.

+

The method append adds a message to the transcript by updating the internal state of the hasher with the raw bytes of the message.

+

The method challenge returns the result of the hasher using the current internal state of the hasher. It subsequently resets the hasher and updates the internal state with the last result.

+

Here is an example of this process:

+
    +
  1. Start a fresh transcript.
  2. +
  3. Call append and pass message_1.
  4. +
  5. Call append and pass message_2.
  6. +
  7. The internal state of the hasher at this point is message_2 || message_1.
  8. +
  9. Call challenge. The output is Hash(message_2 || message_1).
  10. +
  11. Call append and pass message_3.
  12. +
  13. Call challenge. The output is Hash(message_3 || Hash(message_2 || message_1)).
  14. +
  15. Call append and pass message_4.
  16. +
+

The internal state of the hasher at the end of this exercise is message_4 || Hash(message_3 || Hash(message_2 || message_1))

+

The underlying hasher function we use is h=sha3.

+

Field elements

+

The result of every challenge is a -bit string, which is interpreted as an integer in big-endian order. A field element is constructed out of it by taking modulo the field order. The prime field used in this implementation has a -bit order. Therefore some field elements are more probable to occur than others because they have more representatives as 256-bit integers.

+

Strong Fiat-Shamir

+

The first messages added to the transcript are all commitments of the polynomials of the common preprocessed input and the values of the public inputs. This prevents a known vulnerability called "weak Fiat-Shamir". +Check out the following resources to learn more about it.

+ +
+

Circuit API

+

In this section, we'll discuss how to build your own constraint system to prove the execution of a particular program.

+

Simple Example

+

Let's take the following simple program as an example. We have two public inputs: x and y. We want to prove to a verifier that we know a private input e such that x * e = y. You can achieve this by building the following constraint system:

+
use lambdaworks_plonk::constraint_system::ConstraintSystem;
+use lambdaworks_math::elliptic_curve::short_weierstrass::curves::bls12_381::default_types::FrField;
+
+fn main() {
+    let system = &mut ConstraintSystem::<FrField>::new();
+    let x = system.new_public_input();
+    let y = system.new_public_input();
+    let e = system.new_variable();
+
+    let z = system.mul(&x, &e);
+    
+    // This constraint system asserts that x * e == y
+    system.assert_eq(&y, &z);
+}
+
+

This code creates a constraint system over the field of the BLS12381 curve. Then, it creates three variables: two public inputs x and y, and a private variable e. Note that every variable is private except for the public inputs. Finally, it adds the constraints that represent a multiplication and an assertion.

+

Before generating proofs for this system, we need to run a setup and obtain a verifying key:

+

+#![allow(unused)]
+fn main() {
+let common = CommonPreprocessedInput::from_constraint_system(&system, &ORDER_R_MINUS_1_ROOT_UNITY);
+let srs = test_srs(common.n);
+let kzg = KZG::new(srs); // The commitment scheme for plonk.
+let vk = setup(&common, &kzg);
+}
+
+

Now we can generate proofs for our system. We just need to specify the public inputs and obtain a witness that is a solution for our constraint system:

+

+#![allow(unused)]
+fn main() {
+let inputs = HashMap::from([(x, FieldElement::from(4)), (e, FieldElement::from(3))]);
+let assignments = system.solve(inputs).unwrap();
+let witness = Witness::new(assignments, &system);
+}
+
+

Once you have all these ingredients, you can call the prover:

+

+#![allow(unused)]
+fn main() {
+let public_inputs = system.public_input_values(&assignments);
+let prover = Prover::new(kzg.clone(), TestRandomFieldGenerator {});
+let proof = prover.prove(&witness, &public_inputs, &common, &vk);
+}
+
+

and verify:

+

+#![allow(unused)]
+fn main() {
+let verifier = Verifier::new(kzg);
+assert!(verifier.verify(&proof, &public_inputs, &common, &vk));
+}
+
+

Building Complex Systems

+

Some operations are common, and it makes sense to wrap the set of constraints that do these operations in a function and use it several times. Lambdaworks comes with a collection of functions to help you build your own constraint systems, such as conditionals, inverses, and hash functions.

+

However, if you have an operation that does not come with Lambdaworks, you can easily extend Lambdaworks functionality. Suppose that the exponentiation operation is something common in your program. You can write the square and multiply algorithm and put it inside a function:

+

+#![allow(unused)]
+fn main() {
+pub fn pow(
+    system: &mut ConstraintSystem<FrField>,
+    base: Variable,
+    exponent: Variable,
+) -> Variable {
+    let exponent_bits = system.new_u32(&exponent);
+    let mut result = system.new_constant(FieldElement::one());
+
+    for i in 0..32 {
+        if i != 0 {
+            result = system.mul(&result, &result);
+        }
+        let result_times_base = system.mul(&result, &base);
+        result = system.if_else(&exponent_bits[i], &result_times_base, &result);
+    }
+    result
+}
+}
+
+

This function can then be used to modify our simple program from the previous section. The following circuit checks that the prover knows e such that pow(x, e) = y:

+
use lambdaworks_plonk::constraint_system::ConstraintSystem;
+use lambdaworks_math::elliptic_curve::short_weierstrass::curves::bls12_381::default_types::FrField;
+
+fn main() {
+    let system = &mut ConstraintSystem::<FrField>::new();
+    let x = system.new_public_input();
+    let y = system.new_public_input();
+    let e = system.new_variable();
+
+    let z = pow(system, &x, &e);
+    system.assert_eq(&y, &z);
+}
+
+

You can keep composing these functions in order to create more complex systems.

+
+

STARK Prover

+

The goal of this document is to give a good a understanding of our stark prover code. To this end, in the first section we go through a recap of how the proving system works at a high level mathematically; then we dive into how that's actually implemented in our code.

+
+

STARKs Recap

+

Verifying Computation through Polynomials

+

In general, we express computation in our proving system by providing an execution trace satisfying certain constraints. The execution trace is a table containing the state of the system at every step of computation. This computation needs to follow certain rules to be valid; these rules are our constraints.

+

The constraints for our computation are expressed using an Algebraic Intermediate Representation or AIR. This representation uses polynomials to encode constraints, which is why sometimes they are called polynomial constraints.

+

To make all this less abstract, let's go through two examples.

+

Fibonacci numbers

+

Throughout this section and the following we will use this example extensively to have a concrete example. Even though it's a bit contrived (no one cares about computing fibonacci numbers), it's simple enough to be useful. STARKs and proving systems in general are very abstract things; having an example in mind is essential to not get lost.

+

Let's say our computation consists of calculating the k-th number in the fibonacci sequence. This is just the sequence of numbers \(a_n\) satisfying

+

\[ +a_0 = 1 +\] +\[ +a_1 = 1 +\] +\[ +a_{n+2} = a_{n + 1} + a_n +\]

+

An execution trace for this just consists of a table with one column, where each row is the i-th number in the sequence:

+ + + + + + + + + +
a_i
1
1
2
3
5
8
13
21
+

A valid trace for this computation is a table satisfying two things:

+
    +
  • The first two rows are 1.
  • +
  • The value on any other row is the sum of the two preceding ones.
  • +
+

The first item is called a boundary constraint, it just enforces specific values on the trace at certain points. The second one is a transition constraint; it tells you how to go from one step of computation to the next.

+

Cairo

+

The example above is extremely useful to have a mental model, but it's not really useful for anything else. The problem is it just works for the very narrow example of computing fibonacci numbers. If we wanted to prove execution of something else, we would have to write an AIR for it.

+

What we're actually aiming for is an AIR for an entire general purpose Virtual Machine. This way, we can provide proofs of execution for any computation using just one AIR. This is what cairo as a programming language does. Cairo code compiles to the bytecode of a virtual machine with an already defined AIR. The general flow when using cairo is the following:

+
    +
  • User writes a cairo program.
  • +
  • The program is compiled into Cairo's VM bytecode.
  • +
  • The VM executes said code and provides an execution trace for it.
  • +
  • The trace is passed on to a STARK prover, which creates a proof of correct execution according to Cairo's AIR.
  • +
  • The proof is passed to a verifier, who checks that the proof is valid.
  • +
+

Ultimately, our goal is to give the tools to write a STARK prover for the cairo VM and do so. However, this is not a good example to start out as it's incredibly complex. The execution trace of a cairo program has around 30 columns, some for general purpose registers, some for other reasons. Cairo's AIR contains a lot of different transition constraints, encoding all the different possible instructions (arithmetic operations, jumps, etc).

+

Use the fibonacci example as your go-to for understanding all the moving parts; keep the Cairo example in mind as the thing we are actually building towards.

+

Fibonacci step by step walkthrough

+

Below we go through a step by step explanation of a STARK prover. We will assume the trace of the fibonacci sequence mentioned above; it consists of only one column of length \(2^n\). In this case, we'll take n=3. The trace looks like this

+ + + + + + + + + +
a_i
a_0
a_1
a_2
a_3
a_4
a_5
a_6
a_7
+

Trace polynomial

+

The first step is to interpolate these values to generate the trace polynomial. This will be a polynomial encoding all the information about the trace. The way we do it is the following: in the finite field we are working in, we take an 8-th primitive root of unity, let's call it g. It being a primitive root means two things:

+
    +
  • g is an 8-th root of unity, i.e., \(g^8 = 1\).
  • +
  • Every 8-th root of unity is of the form \(g^i\) for some \(0 \leq i \leq 7\).
  • +
+

With g in hand, we take the trace polynomial t to be the one satisfying

+

+

From here onwards, we will talk about the validity of the trace in terms of properties that this polynomial must satisfy. We will also implicitly identify a certain power of \(g\) with its corresponding trace element, so for example we sometimes think of \(g^5\) as \(a_5\), the fifth row in the trace, even though technically it's \(t\) evaluated in \(g^5\) that equals \(a_5\).

+

We talked about two different types of constraints the trace must satisfy to be valid. They were:

+
    +
  • The first two rows are 1.
  • +
  • The value on any other row is the sum of the two preceding ones.
  • +
+

In terms of t, this translates to

+
    +
  • \(t(g^0) = 1\) and \(t(g) = 1\).
  • +
  • \(t(x g^2) - t(xg) - t(x) = 0\) for all \(x \in {g^0, g^1, g^2, g^3, g^4, g^5}\). This is because multiplying by g is the same as advancing a row in the trace.
  • +
+

Composition Polynomial

+

To convince the verifier that the trace polynomial satisfies the relationships above, the prover will construct another polynomial that shows that both the boundary and transition constraints are satisfied and commit to it. We call this polynomial the composition polynomial, and usually denote it with \(H\). Constructing it involves a lot of different things, so we'll go step by step introducing all the moving parts required.

+

Boundary polynomial

+

To show that the boundary constraints are satisfied, we construct the boundary polynomial. Recall that our boundary constraints are \(t(g^0) = t(g) = 1\). Let's call \(P\) the polynomial that interpolates these constraints, that is, \(P\) satisfies:

+

+

The boundary polynomial \(B\) is defined as follows:

+

+

The denominator here is called the boundary zerofier, and it's the polynomial whose roots are the elements of the trace where the boundary constraints must hold.

+

How does \(B\) encode the boundary constraints? The idea is that, if the trace satisfies said constraints, then

+

+

+

so \(t(x) - P(x)\) has \(1\) and \(g\) as roots. Showing these values are roots is the same as showing that \(B(x)\) is a polynomial instead of a rational function, and that's why we construct \(B\) this way.

+

Transition constraint polynomial

+

To convince the verifier that the transition constraints are satisfied, we construct the transition constraint polynomial and call it \(C(x)\). It's defined as follows:

+

+

How does \(C\) encode the transition constraints? We mentioned above that these are satisfied if the polynomial in the numerator vanishes in the elements \({g^0, g^1, g^2, g^3, g^4, g^5}\). As with \(B\), this is the same as showing that \(C(x)\) is a polynomial instead of a rational function.

+

Constructing \(H\)

+

With the boundary and transition constraint polynomials in hand, we build the composition polynomial \(H\) as follows: The verifier will sample four numbers \(\beta_1, \beta_2\) and \(H\) will be

+

+

Why not just take \(H(x) = B(x) + C(x)\)? The reason for the betas is to make the resulting \(H\) be always different and unpredictable for the prover, so they can't precompute stuff beforehand.

+

With what we discussed above, showing that the constraints are satisfied is equivalent to saying that H is a polynomial and not a rational function (we are simplifying things a bit here, but it works for our purposes).

+

Commiting to \(H\)

+

To show \(H\) is a polynomial we are going to use the FRI protocol, which we treat as a black box. For all we care, a FRI proof will verify if what we committed to is indeed a polynomial. Thus, the prover will provide a FRI commitment to H, and if it passes, the verifier will be convinced that the constraints are satisfied.

+

There is one catch here though: how does the verifier know that FRI was applied to H and not any other polynomial? For this we need to add an additional step to the protocol.

+

Consistency check

+

After commiting to H, the prover needs to show that H was constructed correctly according to the formula above. To do this, it will ask the prover to provide an evaluation of H on some random point z and evaluations of the trace at the points \(t(z), t(zg)\) and \(t(zg^2)\).

+

Because the boundary and transition constraints are a public part of the protocol, the verifier knows them, and thus the only thing it needs to compute the evaluation \((z)\) by itself are the three trace evaluations mentioned above. Because it asked the prover for them, it can check both sides of the equation:

+

+

and be convinced that \(H\) was constructed correctly.

+

We are still not done, however, as the prover could have now cheated on the values of the trace or composition polynomial evaluations.

+

Deep Composition Polynomial

+

There are two things left the prover needs to show to complete the proof:

+
    +
  • That \(H\) effectively is a polynomial, i.e., that the constraints are satisfied.
  • +
  • That the evaluations the prover provided on the consistency check were indeed evaluations of the trace polynomial and composition polynomial on the out of domain point z.
  • +
+

Earlier we said we would use the FRI protocol to commit to H and show the first item in the list. However, we can slightly modify the polynomial we do FRI on to show both the first and second items at the same time. This new modified polynomial is called the DEEP composition polynomial. We define it as follows:

+

+

where the numbers \(\gamma_i\) are randomly sampled by the verifier.

+

The high level idea is the following: If we apply FRI to this polynomial and it verifies, we are simultaneously showing that

+
    +
  • \(H\) is a polynomial and the prover indeed provided H(z) as one of the out of domain evaluations. This is the first summand in Deep(x).
  • +
  • The trace evaluations provided by the prover were the correct ones, i.e., they were \(t(z)\), \(t(zg)\), and \(t(zg^2)\). These are the remaining summands of the Deep(x).
  • +
+

Consistency check

+

The prover needs to show that Deep was constructed correctly according to the formula above. To do this, the verifier will ask the prover to provide:

+
    +
  • An evaluation of H on z and x_0
  • +
  • Evaluations of the trace at the points \(t(z)\), \(t(zg)\), \(t(zg^2)\) and \(t(x_0)\)
  • +
+

Where z is the same random, out of domain point used in the consistency check of the composition polynomial, and x_0 is a random point that belongs to the trace domain.

+

With the values provided by the prover, the verifier can check both sides of the equation:

+

+

The prover also needs to show that the trace evaluation \(t(x_0)\) belongs to the trace. To achieve this, it needs to commit the merkle roots of t and the merkle proof of \(t(x_0)\).

+

Summary

+

We summarize below the steps required in a STARK proof for both prover and verifier.

+

Prover side

+
    +
  • Compute the trace polynomial t by interpolating the trace column over a set of \(2^n\)-th roots of unity \({g^i : 0 \leq i < 2^n}\).
  • +
  • Compute the boundary polynomial B.
  • +
  • Compute the transition constraint polynomial C.
  • +
  • Construct the composition polynomial H from B and C.
  • +
  • Sample an out of domain point z and provide the evaluations \(H(z)\), \(t(z)\), \(t(zg)\), and \(t(zg^2)\) to the verifier.
  • +
  • Sample a domain point x_0 and provide the evaluations \(H(x_0)\) and \(t(x_0)\) to the verifier.
  • +
  • Construct the deep composition polynomial Deep(x) from H, t, and the evaluations from the item above.
  • +
  • Do FRI on Deep(x) and provide the resulting FRI commitment to the verifier.
  • +
  • Provide the merkle root of t and the merkle proof of \(t(x_0)\).
  • +
+

Verifier side

+
    +
  • Take the evaluations \(H(z)\), \(H(x_0)\), \(t(z)\), \(t(zg)\), \(t(zg^2)\) and \(t(x_0)\) the prover provided.
  • +
  • Reconstruct the evaluations \(B(z)\) and \(C(z)\) from the trace evaluations we were given. Check that the claimed evaluation \(H(z)\) the prover gave us actually satisfies +
  • +
  • Check that the claimed evaluation \(Deep(x_0)\) the prover gave us actually satisfies +
  • +
  • Using the merkle root and the merkle proof the prover provided, check that \(t(x_0)\) belongs to the trace.
  • +
  • Take the provided FRI commitment and check that it verifies.
  • +
+

Simplifications and Omissions

+

The walkthrough above was for the fibonacci example which, because of its simplicity, allowed us to sweep under the rug a few more complexities that we'll have to tackle on the implementation side. They are:

+

Multiple trace columns

+

Our trace contained only one column, but in the general setting there can be multiple (the Cairo AIR has around 30). This means there isn't just one trace polynomial, but several; one for each column. This also means there are multiple boundary constraint polynomials.

+

The general idea, however, remains the same. The deep composition polynomial H is now the sum of several terms containing the boundary constraint polynomials \(B_1(x), \dots, B_k(x)\) (one per column), and each \(B_i\) is in turn constructed from the \(i\)-th trace polynomial \(t_i(x)\).

+

Multiple transition constraints

+

Much in the same way, our fibonacci AIR had only one transition constraint, but there could be several. We will therefore have multiple transition constraint polynomials \(C_1(x), \dots, C_n(x)\), each of which encodes a different relationship between rows that must be satisfied. Also, because there are multiple trace columns, a transition constraint can mix different trace polynomials. One such constraint could be

+

+

which means "The first column on the next row has to be equal to the second column in the current row".

+

Again, even though this seems way more complex, the ideas remain the same. The composition polynomial H will now include a term for every \(C_i(x)\), and for each one the prover will have to provide out of domain evaluations of the trace polynomials at the appropriate values. In our example above, to perform the consistency check on \(C_1(x)\) the prover will have to provide the evaluations \(t_1(zg)\) and \(t_2(z)\).

+

Composition polynomial decomposition

+

In the actual implementation, we won't commit to \(H\), but rather to a decomposition of \(H\) into an even term \(H_1(x)\) and an odd term \(H_2(x)\), which satisfy

+

+

This way, we don't commit to \(H\) but to \(H_1\) and \(H_2\). This is just an optimization at the code level; once again, the ideas remain exactly the same.

+

FRI, low degree extensions and roots of unity

+

We treated FRI as a black box entirely. However, there is one thing we do need to understand about it: low degree extensions.

+

When applying FRI to a polynomial of degree \(n\), we need to provide evaluations of it over a domain with more than \(n\) points. In our case, the DEEP composition polynomial's degree is around the same as the trace's, which is, at most, \(2^n - 1\) (because it interpolates the trace containing \(2^n\) points).

+

The domain we are going to choose to evaluate our DEEP polynomial on will be a set of higher roots of unity. In our fibonacci example, we will take a primitive \(16\)-th root of unity \(\omega\). As a reminder, this means:

+
    +
  • \(\omega\) is an \(16\)-th root of unity, i.e., \(\omega^{16} = 1\).
  • +
  • Every \(16\)-th root of unity is of the form \(\omega^i\) for some \(0 \leq i \leq 15\).
  • +
+

Additionally, we also take it so that \(\omega\) satisfies \(\omega^2 = g\) (\(g\) being the \(8\)-th primitive root of unity we used to construct t).

+

The evaluation of \(t\) on the set \({\omega^i : 0 \leq i \leq 15}\) is called a low degree extension (LDE) of \(t\). Notice this is not a new polynomial, they're evaluations of \(t\) on some set of points. Also note that, because \(\omega^2 = g\), the LDE contains all the evaluations of \(t\) on the set of powers of \(g\). In fact,

+

+

This will be extremely important when we get to implementation.

+

For our LDE, we chose \(16\)-th roots of unity, but we could have chosen any other power of two greater than \(8\). In general, this choice is called the blowup factor, so that if the trace has \(2^n\) elements, a blowup factor of \(b\) means our LDE evaluates over the \(2^{n} * b\) roots of unity (\(b\) needs to be a power of two). The blowup factor is a parameter of the protocol related to its security.

+
+

Protocol Overview

+

In this section, we start diving deeper before showing the formal protocol. If you haven't done so, we recommend reading the "Recap" section first.

+

At a high level, the protocol works as follows. The starting point is a matrix that encodes the trace of a valid execution of the program. This matrix needs to be in a particular format so that its correctness is equivalent to checking a finite number of polynomial equations on its rows. Transforming the execution to this matrix is what's called the arithmetization process.

+

Then a single polynomial is constructed that encodes the set of all the polynomial constraints. The satisfiability of all these constraints is equivalent to being divisible by some public polynomial . So the prover constructs as the quotient called the composition polynomial.

+

Then the verifier chooses a random point and challenges the prover to reveal the values and . Then the verifier checks that , which convinces him that the same relation holds at a level of polynomials and, in consequence, convinces the verifier that the private trace of the prover is valid.

+

In summary, at a very high level, the STARK protocol can be organized into three major parts:

+
    +
  • Arithmetization and commitment of execution trace.
  • +
  • Construction and commitment of composition polynomial .
  • +
  • Opening of polynomials at random .
  • +
+

Arithmetization

+

As the Recap mentions, the trace is a table containing the system's state at every step. In this section, we will denote the trace as . A trace can have several columns to store different aspects or features of a particular state at a specific moment. We will refer to the -th column as . You can think of a trace as a matrix where the entry is the -th element of the -th state.

+

Most proving systems' primary tool is polynomials over a finite field . Each column of the trace will be interpreted as evaluations of such a polynomial . Consequently, any information about the states must be encoded somehow as an element in .

+

To ease notation, we will assume here and in the protocol that the constraints encoding transition rules depend only on a state and the previous one. Everything can be easily generalized to transitions that depend on many preceding states. Then, constraints can be expressed as multivariate polynomials in variables + +A transition from state to state will be valid if and only if when we plug row of in the first variables and row in the second variables of , we get for all . In mathematical notation, this is +

+

These are called transition constraints and check the trace's local properties, where local means relative to specific rows. There is another type of constraint, called boundary constraint, and denoted . These enforce parts of the trace to take particular values. It is helpful, for example, to verify the initial states.

+

So far, these constraints can only express the local properties of the trace. There are situations where the global properties of the trace need to be checked for consistency. For example, a column may need to take all values in a range but not in any predefined way. Several methods exist to express these global properties as local by adding redundant columns. Usually, they need to involve randomness from the verifier to make sense, and they turn into an interactive protocol called Randomized AIR with Preprocessing.

+

Polynomial commitment scheme

+

To make interactions possible, a crucial cryptographic primitive is the Polynomial Commitment Scheme. This prevents the prover from changing the polynomials to adjust them to what the verifier expects.

+

Such a scheme consists of the commit and the open protocols. STARK uses a univariate polynomial commitment scheme that internally combines a vector commitment scheme and a protocol called FRI. Let's begin with these two components and see how they build up the polynomial commitment scheme.

+

Vector commitments

+

Given a vector , commiting to means the following. The prover builds a Merkle tree out of it and sends its root to the verifier. The verifier can then ask the prover to reveal, or open, the value of the vector at some index . The prover won't have any choice except to send the correct value. The verifier will expect the corresponding value and the authentication path to the tree's root to check its authenticity. The authentication path also encodes the vector's position and its length .

+

The root of the Merkle tree is said to be the commitment of , and we denote it here by .

+

FRI

+

In STARKs, all commited vectors are of the form for some polynomial and some fixed domain . The domain is always known to the prover and the verifier. It can be proved, as long as is less than the total number of field elements, that every vector is equal to for a unique polynomial of degree at most . This is called the Lagrange interpolation theorem. It means, there is a unique polynomial of degree at most such that for all . And is an upper bound to the degree of . It could be less. For example, the vector of all ones is the evaluation of the constant polynomial , which has degree .

+

Suppose the vector is the vector of evaluations of a polynomial of degree strictly less than . Suppose one party holds the vector and another party holds only the commitment of it. The FRI protocol is an efficient interactive protocol with which the former can convince the latter that the commitment they hold corresponds to the vector of evaluations of a polynomial of degree strictly less than .

+

More precisely, the protocol depends on the following parameters

+
    +
  • Powers of two and with .
  • +
  • A vector , with , with a nonzero value in and a primitive -root of unity
  • +
+

A prover holds a vector , and the verifier holds the commitment of it. The result of the FRI protocol will be Accept if the unique polynomial of degree less than such that has degree less than . Even more precisely, the protocol proves that is very close to a vector with of degree less than , but it may differ in negligible proportion of the coordinates.

+

The number is called the blowup factor and the security of the protocol depends in part on this parameter. The specific shape of the domain set has some symmetric properties important for the inner workings of FRI, such as for all .

+

Variant useful for STARKs

+

FRI is usually described as above. In STARK, FRI is used as a building block for the polynomial commitment scheme of the next section. For that, a small variant of FRI is needed.

+

Suppose the prover holds a vector and the verifier holds its commitment as before. Suppose further that both parties know a function that takes two field elements and outputs another field element. For example could be the function . More precisely, the kind of functions we need are .

+

The protocol can be used to prove that the transformed vector is the vector of evaluations of a polynomial of degree at most . Note that in this variant, the verifier holds originally the commitment of the vector and not the commitment of the transformed vector. In the example, the verifier holds the commitment and FRI will return Accept if is the vector of evaluations of a polynomial of degree at most .

+

Polynomial commitments

+

STARK uses a univariate polynomial commitment scheme. The following is what is expected from the commit and open protocols:

+
    +
  • Commit: given a polynomial , the prover produces a sort of hash of it. We denote it here by , called the commitment of . This hash is unique to . The prover usually sends to the verifier.
  • +
  • Open: this is an interactive protocol between the prover and the verifier. The prover holds the polynomial . The verifier only has the commitment . The verifier sends a value to the prover at which he wants to know the value . The prover sends a value to the verifier, and then they engage in the Open protocol. As a result, the verifier gets convinced that the polynomial corresponding to the hash evaluates to at .
  • +
+

Let's see how both of these protocols work in detail. The same configuration parameters of FRI are needed:

+
    +
  • Powers of two and with .
  • +
  • A vector , with , with a nonzero value in and a primitive -root of unity
  • +
+

The commitment scheme will only work for polynomials of degree at most (polynomials of degree are allowed). This means: anyone can commit to any polynomial, but the Open protocol will pass only for polynomials satisfying that degree bound.

+

Commit

+

Given a polynomial , the commitment is just the commitment of the vector . That is, is the root of the Merkle tree of the vector of evaluations of at .

+

Open

+

It is an interactive protocol. So assume there is a prover and a verifier. We describe the process considering an honest prover. In the next section, we analyze what happens for malicious provers.

+

The prover holds the polynomial , and the verifier only the commitment of it. There is also an element chosen by the verifier. The prover evaluates and sends the result back. As we mentioned, the goal is to generate proof of the validity of the evaluation. Let us denote the value received by the verifier.

+

Now they engage in the variant of the FRI protocol for the function . The verifier accepts the value if and only if the result of FRI is Accept.

+

Let's see why this makes sense.

+

Completeness

+

If the prover is honest, is of degree at most and equals . That means that + +for some polynomial . Since is of degree at most , then is of degree at most . The vector is then a vector of evaluations of a polynomial of degree at most . And it is equal to . So the FRI protocol will succeed.

+

Soundness

+

Let's sketch an idea of the soundness. Note that the value is chosen by the verifier after receiving the commitment of . So the prover does not know in advance, at the moment of sending , what will be.

+

Suppose the prover is trying to cheat and sends the commitment of a vector that's not the vector of evaluations of a polynomial of degree at most . Then the coordinates of the transformed vector are . Since was chosen by the verifier, dividing by shuffles all the elements in a very unpredictable way for the prover. So it is extremely unlikely that the cheating prover can craft an invalid vector such that the transformed vector turns out to be of degree at most . The expected degree of the polynomial associated with a random vector is .

+

Batch

+

During proof generation, polynomials are committed and opened several times. Computing these for each polynomial independently is costly. In this section, we'll see how batching polynomials can reduce the amount of computation. Let be a set of polynomials. We will commit and open as a whole. We note this batch commitment as .

+

We need the same configuration parameters as before: , with , a vector .

+

As described earlier, to commit to a single polynomial , a Merkle tree is built over the vector . When committing to a batch of polynomials , the leaves of the Merkle tree are instead the concatenation of the polynomial evaluations. That is, in the batch setting, the Merkle tree is built for the vector + +The commitment is the root of this Merkle tree. This reduces the proof size: we only need one Merkle tree for polynomials. The verifier can then only ask for values in batches. When the verifier chooses an index , the prover sends along with one authentication path. The verifier on his side computes the concatenation and validates it with the authentication path and . This also reduces the computational time. By traversing the Merkle tree one time, it can reveal several components simultaneously.

+

The batch open protocol proceeds similarly to the case of a single polynomial. The verifier sends evaluations points to the prover at which they wish to know the value of . The prover will try to convince the verifier that the committed polynomials , evaluate to some values . There is a generalization of the variant of FRI where the function takes more parameters, and in this case is +Where are challenges provided by the verifier. Then FRI return Accept if and only if the vector +is close to the vector of evaluations of a polynomial of degree at most . If this is the case, the verifier accepts the openings. In the context of STARKs, the polynomial is called the DEEP composition polynomial.

+

This is equivalent to running the open protocol times, one for each term and . Note that this optimization makes a huge difference, as we only need to run the FRI protocol once instead of running it once for each polynomial.

+

References

+ +

High-level description of the protocol

+

The protocol is split into rounds. Each round more or less represents an interaction with the verifier. Each round will generally start by getting a challenge from the verifier.

+

The prover will need to interpolate polynomials, and he will always do it over the set , where is a root of unity in . Also, the vector commitments will be performed over the set where is a root of unity and is some field element. This is the set we denoted in the commitment scheme section.

+

Round 1: Arithmetization and commitment of the execution trace

+

In round 1, the prover commits to the columns of the trace . He does so by interpolating each column and obtaining univariate polynomials . +Then the prover commits to over . In this way, we have . +From now on, the prover won't be able to change the trace values . The verifier will leverage this and send challenges to the prover. The prover cannot know in advance what these challenges will be. Thus he cannot handcraft a trace to deceive the verifier.

+

As mentioned before, if some constraints cannot be expressed locally, more columns can be added to make a constraint-friendly trace. This is done by committing to the first set of columns, then sampling challenges from the verifier and repeating round 1. The sampling of challenges serves to add new constraints. These constraints will ensure the new columns have some common structure with the original trace. In the protocol, extended columns are referred to as the RAP2 (Randomized AIR with Preprocessing). The matrix of the extended columns is denoted .

+

Round 2: Construction of composition polynomial

+

round 2 aims to build the composition polynomial . This function will have the property that it is a polynomial if and only if the trace that the prover committed to at round 1 is valid and satisfies the agreed polynomial constraints. That is, will be a polynomial if and only if is a trace that satisfies all the transition and boundary constraints.

+

Note that we can compose the polynomials , the ones that interpolate the columns of the trace , with the multivariate constraint polynomials as follows. + +These result in univariate polynomials. The same can be done for the boundary constraints. Since , these univariate polynomials vanish at every element of if and only if the trace is valid.

+

As we already mentioned, this is assuming that transitions only depend on the current and previous state. But it can be generalized to include frames with three or more rows or more context for each constraint. For example, in the Fibonacci case, the most natural way is to encode it as one transition constraint that depends on a row and the two preceding it, as we already did in the Recap section. The STARK protocol checks whether the function is a polynomial instead of checking that the polynomial is zero over the domain . The two statements are equivalent.

+

The verifier could check that all are polynomials one by one, and the same for the polynomials coming from the boundary constraints. However, this is inefficient; the same can be obtained with a single polynomial. To do this, the prover samples challenges and obtains a random linear combination of these polynomials. The result of this is denoted by and is called the composition polynomial. It integrates all the constraints by adding them up. So after computing , the prover commits to it and sends the commitment to the verifier. The rest of the protocol aims to prove that was constructed correctly and is a polynomial, which can only be true if the prover has a valid extension of the original trace.

+

Round 3: Evaluation of polynomials at

+

The verifier must check that was constructed according to the protocol rules. That is, has to be a linear combination of all the functions and similar terms for the boundary constraints. To do so, in round 3 the verifier chooses a random point and the prover computes , and for all . With all these, the verifier can check that and the expected linear combination coincide, at least when evaluated at . Since was chosen randomly, this proves with overwhelming probability that was properly constructed.

+

Round 4: Run batch open protocol

+

In this round, the prover and verifier engage in the batch open protocol of the polynomial commitment scheme described above to validate all the evaluations at from the previous round.

+
+

STARKs protocol

+

In this section we describe precisely the STARKs protocol used in Lambdaworks.

+

We begin with some additional considerations and notation for most of the relevant objects and values to refer to them later on.

+

Grinding

+

This is a technique to increase the soundness of the protocol by adding proof of work. It works as follows. At some fixed point in the protocol, the prover needs to find a string nonce such that H(H(prefix || state || grinding_factor) || nonce) has grinding_factor number of zeros to the left, where H is a hash function, prefix is the bit-string 0x0123456789abcded and state is the state of the transcript. Here x || y denotes the concatenation of the bit-strings x and y.

+

Transcript

+

The Fiat-Shamir heuristic is used to make the protocol noninteractive. We assume there is a transcript object to which values can be added and from which challenges can be sampled.

+

General notation

+
    +
  • denotes a finite field.
  • +
  • Given a vector and a function , denote by the vector . Here denotes the underlying set of .
  • +
  • A polynomial induces a function for every subset of , where .
  • +
  • Let be two polynomials. A function can be induced from them for every subset disjoint from the set of roots of , defined by . We abuse notation and denote by .
  • +
+

Definitions

+

We assume the prover has already obtained the trace of the execution of the program. This is a matrix with entries in a finite field . We assume the number of rows of is for some in .

+

Values known by the prover and verifier prior to the interactions

+

These values are determined the program, the specifications of the AIR being used and the security parameters chosen.

+
    +
  • is the number of columns of the trace matrix .
  • +
  • the number of RAP challenges.
  • +
  • is the number of extended columns of the trace matrix in the (optional) second round of RAP.
  • +
  • is the total number of columns: .
  • +
  • denote the transition constraint polynomials for . We are assuming these are of degree at most 2.
  • +
  • denote the transition constraint zerofiers for .
  • +
  • is the blowup factor.
  • +
  • is the grinding factor.
  • +
  • is number of FRI queries.
  • +
  • We assume there is a fixed hash function from to binary strings. We also assume all Merkle trees are constructed using this hash function.
  • +
+

Values computed by the prover

+

These values are computed by the prover from the execution trace and are sent to the verifier along with the proof.

+
    +
  • is the number of rows of the trace matrix after RAP.
  • +
  • a primitive -th root of unity.
  • +
  • .
  • +
  • An element . This is called the coset factor.
  • +
  • Boundary constraints polynomials for .
  • +
  • Boundary constraint zerofiers for ..
  • +
+

Derived values

+

Both prover and verifier compute the following.

+
    +
  • The interpolation domain: the vector .
  • +
  • The Low Degree Extension . Recall is the blowup factor.
  • +
+

Notation of important operations

+

Vector commitment scheme

+

Given a vector . The operation returns the root of the Merkle tree that has the hash of the elements of as leaves.

+

For , the operation returns the pair , where is the authentication path to the Merkle tree root.

+

The operation returns Accept or Reject depending on whether the -th element of is . It checks whether the authentication path is compatible with , and the Merkle tree root .

+

In our cases the sets will be of the form for some elements . It will be convenient to use the following abuse of notation. We will write to mean . Similarly, we will write instead of . Note that this is only notation and is only checking that the is the -th element of the commited vector.

+
Batch
+

As we mentioned in the protocol overview. When committing to multiple vectors , where one can build a single Merkle tree. Its -th leaf is the concatenation of all the -th coordinates of all vectors, that is, . The commitment to this batch of vectors is the root of this Merkle tree.

+

Protocol

+

Prover

+

Round 0: Transcript initialization

+
    +
  • Start a new transcript.
  • +
  • (Strong Fiat Shamir) Add to it all the public values.
  • +
+

Round 1: Arithmetization and commitment of the execution trace

+
Round 1.1: Commit main trace
+
    +
  • For each column of the execution trace matrix , interpolate its values at the domain and obtain polynomials such that .
  • +
  • Compute for all (Batch commitment optimization applies here).
  • +
  • Add to the transcript in increasing order.
  • +
+
Round 1.2: Commit extended trace
+
    +
  • Sample random values in from the transcript.
  • +
  • Use to build following the specifications of the RAP process.
  • +
  • For each column of the matrix , interpolate its values at the domain and obtain polynomials such that .
  • +
  • Compute for all (Batch commitment optimization applies here).
  • +
  • Add to the transcript in increasing order for all .
  • +
+

Round 2: Construction of composition polynomial

+
    +
  • Sample in from the transcript.
  • +
  • Sample in from the transcript.
  • +
  • Compute .
  • +
  • Compute .
  • +
  • Compute the composition polynomial +
  • +
  • Decompose as +
  • +
  • Compute commitments and (Batch commitment optimization applies here).
  • +
  • Add and to the transcript.
  • +
+

Round 3: Evaluation of polynomials at

+
    +
  • Sample from the transcript until obtaining .
  • +
  • Compute , , and and for all .
  • +
  • Add , , and and for all to the transcript.
  • +
+

Round 4: Run batch open protocol

+
    +
  • Sample , , and , in from the transcript.
  • +
  • Compute as
  • +
+
Round 4.1.k: FRI commit phase
+
    +
  • Let .
  • +
  • For do the following: +
      +
    • Sample from the transcript.
    • +
    • Decompose into even and odd parts, that is, .
    • +
    • Define .
    • +
    • If : +
        +
      • Let . Define , where .
      • +
      • Let .
      • +
      • Add to the transcript.
      • +
      +
    • +
    +
  • +
  • is a constant polynomial and therefore . Add to the transcript.
  • +
+
Round 4.2: Grinding
+
    +
  • Let be the internal state of the transcript.
  • +
  • Compute such that has leading zeroes.
  • +
  • Add to the transcript.
  • +
+
Round 4.3: FRI query phase
+
    +
  • For do the following: +
      +
    • Sample random index from the transcript and let .
    • +
    • Compute and for all .
    • +
    • Compute and .
    • +
    • Compute and .
    • +
    • Compute and for all .
    • +
    +
  • +
+

Build proof

+
    +
  • Send the proof to the verifier: +
  • +
+

Verifier

+

From the point of view of the verifier, the proof they receive is a bunch of values that may or may not be what they claim to be. To make this explicit, we avoid denoting values like as such, because that implicitly assumes that the value was obtained after evaluating a polynomial at . And that's something the verifier can't assume. We use the following convention.

+
    +
  • Bold capital letters refer to commitments. For example is the claimed commitment .
  • +
  • Greek letters with superscripts refer to claimed function evaluations. For example is the claimed evaluation and is the claimed evaluation of . Note that field elements in superscripts never indicate powers. They are just notation.
  • +
  • Gothic letters refer to authentication paths. For example is the authentication path of a opening of .
  • +
  • Recall that every opening is a pair , where is the claimed value at index and is the authentication path. So for example, is denoted as from the verifier's end.
  • +
+

Input

+

This is the proof using the notation described above. The elements appear in the same exact order as they are in the Prover section, serving also as a complete reference of the meaning of each value.

+

+

Step 1: Replay interactions and recover challenges

+
    +
  • Start a transcript
  • +
  • (Strong Fiat Shamir) Add all public values to the transcript.
  • +
  • Add to the transcript for all .
  • +
  • Sample random values from the transcript.
  • +
  • Add to the transcript for .
  • +
  • Sample and in from the transcript.
  • +
  • Sample and in from the transcript.
  • +
  • Add and to the transcript.
  • +
  • Sample from the transcript.
  • +
  • Add , , and to the transcript.
  • +
  • Sample , , and from the transcript.
  • +
  • For do the following: +
      +
    • Sample
    • +
    • If : add to the transcript
    • +
    +
  • +
  • Add to the transcript.
  • +
  • Add to the transcript.
  • +
  • For : +
      +
    • Sample random index from the transcript and let .
    • +
    +
  • +
+

Verify grinding:

+

Check that has leading zeroes.

+

Step 2: Verify claimed composition polynomial

+
    +
  • Compute
  • +
  • Compute
  • +
  • Compute
  • +
  • Verify +
  • +
+

Step 3: Verify FRI

+
    +
  • Reconstruct the deep composition polynomial values at and . That is, define +
  • +
  • For all : +
      +
    • For all : +
        +
      • Check that and are Accept.
      • +
      • Solve the following system of equations on the variables +
      • +
      • If , check that equals
      • +
      • If , check that equals .
      • +
      +
    • +
    +
  • +
+

Step 4: Verify trace and composition polynomials openings

+
    +
  • For do the following: +
      +
    • Check that the following are all Accept: +
        +
      • for all .
      • +
      • .
      • +
      • .
      • +
      • for all .
      • +
      • .
      • +
      • .
      • +
      +
    • +
    +
  • +
+

Notes on Optimizations and variants

+

Sampling of challenges variant

+

To build the composition the prover samples challenges and for and . A variant of this is sampling a single challenge and defining and as powers of . That is, define for and for .

+

The same variant applies for the challenges for used to build the deep composition polynomial. In this case the variant samples a single challenge and defines , for all , and .

+

Batch inversion

+

Inversions of finite field elements are slow. There is a very well known trick to batch invert many elements at once replacing inversions by multiplications. See here for the algorithm.

+

FFT

+

One of the most computationally intensive operations performed is polynomial division. These can be optimized by utilizing Fast Fourier Transform (FFT) to divide each field element in Lagrange form.

+

Ruffini's rule

+

In specific scenarios, such as dividing by a polynomial of the form , for example when building the deep composition polynomial, Ruffini's rule can be employed to further enhance performance.

+

Bit-reversal ordering of Merkle tree leaves

+

As one can see from inspecting the protocol, there are multiple times where, for a polynomial , the prover sends both openings and . This implies, a priori, sending two authentication paths. Domains can be indexed using bit-reverse ordering to reduce this to a single authentication path for both openings, as follows.

+

The natural way of building a Merkle tree to commit to a vector , is assigning the value to leaf . If this is the case, the value is at position and the value is at position . This is because equals for the value used in the protocol.

+

Instead of this naive approach, a better solution is to assign the value to leaf , where is the bit-reversal permutation. This is the permutation that maps to the index whose binary representation (padded to bits), is the binary representation of but in reverse order. For example, if and , then its binary representation is , which reversed is . Therefore . In the same way and . Check out the wikipedia article. With this ordering of the leaves, if is even, element is at index and is at index . Which means that a single authentication path serves to validate both points simultaneously.

+

Redundant values in the proof

+

The prover opens the polynomials of the FRI layers at and for all . Later on, the verifier uses each of those pairs to reconstruct one of the values of the next layer, namely . So there's no need to add the value to the proof, as the verifier reconstructs them. The prover only needs to send the authentication paths for them.

+

The protocol is only modified at Step 3 of the verifier as follows. Checking that is skipped. After computing , the verifier uses it to check that is Accept, which proves that is actually , and continues to the next iteration of the loop.

+
+

STARKs Prover Lambdaworks Implementation

+

The goal of this section will be to go over the details of the implementation of the proving system. To this end, we will follow the flow the example in the recap chapter, diving deeper into the code when necessary and explaining how it fits into a more general case.

+

This implementation couldn't be done without checking Facebook's Winterfell and Max Gillett's Giza. We want to thank everyone involved in them, along with Shahar Papini and Lior Goldberg from Starkware who also provided us valuable insight.

+
+

High level API: Fibonacci example

+

Let's go over the main test we use for our prover, where we compute a STARK proof for a fibonacci trace with 4 rows and then verify it.

+

+#![allow(unused)]
+fn main() {
+fn test_prove_fib() {
+    let trace = simple_fibonacci::fibonacci_trace([FE::from(1), FE::from(1)], 8);
+    let proof_options = ProofOptions::default_test_options();
+
+    let pub_inputs = FibonacciPublicInputs {
+        a0: FE::one(),
+        a1: FE::one(),
+    };
+
+    let proof = prove::<F, FibonacciAIR<F>>(&trace, &pub_inputs, &proof_options).unwrap();
+    assert!(verify::<F, FibonacciAIR<F>>(&proof, &pub_inputs, &proof_options));
+}
+}
+
+

The proving system revolves around the prove function, that takes a trace, public inputs and proof options as inputs to generate a proof, and a verify function that takes the generated proof, the public inputs and the proof options as inputs, outputting true when the proof is verified correctly and false otherwise. Note that the public inputs and proof options should be the same for both. Public inputs should be shared by the Cairo runner to prover and verifier, and the proof options should have been agreed on beforehand by the two entities beforehand.

+

Below we go over the main things involved in this code.

+

AIR

+

To prove the integrity of a fibonacci trace, we first need to define what it means for a trace to be valid. As we've talked about in the recap, this involves defining an AIR for our computation where we specify both the boundary and transition constraints for a fibonacci sequence.

+

In code, this is done through the AIR trait. Implementing AIR requires defining a couple methods, but the two most important ones are boundary_constraints and compute_transition, which encode the boundary and transition constraints of our computation.

+

Boundary Constraints

+

For our Fibonacci AIR, boundary constraints look like this:

+

+#![allow(unused)]
+fn main() {
+fn boundary_constraints(
+    &self,
+    _rap_challenges: &Self::RAPChallenges,
+) -> BoundaryConstraints<Self::Field> {
+    let a0 = BoundaryConstraint::new_simple(0, self.pub_inputs.a0.clone());
+    let a1 = BoundaryConstraint::new_simple(1, self.pub_inputs.a1.clone());
+
+    BoundaryConstraints::from_constraints(vec![a0, a1])
+}
+}
+
+

The BoundaryConstraint struct represents a specific boundary constraint, meaning "column i at row j should be equal to x". In this case, because we have only one column, we are using the new_simple method to simply say

+
    +
  • Row 0 should equal the public input a0, which in the typical fibonacci is set to 1.
  • +
  • Row 1 should equal the public input a1, which in the typical fibonacci is set to 1.
  • +
+

In the case of multiple columns, the new method exists so you can also specify column number.

+

After instantiating each of these constraints, we return all of them through the struct BoundaryConstraints.

+

Transition Constraints

+

The way we specify our fibonacci transition constraint looks like this:

+

+#![allow(unused)]
+fn main() {
+fn compute_transition(
+    &self,
+    frame: &air::frame::Frame<Self::Field>,
+    _rap_challenges: &Self::RAPChallenges,
+) -> Vec<FieldElement<Self::Field>> {
+    let first_row = frame.get_row(0);
+    let second_row = frame.get_row(1);
+    let third_row = frame.get_row(2);
+
+    vec![third_row[0] - second_row[0] - first_row[0]]
+}
+}
+
+

It's not completely obvious why this is how we chose to express transition constraints, so let's talk a little about it.

+

What we need to specify in this method is the relationship that has to hold between the current step of computation and the previous ones. For this, we get a Frame as an argument. This is a struct holding the current step (i.e. the current row of the trace) and all previous ones needed to encode our constraint. In our case, this is the current row and the two previous ones. To access rows we use the get_row method. The current step is always the last row (in our case 2), with the others coming before it.

+

In our compute_transition method we get the three rows we need and return

+

+#![allow(unused)]
+fn main() {
+third_row[0] - second_row[0] - first_row[0]
+}
+
+

which is the value that needs to be zero for our constraint to hold. Because we support multiple transition constraints, we actually return a vector with one value per constraint, so the first element holds the first constraint value and so on.

+

TraceTable

+

After defining our AIR, we create our specific trace to prove against it.

+

+#![allow(unused)]
+fn main() {
+let trace = fibonacci_trace([FE17::new(1), FE17::new(1)], 4);
+
+let trace_table = TraceTable {
+    table: trace.clone(),
+    num_cols: 1,
+};
+}
+
+

TraceTable is the struct holding execution traces; the num_cols says how many columns the trace has, the table field is a vec holding the actual values of the trace in row-major form, meaning if the trace looks like this

+
| 1  | 2  |
+| 3  | 4  |
+| 5  | 6  |
+
+

then its corresponding TraceTable is

+

+#![allow(unused)]
+fn main() {
+let trace_table = TraceTable {
+    table: vec![1, 2, 3, 4, 5, 6],
+    num_cols: 2,
+};
+}
+
+

In our example, fibonacci_trace is just a helper function we use to generate the fibonacci trace with 4 rows and [1, 1] as the first two values.

+

AIR Context

+

After specifying our constraints and trace, the only thing left to do is provide a few parameters related to the STARK protocol and our AIR. These specify things such as the number of columns of the trace and proof configuration, among others. They are all encapsulated in the AirContext struct, which in our example we instantiate like this:

+

+#![allow(unused)]
+fn main() {
+let context = AirContext {
+    options: ProofOptions {
+        blowup_factor: 2,
+        fri_number_of_queries: 1,
+        coset_offset: 3,
+    },
+    trace_columns: trace_table.n_cols,
+    transition_degrees: vec![1],
+    transition_exemptions: vec![2],
+    transition_offsets: vec![0, 1, 2],
+    num_transition_constraints: 1,
+};
+}
+
+

Let's go over each of them:

+
    +
  • options requires a ProofOptions struct holding specific parameters related to the STARK protocol to be used when proving. They are: +
      +
    • The blowup_factor used for the trace LDE extension, a parameter related to the security of the protocol.
    • +
    • The number of queries performed by the verifier when doing FRI, also related to security.
    • +
    • The offset used for the LDE coset. This depends on the field being used for the STARK proof.
    • +
    +
  • +
  • trace_columns are the number of columns of the trace, respectively.
  • +
  • transition_degrees holds the degree of each transition constraint.
  • +
  • transition_exemptions is a Vec which tells us, for each column, the number of rows the transition constraints should not apply, starting from the end of the trace. In the example, the transition constraints won't apply on the last two rows of the trace.
  • +
  • transition_offsets holds the indexes that define a frame for our AIR. In our fibonacci case, these are [0, 1, 2] because we need the current row and the two previous one to define our transition constraint.
  • +
  • num_transition_constraints simply says how many transition constraints our AIR has.
  • +
+

Proving execution

+

Having defined all of the above, proving our fibonacci example amounts to instantiating the necessary structs and then calling prove passing the trace, public inputs and proof options. We use a simple implementation of a hasher called TestHasher to handle merkle proof building.

+

+#![allow(unused)]
+fn main() {
+let proof = prove(&trace_table, &pub_inputs, &proof_options);
+}
+
+

Verifying is then done by passing the proof of execution along with the same AIR to the verify function.

+

+#![allow(unused)]
+fn main() {
+assert!(verify(&proof, &pub_inputs, &proof_options));
+}
+
+
+

How this works under the hood

+

In this section we go over how a few things in the prove and verify functions are implemented. If you just need to use the prover, then you probably don't need to read this. If you're going through the code to try to understand it, read on.

+

We will once again use the fibonacci example as an ilustration. Recall from the recap that the main steps for the prover and verifier are the following:

+

Prover side

+
    +
  • Compute the trace polynomial t by interpolating the trace column over a set of -th roots of unity .
  • +
  • Compute the boundary polynomial B.
  • +
  • Compute the transition constraint polynomial C.
  • +
  • Construct the composition polynomial H from B and C.
  • +
  • Sample an out of domain point z and provide the evaluation and all the necessary trace evaluations to reconstruct it. In the fibonacci case, these are , , and .
  • +
  • Sample a domain point x_0 and provide the evaluation and .
  • +
  • Construct the deep composition polynomial Deep(x) from H, t, and the evaluations from the item above.
  • +
  • Do FRI on Deep(x) and provide the resulting FRI commitment to the verifier.
  • +
  • Provide the merkle root of t and the merkle proof of .
  • +
+

Verifier side

+
    +
  • Take the evaluation along with the trace evaluations the prover provided.
  • +
  • Reconstruct the evaluations and from the trace evaluations. Check that the claimed evaluation the prover gave us actually satisfies +
  • +
  • Take the evaluations and .
  • +
  • Check that the claimed evaluation the prover gave us actually satisfies +
  • +
  • Take the provided FRI commitment and check that it verifies.
  • +
  • Using the merkle root and the merkle proof the prover provided, check that belongs to the trace.
  • +
+

Following along the code in the prove and verify functions, most of it maps pretty well to the steps above. The main things that are not immediately clear are:

+
    +
  • How we take the constraints defined in the AIR through the compute_transition method and map them to transition constraint polynomials.
  • +
  • How we then construct H from them and the boundary constraint polynomials.
  • +
  • What the composition polynomial even/odd decomposition is.
  • +
  • What an ood frame is.
  • +
  • What the transcript is.
  • +
+

Reconstructing the transition constraint polynomials

+

This is possibly the most complex part of the code, so what follows is a long explanation for it.

+

In our fibonacci example, after obtaining the trace polynomial t by interpolating, the transition constraint polynomial is

+

+

On our prove code, if someone passes us a fibonacci AIR like the one we showed above used in one of our tests, we somehow need to construct . However, what we are given is not a polynomial, but rather this method

+

+#![allow(unused)]
+fn main() {
+fn compute_transition(
+        &self,
+        frame: &air::frame::Frame<Self::Field>,
+    ) -> Vec<FieldElement<Self::Field>> {
+    let first_row = frame.get_row(0);
+    let second_row = frame.get_row(1);
+    let third_row = frame.get_row(2);
+
+    vec![third_row[0] - second_row[0] - first_row[0]]
+}
+}
+
+

So how do we get to from this? The answer is interpolation. What the method above is doing is the following: if you pass it a frame that looks like this

+

+

for any given point , it will return the value

+

+

which is the numerator in . Using the transition_exemptions field we defined in our AIR, we can also compute evaluations in the denominator, i.e. the zerofier evaluations. This is done under the hood by the transition_divisors() method.

+

The above means that even though we don't explicitly have the polynomial , we can evaluate it on points given an appropriate frame. If we can evaluate it on enough points, we can then interpolate them to recover . This is exactly how we construct both transition constraint polynomials and subsequently the composition polynomial H.

+

The job of evaluating H on enough points so we can then interpolate it is done by the ConstraintEvaluator struct. You'll notice prove does the following

+

+#![allow(unused)]
+fn main() {
+let constraint_evaluations = evaluator.evaluate(
+    &lde_trace,
+    &lde_roots_of_unity_coset,
+    &alpha_and_beta_transition_coefficients,
+    &alpha_and_beta_boundary_coefficients,
+);
+}
+
+

This function call will return the evaluations of the boundary terms

+

+

and constraint terms

+

+

for every . The constraint_evaluations value returned is a ConstraintEvaluationTable struct, which is nothing more than a big list of evaluations of each polynomial required to construct H.

+

With this in hand, we just call

+

+#![allow(unused)]
+fn main() {
+let composition_poly =  
+    constraint_evaluations.compute_composition_poly(&   lde_roots_of_unity_coset);
+}
+
+

which simply interpolates the sum of all evaluations to obtain H.

+

Let's go into more detail on how the evaluate method reconstructs in our fibonacci example. It receives the lde_trace as an argument, which is this:

+

+

where is the primitive root of unity used for the LDE, that is, satisfies . We need to recover , a polynomial whose degree can't be more than 's. Because was built by interpolating 8 points (the trace), we know we can recover by interpolating it on 16 points. We choose these points to be the LDE roots of unity

+

+

Remember that to evaluate on these points, all we need are the evaluations of the polynomial

+

+

as the zerofier ones we can compute easily. These become:

+

+

If we remember that , this is

+

+

and we can compute each evaluation here by calling compute_transition on the appropriate frame built from the lde_trace. Specifically, for the first evaluation we can build the frame:

+

+

Calling compute_transition on this frame gives us the first evaluation. We can get the rest in a similar fashion, which is what this piece of code in the evaluate method does:

+

+#![allow(unused)]
+fn main() {
+for (i, d) in lde_domain.iter().enumerate() {
+    let frame = Frame::read_from_trace(
+        lde_trace,
+        i,
+        blowup_factor,
+        &self.air.context().transition_offsets,
+    )
+
+    let mut evaluations = self.air.compute_transition(&frame);
+
+    ...
+}
+}
+
+

Each iteration builds a frame as above and computes one of the evaluations needed. The rest of the code just adds the zerofier evaluations, along with the alphas and betas. It then also computes boundary polynomial evaluations by explicitly constructing them.

+

Verifier

+

The verifier employs the same trick to reconstruct the evaluations on the out of domain point for the consistency check.

+

Even/odd decomposition for H

+

At the end of the recap we talked about how in our code we don't actually commit to H, but rather an even/odd decomposition for it. These are two polynomials H_1 and H_2 that satisfy

+

+

This all happens on this piece of code

+

+#![allow(unused)]
+fn main() {
+let composition_poly =
+    constraint_evaluations.compute_composition_poly(&lde_roots_of_unity_coset);
+
+let (composition_poly_even, composition_poly_odd) = composition_poly.even_odd_decomposition();
+
+// Evaluate H_1 and H_2 in z^2.
+let composition_poly_evaluations = vec![
+    composition_poly_even.evaluate(&z_squared),
+    composition_poly_odd.evaluate(&z_squared),
+];
+}
+
+

After this, we don't really use H anymore, but rather H_1 and H_2. There's not that much to say other than that.

+

Out of Domain Frame

+

As part of the consistency check, the prover needs to provide evaluations of the trace polynomials in all the points needed by the verifier to check that H was constructed correctly. In the fibonacci example, these are , , and . In code, the prover passes these evaluations as a Frame, which we call the out of domain (ood) frame.

+

The reason we do this is simple: with the frame in hand, the verifier can reconstruct the evaluations of the constraint polynomials by calling the compute_transition method on the ood frame and then adding the alphas, betas, and so on, just like we explained in the section above.

+

Transcript

+

Throughout the protocol, there are a number of times where the verifier randomly samples some values that the prover needs to use (think of the alphas and betas used when constructing H). Because we don't actually have an interaction between prover and verifier, we emulate it by using a hash function, which we assume is a source of randomness the prover can't control.

+

The job of providing these samples for both prover and verifier is done by the Transcript struct, which you can think of as a stateful rng; whenever you call challenge() on a transcript you get a random value and the internal state gets mutated, so the next time you call challenge() you get a different one. You can also call append on it to mutate its internal state yourself. This is done a number of times throughout the protocol to keep the prover honest so it can't predict or manipulate the outcome of challenge().

+

Notice that to sample the same values, both prover and verifier need to call challenge and append in the same order (and with the same values in the case of append) and the same number of times.

+

The idea explained above is called the Fiat-Shamir heuristic or just Fiat-Shamir, and is more generally used throughout proving systems to remove interaction between prover and verifier. Though the concept is very simple, getting it right so the prover can't cheat is not, but we won't go into that here.

+

Proof

+

The generated proof has got all the information needed for the verifier to verify it:

+
    +
  • Trace length: The number of rows of the trace table, needed to know the max degree of the polynomials that appear in the system.
  • +
  • LDE trace commitments.
  • +
  • DEEP composition polynomial out of domain even and odd evaluations.
  • +
  • DEEP composition polynomial root.
  • +
  • FRI layers merkle roots.
  • +
  • FRI last layer value.
  • +
  • Query list.
  • +
  • DEEP composition poly openings.
  • +
  • Nonce: Proof of work setting used to generate the proof.
  • +
+

Special considerations

+

FFT evaluation and interpolation

+

When evaluating or interpolating a polynomial, if the input (be it coefficients or evaluations) size isn't a power of two then the FFT API will extend it with zero padding until this requirement is met. This is because the library currently only uses a radix-2 FFT algorithm.

+

Also, right now FFT only supports inputs with a size up to elements.

+

Other

+

Why use roots of unity?

+

Whenever we interpolate or evaluate trace, boundary and constraint polynomials, we use some -th roots of unity. There are a few reasons for this:

+
    +
  • +

    Using roots of unity means we can use the Fast Fourier Transform and its inverse to evaluate and interpolate polynomials. This method is much faster than the naive Lagrange interpolation one. Since a huge part of the STARK protocol involves both evaluating and interpolating, this is a huge performance improvement.

    +
  • +
  • +

    When computing boundary and constraint polynomials, we divide them by their zerofiers, polynomials that vanish on a few points (the trace elements where the constraints do not hold). These polynomials take the form

    +

    +

    where the are the points where we want it to vanish.

    +

    When implementing this, evaluating this polynomial can be very expensive as it involves a huge product. However, if we are using roots of unity, we can use the following trick. The vanishing polynomial for all the roots of unity is

    +

    +

    Instead of expressing the zerofier as a product of the places where it should vanish, we express it as the vanishing polynomial above divided by the exemptions polynomial; the polynomial whose roots are the places where constraints don't need to hold.

    +

    +

    where the are now the points where we don't want it to vanish. This exemptions polynomial in the denominator is usually much smaller, and because the vanishing polynomial in the numerator is only two terms, evaluating it is really fast.

    +
  • +
+

What is a primitive root of unity?

+

The -th roots of unity are the numbers that satisfy

+

+

There are such numbers, because they are the roots of the polynomial . The set of -th roots of unity always has a generator, a root that can be used to obtain every other root of unity by exponentiating. What this means is that the set of -th roots of unity is

+

+

Any such generator g is called a primitive root of unity. It's called primitive because it allows us to recover any other root.

+

Here are a few important things to keep in mind, some of which we use throughout our implementation:

+
    +
  • +

    There are always several primitive roots. If is primitive, then any power with coprime with is also primitive. As an example, if is a primitive -th root of unity, then is also primitive.

    +
  • +
  • +

    We generally will not care about which primitive root we choose; what we do care about is being consistent. We should always choose the same one throughout our code, otherwise computations will go wrong.

    +
  • +
  • +

    Because , the powers of wrap around. This means

    +

    +

    and so on.

    +
  • +
  • +

    If is a primitive -th root of unity, then is a primitive -th root of unity. In general, if is a primitive -th primitive root of unity, then is a primitive -th root of unity.

    +
  • +
+

Why use Cosets?

+

When we perform FRI on the DEEP composition polynomial, the low degree extension we use is not actually over a set of higher roots of unity than the ones used for the trace, but rather a coset of it. A coset is simply a set of numbers all multiplied by the same element. We call said element the offset. In our case, a coset of the -th roots of unity with primitive root and offset h is the set

+

+

So why not just do the LDE without the offset? The problem is in how we construct and evaluate the composition polynomial H. Let's say our trace polynomial was interpolated over the -th roots of unity with primitive root , and we are doing the LDE over the -th roots of unity with primitive root , so (i.e. the blowup factor is 2).

+

Recall that H is a sum of terms that include boundary and transition constraint polynomials, and each one of them includes a division by a zerofier; a polynomial that vanishes on some roots of unity . This is because the zerofier is what tells us which rows of the trace our constraint should apply on.

+

When doing FRI, we have to provide evaluations over the LDE domain we are using. If we don't include the offset, our domain is

+

+

Note that, because , some of the elements on this set (actually, half of them) are powers of . If while doing FRI we evaluate H on them, the zerofier could vanish and we'd be dividing by zero. We introduce the offset to make sure this can't happen.

+

NOTE: a careful reader might note that we can actually evaluate H on the elements , since on a valid trace the zerofiers will actually divide the polynomials on their numerator. The problem still remains, however, because of performance. We don't want to do polynomial division if we don't need to, it's much cheaper to just evaluate numerator and denominator and then divide. Of course, this only works if the denominator doesn't vanish; hence, cosets.

+
+

Stone prover documentation

+

This section is a reference to the information gathered regarding Starkware's Stone prover.

+
+

Stone prover trace - Layout plain

+

For a Cairo program executed in N steps, the stone prover trace with the plain layout configuration is a table of 16xN rows and 8 columns.

+

From the 8 columns, 6 are built directly from the register states and memory, while the 2 other are built from the interaction phase with the verifier.

+

For every step or cycle of the Cairo VM, the trace cells representing its state are arranged along 16 rows. +This means that the subtable from row 0 to 15 has all trace cells representing the VM state at step 0, the trace cells from 16 to 31 the state at step 1, and so on.

+

The implementation details of the trace for this layout can be found in the Stone source code, cpu_air_definition10.h and cpu_air_definition10.inl.

+

Columns & virtual columns

+

In simple terms, a virtual column is just a subset of rows of a real column. +Virtual columns are broadly defined by three parameters:

+
    +
  • The real column they are a subset of.
  • +
  • The step. Given an element of the virtual column, this value specifies the number of rows you have to move to find the next element of the virtual column.
  • +
  • The row offset. Basically the number of rows you have to move in the real column to find the first element of the virtual column.
  • +
+

For a single step or cycle, the main trace can be visualized like the following. Note that it is missing the two additional interaction columns:

+

+

This representation will be useful for the explanation of each column and virtual column.

+

Main trace columns

+

Column 0 - rc pool

+

This column is made up of trace cells that need to be range checked, hence the name. While they all have to satisfy this constraint, there are three virtual columns embedded in this column, each one having its own additional constraint. The three of them are associated with the offsets found in each Cairo instruction, off_dst, off_op0 and off_op1, as they appear in the Cairo whitepaper, section 4.4.

+
    +
  • off0 (off_dst) - step 16, row offset 0.
  • +
  • off1 (off_op1) - step 16, row offset 8.
  • +
  • off2 (off_op0) - step 16, row offset 4.
  • +
+

As it can be seen, there are plenty of unused cells in this column (rows 1, 2, 3, 5, 6, 7, 8, 10, 11, 12, 13, 14 and 15). Since there is a continuity constraint in this column, the should be no holes in their values, so the unused cells are used to fill the value holes. As holes are found, they are filled in ascending order in these cells. +Since there can't be missing values in the trace, these cells have to be filled with something that doesn't break the constraints. When there are no more value holes to fill, the remaining unused cells are filled with the rc max value (although any value given by the offsets would do the trick, since we only check they are in a certain range, but this is the way it is done in Stone).

+

As an example, consider that the values in this column are 2, 4 and 5. We would insert the value 3 in the first available unused cell at row 1, and then fill all the other unused cells with the value 5 until the end of the trace.

+

For reference, check range_check_cell.h and range_check_cell.inl.

+

Column 1 - flags (decode/opcode rc)

+

This is the column in charge of holding the flags configuration of each executed Cairo instruction. As a quick summary, each Cairo instruction has 15 bit of flags. The constraint over these 15 bits is that they are in fact bits, so only 0 or 1 are allowed. Mathematically, this means that , for in , while always. +As can be seen in the Cairo whitepaper section 9.4, we can define

+

+

so that is the full 15 bit value and . This way, instead of allocating 15 virtual columns for each flag, we can allocate one of lenght 16 (for each step of the Cairo execution), with the values . These are the actual values that appear on the Stone prover trace, rather than the 0s or 1s.

+

Noting that , we get that the constraint over this virtual column becomes

+

.

+

Column 2 - sorted rc (rc16/sorted)

+

Pretty straightforward. This column has the same as the ones in column 0 but sorted in ascending order.

+

Column 3 - mem pool

+

All trace cells that reflect state of the Cairo VM memory can be found here. They are two main virtual columns:

+
    +
  • memory addresses - step 2, row offset 0.
  • +
  • memory values - step 2, row offset 1.
  • +
+

Each one has, at the same time, virtual sub-columns. In essence, this just means that in addition to the constraints that memory cells must hold, some of these memory cells have additional constraints. +To visualize them better, here is a more detailed diagram of the column:

+

+

The virtual sub-columns are

+
    +
  • instruction addr - step 16, row offset 0. The address of the instruction executed in the current step. In other words, the content of the pc register
  • +
  • instruction value - step 16, row offset 1. The actual encoded instruction pointed by the instruction address.
  • +
  • dst addr - step 16, row offset 8. The address of dst.
  • +
  • dst value - step 16, row offset 9. The actual value of dst.
  • +
  • op0 addr - step 16, row offset 4. The address of op0.
  • +
  • op0 value - step 16, row offset 5. The actual value of op0.
  • +
  • op1 addr - step 16, row offset 12. The address of op1.
  • +
  • op1 value - step 16, row offset 13. The actual value of op1.
  • +
  • pub memory addr - step 8, row offset 2. Address of a public memory cell
  • +
  • pub memory value - step 8, row offset 3. Value of a public memory cell.
  • +
+

The public memory address and value in this column are actually always . In other words, these are the dummy memory accesses that are added to the trace. +For a Cairo program with a number of instructions, there should be at least dummy memory accesses inserted. As can be seen, there are 2 dummy accesses inserted in each trace step, since the total rows in a step is 16 and the step of the public memory virtual sub-column is 8. +This means that we need at least steps to fit the necessary public memory cells. In usual Cairo programs, this is achieved easily.

+

Unused cells in mem pool

+

If we analyze the rows used by virtual sub-columns in the mem pool, we see there are 4 cells left unused in row offsets 6, 7, 14 and 15. +Since the constraints in the memory require addresses to be continuous, these unused cells are used to fill any memory holes found in the range of the accessed addresses. +As memory holes are found, they are inserted in these empty cells in ascending order, with the address of the hole and a 0 in its corresponding value. +As an example, the first memory hole address will be inserted in row 6 and the value 0 will be inserted at row 7. The next memory hole will be filled in rows 14 and 15, while the next memory hole would be filled in rows 22 and 23, etc. +An important detail is that the last accessed address + 1 will always appear as a memory hole. This means that once all gaps are filled, the (max address + 1, 0) entry will appear in all these unused cells, until the end of the trace. In particular, if the memory didn't have any hole, this will be the only value that will be appear in these unused cells. +For reference, check memory_cell.h and memory_cell.inl.

+

Column 4 - sorted mem pool

+

The same values as column 3, but the (address, value) pairs get sorted by their address, in ascending order, but there is no accesses here. +The dummy memory accesses are replaced by the real public memory values, repeating the first (pub addr, pub value) until all the dummy accesses are replaced. +For example, if the public memory is:

+
        (1, 4334524)
+        (2, 3252643)
+        (3, 3245444)
+
+

and the total dummy accesses are (in total, one more than the actual publc memory):

+
        (0, 0)
+        (0, 0)
+        (0, 0)
+        (0, 0)
+
+

the final result in the sorted column will be

+
        (1, 4334524)
+        (1, 4334524)
+        (2, 3252643)
+        (3, 3245444)
+
+

Column 5 - registers / pc update

+

This column is used to store the values of the ap and fp registers, the res value and some values used as an optimization, *t0 +*, t1 and ops_mul related to the update of the pc register. Each one has a corresponding virtual column:

+
    +
  • ap - step 16, row offset 0.
  • +
  • fp - step 16, row offset 8.
  • +
  • ops_mul - step 16, row offset 4.
  • +
  • res - step 16, row offset 12.
  • +
  • tmp0 - step 16, row offset 2.
  • +
  • tmp1 - step 16, row offset 10.
  • +
+

Interaction trace columns

+

As these columns are built from an interaction with the verifier, the values found here are not deterministic. +The interaction trace columns can be visualized with the following table:

+

+

Column 6 - range check cumulative product

+

The details about how this column is built can be found in the Cairo whitepaper, sections 9.4 and 9.9. In summary, just the cumulative product to prove the permutation between the the rc pool and the sorted rc pool.

+

Column 7 - multi column permutation cum product

+

Similarly to column 6, the details of how this column is built is referred to sections 9.7 and 9.8 of the whitepaper. +The only relevant detail is that this cumulative product is in fact a virtual column, with step 2 and row offset 0. All the other values in this column (step 2 and row offset 1) are unused and filled with 0s.

+
+

Cairo

+
+

Trace

+

The execution of a Cairo program produces a memory vector and a matrix of size with the evolution of the three registers pc, ap, fp. All of them with entries in .

+

Construction of execution trace :

+

In this section we describe the construction of the execution trace . This is the matrix mentioned here in the description of the STARK protocol

+
    +
  1. Augment each row of with information about the pointed instruction as follows: For each entry of , unpack the -th value of . The result is a new matrix with the following layout
  2. +
+
 A.  flags     (16) : Decoded instruction flags
+ B.  res       (1)  : Res value
+ C.  pointers  (2)  : Temporary memory pointers (ap and fp)
+ D.  mem_a     (4)  : Memory addresses (pc, dst_addr, op0_addr, op1_addr)
+ E.  mem_v     (4)  : Memory values (inst, dst, op0, op1)
+ F.  offsets   (3)  : (off_dst, off_op0, off_op1)
+ G.  derived   (3)  : (t0, t1, mul)
+
+ A                B C  D    E    F   G
+|xxxxxxxxxxxxxxxx|x|xx|xxxx|xxxx|xxx|xxx|
+
+
    +
  1. +

    Let and be respectively the minimum and maximum values of the entries of the submatrix defined by the columns of the group offsets. Let be the vector of all the values between and that are not in . If the length of is not a multiple of three, extend it to the nearest multiple of three using one arbitrary value of .

    +
  2. +
  3. +

    Let be the last row of , and let be the vector that's equal to except that it has zeroes in entries corresponding to the ordered set of columns mem_a and mem_v. The set is ordered incrementally by mem_a. Let be the length of the public input (program code). Extend with additional rows to obtain a matrix by appending copies of at the bottom (the notation means the ceiling function, defined as the smallest integer that is not smaller than ).

    +
  4. +
  5. +

    Let be the vector that's equal to except that it has zeroes in entries corresponding to the set of columns mem_a and mem_v, let be the submatrix defined by the columns of the group addresses, let the submatrix that asserts , where and and . Extend with additional rows to obtain a matrix by appending copies of at the bottom.

    +
  6. +
  7. +

    Pad with copies of its last row until it has a power of two number of rows. As a result we obtain a matrix .

    +
  8. +
+
+

Cairo execution trace

+

Raw materials

+

After the execution of a Cairo program in the Cairo VM, three files are generated that are the core components for the construction of the execution trace, needed for the proving system:

+
    +
  • trace file: Has the information on the state of the three Cairo VM registers ap, +fp, and pc at every cycle of the execution of the program. To reduce ambiguity in terms, +we should call these the register states of the Cairo VM, and leave the term trace to +the final product that is passed to the prover to generate a proof.
  • +
  • memory file: A file with the information of the VM's memory at the end of the program +run, after the memory has been relocated.
  • +
  • public inputs: A file with all the information that must be publicly available to the prover +and verifier, such as the total number of execution steps, public memory, used builtins and +their respective addresses range in memory.
  • +
+

The next section will explain in detail how these elements are used to build the final execution +trace.

+

Construction details

+

The execution trace is built in two stages. In the first one, the information on the files +described in the previous section is aggregated to build a main trace table. +In the second stage, there is an interaction with the verifier to add some extension +columns to the main trace.

+

Main trace construction

+

The layout of the main execution trace is as follows:

+
 A.  flags     (16): Decoded instruction flags
+ B.  res       (1): Res value
+ C.  pointers  (2): Temporary memory pointers (ap and fp)
+ D.  mem_a     (4): Memory addresses (pc, dst_addr, op0_addr, op1_addr)
+ E.  mem_v     (4): Memory values (inst, dst, op0, op1)
+ F.  offsets   (3)  : (off_dst, off_op0, off_op1)
+ G.  derived   (3)  : (t0, t1, mul)
+
+ A                B C  D    E    F   G
+|xxxxxxxxxxxxxxxx|x|xx|xxxx|xxxx|xxx|xxx|
+
+

Each letter from A to G represents some subsection of columns, and the number specifies how many +columns correspond to that subsection.

+

Cairo instructions

+

It is important to have in mind the information that each executed Cairo instruction holds, since it +is a key component of the construction of the execution trace. For a detailed explanation of how the +building components of the instruction interact to change the VM state, refer to the Cairo +whitepaper, sections 4.4 and 4.5.

+

Structure of the 63-bit that forms the first word of each instruction:

+
 ┌─────────────────────────────────────────────────────────────────────────┐
+ │                     off_dst (biased representation)                     │
+ ├─────────────────────────────────────────────────────────────────────────┤
+ │                     off_op0 (biased representation)                     │
+ ├─────────────────────────────────────────────────────────────────────────┤
+ │                     off_op1 (biased representation)                     │
+ ├─────┬─────┬───────┬───────┬───────────┬────────┬───────────────────┬────┤
+ │ dst │ op0 │  op1  │  res  │    pc     │   ap   │      opcode       │ 0  │
+ │ reg │ reg │  src  │ logic │  update   │ update │                   │    │
+ ├─────┼─────┼───┬───┼───┬───┼───┬───┬───┼───┬────┼────┬────┬────┬────┼────┤
+ │  0  │  1  │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │ 8 │ 9 │ 10 │ 11 │ 12 │ 13 │ 14 │ 15 │
+ └─────┴─────┴───┴───┴───┴───┴───┴───┴───┴───┴────┴────┴────┴────┴────┴────┘
+
+

Columns

+

The construction of the following columns corresponds to a colloquial explanation of what is done in +the build_cairo_execution_trace function.

+
Section A - Flags
+

The flags section A corresponds to the 16 bits that represent the configuration of the dst_reg, +op0_reg, op1_src, res_logic, pc_update, ap_update and opcode flags, as well as the zero +flag. So there is one column for each bit of the flags decomposition.

+
Section C - Temporary memory pointers
+

The two columns in this section, as well as the pc column from section D, are the most trivial. +For each step of the register states, the corresponding values are added to the columns, which are +pointers to some memory cell in the VM's memory.

+
Section D - Memory addresses
+

As already mentioned, the first column of this section, pc, is trivially obtained from the register +states for each cycle.
+Columns dst_addr, op0_addr, and op1_addr from section D are addresses constructed from pointers +stored at ap or fp, and their respective offsets off_dst, off_op0 and off_op1. The exact way these +are computed depends on the particular values of the flags for each instruction.

+
Section E - Memory values
+

The inst column, is obtained by fetching in memory the value stored at pointer pc, which +corresponds to a 63-bit Cairo instruction. +Columns dst, op0, and op1 are computed by fetching in memory by their respective addresses.

+
Section F - Offsets/Range-checked values
+

These columns represent integer values that are used to construct addresses dst_addr, op0_addr and +op1_addr and are decoded directly from the instruction. +These values have the property to be numbered in the range from 0 to 2^16.

+
Section B - Res
+

This column is computed depending on the decoded opcode and res_logic of every instruction. +In some cases, res is unused in the instruction, and the value for (dst)^(-1) is used in that +place as an optimization.

+
Section G - Derived
+

To have constraints of max degree two, some more columns are derived from the already calculated, +t0, t1, and mul:

+
    +
  • t0 is the product of the values of DST and the PC_JNZ flag for each step.
  • +
  • t1 is the product of t0 and res for each step.
  • +
  • mul is the product of op0 and op1 for each step.
  • +
+

Range check and Memory holes

+

For the values constrained between ranges and , the offsets, the prover uses a permutation argument to optimize enforcing this. In particular, it checks an ordered list with the offsets are the same as the original one, is continuous, the first value is , and the last one is less than .

+

Since not all values are used, there may be unused values, and so the ordered offset may not be continuous. These unused values are called holes, and they need to be filled with the missing values, so the checks can be done.

+

This is explained in section 9.9 of the Cairo Paper

+

In the case of memory, something similar happens, where the values should be continuous, but if there are built-ins, this may not be the case. For example, the built-in may be using addresses in ranges higher than the ones used by the program.

+

To fix this, holes in the memory cells are filled, just like the ones of the RC.

+

It's something important to note that when filling the holes, we can't use dedicated columns like op0_addr, since this would break the constraints. For this to work, we either need new columns for holes, or make use of subcolumns, which are explained in their dedicated section.

+

No matter which approach is used , either by subcolumns or columns, we will need cells where the constraints of the range check and memory are applied, but not the specific ones related to the instructions.

+

Finally, using these columns, we can fill the holes without breaking the constraint system.

+

Dummy memory accesses

+

As part of proving the execution of a Cairo program, we need to prove that memory used by the program extends the public memory. This is important since public memory contains for example the bytecode that was executed and the outputs.

+

The bytecode is something critical for the verifier to not only know that something was executed correctly but to know what was executed.

+

To do this, we the permutation check, which that proves the memory is continuous and single-valued

+

To do this, the permutation check of the memory is modified.

+

Initially, we had , and , pairs of memory addresses and values, where and are the values as used and without order, and , the pairs ordered by address.

+

For the public memory, we will add it directly to , . We also need to add dummy accesses to the pairs . These dummy accesses are just pairs of .

+

This change makes the statement that is a permutation of , which means that they are the same values in a different order, no longer true. This means that the two cumulative products used to check the statement are no longer equal, and their division

+

Luckily, we can know the new expected value on the verifier, since we have access to the public memory. Even more, it's this fact that enables the verifier to check the memory is an extension of the public memory.

+

The math is quite straightforward. When the memory was the same, we expected a final value . This came from two cumulative products that should equal, one from the unordered pairs, and one from the ordered ones.

+

Now, adding zeros to one side and the real values to the other unbalances the cumulative products, so the verifier will need to balance it by dividing by the extra factors that appeared with the addition of the public memory. Doing so will make the final value again.

+

Since they only depend on the public memory, the verifier has enough data to recalculate them and use them. Even more, if the prover lies, the equality that the verifier is expecting won't hold.

+

In reality, instead of dividing and expecting the result to equal to , we can just check the equality against the new expected value, and avoid doing that inversion.

+

All of this is explained in section 9.8 of the Cairo paper.

+

Trace extension / Padding

+

The last step is padding the trace to a power of two for efficiency. We may also need to pad the trace if for some reason some unbalance is given by the layout.

+

For this, we will copy the last executed instruction until reaching the desired length.

+

But there's a trick. If the last executed instruction is any instruction, and it's copied, the transition constraints won't be satisfied. To be able to do this, we need to use something called "proof mode". In proof mode, the main function of the program is wrapped in another one, which calls it and returns to an infinite loop. This loop is a jump relative 0.

+

Since this loop can be executed many times without changing the validity of the trace, it can be copied as many times as +needed, solving the issues mentioned before.

+

Summary

+

To construct the execution trace, we augment the RegisterStates with the information obtained from the Memory. This includes decoding instruction of each steps, and writing all the data needed to check the execution is valid.

+

Additionally, memory holes have to be filled, public memory added, and a final pad using an infinite loop is needed for everything to work properly.

+

Adding all of that, we create an execution trace that's ready for the prover to generate a proof.

+
+

Extended columns

+

The verifier sends challenges (or the prover samples them from the transcript). Additional columns are added to incorporate the memory constraints. To define them the prover follows these steps:

+
    +
  1. Stack the rows of the submatrix of defined by the columns pc, dst_addr, op0_addr, op1_addr into a vector a of length (this means that the first entries of a are pc[0], dst_addr[0], op0_addr[0], op1_addr[0], pc[1], dst_addr[1],...).
  2. +
  3. Stack the the rows of the submatrix defined by the columns inst, dst, op0, op1 into a vector v of length .
  4. +
  5. Define to be the matrix with columns , .
  6. +
  7. Define to be the matrix that's equal to in the first rows, and its last entries are the addresses and values of the actual public memory (program code).
  8. +
  9. Sort by the first column in increasing order. The result is a matrix of size . Denote its columns by and .
  10. +
  11. Compute the vector of size with entries +
  12. +
  13. Reshape the matrix into a in row-major. Reshape the vector into a matrix in row-major.
  14. +
  15. Concatenate these 12 rows. The result is a matrix of size
  16. +
+

The verifier sends challenge . Further columns are added to incorporate the range check constraints following these steps:

+
    +
  1. Stack the rows of the submatrix of defined by the columns in the group offsets into a vector of length .
  2. +
  3. Sort the values of in increasing order. Let be the result.
  4. +
  5. Compute the vector of size with entries +
  6. +
  7. Reshape and into matrices of size each and concatenate them into a matrix of size .
  8. +
  9. Concatenate and into a matrix of size .
  10. +
+

Using the notation described at the beginning, , and . They are respectively the columns of the first and second part of the rap, and the total number of columns.

+

Putting all together, the final layout of the trace is the following

+
 A.  flags      (16) : Decoded instruction flags
+ B.  res        (1)  : Res value
+ C.  pointers   (2)  : Temporary memory pointers (ap and fp)
+ D.  mem_a      (4)  : Memory addresses (pc, dst_addr, op0_addr, op1_addr)
+ E.  mem_v      (4)  : Memory values (inst, dst, op0, op1)
+ F.  offsets    (3)  : (off_dst, off_op0, off_op1)
+ G.  derived    (3)  : (t0, t1, mul)
+ H.  mem_a'     (4)  : Sorted memory addresses
+ I.  mem_v'     (4)  : Sorted memory values
+ J.  mem_p      (4)  : Memory permutation argument columns
+ K.  offsets_b' (3)  : Sorted offset columns
+ L.  offsets_p' (3)  : Range check permutation argument columns
+
+ A                B C  D    E    F   G   H    I    J    K   L
+|xxxxxxxxxxxxxxxx|x|xx|xxxx|xxxx|xxx|xxx|xxxx|xxxx|xxxx|xxx|xxx|
+
+
+

Virtual columns and Subcolumns

+

Virtual Columns

+

In previous chapters, we have seen how the registers states and the memory are augmented to generate a provable trace.

+

While we have shown a way of doing that, there isn't only one possible provable trace. In fact, there are multiple configurations possible.

+

For example, in the Cairo VM, we have 15 flags. These flags include "DstReg", "Op0Reg", "OpCode" and others. For simplification, let's imagine we have 3 flags with letters from "A" to "C", where "A" is the first flag.

+

Now, let's assume we have 4 steps in our trace. If we were to only use plain columns, the layout would look like this:

+ + + + + +
FlagAFlagBFlagB
A0B0C0
A1B1C1
A2B2C2
A3B3C3
+

But, we could also organize them like this

+ + + + + + + + + + + + + +
Flags
A0
B0
C0
A1
B1
C1
A2
B2
C2
A3
B3
C3
+

The only problem is that now the constraints for each transition of the rows are not the same. We will have to define then a concept called "Virtual Column".

+

A Virtual Column is like a traditional column, which has its own set of constraints, but it exists interleaved with another one. In the previous example, each row is associated with a column, but in practice, we could have different ratios. We could have 3 rows corresponding to one Virtual Column, and the next one corresponding to another one. For the time being, let's focus on this simpler example.

+

Each row corresponding to Flag A will have the constraints associated with its own Virtual Column, and the same will apply to Flag B and Flag C.

+

Now, to do this, we will need to evaluate the multiple rows taking into account that they are part of the same step. For a real case, we will add a dummy flag D, whose purpose is to make the evaluation move in a number that is a power of 2.

+

Let's see how it works. If we were evaluating the Frame where the constraints should give 0, the frame movement would look like this:

+
+ A0 | B0 | C0
++ A1 | B1 | C1
+  A2 | B2 | C2
+  A3 | B3 | C3
+
+
  A0 | B0 | C0
++ A1 | B1 | C1
++ A2 | B2 | C2
+  A3 | B3 | C3
+
+
  A0 | B0 | C0
+  A1 | B1 | C1
++ A2 | B2 | C2
++ A3 | B3 | C3
+
+

In the second case, the evaluation would look like this:

+
+ A0 |
++ B0 |
++ C0 |
++ D0 |
++ A1 |
++ B1 |
++ C1 |
++ D1 |
+  A2 |
+  B2 |
+  C2 |
+  D2 |
+  A3 |
+  B3 |
+  C3 |
+  D3 |
+
+
  A0 |
+  B0 |
+  C0 |
+  D0 |
++ A1 |
++ B1 |
++ C1 |
++ D1 |
++ A2 |
++ B2 |
++ C2 |
++ D2 |
+  A3 |
+  B3 |
+  C3 |
+  D3 |
+
+
  A0 |
+  B0 |
+  C0 |
+  D0 |
+  A1 |
+  B1 |
+  C1 |
+  D1 |
++ A2 |
++ B2 |
++ C2 |
++ D2 |
++ A3 |
++ B3 |
++ C3 |
++ D3 |
+
+

When evaluating the composition polynomial, we will do it over the points on the LDE, where the constraints won't evaluate to 0, but we will use the same spacing. Assume we have three constraints for each flag, , , and , and that they don't involve other trace cells. Let's call the index of the frame evaluation i, starting from 0.

+

In the first case, the constraint , and would be applied over the same rows, giving an equation that looks like this:

+

+

In the second case, the equations would look like:

+

+

+

+

Virtual Subcolumns

+

Assume now we have 3 columns that share some constraints. For example, let's have three flags that can be either 0 or 1. Each flag will also have its own set of dedicated constraints.

+

Let's denote the shared constraint , and the independent constraints .

+

What we can do is define a Column for the flags, where the binary constraint is enforced. Additionally, we will define a subcolumn for each flag, which will enforce each .

+

In summary, if we have a set of shared constraints to apply, we will be using a Column. If we want to mix or interleave Columns, we will define them as Virtual Columns. And if we want to apply more constraints to a subset of a Column of Virtual Columns, or share constraints between columns, we will define Virtual Subcolumns.

+
+

Builtins

+

We can understand the built-in as a small machine, that we can use to efficiently prove a subprogram. For example, it may be able to prove a hash, like Poseidon or Keccak, verify a signature, or check that some variable is in a range, and the cost would be less than what we would have if using the Cairo VM instructions.

+

For each subprogram we want to prove, we will have a machine, which will have its own set of constraints in the prover. Let's take for example the Range Check built-in. This builtin enforces that a value is between 0 and .

+

The logic behind the built-in is pretty straightforward. We split into 8 parts. So we will say that

+

Then we require that each is in the range . The idea here is to reuse the Range Check constraint that checks if the offsets are between and . If we can decompose the number in eight limbs of 16 bits, and we don't need any more limbs, it follows that the number will be less than

+

The missing ingredient is how we make sure that each value that should be constrained by the built-in is actually constrained.

+

The process starts with the VM designating special memory positions for the built-in. You can think of this as a way of communicating the VM with the specific built-in machine by sharing memory.

+

The VM won't save any instruction associated with how the built-in gets to the result and will assume the output is correct. You can think of this as an IO device in any computer, which works in a similar fashion. The VM delegates the work to an external device and takes the result from the memory.

+

Knowing which specific positions of the memory are used by the built-in, the prover can add more constraints that enforce the calculations of the built-in were done correctly. Let's see how it's done.

+

In the constraint system of the VM, we will treat every memory cell associated with the built-in as any other, treating it as a pair of addresses and values with the usual constraints. Additionally, we will add more that are specific to the builtin.

+

Let's say we have multiple values , such that each needs to be range checked by the built-in. Let each value be stored in a memory address . Let the initial expected memory position for the range check built-in be . Here is a value known and a public input.

+

We need to enforce then that , and that the built in . These constraints have to be put on top of the constraints that are used by the memory, and that's the key to all of this. If these constraints weren't in place, there wouldn't be an enforced link between the Builtin and the VM, which would lead to security issues.

+

As one last detail, since the memory cells share the same constraints, and we add more for the ones in the builtin, we can treat the builtin cells as a subcolumn. In that case, we can assign one cell for the memory every N cell, giving a ratio that will be observable in the layout.

+

This gives a better relationship between the number of cells used for the VM, and the builtin, giving an improvement in performance.

+
+

CLI

+ +
+ + +
+
+ + + +
+ + + + + + + + + + + + + + + + + + + + + diff --git a/searcher.js b/searcher.js new file mode 100644 index 000000000..d2b0aeed3 --- /dev/null +++ b/searcher.js @@ -0,0 +1,483 @@ +"use strict"; +window.search = window.search || {}; +(function search(search) { + // Search functionality + // + // You can use !hasFocus() to prevent keyhandling in your key + // event handlers while the user is typing their search. + + if (!Mark || !elasticlunr) { + return; + } + + //IE 11 Compatibility from https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/startsWith + if (!String.prototype.startsWith) { + String.prototype.startsWith = function(search, pos) { + return this.substr(!pos || pos < 0 ? 0 : +pos, search.length) === search; + }; + } + + var search_wrap = document.getElementById('search-wrapper'), + searchbar = document.getElementById('searchbar'), + searchbar_outer = document.getElementById('searchbar-outer'), + searchresults = document.getElementById('searchresults'), + searchresults_outer = document.getElementById('searchresults-outer'), + searchresults_header = document.getElementById('searchresults-header'), + searchicon = document.getElementById('search-toggle'), + content = document.getElementById('content'), + + searchindex = null, + doc_urls = [], + results_options = { + teaser_word_count: 30, + limit_results: 30, + }, + search_options = { + bool: "AND", + expand: true, + fields: { + title: {boost: 1}, + body: {boost: 1}, + breadcrumbs: {boost: 0} + } + }, + mark_exclude = [], + marker = new Mark(content), + current_searchterm = "", + URL_SEARCH_PARAM = 'search', + URL_MARK_PARAM = 'highlight', + teaser_count = 0, + + SEARCH_HOTKEY_KEYCODE = 83, + ESCAPE_KEYCODE = 27, + DOWN_KEYCODE = 40, + UP_KEYCODE = 38, + SELECT_KEYCODE = 13; + + function hasFocus() { + return searchbar === document.activeElement; + } + + function removeChildren(elem) { + while (elem.firstChild) { + elem.removeChild(elem.firstChild); + } + } + + // Helper to parse a url into its building blocks. + function parseURL(url) { + var a = document.createElement('a'); + a.href = url; + return { + source: url, + protocol: a.protocol.replace(':',''), + host: a.hostname, + port: a.port, + params: (function(){ + var ret = {}; + var seg = a.search.replace(/^\?/,'').split('&'); + var len = seg.length, i = 0, s; + for (;i': '>', + '"': '"', + "'": ''' + }; + var repl = function(c) { return MAP[c]; }; + return function(s) { + return s.replace(/[&<>'"]/g, repl); + }; + })(); + + function formatSearchMetric(count, searchterm) { + if (count == 1) { + return count + " search result for '" + searchterm + "':"; + } else if (count == 0) { + return "No search results for '" + searchterm + "'."; + } else { + return count + " search results for '" + searchterm + "':"; + } + } + + function formatSearchResult(result, searchterms) { + var teaser = makeTeaser(escapeHTML(result.doc.body), searchterms); + teaser_count++; + + // The ?URL_MARK_PARAM= parameter belongs inbetween the page and the #heading-anchor + var url = doc_urls[result.ref].split("#"); + if (url.length == 1) { // no anchor found + url.push(""); + } + + // encodeURIComponent escapes all chars that could allow an XSS except + // for '. Due to that we also manually replace ' with its url-encoded + // representation (%27). + var searchterms = encodeURIComponent(searchterms.join(" ")).replace(/\'/g, "%27"); + + return '' + result.doc.breadcrumbs + '' + + '' + + teaser + ''; + } + + function makeTeaser(body, searchterms) { + // The strategy is as follows: + // First, assign a value to each word in the document: + // Words that correspond to search terms (stemmer aware): 40 + // Normal words: 2 + // First word in a sentence: 8 + // Then use a sliding window with a constant number of words and count the + // sum of the values of the words within the window. Then use the window that got the + // maximum sum. If there are multiple maximas, then get the last one. + // Enclose the terms in . + var stemmed_searchterms = searchterms.map(function(w) { + return elasticlunr.stemmer(w.toLowerCase()); + }); + var searchterm_weight = 40; + var weighted = []; // contains elements of ["word", weight, index_in_document] + // split in sentences, then words + var sentences = body.toLowerCase().split('. '); + var index = 0; + var value = 0; + var searchterm_found = false; + for (var sentenceindex in sentences) { + var words = sentences[sentenceindex].split(' '); + value = 8; + for (var wordindex in words) { + var word = words[wordindex]; + if (word.length > 0) { + for (var searchtermindex in stemmed_searchterms) { + if (elasticlunr.stemmer(word).startsWith(stemmed_searchterms[searchtermindex])) { + value = searchterm_weight; + searchterm_found = true; + } + }; + weighted.push([word, value, index]); + value = 2; + } + index += word.length; + index += 1; // ' ' or '.' if last word in sentence + }; + index += 1; // because we split at a two-char boundary '. ' + }; + + if (weighted.length == 0) { + return body; + } + + var window_weight = []; + var window_size = Math.min(weighted.length, results_options.teaser_word_count); + + var cur_sum = 0; + for (var wordindex = 0; wordindex < window_size; wordindex++) { + cur_sum += weighted[wordindex][1]; + }; + window_weight.push(cur_sum); + for (var wordindex = 0; wordindex < weighted.length - window_size; wordindex++) { + cur_sum -= weighted[wordindex][1]; + cur_sum += weighted[wordindex + window_size][1]; + window_weight.push(cur_sum); + }; + + if (searchterm_found) { + var max_sum = 0; + var max_sum_window_index = 0; + // backwards + for (var i = window_weight.length - 1; i >= 0; i--) { + if (window_weight[i] > max_sum) { + max_sum = window_weight[i]; + max_sum_window_index = i; + } + }; + } else { + max_sum_window_index = 0; + } + + // add around searchterms + var teaser_split = []; + var index = weighted[max_sum_window_index][2]; + for (var i = max_sum_window_index; i < max_sum_window_index+window_size; i++) { + var word = weighted[i]; + if (index < word[2]) { + // missing text from index to start of `word` + teaser_split.push(body.substring(index, word[2])); + index = word[2]; + } + if (word[1] == searchterm_weight) { + teaser_split.push("") + } + index = word[2] + word[0].length; + teaser_split.push(body.substring(word[2], index)); + if (word[1] == searchterm_weight) { + teaser_split.push("") + } + }; + + return teaser_split.join(''); + } + + function init(config) { + results_options = config.results_options; + search_options = config.search_options; + searchbar_outer = config.searchbar_outer; + doc_urls = config.doc_urls; + searchindex = elasticlunr.Index.load(config.index); + + // Set up events + searchicon.addEventListener('click', function(e) { searchIconClickHandler(); }, false); + searchbar.addEventListener('keyup', function(e) { searchbarKeyUpHandler(); }, false); + document.addEventListener('keydown', function(e) { globalKeyHandler(e); }, false); + // If the user uses the browser buttons, do the same as if a reload happened + window.onpopstate = function(e) { doSearchOrMarkFromUrl(); }; + // Suppress "submit" events so the page doesn't reload when the user presses Enter + document.addEventListener('submit', function(e) { e.preventDefault(); }, false); + + // If reloaded, do the search or mark again, depending on the current url parameters + doSearchOrMarkFromUrl(); + } + + function unfocusSearchbar() { + // hacky, but just focusing a div only works once + var tmp = document.createElement('input'); + tmp.setAttribute('style', 'position: absolute; opacity: 0;'); + searchicon.appendChild(tmp); + tmp.focus(); + tmp.remove(); + } + + // On reload or browser history backwards/forwards events, parse the url and do search or mark + function doSearchOrMarkFromUrl() { + // Check current URL for search request + var url = parseURL(window.location.href); + if (url.params.hasOwnProperty(URL_SEARCH_PARAM) + && url.params[URL_SEARCH_PARAM] != "") { + showSearch(true); + searchbar.value = decodeURIComponent( + (url.params[URL_SEARCH_PARAM]+'').replace(/\+/g, '%20')); + searchbarKeyUpHandler(); // -> doSearch() + } else { + showSearch(false); + } + + if (url.params.hasOwnProperty(URL_MARK_PARAM)) { + var words = decodeURIComponent(url.params[URL_MARK_PARAM]).split(' '); + marker.mark(words, { + exclude: mark_exclude + }); + + var markers = document.querySelectorAll("mark"); + function hide() { + for (var i = 0; i < markers.length; i++) { + markers[i].classList.add("fade-out"); + window.setTimeout(function(e) { marker.unmark(); }, 300); + } + } + for (var i = 0; i < markers.length; i++) { + markers[i].addEventListener('click', hide); + } + } + } + + // Eventhandler for keyevents on `document` + function globalKeyHandler(e) { + if (e.altKey || e.ctrlKey || e.metaKey || e.shiftKey || e.target.type === 'textarea' || e.target.type === 'text') { return; } + + if (e.keyCode === ESCAPE_KEYCODE) { + e.preventDefault(); + searchbar.classList.remove("active"); + setSearchUrlParameters("", + (searchbar.value.trim() !== "") ? "push" : "replace"); + if (hasFocus()) { + unfocusSearchbar(); + } + showSearch(false); + marker.unmark(); + } else if (!hasFocus() && e.keyCode === SEARCH_HOTKEY_KEYCODE) { + e.preventDefault(); + showSearch(true); + window.scrollTo(0, 0); + searchbar.select(); + } else if (hasFocus() && e.keyCode === DOWN_KEYCODE) { + e.preventDefault(); + unfocusSearchbar(); + searchresults.firstElementChild.classList.add("focus"); + } else if (!hasFocus() && (e.keyCode === DOWN_KEYCODE + || e.keyCode === UP_KEYCODE + || e.keyCode === SELECT_KEYCODE)) { + // not `:focus` because browser does annoying scrolling + var focused = searchresults.querySelector("li.focus"); + if (!focused) return; + e.preventDefault(); + if (e.keyCode === DOWN_KEYCODE) { + var next = focused.nextElementSibling; + if (next) { + focused.classList.remove("focus"); + next.classList.add("focus"); + } + } else if (e.keyCode === UP_KEYCODE) { + focused.classList.remove("focus"); + var prev = focused.previousElementSibling; + if (prev) { + prev.classList.add("focus"); + } else { + searchbar.select(); + } + } else { // SELECT_KEYCODE + window.location.assign(focused.querySelector('a')); + } + } + } + + function showSearch(yes) { + if (yes) { + search_wrap.classList.remove('hidden'); + searchicon.setAttribute('aria-expanded', 'true'); + } else { + search_wrap.classList.add('hidden'); + searchicon.setAttribute('aria-expanded', 'false'); + var results = searchresults.children; + for (var i = 0; i < results.length; i++) { + results[i].classList.remove("focus"); + } + } + } + + function showResults(yes) { + if (yes) { + searchresults_outer.classList.remove('hidden'); + } else { + searchresults_outer.classList.add('hidden'); + } + } + + // Eventhandler for search icon + function searchIconClickHandler() { + if (search_wrap.classList.contains('hidden')) { + showSearch(true); + window.scrollTo(0, 0); + searchbar.select(); + } else { + showSearch(false); + } + } + + // Eventhandler for keyevents while the searchbar is focused + function searchbarKeyUpHandler() { + var searchterm = searchbar.value.trim(); + if (searchterm != "") { + searchbar.classList.add("active"); + doSearch(searchterm); + } else { + searchbar.classList.remove("active"); + showResults(false); + removeChildren(searchresults); + } + + setSearchUrlParameters(searchterm, "push_if_new_search_else_replace"); + + // Remove marks + marker.unmark(); + } + + // Update current url with ?URL_SEARCH_PARAM= parameter, remove ?URL_MARK_PARAM and #heading-anchor . + // `action` can be one of "push", "replace", "push_if_new_search_else_replace" + // and replaces or pushes a new browser history item. + // "push_if_new_search_else_replace" pushes if there is no `?URL_SEARCH_PARAM=abc` yet. + function setSearchUrlParameters(searchterm, action) { + var url = parseURL(window.location.href); + var first_search = ! url.params.hasOwnProperty(URL_SEARCH_PARAM); + if (searchterm != "" || action == "push_if_new_search_else_replace") { + url.params[URL_SEARCH_PARAM] = searchterm; + delete url.params[URL_MARK_PARAM]; + url.hash = ""; + } else { + delete url.params[URL_MARK_PARAM]; + delete url.params[URL_SEARCH_PARAM]; + } + // A new search will also add a new history item, so the user can go back + // to the page prior to searching. A updated search term will only replace + // the url. + if (action == "push" || (action == "push_if_new_search_else_replace" && first_search) ) { + history.pushState({}, document.title, renderURL(url)); + } else if (action == "replace" || (action == "push_if_new_search_else_replace" && !first_search) ) { + history.replaceState({}, document.title, renderURL(url)); + } + } + + function doSearch(searchterm) { + + // Don't search the same twice + if (current_searchterm == searchterm) { return; } + else { current_searchterm = searchterm; } + + if (searchindex == null) { return; } + + // Do the actual search + var results = searchindex.search(searchterm, search_options); + var resultcount = Math.min(results.length, results_options.limit_results); + + // Display search metrics + searchresults_header.innerText = formatSearchMetric(resultcount, searchterm); + + // Clear and insert results + var searchterms = searchterm.split(' '); + removeChildren(searchresults); + for(var i = 0; i < resultcount ; i++){ + var resultElem = document.createElement('li'); + resultElem.innerHTML = formatSearchResult(results[i], searchterms); + searchresults.appendChild(resultElem); + } + + // Display results + showResults(true); + } + + fetch(path_to_root + 'searchindex.json') + .then(response => response.json()) + .then(json => init(json)) + .catch(error => { // Try to load searchindex.js if fetch failed + var script = document.createElement('script'); + script.src = path_to_root + 'searchindex.js'; + script.onload = () => init(window.search); + document.head.appendChild(script); + }); + + // Exported functions + search.hasFocus = hasFocus; +})(window.search); diff --git a/searchindex.js b/searchindex.js new file mode 100644 index 000000000..3744d0592 --- /dev/null +++ b/searchindex.js @@ -0,0 +1 @@ +Object.assign(window.search, {"doc_urls":["introduction.html#introduction","introduction.html#crates","fft/benchmarks.html#fft-benchmarks","fft/benchmarks.html#polynomial-interpolation-methods-comparison","plonk/recap.html#plonk","plonk/recap.html#notation","plonk/recap.html#the-ideas-and-components","plonk/recap.html#programs-our-toy-example","plonk/recap.html#plonk-arithmetization","plonk/recap.html#circuits-and-execution-traces","plonk/recap.html#the-q-matrix","plonk/recap.html#the-v-matrix","plonk/recap.html#custom-gates","plonk/recap.html#public-inputs","plonk/recap.html#from-matrices-to-polynomials","plonk/recap.html#common-preprocessed-input","plonk/recap.html#wrapping-up-the-whole-thing","plonk/protocol.html#protocol","plonk/protocol.html#details-and-tricks","plonk/protocol.html#polynomial-commitment-scheme","plonk/protocol.html#blindings","plonk/protocol.html#linearization-trick","plonk/protocol.html#setup","plonk/protocol.html#proving-algorithm","plonk/protocol.html#round-1","plonk/protocol.html#round-2","plonk/protocol.html#round-3","plonk/protocol.html#round-4","plonk/protocol.html#round-5","plonk/protocol.html#proof","plonk/protocol.html#verification-algorithm","plonk/protocol.html#transcript-initialization","plonk/protocol.html#extraction-of-values-and-commitments","plonk/protocol.html#proof-check","plonk/implementation.html#implementation","plonk/implementation.html#usage","plonk/implementation.html#padding","plonk/implementation.html#implementation-details","plonk/implementation.html#commitment-scheme","plonk/implementation.html#fiat-shamir","plonk/implementation.html#transcript-strategy","plonk/implementation.html#field-elements","plonk/implementation.html#strong-fiat-shamir","plonk/constraint_system.html#circuit-api","plonk/constraint_system.html#simple-example","plonk/constraint_system.html#building-complex-systems","starks/starks.html#stark-prover","starks/recap.html#starks-recap","starks/recap.html#verifying-computation-through-polynomials","starks/recap.html#fibonacci-numbers","starks/recap.html#cairo","starks/recap.html#fibonacci-step-by-step-walkthrough","starks/recap.html#trace-polynomial","starks/recap.html#composition-polynomial","starks/recap.html#boundary-polynomial","starks/recap.html#transition-constraint-polynomial","starks/recap.html#constructing-h","starks/recap.html#commiting-to-h","starks/recap.html#consistency-check","starks/recap.html#deep-composition-polynomial","starks/recap.html#consistency-check","starks/recap.html#summary","starks/recap.html#prover-side","starks/recap.html#verifier-side","starks/recap.html#simplifications-and-omissions","starks/recap.html#multiple-trace-columns","starks/recap.html#multiple-transition-constraints","starks/recap.html#composition-polynomial-decomposition","starks/recap.html#fri-low-degree-extensions-and-roots-of-unity","starks/protocol_overview.html#protocol-overview","starks/protocol_overview.html#arithmetization","starks/protocol_overview.html#polynomial-commitment-scheme","starks/protocol_overview.html#vector-commitments","starks/protocol_overview.html#fri","starks/protocol_overview.html#variant-useful-for-starks","starks/protocol_overview.html#polynomial-commitments","starks/protocol_overview.html#commit","starks/protocol_overview.html#open","starks/protocol_overview.html#completeness","starks/protocol_overview.html#soundness","starks/protocol_overview.html#batch","starks/protocol_overview.html#references","starks/protocol_overview.html#high-level-description-of-the-protocol","starks/protocol_overview.html#round-1-arithmetization-and-commitment-of-the-execution-trace","starks/protocol_overview.html#round-2-construction-of-composition-polynomial-h","starks/protocol_overview.html#round-3-evaluation-of-polynomials-at-z","starks/protocol_overview.html#round-4-run-batch-open-protocol","starks/protocol.html#starks-protocol","starks/protocol.html#grinding","starks/protocol.html#transcript","starks/protocol.html#general-notation","starks/protocol.html#definitions","starks/protocol.html#notation-of-important-operations","starks/protocol.html#protocol","starks/protocol.html#prover","starks/protocol.html#verifier","starks/protocol.html#notes-on-optimizations-and-variants","starks/protocol.html#sampling-of-challenges-variant","starks/protocol.html#batch-inversion","starks/protocol.html#fft","starks/protocol.html#ruffinis-rule","starks/protocol.html#bit-reversal-ordering-of-merkle-tree-leaves","starks/protocol.html#redundant-values-in-the-proof","starks/implementation.html#starks-prover-lambdaworks-implementation","starks/api.html#high-level-api-fibonacci-example","starks/api.html#air","starks/api.html#boundary-constraints","starks/api.html#transition-constraints","starks/api.html#tracetable","starks/api.html#air-context","starks/api.html#proving-execution","starks/under_the_hood.html#how-this-works-under-the-hood","starks/under_the_hood.html#prover-side","starks/under_the_hood.html#verifier-side","starks/under_the_hood.html#reconstructing-the-transition-constraint-polynomials","starks/under_the_hood.html#verifier","starks/under_the_hood.html#evenodd-decomposition-for-h","starks/under_the_hood.html#out-of-domain-frame","starks/under_the_hood.html#transcript","starks/under_the_hood.html#proof","starks/under_the_hood.html#special-considerations","starks/under_the_hood.html#fft-evaluation-and-interpolation","starks/under_the_hood.html#other","starks/under_the_hood.html#why-use-roots-of-unity","starks/under_the_hood.html#what-is-a-primitive-root-of-unity","starks/under_the_hood.html#why-use-cosets","starks/stone_prover/introduction.html#stone-prover-documentation","starks/stone_prover/trace_plain_layout.html#stone-prover-trace---layout-plain","starks/stone_prover/trace_plain_layout.html#columns--virtual-columns","starks/stone_prover/trace_plain_layout.html#main-trace-columns","starks/stone_prover/trace_plain_layout.html#column-0---rc-pool","starks/stone_prover/trace_plain_layout.html#column-1---flags-decodeopcode-rc","starks/stone_prover/trace_plain_layout.html#column-2---sorted-rc-rc16sorted","starks/stone_prover/trace_plain_layout.html#column-3---mem-pool","starks/stone_prover/trace_plain_layout.html#unused-cells-in-mem-pool","starks/stone_prover/trace_plain_layout.html#column-4---sorted-mem-pool","starks/stone_prover/trace_plain_layout.html#column-5---registers--pc-update","starks/stone_prover/trace_plain_layout.html#interaction-trace-columns","starks/stone_prover/trace_plain_layout.html#column-6---range-check-cumulative-product","starks/stone_prover/trace_plain_layout.html#column-7---multi-column-permutation-cum-product","starks/cairo.html#cairo","starks/cairo_trace_succinct.html#trace","starks/cairo_trace_succinct.html#construction-of-execution-trace-t","starks/cairo_trace_descriptive.html#cairo-execution-trace","starks/cairo_trace_descriptive.html#raw-materials","starks/cairo_trace_descriptive.html#construction-details","starks/cairo_trace_descriptive.html#main-trace-construction","starks/cairo_rap.html#extended-columns","starks/virtual_cols.html#virtual-columns-and-subcolumns","starks/virtual_cols.html#virtual-columns","starks/virtual_cols.html#virtual-subcolumns","starks/builtins.html#builtins","starks/cairo_cli.html#cli"],"index":{"documentStore":{"docInfo":{"0":{"body":9,"breadcrumbs":2,"title":1},"1":{"body":6,"breadcrumbs":2,"title":1},"10":{"body":188,"breadcrumbs":4,"title":2},"100":{"body":18,"breadcrumbs":4,"title":2},"101":{"body":120,"breadcrumbs":8,"title":6},"102":{"body":60,"breadcrumbs":5,"title":3},"103":{"body":46,"breadcrumbs":6,"title":4},"104":{"body":98,"breadcrumbs":10,"title":5},"105":{"body":45,"breadcrumbs":6,"title":1},"106":{"body":74,"breadcrumbs":7,"title":2},"107":{"body":111,"breadcrumbs":7,"title":2},"108":{"body":73,"breadcrumbs":6,"title":1},"109":{"body":150,"breadcrumbs":7,"title":2},"11":{"body":106,"breadcrumbs":4,"title":2},"110":{"body":45,"breadcrumbs":7,"title":2},"111":{"body":36,"breadcrumbs":7,"title":3},"112":{"body":78,"breadcrumbs":6,"title":2},"113":{"body":91,"breadcrumbs":6,"title":2},"114":{"body":298,"breadcrumbs":8,"title":4},"115":{"body":12,"breadcrumbs":5,"title":1},"116":{"body":41,"breadcrumbs":7,"title":3},"117":{"body":52,"breadcrumbs":7,"title":3},"118":{"body":132,"breadcrumbs":5,"title":1},"119":{"body":55,"breadcrumbs":5,"title":1},"12":{"body":79,"breadcrumbs":4,"title":2},"120":{"body":0,"breadcrumbs":6,"title":2},"121":{"body":34,"breadcrumbs":7,"title":3},"122":{"body":0,"breadcrumbs":4,"title":0},"123":{"body":128,"breadcrumbs":7,"title":3},"124":{"body":129,"breadcrumbs":7,"title":3},"125":{"body":177,"breadcrumbs":6,"title":2},"126":{"body":8,"breadcrumbs":6,"title":3},"127":{"body":70,"breadcrumbs":12,"title":5},"128":{"body":65,"breadcrumbs":10,"title":3},"129":{"body":0,"breadcrumbs":10,"title":3},"13":{"body":260,"breadcrumbs":4,"title":2},"130":{"body":154,"breadcrumbs":11,"title":4},"131":{"body":79,"breadcrumbs":12,"title":5},"132":{"body":10,"breadcrumbs":12,"title":5},"133":{"body":222,"breadcrumbs":11,"title":4},"134":{"body":113,"breadcrumbs":11,"title":4},"135":{"body":70,"breadcrumbs":12,"title":5},"136":{"body":60,"breadcrumbs":12,"title":5},"137":{"body":14,"breadcrumbs":10,"title":3},"138":{"body":20,"breadcrumbs":13,"title":6},"139":{"body":33,"breadcrumbs":14,"title":7},"14":{"body":941,"breadcrumbs":4,"title":2},"140":{"body":0,"breadcrumbs":2,"title":1},"141":{"body":19,"breadcrumbs":5,"title":1},"142":{"body":226,"breadcrumbs":8,"title":4},"143":{"body":0,"breadcrumbs":7,"title":3},"144":{"body":89,"breadcrumbs":6,"title":2},"145":{"body":26,"breadcrumbs":6,"title":2},"146":{"body":820,"breadcrumbs":7,"title":3},"147":{"body":289,"breadcrumbs":4,"title":2},"148":{"body":0,"breadcrumbs":6,"title":3},"149":{"body":320,"breadcrumbs":5,"title":2},"15":{"body":11,"breadcrumbs":5,"title":3},"150":{"body":69,"breadcrumbs":5,"title":2},"151":{"body":271,"breadcrumbs":4,"title":1},"152":{"body":0,"breadcrumbs":3,"title":1},"16":{"body":207,"breadcrumbs":6,"title":4},"17":{"body":0,"breadcrumbs":3,"title":1},"18":{"body":0,"breadcrumbs":4,"title":2},"19":{"body":131,"breadcrumbs":5,"title":3},"2":{"body":0,"breadcrumbs":5,"title":2},"20":{"body":129,"breadcrumbs":3,"title":1},"21":{"body":166,"breadcrumbs":4,"title":2},"22":{"body":18,"breadcrumbs":3,"title":1},"23":{"body":42,"breadcrumbs":4,"title":2},"24":{"body":23,"breadcrumbs":4,"title":2},"25":{"body":25,"breadcrumbs":4,"title":2},"26":{"body":33,"breadcrumbs":4,"title":2},"27":{"body":6,"breadcrumbs":4,"title":2},"28":{"body":46,"breadcrumbs":4,"title":2},"29":{"body":2,"breadcrumbs":3,"title":1},"3":{"body":115,"breadcrumbs":7,"title":4},"30":{"body":0,"breadcrumbs":4,"title":2},"31":{"body":11,"breadcrumbs":4,"title":2},"32":{"body":115,"breadcrumbs":5,"title":3},"33":{"body":40,"breadcrumbs":4,"title":2},"34":{"body":100,"breadcrumbs":3,"title":1},"35":{"body":486,"breadcrumbs":3,"title":1},"36":{"body":74,"breadcrumbs":3,"title":1},"37":{"body":12,"breadcrumbs":4,"title":2},"38":{"body":40,"breadcrumbs":4,"title":2},"39":{"body":0,"breadcrumbs":4,"title":2},"4":{"body":64,"breadcrumbs":3,"title":1},"40":{"body":97,"breadcrumbs":4,"title":2},"41":{"body":37,"breadcrumbs":4,"title":2},"42":{"body":43,"breadcrumbs":5,"title":3},"43":{"body":10,"breadcrumbs":5,"title":2},"44":{"body":151,"breadcrumbs":5,"title":2},"45":{"body":120,"breadcrumbs":6,"title":3},"46":{"body":25,"breadcrumbs":3,"title":2},"47":{"body":0,"breadcrumbs":4,"title":2},"48":{"body":52,"breadcrumbs":6,"title":4},"49":{"body":113,"breadcrumbs":4,"title":2},"5":{"body":77,"breadcrumbs":3,"title":1},"50":{"body":147,"breadcrumbs":3,"title":1},"51":{"body":34,"breadcrumbs":6,"title":4},"52":{"body":135,"breadcrumbs":4,"title":2},"53":{"body":38,"breadcrumbs":4,"title":2},"54":{"body":71,"breadcrumbs":4,"title":2},"55":{"body":40,"breadcrumbs":5,"title":3},"56":{"body":52,"breadcrumbs":4,"title":2},"57":{"body":43,"breadcrumbs":4,"title":2},"58":{"body":66,"breadcrumbs":4,"title":2},"59":{"body":101,"breadcrumbs":5,"title":3},"6":{"body":0,"breadcrumbs":4,"title":2},"60":{"body":67,"breadcrumbs":4,"title":2},"61":{"body":9,"breadcrumbs":3,"title":1},"62":{"body":77,"breadcrumbs":4,"title":2},"63":{"body":52,"breadcrumbs":4,"title":2},"64":{"body":16,"breadcrumbs":4,"title":2},"65":{"body":55,"breadcrumbs":5,"title":3},"66":{"body":86,"breadcrumbs":5,"title":3},"67":{"body":30,"breadcrumbs":5,"title":3},"68":{"body":172,"breadcrumbs":8,"title":6},"69":{"body":124,"breadcrumbs":5,"title":2},"7":{"body":172,"breadcrumbs":5,"title":3},"70":{"body":192,"breadcrumbs":4,"title":1},"71":{"body":46,"breadcrumbs":6,"title":3},"72":{"body":57,"breadcrumbs":5,"title":2},"73":{"body":186,"breadcrumbs":4,"title":1},"74":{"body":90,"breadcrumbs":6,"title":3},"75":{"body":121,"breadcrumbs":5,"title":2},"76":{"body":16,"breadcrumbs":4,"title":1},"77":{"body":62,"breadcrumbs":4,"title":1},"78":{"body":30,"breadcrumbs":4,"title":1},"79":{"body":72,"breadcrumbs":4,"title":1},"8":{"body":47,"breadcrumbs":4,"title":2},"80":{"body":212,"breadcrumbs":4,"title":1},"81":{"body":21,"breadcrumbs":4,"title":1},"82":{"body":48,"breadcrumbs":7,"title":4},"83":{"body":93,"breadcrumbs":9,"title":6},"84":{"body":164,"breadcrumbs":9,"title":6},"85":{"body":47,"breadcrumbs":8,"title":5},"86":{"body":17,"breadcrumbs":9,"title":6},"87":{"body":16,"breadcrumbs":4,"title":2},"88":{"body":45,"breadcrumbs":3,"title":1},"89":{"body":14,"breadcrumbs":3,"title":1},"9":{"body":163,"breadcrumbs":5,"title":3},"90":{"body":44,"breadcrumbs":4,"title":2},"91":{"body":158,"breadcrumbs":3,"title":1},"92":{"body":107,"breadcrumbs":5,"title":3},"93":{"body":0,"breadcrumbs":3,"title":1},"94":{"body":261,"breadcrumbs":3,"title":1},"95":{"body":264,"breadcrumbs":3,"title":1},"96":{"body":0,"breadcrumbs":5,"title":3},"97":{"body":43,"breadcrumbs":5,"title":3},"98":{"body":20,"breadcrumbs":4,"title":2},"99":{"body":19,"breadcrumbs":3,"title":1}},"docs":{"0":{"body":"This site hosts the main documentation for Lambdaworks as a whole. It is still a work in progress.","breadcrumbs":"Introduction » Introduction","id":"0","title":"Introduction"},"1":{"body":"lambdaworks-math lambdaworks-crypto lambdaworks-gpu","breadcrumbs":"Introduction » Crates","id":"1","title":"Crates"},"10":{"body":"As we said, it only depends on the program itself and not on any particular evaluation of it. It has one row for each gate and its columns are called QL​,QR​,QO​,QM​,QC​. They encode the type of gate of the rows and are designed to satisfy the following. Claim: if columns L,R,O correspond to a valid evaluation of the circuit then for all i the following equality holds Ai​(QL​)i​+Bi​(QR​)i​+Ai​Bi​QM​+Ci​(QO​)i​+(QC​)i​=0 This is better seen with examples. A multiplication gate is represented by the row: QL​ QR​ QM​ QO​ QC​ 0 0 1 -1 0 And the row in the trace matrix that corresponds to the execution of that gate is A B C 2 3 6 The equation in the claim for that row is that 2×0+3×0+2×3×1+6×(−1)+0, which equals 0. The next is an addition gate. This is represented by the row QL​ QR​ QM​ QO​ QC​ 1 1 0 -1 0 The corresponding row in the trace matrix its A B C 6 3 9 And the equation of the claim is 6×1+3×1+2×3×0+9×(−1)+0, which adds up to 0. Our last row is the gate that adds a constant. Addition by constant C can be represented by the row QL​ QR​ QM​ QO​ QC​ 1 0 0 -1 C In our case C=−1. The corresponding row in the execution trace is A B C 9 - 8 And the equation of the claim is 9×1+0×0+9×0×0+8×(−1)+C. This is also zero. Putting it altogether, the full Q matrix is QL​ QR​ QM​ QO​ QC​ 0 0 1 -1 0 1 1 0 -1 0 1 0 0 -1 -1 And we saw that the claim is true for our particular execution: 2×0+3×0+2×3×1+6×(−1)+0=0 6×1+3×1+6×3×0+9×(−1)+0=0 9×1+0×0+9×0×0+8×(−1)+(−1)=0 Not important to our example, but multiplication by constant C can be represented by: QL​ QR​ QM​ QO​ QC​ C 0 0 -1 0 As you might have already noticed, there are several ways of representing the same gate in some cases. We'll exploit this in a moment.","breadcrumbs":"Plonk » Recap » The Q matrix","id":"10","title":"The Q matrix"},"100":{"body":"In specific scenarios, such as dividing by a polynomial of the form X−a, for example when building the deep composition polynomial, Ruffini's rule can be employed to further enhance performance.","breadcrumbs":"STARKs » Protocol » Ruffini's rule","id":"100","title":"Ruffini's rule"},"101":{"body":"As one can see from inspecting the protocol, there are multiple times where, for a polynomial p, the prover sends both openings Open(p(D),hωi) and Open(p(D),−hωi). This implies, a priori, sending two authentication paths. Domains can be indexed using bit-reverse ordering to reduce this to a single authentication path for both openings, as follows. The natural way of building a Merkle tree to commit to a vector (p(h),p(hω),p(hω2),…,p(hω2k−1)), is assigning the value p(hωi) to leaf i. If this is the case, the value p(hωi) is at position i and the value p(−hωi) is at position i+2k−1. This is because −1 equals ω2k−1 for the value ω used in the protocol. Instead of this naive approach, a better solution is to assign the value p(hωσ(i)) to leaf i, where σ is the bit-reversal permutation. This is the permutation that maps i to the index whose binary representation (padded to k bits), is the binary representation of i but in reverse order. For example, if k=3 and i=1, then its binary representation is 001, which reversed is 100. Therefore σ(1)=8. In the same way σ(0)=0 and σ(2)=4. Check out the wikipedia article. With this ordering of the leaves, if i is even, element p(hωσ(i)) is at index i and p(−hωσ(i)) is at index i+1. Which means that a single authentication path serves to validate both points simultaneously.","breadcrumbs":"STARKs » Protocol » Bit-reversal ordering of Merkle tree leaves","id":"101","title":"Bit-reversal ordering of Merkle tree leaves"},"102":{"body":"The prover opens the polynomials pk​ of the FRI layers at υs2k​ and −υs2k​ for all k>1. Later on, the verifier uses each of those pairs to reconstruct one of the values of the next layer, namely pk+1​(υ2k+1). So there's no need to add the value pk​(υ2k+1) to the proof, as the verifier reconstructs them. The prover only needs to send the authentication paths Pk​ for them. The protocol is only modified at Step 3 of the verifier as follows. Checking that Verify((υs2k​,πkυs2k​​),Pk​,Pk​) is skipped. After computing x:=G+ζk​H, the verifier uses it to check that Verify((υs2k​,x),Pk​,Pk​) is Accept , which proves that x is actually πkυs2k​​, and continues to the next iteration of the loop.","breadcrumbs":"STARKs » Protocol » Redundant values in the proof","id":"102","title":"Redundant values in the proof"},"103":{"body":"The goal of this section will be to go over the details of the implementation of the proving system. To this end, we will follow the flow the example in the recap chapter, diving deeper into the code when necessary and explaining how it fits into a more general case. This implementation couldn't be done without checking Facebook's Winterfell and Max Gillett's Giza . We want to thank everyone involved in them, along with Shahar Papini and Lior Goldberg from Starkware who also provided us valuable insight.","breadcrumbs":"STARKs » Implementation » STARKs Prover Lambdaworks Implementation","id":"103","title":"STARKs Prover Lambdaworks Implementation"},"104":{"body":"Let's go over the main test we use for our prover, where we compute a STARK proof for a fibonacci trace with 4 rows and then verify it. fn test_prove_fib() { let trace = simple_fibonacci::fibonacci_trace([FE::from(1), FE::from(1)], 8); let proof_options = ProofOptions::default_test_options(); let pub_inputs = FibonacciPublicInputs { a0: FE::one(), a1: FE::one(), }; let proof = prove::>(&trace, &pub_inputs, &proof_options).unwrap(); assert!(verify::>(&proof, &pub_inputs, &proof_options));\n} The proving system revolves around the prove function, that takes a trace, public inputs and proof options as inputs to generate a proof, and a verify function that takes the generated proof, the public inputs and the proof options as inputs, outputting true when the proof is verified correctly and false otherwise. Note that the public inputs and proof options should be the same for both. Public inputs should be shared by the Cairo runner to prover and verifier, and the proof options should have been agreed on beforehand by the two entities beforehand. Below we go over the main things involved in this code.","breadcrumbs":"STARKs » Implementation » High Level API » High level API: Fibonacci example","id":"104","title":"High level API: Fibonacci example"},"105":{"body":"To prove the integrity of a fibonacci trace, we first need to define what it means for a trace to be valid. As we've talked about in the recap, this involves defining an AIR for our computation where we specify both the boundary and transition constraints for a fibonacci sequence. In code, this is done through the AIR trait. Implementing AIR requires defining a couple methods, but the two most important ones are boundary_constraints and compute_transition, which encode the boundary and transition constraints of our computation.","breadcrumbs":"STARKs » Implementation » High Level API » AIR","id":"105","title":"AIR"},"106":{"body":"For our Fibonacci AIR, boundary constraints look like this: fn boundary_constraints( &self, _rap_challenges: &Self::RAPChallenges,\n) -> BoundaryConstraints { let a0 = BoundaryConstraint::new_simple(0, self.pub_inputs.a0.clone()); let a1 = BoundaryConstraint::new_simple(1, self.pub_inputs.a1.clone()); BoundaryConstraints::from_constraints(vec![a0, a1])\n} The BoundaryConstraint struct represents a specific boundary constraint, meaning \"column i at row j should be equal to x\". In this case, because we have only one column, we are using the new_simple method to simply say Row 0 should equal the public input a0, which in the typical fibonacci is set to 1. Row 1 should equal the public input a1, which in the typical fibonacci is set to 1. In the case of multiple columns, the new method exists so you can also specify column number. After instantiating each of these constraints, we return all of them through the struct BoundaryConstraints.","breadcrumbs":"STARKs » Implementation » High Level API » Boundary Constraints","id":"106","title":"Boundary Constraints"},"107":{"body":"The way we specify our fibonacci transition constraint looks like this: fn compute_transition( &self, frame: &air::frame::Frame, _rap_challenges: &Self::RAPChallenges,\n) -> Vec> { let first_row = frame.get_row(0); let second_row = frame.get_row(1); let third_row = frame.get_row(2); vec![third_row[0] - second_row[0] - first_row[0]]\n} It's not completely obvious why this is how we chose to express transition constraints, so let's talk a little about it. What we need to specify in this method is the relationship that has to hold between the current step of computation and the previous ones. For this, we get a Frame as an argument. This is a struct holding the current step (i.e. the current row of the trace) and all previous ones needed to encode our constraint. In our case, this is the current row and the two previous ones. To access rows we use the get_row method. The current step is always the last row (in our case 2), with the others coming before it. In our compute_transition method we get the three rows we need and return third_row[0] - second_row[0] - first_row[0] which is the value that needs to be zero for our constraint to hold. Because we support multiple transition constraints, we actually return a vector with one value per constraint, so the first element holds the first constraint value and so on.","breadcrumbs":"STARKs » Implementation » High Level API » Transition Constraints","id":"107","title":"Transition Constraints"},"108":{"body":"After defining our AIR, we create our specific trace to prove against it. let trace = fibonacci_trace([FE17::new(1), FE17::new(1)], 4); let trace_table = TraceTable { table: trace.clone(), num_cols: 1,\n}; TraceTable is the struct holding execution traces; the num_cols says how many columns the trace has, the table field is a vec holding the actual values of the trace in row-major form, meaning if the trace looks like this | 1 | 2 |\n| 3 | 4 |\n| 5 | 6 | then its corresponding TraceTable is let trace_table = TraceTable { table: vec![1, 2, 3, 4, 5, 6], num_cols: 2,\n}; In our example, fibonacci_trace is just a helper function we use to generate the fibonacci trace with 4 rows and [1, 1] as the first two values.","breadcrumbs":"STARKs » Implementation » High Level API » TraceTable","id":"108","title":"TraceTable"},"109":{"body":"After specifying our constraints and trace, the only thing left to do is provide a few parameters related to the STARK protocol and our AIR. These specify things such as the number of columns of the trace and proof configuration, among others. They are all encapsulated in the AirContext struct, which in our example we instantiate like this: let context = AirContext { options: ProofOptions { blowup_factor: 2, fri_number_of_queries: 1, coset_offset: 3, }, trace_columns: trace_table.n_cols, transition_degrees: vec![1], transition_exemptions: vec![2], transition_offsets: vec![0, 1, 2], num_transition_constraints: 1,\n}; Let's go over each of them: options requires a ProofOptions struct holding specific parameters related to the STARK protocol to be used when proving. They are: The blowup_factor used for the trace LDE extension, a parameter related to the security of the protocol. The number of queries performed by the verifier when doing FRI, also related to security. The offset used for the LDE coset. This depends on the field being used for the STARK proof. trace_columns are the number of columns of the trace, respectively. transition_degrees holds the degree of each transition constraint. transition_exemptions is a Vec which tells us, for each column, the number of rows the transition constraints should not apply, starting from the end of the trace. In the example, the transition constraints won't apply on the last two rows of the trace. transition_offsets holds the indexes that define a frame for our AIR. In our fibonacci case, these are [0, 1, 2] because we need the current row and the two previous one to define our transition constraint. num_transition_constraints simply says how many transition constraints our AIR has.","breadcrumbs":"STARKs » Implementation » High Level API » AIR Context","id":"109","title":"AIR Context"},"11":{"body":"The claim in the previous section is clearly not an \"if and only if\" statement because the following trace columns do satisfy the equations but do not correspond to a valid execution: A B C 2 3 6 0 0 0 20 - 19 The V matrix encodes the carry of the results from one gate to the right or left operand of a subsequent one. These are called wirings . Like the Q matrix, it's independent of the particular evaluation. It consists of indices for all input and intermediate variables. In this case that matrix is: L R O 0 1 2 2 1 3 3 - 4 Here 0 is the index of e, 1 is the index of x, 2 is the index of u, 3 is the index of v and 4 is the index of the output w. Now we can update the claim to have an \"if and only if\" statement. Claim: Let T be a matrix with columns A,B,C. It correspond to a valid evaluation of the circuit if and only if a) for all i the following equality holds Ai​(QL​)i​+Bi​(QR​)i​+Ai​Bi​QM​+Ci​(QO​)i​+(QC​)i​=0, b) for all i,j,k,l such that Vi,j​=Vk,l​ we have Ti,j​=Tk,l​. So now our malformed example does not pass the second check.","breadcrumbs":"Plonk » Recap » The V matrix","id":"11","title":"The V matrix"},"110":{"body":"Having defined all of the above, proving our fibonacci example amounts to instantiating the necessary structs and then calling prove passing the trace, public inputs and proof options. We use a simple implementation of a hasher called TestHasher to handle merkle proof building. let proof = prove(&trace_table, &pub_inputs, &proof_options); Verifying is then done by passing the proof of execution along with the same AIR to the verify function. assert!(verify(&proof, &pub_inputs, &proof_options));","breadcrumbs":"STARKs » Implementation » High Level API » Proving execution","id":"110","title":"Proving execution"},"111":{"body":"In this section we go over how a few things in the prove and verify functions are implemented. If you just need to use the prover, then you probably don't need to read this. If you're going through the code to try to understand it, read on. We will once again use the fibonacci example as an ilustration. Recall from the recap that the main steps for the prover and verifier are the following:","breadcrumbs":"STARKs » Implementation » Under the hood » How this works under the hood","id":"111","title":"How this works under the hood"},"112":{"body":"Compute the trace polynomial t by interpolating the trace column over a set of 2n-th roots of unity {gi:0≤i<2n}. Compute the boundary polynomial B. Compute the transition constraint polynomial C. Construct the composition polynomial H from B and C. Sample an out of domain point z and provide the evaluation H(z) and all the necessary trace evaluations to reconstruct it. In the fibonacci case, these are t(z), t(zg), and t(zg2). Sample a domain point x_0 and provide the evaluation H(x0​) and t(x0​). Construct the deep composition polynomial Deep(x) from H, t, and the evaluations from the item above. Do FRI on Deep(x) and provide the resulting FRI commitment to the verifier. Provide the merkle root of t and the merkle proof of t(x0​).","breadcrumbs":"STARKs » Implementation » Under the hood » Prover side","id":"112","title":"Prover side"},"113":{"body":"Take the evaluation H(z) along with the trace evaluations the prover provided. Reconstruct the evaluations B(z) and C(z) from the trace evaluations. Check that the claimed evaluation H(z) the prover gave us actually satisfies H(z)=β1​B(z)+β2​C(z) Take the evaluations H(x0​) and t(x0​). Check that the claimed evaluation Deep(x0​) the prover gave us actually satisfies Deep(x0​)=γ1​x0​−zH(x0​)−H(z)​+γ2​x0​−zt(x0​)−t(z)​+γ3​x0​−zgt(x0​)−t(zg)​+γ4​x0​−zg2t(x0​)−t(zg2)​ Take the provided FRI commitment and check that it verifies. Using the merkle root and the merkle proof the prover provided, check that t(x0​) belongs to the trace. Following along the code in the prove and verify functions, most of it maps pretty well to the steps above. The main things that are not immediately clear are: How we take the constraints defined in the AIR through the compute_transition method and map them to transition constraint polynomials. How we then construct H from them and the boundary constraint polynomials. What the composition polynomial even/odd decomposition is. What an ood frame is. What the transcript is.","breadcrumbs":"STARKs » Implementation » Under the hood » Verifier side","id":"113","title":"Verifier side"},"114":{"body":"This is possibly the most complex part of the code, so what follows is a long explanation for it. In our fibonacci example, after obtaining the trace polynomial t by interpolating, the transition constraint polynomial is C(x)=∏i=05​(x−gi)t(xg2)−t(xg)−t(x)​ On our prove code, if someone passes us a fibonacci AIR like the one we showed above used in one of our tests, we somehow need to construct C(x). However, what we are given is not a polynomial, but rather this method fn compute_transition( &self, frame: &air::frame::Frame, ) -> Vec> { let first_row = frame.get_row(0); let second_row = frame.get_row(1); let third_row = frame.get_row(2); vec![third_row[0] - second_row[0] - first_row[0]]\n} So how do we get to C(x) from this? The answer is interpolation. What the method above is doing is the following: if you pass it a frame that looks like this ​t(x0​)t(x0​g)t(x0​g2)​​ for any given point x0​, it will return the value t(x0​g2)−t(x0​g)−t(x0​) which is the numerator in C(x0​). Using the transition_exemptions field we defined in our AIR, we can also compute evaluations in the denominator, i.e. the zerofier evaluations. This is done under the hood by the transition_divisors() method. The above means that even though we don't explicitly have the polynomial C(x), we can evaluate it on points given an appropriate frame. If we can evaluate it on enough points, we can then interpolate them to recover C(x). This is exactly how we construct both transition constraint polynomials and subsequently the composition polynomial H. The job of evaluating H on enough points so we can then interpolate it is done by the ConstraintEvaluator struct. You'll notice prove does the following let constraint_evaluations = evaluator.evaluate( &lde_trace, &lde_roots_of_unity_coset, &alpha_and_beta_transition_coefficients, &alpha_and_beta_boundary_coefficients,\n); This function call will return the evaluations of the boundary terms βiB​Bi​(x) and constraint terms βiT​Ci​(x) for every i. The constraint_evaluations value returned is a ConstraintEvaluationTable struct, which is nothing more than a big list of evaluations of each polynomial required to construct H. With this in hand, we just call let composition_poly = constraint_evaluations.compute_composition_poly(& lde_roots_of_unity_coset); which simply interpolates the sum of all evaluations to obtain H. Let's go into more detail on how the evaluate method reconstructs C(x) in our fibonacci example. It receives the lde_trace as an argument, which is this: ​t(ω0)t(ω1)…t(ω15)​​ where ω is the primitive root of unity used for the LDE, that is, ω satisfies ω2=g. We need to recover C(x), a polynomial whose degree can't be more than t(x)'s. Because t was built by interpolating 8 points (the trace), we know we can recover C(x) by interpolating it on 16 points. We choose these points to be the LDE roots of unity {ω0,ω,ω2,…,ω15} Remember that to evaluate C(x) on these points, all we need are the evaluations of the polynomial t(xg2)−t(xg)−t(x) as the zerofier ones we can compute easily. These become: t(ω0g2)−t(ω0g)−t(ω0)t(ωg2)−t(ωg)−t(ω)t(ω2g2)−t(ω2g)−t(ω2)⋮t(ω15g2)−t(ω15g)−t(ω15) If we remember that ω2=g, this is t(ω4)−t(ω2)−t(ω0)t(ω5)−t(ω3)−t(ω)t(ω6)−t(ω4)−t(ω2)⋮t(ω3)−t(ω)−t(ω15) and we can compute each evaluation here by calling compute_transition on the appropriate frame built from the lde_trace. Specifically, for the first evaluation we can build the frame: ​t(ω0)t(ω2)t(ω4)​​ Calling compute_transition on this frame gives us the first evaluation. We can get the rest in a similar fashion, which is what this piece of code in the evaluate method does: for (i, d) in lde_domain.iter().enumerate() { let frame = Frame::read_from_trace( lde_trace, i, blowup_factor, &self.air.context().transition_offsets, ) let mut evaluations = self.air.compute_transition(&frame); ...\n} Each iteration builds a frame as above and computes one of the evaluations needed. The rest of the code just adds the zerofier evaluations, along with the alphas and betas. It then also computes boundary polynomial evaluations by explicitly constructing them.","breadcrumbs":"STARKs » Implementation » Under the hood » Reconstructing the transition constraint polynomials","id":"114","title":"Reconstructing the transition constraint polynomials"},"115":{"body":"The verifier employs the same trick to reconstruct the evaluations on the out of domain point Ci​(z) for the consistency check.","breadcrumbs":"STARKs » Implementation » Under the hood » Verifier","id":"115","title":"Verifier"},"116":{"body":"At the end of the recap we talked about how in our code we don't actually commit to H, but rather an even/odd decomposition for it. These are two polynomials H_1 and H_2 that satisfy H(x)=H1​(x2)+xH2​(x2) This all happens on this piece of code let composition_poly = constraint_evaluations.compute_composition_poly(&lde_roots_of_unity_coset); let (composition_poly_even, composition_poly_odd) = composition_poly.even_odd_decomposition(); // Evaluate H_1 and H_2 in z^2.\nlet composition_poly_evaluations = vec![ composition_poly_even.evaluate(&z_squared), composition_poly_odd.evaluate(&z_squared),\n]; After this, we don't really use H anymore, but rather H_1 and H_2. There's not that much to say other than that.","breadcrumbs":"STARKs » Implementation » Under the hood » Even/odd decomposition for H","id":"116","title":"Even/odd decomposition for H"},"117":{"body":"As part of the consistency check, the prover needs to provide evaluations of the trace polynomials in all the points needed by the verifier to check that H was constructed correctly. In the fibonacci example, these are t(z), t(zg), and t(zg2). In code, the prover passes these evaluations as a Frame, which we call the out of domain (ood) frame. The reason we do this is simple: with the frame in hand, the verifier can reconstruct the evaluations of the constraint polynomials Ci​(z) by calling the compute_transition method on the ood frame and then adding the alphas, betas, and so on, just like we explained in the section above.","breadcrumbs":"STARKs » Implementation » Under the hood » Out of Domain Frame","id":"117","title":"Out of Domain Frame"},"118":{"body":"Throughout the protocol, there are a number of times where the verifier randomly samples some values that the prover needs to use (think of the alphas and betas used when constructing H). Because we don't actually have an interaction between prover and verifier, we emulate it by using a hash function, which we assume is a source of randomness the prover can't control. The job of providing these samples for both prover and verifier is done by the Transcript struct, which you can think of as a stateful rng; whenever you call challenge() on a transcript you get a random value and the internal state gets mutated, so the next time you call challenge() you get a different one. You can also call append on it to mutate its internal state yourself. This is done a number of times throughout the protocol to keep the prover honest so it can't predict or manipulate the outcome of challenge(). Notice that to sample the same values, both prover and verifier need to call challenge and append in the same order (and with the same values in the case of append) and the same number of times. The idea explained above is called the Fiat-Shamir heuristic or just Fiat-Shamir, and is more generally used throughout proving systems to remove interaction between prover and verifier. Though the concept is very simple, getting it right so the prover can't cheat is not, but we won't go into that here.","breadcrumbs":"STARKs » Implementation » Under the hood » Transcript","id":"118","title":"Transcript"},"119":{"body":"The generated proof has got all the information needed for the verifier to verify it: Trace length: The number of rows of the trace table, needed to know the max degree of the polynomials that appear in the system. LDE trace commitments. DEEP composition polynomial out of domain even and odd evaluations. DEEP composition polynomial root. FRI layers merkle roots. FRI last layer value. Query list. DEEP composition poly openings. Nonce: Proof of work setting used to generate the proof.","breadcrumbs":"STARKs » Implementation » Under the hood » Proof","id":"119","title":"Proof"},"12":{"body":"Our matrices are fine now. But they can be optimized. Let's do that to showcase this flexibility of PLONK and also reduce the size of our example. PLONK has the flexibility to construct more sophisticated gates as combinations of the five columns. And therefore the same program can be expressed in multiple ways. In our case all three gates can actually be merged into a single custom gate. The Q matrix ends up being a single row. QL​ QR​ QM​ QO​ QC​ 0 1 1 -1 1 and also the V matrix L R O 0 1 2 The trace matrix for this representation is just A B C 2 3 8 And we check that it satisfies the equation 2×0+3×1+2×3×1+8×(−1)+(−1)=0 Of course, we can't always squash an entire program into a single gate.","breadcrumbs":"Plonk » Recap » Custom gates","id":"12","title":"Custom gates"},"120":{"body":"","breadcrumbs":"STARKs » Implementation » Under the hood » Special considerations","id":"120","title":"Special considerations"},"121":{"body":"When evaluating or interpolating a polynomial, if the input (be it coefficients or evaluations) size isn't a power of two then the FFT API will extend it with zero padding until this requirement is met. This is because the library currently only uses a radix-2 FFT algorithm. Also, right now FFT only supports inputs with a size up to 2264 elements.","breadcrumbs":"STARKs » Implementation » Under the hood » FFT evaluation and interpolation","id":"121","title":"FFT evaluation and interpolation"},"122":{"body":"","breadcrumbs":"STARKs » Implementation » Under the hood » Other","id":"122","title":"Other"},"123":{"body":"Whenever we interpolate or evaluate trace, boundary and constraint polynomials, we use some 2n-th roots of unity. There are a few reasons for this: Using roots of unity means we can use the Fast Fourier Transform and its inverse to evaluate and interpolate polynomials. This method is much faster than the naive Lagrange interpolation one. Since a huge part of the STARK protocol involves both evaluating and interpolating, this is a huge performance improvement. When computing boundary and constraint polynomials, we divide them by their zerofiers, polynomials that vanish on a few points (the trace elements where the constraints do not hold). These polynomials take the form Z(X)=∏(X−xi​) where the xi​ are the points where we want it to vanish. When implementing this, evaluating this polynomial can be very expensive as it involves a huge product. However, if we are using roots of unity, we can use the following trick. The vanishing polynomial for all the 2n roots of unity is X2n−1 Instead of expressing the zerofier as a product of the places where it should vanish, we express it as the vanishing polynomial above divided by the exemptions polynomial; the polynomial whose roots are the places where constraints don't need to hold. Z(X)=∏(X−ei​)X2n−1​ where the ei​ are now the points where we don't want it to vanish. This exemptions polynomial in the denominator is usually much smaller, and because the vanishing polynomial in the numerator is only two terms, evaluating it is really fast.","breadcrumbs":"STARKs » Implementation » Under the hood » Why use roots of unity?","id":"123","title":"Why use roots of unity?"},"124":{"body":"The n-th roots of unity are the numbers x that satisfy xn=1 There are n such numbers, because they are the roots of the polynomial Xn−1. The set of n-th roots of unity always has a generator, a root g that can be used to obtain every other root of unity by exponentiating. What this means is that the set of n-th roots of unity is {gi:0≤in. Polynomials enter now to squash most of these equations. We will traduce the set of all equations in conditions (a) and (b) to just a few equations on polynomials. Let ω be a primitive N-th root of unity and let H=ωi:0≤i1 and 0≤j≤J and I=∣Lj′′​∣. Extend M with additional L′′ rows to obtain a matrix M∈F(L+L′+L′′)×33 by appending copies of R′′ at the bottom. Pad M with copies of its last row until it has a power of two number of rows. As a result we obtain a matrix T∈F2n×33.","breadcrumbs":"Cairo » Trace Formal Description » Construction of execution trace T:","id":"142","title":"Construction of execution trace T:"},"143":{"body":"","breadcrumbs":"Cairo » Trace Detailed Description » Cairo execution trace","id":"143","title":"Cairo execution trace"},"144":{"body":"After the execution of a Cairo program in the Cairo VM, three files are generated that are the core components for the construction of the execution trace, needed for the proving system: trace file : Has the information on the state of the three Cairo VM registers ap, fp, and pc at every cycle of the execution of the program. To reduce ambiguity in terms, we should call these the register states of the Cairo VM, and leave the term trace to the final product that is passed to the prover to generate a proof. memory file : A file with the information of the VM's memory at the end of the program run, after the memory has been relocated. public inputs : A file with all the information that must be publicly available to the prover and verifier, such as the total number of execution steps, public memory, used builtins and their respective addresses range in memory. The next section will explain in detail how these elements are used to build the final execution trace.","breadcrumbs":"Cairo » Trace Detailed Description » Raw materials","id":"144","title":"Raw materials"},"145":{"body":"The execution trace is built in two stages. In the first one, the information on the files described in the previous section is aggregated to build a main trace table. In the second stage, there is an interaction with the verifier to add some extension columns to the main trace.","breadcrumbs":"Cairo » Trace Detailed Description » Construction details","id":"145","title":"Construction details"},"146":{"body":"The layout of the main execution trace is as follows: A. flags (16): Decoded instruction flags B. res (1): Res value C. pointers (2): Temporary memory pointers (ap and fp) D. mem_a (4): Memory addresses (pc, dst_addr, op0_addr, op1_addr) E. mem_v (4): Memory values (inst, dst, op0, op1) F. offsets (3) : (off_dst, off_op0, off_op1) G. derived (3) : (t0, t1, mul) A B C D E F G\n|xxxxxxxxxxxxxxxx|x|xx|xxxx|xxxx|xxx|xxx| Each letter from A to G represents some subsection of columns, and the number specifies how many columns correspond to that subsection. Cairo instructions It is important to have in mind the information that each executed Cairo instruction holds, since it is a key component of the construction of the execution trace. For a detailed explanation of how the building components of the instruction interact to change the VM state, refer to the Cairo whitepaper, sections 4.4 and 4.5. Structure of the 63-bit that forms the first word of each instruction: ┌─────────────────────────────────────────────────────────────────────────┐ │ off_dst (biased representation) │ ├─────────────────────────────────────────────────────────────────────────┤ │ off_op0 (biased representation) │ ├─────────────────────────────────────────────────────────────────────────┤ │ off_op1 (biased representation) │ ├─────┬─────┬───────┬───────┬───────────┬────────┬───────────────────┬────┤ │ dst │ op0 │ op1 │ res │ pc │ ap │ opcode │ 0 │ │ reg │ reg │ src │ logic │ update │ update │ │ │ ├─────┼─────┼───┬───┼───┬───┼───┬───┬───┼───┬────┼────┬────┬────┬────┼────┤ │ 0 │ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │ 8 │ 9 │ 10 │ 11 │ 12 │ 13 │ 14 │ 15 │ └─────┴─────┴───┴───┴───┴───┴───┴───┴───┴───┴────┴────┴────┴────┴────┴────┘ Columns The construction of the following columns corresponds to a colloquial explanation of what is done in the build_cairo_execution_trace function. Section A - Flags The flags section A corresponds to the 16 bits that represent the configuration of the dst_reg, op0_reg, op1_src, res_logic, pc_update, ap_update and opcode flags, as well as the zero flag. So there is one column for each bit of the flags decomposition. Section C - Temporary memory pointers The two columns in this section, as well as the pc column from section D , are the most trivial. For each step of the register states, the corresponding values are added to the columns, which are pointers to some memory cell in the VM's memory. Section D - Memory addresses As already mentioned, the first column of this section, pc, is trivially obtained from the register states for each cycle. Columns dst_addr, op0_addr, and op1_addr from section D are addresses constructed from pointers stored at ap or fp, and their respective offsets off_dst, off_op0 and off_op1. The exact way these are computed depends on the particular values of the flags for each instruction. Section E - Memory values The inst column, is obtained by fetching in memory the value stored at pointer pc, which corresponds to a 63-bit Cairo instruction. Columns dst, op0, and op1 are computed by fetching in memory by their respective addresses. Section F - Offsets/Range-checked values These columns represent integer values that are used to construct addresses dst_addr, op0_addr and op1_addr and are decoded directly from the instruction. These values have the property to be numbered in the range from 0 to 2^16. Section B - Res This column is computed depending on the decoded opcode and res_logic of every instruction. In some cases, res is unused in the instruction, and the value for (dst)^(-1) is used in that place as an optimization. Section G - Derived To have constraints of max degree two, some more columns are derived from the already calculated, t0, t1, and mul: t0 is the product of the values of DST and the PC_JNZ flag for each step. t1 is the product of t0 and res for each step. mul is the product of op0 and op1 for each step. Range check and Memory holes For the values constrained between ranges 0 and 216, the offsets, the prover uses a permutation argument to optimize enforcing this. In particular, it checks an ordered list with the offsets are the same as the original one, is continuous, the first value is rcmin​, and the last one is less than rcmax​. Since not all values are used, there may be unused values, and so the ordered offset may not be continuous. These unused values are called holes, and they need to be filled with the missing values, so the checks can be done. This is explained in section 9.9 of the Cairo Paper In the case of memory, something similar happens, where the values should be continuous, but if there are built-ins, this may not be the case. For example, the built-in may be using addresses in ranges higher than the ones used by the program. To fix this, holes in the memory cells are filled, just like the ones of the RC. It's something important to note that when filling the holes, we can't use dedicated columns like op0_addr, since this would break the constraints. For this to work, we either need new columns for holes, or make use of subcolumns, which are explained in their dedicated section. No matter which approach is used , either by subcolumns or columns, we will need cells where the constraints of the range check and memory are applied, but not the specific ones related to the instructions. Finally, using these columns, we can fill the holes without breaking the constraint system. Dummy memory accesses As part of proving the execution of a Cairo program, we need to prove that memory used by the program extends the public memory. This is important since public memory contains for example the bytecode that was executed and the outputs. The bytecode is something critical for the verifier to not only know that something was executed correctly but to know what was executed. To do this, we the permutation check, which that proves the memory is continuous and single-valued To do this, the permutation check of the memory is modified. Initially, we had M, V and M′, V′ pairs of memory addresses and values, where M and V are the values as used and without order, and M′, V′ the pairs ordered by address. For the public memory, we will add it directly to M′, V′. We also need to add dummy accesses to the pairs M V. These dummy accesses are just pairs of (M,V)=(0,0). This change makes the statement that (M′,V′) is a permutation of (M,V), which means that they are the same values in a different order, no longer true. This means that the two cumulative products used to check the statement are no longer equal, and their division Luckily, we can know the new expected value on the verifier, since we have access to the public memory. Even more, it's this fact that enables the verifier to check the memory is an extension of the public memory. The math is quite straightforward. When the memory was the same, we expected a final value pn−1​=1. This came from two cumulative products that should equal, one from the unordered pairs, and one from the ordered ones. Now, adding zeros to one side and the real values to the other unbalances the cumulative products, so the verifier will need to balance it by dividing pn−1​ by the extra factors that appeared with the addition of the public memory. Doing so will make the final value 1 again. Since they only depend on the public memory, the verifier has enough data to recalculate them and use them. Even more, if the prover lies, the equality that the verifier is expecting won't hold. In reality, instead of dividing and expecting the result to equal to 1, we can just check the equality against the new expected value, and avoid doing that inversion. All of this is explained in section 9.8 of the Cairo paper. Trace extension / Padding The last step is padding the trace to a power of two for efficiency. We may also need to pad the trace if for some reason some unbalance is given by the layout. For this, we will copy the last executed instruction until reaching the desired length. But there's a trick. If the last executed instruction is any instruction, and it's copied, the transition constraints won't be satisfied. To be able to do this, we need to use something called \"proof mode\". In proof mode, the main function of the program is wrapped in another one, which calls it and returns to an infinite loop. This loop is a jump relative 0. Since this loop can be executed many times without changing the validity of the trace, it can be copied as many times as needed, solving the issues mentioned before. Summary To construct the execution trace, we augment the RegisterStates with the information obtained from the Memory. This includes decoding instruction of each steps, and writing all the data needed to check the execution is valid. Additionally, memory holes have to be filled, public memory added, and a final pad using an infinite loop is needed for everything to work properly. Adding all of that, we create an execution trace that's ready for the prover to generate a proof.","breadcrumbs":"Cairo » Trace Detailed Description » Main trace construction","id":"146","title":"Main trace construction"},"147":{"body":"The verifier sends challenges α,z∈F (or the prover samples them from the transcript). Additional columns are added to incorporate the memory constraints. To define them the prover follows these steps: Stack the rows of the submatrix of T defined by the columns pc, dst_addr, op0_addr, op1_addr into a vector a of length 2n+2 (this means that the first entries of a are pc[0], dst_addr[0], op0_addr[0], op1_addr[0], pc[1], dst_addr[1],...). Stack the the rows of the submatrix defined by the columns inst, dst, op0, op1 into a vector v of length 2n+2. Define MMem​∈F2n+2×2 to be the matrix with columns a, v. Define MMemRepl​∈F2n+2×2 to be the matrix that's equal to MMem​ in the first 2n+2−Lpub​ rows, and its last Lpub​ entries are the addresses and values of the actual public memory (program code). Sort MMemRepl​ by the first column in increasing order. The result is a matrix MMemReplSorted​ of size 2n+2×2. Denote its columns by a′ and v′. Compute the vector p of size 2n+2 with entries pi​:=j=0∏i​z−(ai​+αvi​)z−(ai′​+αvi′​)​ Reshape the matrix MMemReplSorted​ into a 2n×8 in row-major. Reshape the vector p into a 2n×4 matrix in row-major. Concatenate these 12 rows. The result is a matrix MMemRAP2​ of size 2n×12 The verifier sends challenge z′∈F. Further columns are added to incorporate the range check constraints following these steps: Stack the rows of the submatrix of T defined by the columns in the group offsets into a vector b of length 3⋅2n. Sort the values of b in increasing order. Let b′ be the result. Compute the vector p′ of size 3⋅2n with entries pi′​:=j=0∏i​z′−bi​z′−bi′​​ Reshape b′ and p′ into matrices of size 2n×3 each and concatenate them into a matrix MRangeCheckRAP2​ of size 2n×6. Concatenate MMemRAP2​ and MRangeCheckRAP2​ into a matrix MRAP2​ of size 2n×18. Using the notation described at the beginning, m′=33, m′′=18 and m=52. They are respectively the columns of the first and second part of the rap, and the total number of columns. Putting all together, the final layout of the trace is the following A. flags (16) : Decoded instruction flags B. res (1) : Res value C. pointers (2) : Temporary memory pointers (ap and fp) D. mem_a (4) : Memory addresses (pc, dst_addr, op0_addr, op1_addr) E. mem_v (4) : Memory values (inst, dst, op0, op1) F. offsets (3) : (off_dst, off_op0, off_op1) G. derived (3) : (t0, t1, mul) H. mem_a' (4) : Sorted memory addresses I. mem_v' (4) : Sorted memory values J. mem_p (4) : Memory permutation argument columns K. offsets_b' (3) : Sorted offset columns L. offsets_p' (3) : Range check permutation argument columns A B C D E F G H I J K L\n|xxxxxxxxxxxxxxxx|x|xx|xxxx|xxxx|xxx|xxx|xxxx|xxxx|xxxx|xxx|xxx|","breadcrumbs":"Cairo » RAP » Extended columns","id":"147","title":"Extended columns"},"148":{"body":"","breadcrumbs":"Cairo » Virtual Columns » Virtual columns and Subcolumns","id":"148","title":"Virtual columns and Subcolumns"},"149":{"body":"In previous chapters, we have seen how the registers states and the memory are augmented to generate a provable trace. While we have shown a way of doing that, there isn't only one possible provable trace. In fact, there are multiple configurations possible. For example, in the Cairo VM, we have 15 flags. These flags include \"DstReg\", \"Op0Reg\", \"OpCode\" and others. For simplification, let's imagine we have 3 flags with letters from \"A\" to \"C\", where \"A\" is the first flag. Now, let's assume we have 4 steps in our trace. If we were to only use plain columns, the layout would look like this: FlagA FlagB FlagB A0 B0 C0 A1 B1 C1 A2 B2 C2 A3 B3 C3 But, we could also organize them like this Flags A0 B0 C0 A1 B1 C1 A2 B2 C2 A3 B3 C3 The only problem is that now the constraints for each transition of the rows are not the same. We will have to define then a concept called \"Virtual Column\". A Virtual Column is like a traditional column, which has its own set of constraints, but it exists interleaved with another one. In the previous example, each row is associated with a column, but in practice, we could have different ratios. We could have 3 rows corresponding to one Virtual Column, and the next one corresponding to another one. For the time being, let's focus on this simpler example. Each row corresponding to Flag A will have the constraints associated with its own Virtual Column, and the same will apply to Flag B and Flag C. Now, to do this, we will need to evaluate the multiple rows taking into account that they are part of the same step. For a real case, we will add a dummy flag D, whose purpose is to make the evaluation move in a number that is a power of 2. Let's see how it works. If we were evaluating the Frame where the constraints should give 0, the frame movement would look like this: + A0 | B0 | C0\n+ A1 | B1 | C1 A2 | B2 | C2 A3 | B3 | C3 A0 | B0 | C0\n+ A1 | B1 | C1\n+ A2 | B2 | C2 A3 | B3 | C3 A0 | B0 | C0 A1 | B1 | C1\n+ A2 | B2 | C2\n+ A3 | B3 | C3 In the second case, the evaluation would look like this: + A0 |\n+ B0 |\n+ C0 |\n+ D0 |\n+ A1 |\n+ B1 |\n+ C1 |\n+ D1 | A2 | B2 | C2 | D2 | A3 | B3 | C3 | D3 | A0 | B0 | C0 | D0 |\n+ A1 |\n+ B1 |\n+ C1 |\n+ D1 |\n+ A2 |\n+ B2 |\n+ C2 |\n+ D2 | A3 | B3 | C3 | D3 | A0 | B0 | C0 | D0 | A1 | B1 | C1 | D1 |\n+ A2 |\n+ B2 |\n+ C2 |\n+ D2 |\n+ A3 |\n+ B3 |\n+ C3 |\n+ D3 | When evaluating the composition polynomial, we will do it over the points on the LDE, where the constraints won't evaluate to 0, but we will use the same spacing. Assume we have three constraints for each flag, C0​, C1​, and C2​, and that they don't involve other trace cells. Let's call the index of the frame evaluation i, starting from 0. In the first case, the constraint C0​, C1​ and C2​ would be applied over the same rows, giving an equation that looks like this: ‘Ck​(wi,wi+1)‘ In the second case, the equations would look like: ‘C0​(wi∗4,wi∗4+4)‘ ‘C1​(wi∗4+1,wi∗4+5)‘ ‘C2​(wi∗4+2,wi∗4+6)‘","breadcrumbs":"Cairo » Virtual Columns » Virtual Columns","id":"149","title":"Virtual Columns"},"15":{"body":"We have arrived at the eight polynomials we mentioned at the beginning: qL​,qR​,qM​,qO​,qC​,Sσ1​,Sσ2​,Sσ3​. These are what's called the common preprocessed input .","breadcrumbs":"Plonk » Recap » Common preprocessed input","id":"15","title":"Common preprocessed input"},"150":{"body":"Assume now we have 3 columns that share some constraints. For example, let's have three flags that can be either 0 or 1. Each flag will also have its own set of dedicated constraints. Let's denote the shared constraint B, and the independent constraints Ci​. What we can do is define a Column for the flags, where the binary constraint B is enforced. Additionally, we will define a subcolumn for each flag, which will enforce each Ci​. In summary, if we have a set of shared constraints to apply, we will be using a Column. If we want to mix or interleave Columns, we will define them as Virtual Columns. And if we want to apply more constraints to a subset of a Column of Virtual Columns, or share constraints between columns, we will define Virtual Subcolumns.","breadcrumbs":"Cairo » Virtual Columns » Virtual Subcolumns","id":"150","title":"Virtual Subcolumns"},"151":{"body":"We can understand the built-in as a small machine, that we can use to efficiently prove a subprogram. For example, it may be able to prove a hash, like Poseidon or Keccak, verify a signature, or check that some variable is in a range, and the cost would be less than what we would have if using the Cairo VM instructions. For each subprogram we want to prove, we will have a machine, which will have its own set of constraints in the prover. Let's take for example the Range Check built-in. This builtin enforces that a value X is between 0 and 2128. The logic behind the built-in is pretty straightforward. We split X into 8 parts. So we will say that X=X0​+X1​∗216+X2​∗232+X3​∗248+...+X7​∗2112 Then we require that each is in the range 0n. Then we constructed polynomials qL​,qR​,qM​,qO​,qC​,Sσ1​,Sσ2​,Sσ3​, f, g off the matrices Q and V. They are the result of interpolating at a domain H={1,ω,ω2,…,ωN−1} for some N-th primitive root of unity and a few random values. And also constructed polynomials a,b,c,pi off the matrices T and PI. Loosely speaking, the above fact can be reformulated in terms of polynomial equations as follows. Fact: Let zH​=XN−1. Let T be a N×3 matrix with columns A,B,C and PI a N×1 matrix. They correspond to a valid execution instance with public input given by PI if and only if a) There is a polynomial t1​ such that the following equality holds aqL​+bqR​+abqM​+cqO​+qC​+pi=zH​t1​, b) There are polynomials t2​,t3​, z such that zf−gz′=zH​t2​ and (z−1)L1​=zH​t3​, where z′(X)=z(Xω) You might be wondering where the polynomials ti​ came from. Recall that for a polynomial F, we have F(h)=0 for all h∈H if and only if F=zH​t for some polynomial t. Finally both conditions (a) and (b) are equivalent to a single equation (c) if we let more randomness to come into play. This is: (c) Let α be a random field element. There is a polynomial t such that zH​t=​aqL​+bqR​+abqM​+cqO​+qC​+pi+α(gz′−fz)+α2(z−1)L1​​ This last step is not obvious. You can check the paper to see the proof. Anyway, this is the equation you'll recognize below in the description of the protocol. Randomness is a delicate matter and an important part of the protocol is where it comes from, who chooses it and when they choose it. Check out the protocol to see how it works.","breadcrumbs":"Plonk » Recap » Wrapping up the whole thing","id":"16","title":"Wrapping up the whole thing"},"17":{"body":"","breadcrumbs":"Plonk » Protocol » Protocol","id":"17","title":"Protocol"},"18":{"body":"","breadcrumbs":"Plonk » Protocol » Details and tricks","id":"18","title":"Details and tricks"},"19":{"body":"A polynomial commitment scheme (PCS) is a cryptographic tool that allows one party to commit to a polynomial, and later prove properties of that polynomial. This commitment polynomial hides the original polynomial's coefficients and can be publicly shared without revealing any information about the original polynomial. Later, the party can use the commitment to prove certain properties of the polynomial, such as that it satisfies certain constraints or that it evaluates to a certain value at a specific point. In the implementation section we'll explain the inner workings of the Kate-Zaverucha-Goldberg scheme, a popular PCS chosen in Lambdaworks for PLONK. For the moment we only need the following about it: It consists of a finite group G and the following algorithms: Commit(f) : This algorithm takes a polynomial f and produces an element of the group G. It is called the commitment of f and is denoted by [f]1​. It is homomorphic in the sense that [f+g]1​=[f]1​+[g]1​. The former sum being addition of polynomials. The latter is addition in the group G. Open(f, ζ ) : It takes a polynomial f and a field element ζ and produces an element π of the group G. This element is called an opening proof for f(ζ). It is the proof that f evaluated at ζ gives f(ζ). Verify([f]1​, π, ζ, y) : It takes group elements [f]1​ and π, and also field elements ζ and y. With overwhelming probability it outputs Accept if f(z)=y and Reject otherwise.","breadcrumbs":"Plonk » Protocol » Polynomial commitment scheme","id":"19","title":"Polynomial commitment scheme"},"2":{"body":"","breadcrumbs":"FFT Library » Benchmarks » FFT Benchmarks","id":"2","title":"FFT Benchmarks"},"20":{"body":"As you will see in the protocol, the prover reveals the value taken by a bunch of the polynomials at a random ζ. In order for the protocol to be Honest Verifier Zero Knowledge , these polynomials need to be blinded . This is a process that makes the values of these polynomials at ζ seemingly random by forcing them to be of certain degree. Here's how it works. Let's take for example the polynomial a the prover constructs. This is the interpolation of the first column of the trace matrix T at the domain H. This matrix has all of the left operands of all the gates. The prover wishes to keep them secret. Say the trace matrix T has N rows. H is {1,ω,ω2,…,ωN−1}. The invariant that the prover cannot violate is that ablinded​(ωi) must take the value T0,i​, for all i. This is what the interpolation polynomial a satisfies. And is the unique such polynomial of degree at most N−1 with such property. But for higher degrees, there are many such polynomials. The blinding process takes a and a desired degree M≥N, and produces a new polynomial ablinded​ of degree exactly M. This new polynomial satisfies that ablinded​(ωi)=a(ωi) for all i. But outside H differs from a. This may seem hard but it's actually very simple. Let zH​ be the polynomial zH​=XN−1. If M=N+k, with k≥0, then sample random values b0​,…,bk​ and define ablinded​:=(b0​+b1​X+⋯+bk​Xk)zH​+a The reason why this does the job is that zH​(ωi)=0 for all i. Therefore the added term vanishes at H and leaves the values of a at H unchanged.","breadcrumbs":"Plonk » Protocol » Blindings","id":"20","title":"Blindings"},"21":{"body":"This is an optimization in PLONK to reduce the number of checks of the verifier. One of the main checks in PLONK boils down to check that p(ζ)=zH​(ζ)t(ζ), with p some polynomial that looks like p=aqL​+bqR​+abqM​+⋯, and so on. In particular the verifier needs to get the value p(ζ) from somewhere. For the sake of simplicity, in this section assume p is exactly aqL​+bqR​. Secret to the prover here are only a,b. The polynomials qL​ and qR​ are known also to the verifier. The verifier will already have the commitments [a]1​,[b]1​,[qL​]1​ and [qR​]1​. So the prover could send just a(ζ), b(ζ) along with their opening proofs and let the verifier compute by himself qL​(ζ) and qR​(ζ). Then with all these values the verifier could compute p(ζ)=a(ζ)qL​(ζ)+b(ζ)qR​(ζ). And also use his commitments to validate the opening proofs of a(ζ) and b(ζ). This has the problem that computing qL​(ζ) and qR​(ζ) is expensive. The prover can instead save the verifier this by sending also qL​(ζ),qR​(ζ) along with opening proofs. Since the verifier will have the commitments [qL​]1​ and [qR​]1​ beforehand, he can check that the prover is not cheating and cheaply be convinced that the claimed values are actually qL​(ζ) and qR​(ζ). This is much better. It involves the check of four opening proofs and the computation of p(ζ) off the values received from the prover. But it can be further improved as follows. As before, the prover sends a(ζ),b(ζ) along with their opening proofs. She constructs the polynomial f=a(ζ)qL​+b(ζ)qR​. She sends the value f(ζ) along with an opening proof of it. Notice that the value of f(ζ) is exactly p(ζ). The verifier can compute by himself [f]1​ as a(ζ)[qL​]1​+b(ζ)[qR​]1​. The verifier has everything to check all three openings and get convinced that the claimed value f(ζ) is true. And this value is actually p(ζ). So this means no more work for the verifier. And the whole thing got reduced to three openings. This is called the linearization trick. The polynomial f is called the linearization of p.","breadcrumbs":"Plonk » Protocol » Linearization trick","id":"21","title":"Linearization trick"},"22":{"body":"There's a one time setup phase to compute some values common to any execution and proof of the particular circuit. Precisely, the following commitments are computed and published. [qL​]1​,[qR​]1​,[qM​]1​,[qO​]1​,[qC​]1​,[Sσ1​]1​,[Sσ2​]1​,[Sσ3​]1​","breadcrumbs":"Plonk » Protocol » Setup","id":"22","title":"Setup"},"23":{"body":"Next we describe the proving algorithm for a program of size N. That includes public inputs. Let ω be a primitive N-th root of unity. Let H={1,ω,ω2,…,ωN−1}. Define ZH​:=XN−1. Assume the eight polynomials of common preprocessed input are already given. The prover computes the trace matrix T as described in the first sections. That means, with the first rows corresponding to the public inputs. It should be a N×3 matrix.","breadcrumbs":"Plonk » Protocol » Proving algorithm","id":"23","title":"Proving algorithm"},"24":{"body":"Add to the transcript the following: [Sσ1​]1​,[Sσ2​]1​,[Sσ3]1​,[qL​]1​,[qR​]1​,[qM​]1​,[qO​]1​,[qC​]1​ Compute polynomials a′,b′,c′ as the interpolation polynomials of the columns of T at the domain H. Sample random b1​,b2​,b3​,b4​,b5​,b6​ Let a:=(b1​X+b2​)ZH​+a′ b:=(b3​X+b4​)ZH​+b′ c:=(b5​X+b6​)ZH​+c′ Compute [a]1​,[b]1​,[c]1​ and add them to the transcript.","breadcrumbs":"Plonk » Protocol » Round 1","id":"24","title":"Round 1"},"25":{"body":"Sample β,γ from the transcript. Let z0​=1 and define recursively for 0≤k30000 ms 28745 ms 2^22 >30000 ms >30000 ms 2^23 >30000 ms >30000 ms 2^24 >30000 ms >30000 ms","breadcrumbs":"FFT Library » Benchmarks » Polynomial interpolation methods comparison","id":"3","title":"Polynomial interpolation methods comparison"},"30":{"body":"","breadcrumbs":"Plonk » Protocol » Verification algorithm","id":"30","title":"Verification algorithm"},"31":{"body":"The first step is to initialize the transcript in the same way the prover did, adding to it the following elements. [Sσ1​]1​,[Sσ2​]1​,[Sσ3​]1​,[qL​]1​,[qR​]1​,[qM​]1​,[qO​]1​,[qC​]1​","breadcrumbs":"Plonk » Protocol » Transcript initialization","id":"31","title":"Transcript initialization"},"32":{"body":"Challenges Firstly, the verifier needs to compute all the challenges. For that, he follows these steps: Add [a]1​,[b]1​,[c]1​ to the transcript. Sample two challenges β,γ. Add [z]1​ to the transcript. Sample a challenge α. Add [tlo​]1​,[tmid​]1​,[thi​]1​ to the transcript. Sample a challenge ζ. Add aˉ,bˉ,cˉ,sˉσ1​,sˉσ2​,zˉω​ to the transcript. Sample a challenge υ. Compute p.i(ζ) Also he needs compute a few values off all these data. First, he computes the PI matrix with the public inputs and outputs. He needs to compute pi(ζ), where pi is the interpolation of PI at the domain H. But he doesn't need to compute pi. He can instead compute pi(ζ) as i=0∑n​Li​(ζ)(PI)i​, where n is the number of public inputs and Li​ is the Lagrange basis at the domain H. Compute claimed values of p(ζ) and t(ζ) He computes pˉ​c​:=pi(ζ)+αzˉω​(cˉ+γ)(aˉ+βsˉσ1​+γ)(bˉ+βsˉσ2​+γ)−α2L1​(ζ) This is the constant part of the linearization of p. So adding it to what the prover claims to be pˉ​nc​, he obtains p(ζ)=pˉ​c​+pˉ​nc​ With respect to t(ζ), this is actually already tˉ. Compute [tpartial​]1​ and [pnc​]1​ He computes these off the commitments in the proof as follows [tpartial​]1​=[tlo​]1​+ζN+2[tmid​]1​+ζ2(N+2)[thi​]1​ For [pnc​]1​, first compute [p^​nc1​]1​[p^​nc2​]1​[p^​nc3​]1​​=aˉ[qL​]1​+bˉ[qR​]1​+(aˉbˉ)[qM​]1​+cˉ[qO​]1​+[qC​]1​=(aˉ+βζ+γ)(bˉ+βk1​ζ+γ)(cˉ+βk2​ζ+γ)[z]1​−(aˉ+βsˉσ1​+γ)(bˉ+βsˉσ2​+γ)βzˉω​[Sσ3​]1​=L1​(ζ)[z]1​​ Then [pnc​]1​=[pnc1​]1​+[pnc2​]1​+[pnc3​]1​. Compute claimed value fbatch​(ζ) and [fbatch​]1​ Compute fbatch​(ζ) as fbatch​(ζ)=tˉ+υpˉ​nc​+υ2aˉ+υ3bˉ+υ4cˉ+υ5sˉσ1​+υ6sˉσ2​ Also, the commitment of the polynomial fbatch​ is [fbatch​]1​=[tpartial​]1​+υ[pnc​]1​+υ2[a]1​+υ3[b]1​+υ4[c]1​+υ5[Sσ1​]1​+υ6[Sσ2​]1​","breadcrumbs":"Plonk » Protocol » Extraction of values and commitments","id":"32","title":"Extraction of values and commitments"},"33":{"body":"Now the verifier has all the necessary values to proceed with the checks. Check that p(ζ) equals (ζN−1)t(ζ). Verify the opening of fbatch​ at ζ. That is, check that Verify([fbatch​]1​,πbatch​,ζ,fbatch​(ζ)) outputs Accept . Verify the opening of z at ζω. That is, check the validity of the proof πsingle​ using the commitment [z]1​ and the value zˉω​. That is, check that Verify([z]1​,πsingle​,ζω,zˉω​) outputs Accept . If all checks pass, he outputs Accept . Otherwise outputs Reject .","breadcrumbs":"Plonk » Protocol » Proof check","id":"33","title":"Proof check"},"34":{"body":"In this section we discuss the implementation details of the PLONK algorithm. We use the notation and terminology of the protocol and recap sections. At the moment our API supports the backend of PLONK, that is, all the setup, prove and verify algorithms. We temporarily rely on external sources for the definition of a circuit and the creation of the Q and V matrices, as well as the execution of it to obtain the trace matrix T. We mainly use gnark temporarily for that purpose. To generate proofs and validate them, we need to feed the algorithms with precomputed values of the Q, V and T matrices, and the primitive root of unity ω. Let's see our API on a test circuit that provides all these values. The program in this case is the one that takes an input x, a private input e and computes y=xe+5. As in the toy example of the recap, the output of the program is added to the public inputs and the circuit actually asserts that the output is the claimed value. So more precisely, the prover will generate a proof for the statement ASSERT(x*e+5==y), where both x,y are public inputs.","breadcrumbs":"Plonk » Implementation » Implementation","id":"34","title":"Implementation"},"35":{"body":"Here is the happy path. // This is the common preprocessed input for\n// the test circuit ( ASSERT(x * e + 5 == y) )\nlet common_preprocessed_input = test_common_preprocessed_input_2(); // Input\nlet x = FieldElement::from(2_u64); // Private input\nlet e = FieldElement::from(3_u64); let y, witness = test_witness_2(x, e); let srs = test_srs(common_preprocessed_input.n);\nlet kzg = KZG::new(srs); let verifying_key = setup(&common_preprocessed_input, &kzg); let random_generator = TestRandomFieldGenerator {};\nlet prover = Prover::new(kzg.clone(), random_generator); let public_input = vec![x.clone(), y]; let proof = prover.prove( &witness, &public_input, &common_preprocessed_input, &verifying_key,\n); let verifier = Verifier::new(kzg);\nassert!(verifier.verify( &proof, &public_input, &common_preprocessed_input, &verifying_key\n)); Let's brake it down. The helper function test_common_preprocessed_input_2() returns an instance of the following struct for the particular test circuit: pub struct CommonPreprocessedInput { pub n: usize, pub domain: Vec>, pub omega: FieldElement, pub k1: FieldElement, pub ql: Polynomial>, pub qr: Polynomial>, pub qo: Polynomial>, pub qm: Polynomial>, pub qc: Polynomial>, pub s1: Polynomial>, pub s2: Polynomial>, pub s3: Polynomial>, pub s1_lagrange: Vec>, pub s2_lagrange: Vec>, pub s3_lagrange: Vec>,\n} Apart from the eight polynomials in the canonical basis, we store also here the number of constraints n, the domain H, the primitive n-th of unity ω and the element k1​. The element k2​ will be k12​. For convenience, we also store the polynomials Sσi​ in Lagrange form. The following lines define the particular values of the program input x and the private input e. // Input\nlet x = FieldElement::from(2_u64); // Private input\nlet e = FieldElement::from(3_u64);\nlet y, witness = test_witness_2(x, e); The function test_witness_2(x, e) returns an instance of the following struct, that holds the polynomials that interpolate the columns A,B,C of the trace matrix T. pub struct Witness { pub a: Vec>, pub b: Vec>, pub c: Vec>,\n} Next the commitment scheme KZG (Kate-Zaverucha-Goldberg) is instantiated. let srs = test_srs(common_preprocessed_input.n);\nlet kzg = KZG::new(srs); The setup function performs the setup phase. It only needs the common preprocessed input and the commitment scheme. let verifying_key = setup(&common_preprocessed_input, &kzg); It outputs an instance of the struct VerificationKey: pub struct VerificationKey { pub qm_1: G1Point, pub ql_1: G1Point, pub qr_1: G1Point, pub qo_1: G1Point, pub qc_1: G1Point, pub s1_1: G1Point, pub s2_1: G1Point, pub s3_1: G1Point,\n} It stores the commitments of the eight polynomials of the common preprocessed input. The suffix _1 means it is a commitment. It comes from the notation [f]1​, where f is a polynomial. Then a prover is instantiated let random_generator = TestRandomFieldGenerator {};\nlet prover = Prover::new(kzg.clone(), random_generator); The prover is an instance of the struct Prover: pub struct Prover\nwhere F: IsField, CS: IsCommitmentScheme, R: IsRandomFieldElementGenerator { commitment_scheme: CS, random_generator: R, phantom: PhantomData,\n} It stores an instance of a commitment scheme and a random field element generator needed for blinding polynomials. Then the public input is defined. As we mentioned in the recap, the public input contains the output of the program. let public_input = vec![x.clone(), y]; We then generate a proof using the prover's method prove let proof = prover.prove( &witness, &public_input, &common_preprocessed_input, &verifying_key,\n); The output is an instance of the struct Proof. pub struct Proof> { // Round 1. /// Commitment to the wire polynomial `a(x)` pub a_1: CS::Commitment, /// Commitment to the wire polynomial `b(x)` pub b_1: CS::Commitment, /// Commitment to the wire polynomial `c(x)` pub c_1: CS::Commitment, // Round 2. /// Commitment to the copy constraints polynomial `z(x)` pub z_1: CS::Commitment, // Round 3. /// Commitment to the low part of the quotient polynomial t(X) pub t_lo_1: CS::Commitment, /// Commitment to the middle part of the quotient polynomial t(X) pub t_mid_1: CS::Commitment, /// Commitment to the high part of the quotient polynomial t(X) pub t_hi_1: CS::Commitment, // Round 4. /// Value of `a(ζ)`. pub a_zeta: FieldElement, /// Value of `b(ζ)`. pub b_zeta: FieldElement, /// Value of `c(ζ)`. pub c_zeta: FieldElement, /// Value of `S_σ1(ζ)`. pub s1_zeta: FieldElement, /// Value of `S_σ2(ζ)`. pub s2_zeta: FieldElement, /// Value of `z(ζω)`. pub z_zeta_omega: FieldElement, // Round 5 /// Value of `p_non_constant(ζ)`. pub p_non_constant_zeta: FieldElement, /// Value of `t(ζ)`. pub t_zeta: FieldElement, /// Batch opening proof for all the evaluations at ζ pub w_zeta_1: CS::Commitment, /// Single opening proof for `z(ζω)`. pub w_zeta_omega_1: CS::Commitment,\n} Finally, we instantiate a verifier. let verifier = Verifier::new(kzg); It's an instance of Verifier: struct Verifier> { commitment_scheme: CS, phantom: PhantomData,\n} Finally, we call the verifier's method verify that outputs a bool. assert!(verifier.verify( &proof, &public_input, &common_preprocessed_input, &verifying_key\n));","breadcrumbs":"Plonk » Implementation » Usage","id":"35","title":"Usage"},"36":{"body":"All the matrices Q,V,T,PI are padded with dummy rows so that their length is a power of two. To be able to interpolate their columns, we need a primitive root of unity ω of that order. Given the particular field used in our implementation, that means that the maximum possible size for a circuit is 232. The entries of the dummy rows are filled in with zeroes in the Q, V and PI matrices. The T matrix needs to be consistent with the V matrix. Therefore it is filled with the value of the variable with index 0. Some other rows in the V matrix have also dummy values. These are the rows corresponding to the B and C columns of the public input rows. In the recap we denoted them with the empty - symbol. They are filled in with the same logic as the padding rows, as well as the corresponding values in the T matrix.","breadcrumbs":"Plonk » Implementation » Padding","id":"36","title":"Padding"},"37":{"body":"The implementation pretty much follows the rounds as are described in the protocol section. There are a few details that are worth mentioning.","breadcrumbs":"Plonk » Implementation » Implementation details","id":"37","title":"Implementation details"},"38":{"body":"The commitment scheme we use is the Kate-Zaverucha-Goldberg scheme with the BLS 12 381 curve and the ate pairing. It can be found in the commitments module of the lambdaworks_crypto package. The order r of the cyclic subgroup is 0x73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001 The maximum power of two that divides r−1 is 232. Therefore, that is the maximum possible order for a primitive root of unity in Fr​ with order a power of two.","breadcrumbs":"Plonk » Implementation » Commitment Scheme","id":"38","title":"Commitment Scheme"},"39":{"body":"","breadcrumbs":"Plonk » Implementation » Fiat-Shamir","id":"39","title":"Fiat-Shamir"},"4":{"body":"PLONK is a popular cryptographic proving system within the Zero Knowledge (ZK) community due to its efficiency and flexibility. It enables the verification of complex computations executed by untrusted parties through the transformation of programs into circuit representations. The system relies on a process called arithmetization, which converts logical circuits into polynomial representations. The main idea behind arithmetization is to express the computation as a set of polynomial equations. The solutions to these equations correspond to the outputs of the circuit. In this section, we will delve into the mechanics of how arithmetization works in PLONK, as well as the protocol used to generate and verify proofs. The paper can be found here","breadcrumbs":"Plonk » Recap » PLONK","id":"4","title":"PLONK"},"40":{"body":"Here we describe our implementation of the transcript used for the Fiat-Shamir heuristic. A Transcript exposes two methods: append and challenge. The method append adds a message to the transcript by updating the internal state of the hasher with the raw bytes of the message. The method challenge returns the result of the hasher using the current internal state of the hasher. It subsequently resets the hasher and updates the internal state with the last result. Here is an example of this process: Start a fresh transcript. Call append and pass message_1. Call append and pass message_2. The internal state of the hasher at this point is message_2 || message_1. Call challenge. The output is Hash(message_2 || message_1). Call append and pass message_3. Call challenge. The output is Hash(message_3 || Hash(message_2 || message_1)). Call append and pass message_4. The internal state of the hasher at the end of this exercise is message_4 || Hash(message_3 || Hash(message_2 || message_1)) The underlying hasher function we use is h=sha3.","breadcrumbs":"Plonk » Implementation » Transcript strategy","id":"40","title":"Transcript strategy"},"41":{"body":"The result of every challenge is a 256-bit string, which is interpreted as an integer in big-endian order. A field element is constructed out of it by taking modulo the field order. The prime field used in this implementation has a 255-bit order. Therefore some field elements are more probable to occur than others because they have more representatives as 256-bit integers.","breadcrumbs":"Plonk » Implementation » Field elements","id":"41","title":"Field elements"},"42":{"body":"The first messages added to the transcript are all commitments of the polynomials of the common preprocessed input and the values of the public inputs. This prevents a known vulnerability called \"weak Fiat-Shamir\". Check out the following resources to learn more about it. What can go wrong (zkdocs) How not to Prove Yourself: Pitfalls of the Fiat-Shamir Heuristic and Applications to Helios Weak Fiat-Shamir Attacks on Modern Proof Systems","breadcrumbs":"Plonk » Implementation » Strong Fiat-Shamir","id":"42","title":"Strong Fiat-Shamir"},"43":{"body":"In this section, we'll discuss how to build your own constraint system to prove the execution of a particular program.","breadcrumbs":"Plonk » Circuit API » Circuit API","id":"43","title":"Circuit API"},"44":{"body":"Let's take the following simple program as an example. We have two public inputs: x and y. We want to prove to a verifier that we know a private input e such that x * e = y. You can achieve this by building the following constraint system: use lambdaworks_plonk::constraint_system::ConstraintSystem;\nuse lambdaworks_math::elliptic_curve::short_weierstrass::curves::bls12_381::default_types::FrField; fn main() { let system = &mut ConstraintSystem::::new(); let x = system.new_public_input(); let y = system.new_public_input(); let e = system.new_variable(); let z = system.mul(&x, &e); // This constraint system asserts that x * e == y system.assert_eq(&y, &z);\n} This code creates a constraint system over the field of the BLS12381 curve. Then, it creates three variables: two public inputs x and y, and a private variable e. Note that every variable is private except for the public inputs. Finally, it adds the constraints that represent a multiplication and an assertion. Before generating proofs for this system, we need to run a setup and obtain a verifying key: let common = CommonPreprocessedInput::from_constraint_system(&system, &ORDER_R_MINUS_1_ROOT_UNITY);\nlet srs = test_srs(common.n);\nlet kzg = KZG::new(srs); // The commitment scheme for plonk.\nlet vk = setup(&common, &kzg); Now we can generate proofs for our system. We just need to specify the public inputs and obtain a witness that is a solution for our constraint system: let inputs = HashMap::from([(x, FieldElement::from(4)), (e, FieldElement::from(3))]);\nlet assignments = system.solve(inputs).unwrap();\nlet witness = Witness::new(assignments, &system); Once you have all these ingredients, you can call the prover: let public_inputs = system.public_input_values(&assignments);\nlet prover = Prover::new(kzg.clone(), TestRandomFieldGenerator {});\nlet proof = prover.prove(&witness, &public_inputs, &common, &vk); and verify: let verifier = Verifier::new(kzg);\nassert!(verifier.verify(&proof, &public_inputs, &common, &vk));","breadcrumbs":"Plonk » Circuit API » Simple Example","id":"44","title":"Simple Example"},"45":{"body":"Some operations are common, and it makes sense to wrap the set of constraints that do these operations in a function and use it several times. Lambdaworks comes with a collection of functions to help you build your own constraint systems, such as conditionals, inverses, and hash functions. However, if you have an operation that does not come with Lambdaworks, you can easily extend Lambdaworks functionality. Suppose that the exponentiation operation is something common in your program. You can write the square and multiply algorithm and put it inside a function: pub fn pow( system: &mut ConstraintSystem, base: Variable, exponent: Variable,\n) -> Variable { let exponent_bits = system.new_u32(&exponent); let mut result = system.new_constant(FieldElement::one()); for i in 0..32 { if i != 0 { result = system.mul(&result, &result); } let result_times_base = system.mul(&result, &base); result = system.if_else(&exponent_bits[i], &result_times_base, &result); } result\n} This function can then be used to modify our simple program from the previous section. The following circuit checks that the prover knows e such that pow(x, e) = y: use lambdaworks_plonk::constraint_system::ConstraintSystem;\nuse lambdaworks_math::elliptic_curve::short_weierstrass::curves::bls12_381::default_types::FrField; fn main() { let system = &mut ConstraintSystem::::new(); let x = system.new_public_input(); let y = system.new_public_input(); let e = system.new_variable(); let z = pow(system, &x, &e); system.assert_eq(&y, &z);\n} You can keep composing these functions in order to create more complex systems.","breadcrumbs":"Plonk » Circuit API » Building Complex Systems","id":"45","title":"Building Complex Systems"},"46":{"body":"The goal of this document is to give a good a understanding of our stark prover code. To this end, in the first section we go through a recap of how the proving system works at a high level mathematically; then we dive into how that's actually implemented in our code.","breadcrumbs":"STARKs » STARK Prover","id":"46","title":"STARK Prover"},"47":{"body":"","breadcrumbs":"STARKs » Recap » STARKs Recap","id":"47","title":"STARKs Recap"},"48":{"body":"In general, we express computation in our proving system by providing an execution trace satisfying certain constraints . The execution trace is a table containing the state of the system at every step of computation. This computation needs to follow certain rules to be valid; these rules are our constraints . The constraints for our computation are expressed using an Algebraic Intermediate Representation or AIR. This representation uses polynomials to encode constraints, which is why sometimes they are called polynomial constraints. To make all this less abstract, let's go through two examples.","breadcrumbs":"STARKs » Recap » Verifying Computation through Polynomials","id":"48","title":"Verifying Computation through Polynomials"},"49":{"body":"Throughout this section and the following we will use this example extensively to have a concrete example. Even though it's a bit contrived (no one cares about computing fibonacci numbers), it's simple enough to be useful. STARKs and proving systems in general are very abstract things; having an example in mind is essential to not get lost. Let's say our computation consists of calculating the k-th number in the fibonacci sequence. This is just the sequence of numbers \\(a_n\\) satisfying \\[ a_0 = 1 \\] \\[ a_1 = 1 \\] \\[ a_{n+2} = a_{n + 1} + a_n \\] An execution trace for this just consists of a table with one column, where each row is the i-th number in the sequence: a_i 1 1 2 3 5 8 13 21 A valid trace for this computation is a table satisfying two things: The first two rows are 1. The value on any other row is the sum of the two preceding ones. The first item is called a boundary constraint, it just enforces specific values on the trace at certain points. The second one is a transition constraint; it tells you how to go from one step of computation to the next.","breadcrumbs":"STARKs » Recap » Fibonacci numbers","id":"49","title":"Fibonacci numbers"},"5":{"body":"We use the following notation. The symbol F denotes a finite field. It is fixed all along. The symbol ω denotes a primitive root of unity in F. All polynomials have coefficients in F and the variable is usually denoted by X. We denote polynomials by single letters like p,a,b,z. We only denote them as z(X) when we want to emphasize the fact that it is a polynomial in X, or we need that to explicitly define a polynomial from another one. For example when composing a polynomial z with the polynomial ωX, the result being denoted by z′:=z(ωX). The symbol ′ is not used to denote derivatives. When interpolating at a domain H={h0​,…,hn​}⊂F, the symbols Li​ denote the Lagrange basis. That is Li​ is the polynomial such that Li​(hj​)=0 for all j=i, and that Li​(hi​)=1. If M is a matrix, then Mi,j​ denotes the value at the row i and column j.","breadcrumbs":"Plonk » Recap » Notation","id":"5","title":"Notation"},"50":{"body":"The example above is extremely useful to have a mental model, but it's not really useful for anything else. The problem is it just works for the very narrow example of computing fibonacci numbers. If we wanted to prove execution of something else, we would have to write an AIR for it. What we're actually aiming for is an AIR for an entire general purpose Virtual Machine. This way, we can provide proofs of execution for any computation using just one AIR. This is what cairo as a programming language does. Cairo code compiles to the bytecode of a virtual machine with an already defined AIR. The general flow when using cairo is the following: User writes a cairo program. The program is compiled into Cairo's VM bytecode. The VM executes said code and provides an execution trace for it. The trace is passed on to a STARK prover, which creates a proof of correct execution according to Cairo's AIR. The proof is passed to a verifier, who checks that the proof is valid. Ultimately, our goal is to give the tools to write a STARK prover for the cairo VM and do so. However, this is not a good example to start out as it's incredibly complex. The execution trace of a cairo program has around 30 columns, some for general purpose registers, some for other reasons. Cairo's AIR contains a lot of different transition constraints, encoding all the different possible instructions (arithmetic operations, jumps, etc). Use the fibonacci example as your go-to for understanding all the moving parts; keep the Cairo example in mind as the thing we are actually building towards.","breadcrumbs":"STARKs » Recap » Cairo","id":"50","title":"Cairo"},"51":{"body":"Below we go through a step by step explanation of a STARK prover. We will assume the trace of the fibonacci sequence mentioned above; it consists of only one column of length \\(2^n\\). In this case, we'll take n=3. The trace looks like this a_i a_0 a_1 a_2 a_3 a_4 a_5 a_6 a_7","breadcrumbs":"STARKs » Recap » Fibonacci step by step walkthrough","id":"51","title":"Fibonacci step by step walkthrough"},"52":{"body":"The first step is to interpolate these values to generate the trace polynomial. This will be a polynomial encoding all the information about the trace. The way we do it is the following: in the finite field we are working in, we take an 8-th primitive root of unity, let's call it g. It being a primitive root means two things: g is an 8-th root of unity, i.e., \\(g^8 = 1\\). Every 8-th root of unity is of the form \\(g^i\\) for some \\(0 \\leq i \\leq 7\\). With g in hand, we take the trace polynomial t to be the one satisfying t(gi)=ai​ From here onwards, we will talk about the validity of the trace in terms of properties that this polynomial must satisfy. We will also implicitly identify a certain power of \\(g\\) with its corresponding trace element, so for example we sometimes think of \\(g^5\\) as \\(a_5\\), the fifth row in the trace, even though technically it's \\(t\\) evaluated in \\(g^5\\) that equals \\(a_5\\). We talked about two different types of constraints the trace must satisfy to be valid. They were: The first two rows are 1. The value on any other row is the sum of the two preceding ones. In terms of t, this translates to \\(t(g^0) = 1\\) and \\(t(g) = 1\\). \\(t(x g^2) - t(xg) - t(x) = 0\\) for all \\(x \\in {g^0, g^1, g^2, g^3, g^4, g^5}\\). This is because multiplying by g is the same as advancing a row in the trace.","breadcrumbs":"STARKs » Recap » Trace polynomial","id":"52","title":"Trace polynomial"},"53":{"body":"To convince the verifier that the trace polynomial satisfies the relationships above, the prover will construct another polynomial that shows that both the boundary and transition constraints are satisfied and commit to it. We call this polynomial the composition polynomial, and usually denote it with \\(H\\). Constructing it involves a lot of different things, so we'll go step by step introducing all the moving parts required.","breadcrumbs":"STARKs » Recap » Composition Polynomial","id":"53","title":"Composition Polynomial"},"54":{"body":"To show that the boundary constraints are satisfied, we construct the boundary polynomial. Recall that our boundary constraints are \\(t(g^0) = t(g) = 1\\). Let's call \\(P\\) the polynomial that interpolates these constraints, that is, \\(P\\) satisfies: P(1)=1P(g)=1 The boundary polynomial \\(B\\) is defined as follows: B(x)=(x−1)(x−g)t(x)−P(x)​ The denominator here is called the boundary zerofier, and it's the polynomial whose roots are the elements of the trace where the boundary constraints must hold. How does \\(B\\) encode the boundary constraints? The idea is that, if the trace satisfies said constraints, then t(1)−P(1)=1−1=0 t(g)−P(g)=1−1=0 so \\(t(x) - P(x)\\) has \\(1\\) and \\(g\\) as roots. Showing these values are roots is the same as showing that \\(B(x)\\) is a polynomial instead of a rational function, and that's why we construct \\(B\\) this way.","breadcrumbs":"STARKs » Recap » Boundary polynomial","id":"54","title":"Boundary polynomial"},"55":{"body":"To convince the verifier that the transition constraints are satisfied, we construct the transition constraint polynomial and call it \\(C(x)\\). It's defined as follows: C(x)=∏i=05​(x−gi)t(xg2)−t(xg)−t(x)​ How does \\(C\\) encode the transition constraints? We mentioned above that these are satisfied if the polynomial in the numerator vanishes in the elements \\({g^0, g^1, g^2, g^3, g^4, g^5}\\). As with \\(B\\), this is the same as showing that \\(C(x)\\) is a polynomial instead of a rational function.","breadcrumbs":"STARKs » Recap » Transition constraint polynomial","id":"55","title":"Transition constraint polynomial"},"56":{"body":"With the boundary and transition constraint polynomials in hand, we build the composition polynomial \\(H\\) as follows: The verifier will sample four numbers \\(\\beta_1, \\beta_2\\) and \\(H\\) will be H(x)=β1​B(x)+β2​C(x) Why not just take \\(H(x) = B(x) + C(x)\\)? The reason for the betas is to make the resulting \\(H\\) be always different and unpredictable for the prover, so they can't precompute stuff beforehand. With what we discussed above, showing that the constraints are satisfied is equivalent to saying that H is a polynomial and not a rational function (we are simplifying things a bit here, but it works for our purposes).","breadcrumbs":"STARKs » Recap » Constructing \\(H\\)","id":"56","title":"Constructing \\(H\\)"},"57":{"body":"To show \\(H\\) is a polynomial we are going to use the FRI protocol, which we treat as a black box. For all we care, a FRI proof will verify if what we committed to is indeed a polynomial. Thus, the prover will provide a FRI commitment to H, and if it passes, the verifier will be convinced that the constraints are satisfied. There is one catch here though: how does the verifier know that FRI was applied to H and not any other polynomial? For this we need to add an additional step to the protocol.","breadcrumbs":"STARKs » Recap » Commiting to \\(H\\)","id":"57","title":"Commiting to \\(H\\)"},"58":{"body":"After commiting to H, the prover needs to show that H was constructed correctly according to the formula above. To do this, it will ask the prover to provide an evaluation of H on some random point z and evaluations of the trace at the points \\(t(z), t(zg)\\) and \\(t(zg^2)\\). Because the boundary and transition constraints are a public part of the protocol, the verifier knows them, and thus the only thing it needs to compute the evaluation \\((z)\\) by itself are the three trace evaluations mentioned above. Because it asked the prover for them, it can check both sides of the equation: H(z)=β1​B(z)+β2​C(z) and be convinced that \\(H\\) was constructed correctly. We are still not done, however, as the prover could have now cheated on the values of the trace or composition polynomial evaluations.","breadcrumbs":"STARKs » Recap » Consistency check","id":"58","title":"Consistency check"},"59":{"body":"There are two things left the prover needs to show to complete the proof: That \\(H\\) effectively is a polynomial, i.e., that the constraints are satisfied. That the evaluations the prover provided on the consistency check were indeed evaluations of the trace polynomial and composition polynomial on the out of domain point z. Earlier we said we would use the FRI protocol to commit to H and show the first item in the list. However, we can slightly modify the polynomial we do FRI on to show both the first and second items at the same time. This new modified polynomial is called the DEEP composition polynomial. We define it as follows: Deep(x)=γ1​x−zH(x)−H(z)​+γ2​x−zt(x)−t(z)​+γ3​x−zgt(x)−t(zg)​+γ4​x−zg2t(x)−t(zg2)​ where the numbers \\(\\gamma_i\\) are randomly sampled by the verifier. The high level idea is the following: If we apply FRI to this polynomial and it verifies, we are simultaneously showing that \\(H\\) is a polynomial and the prover indeed provided H(z) as one of the out of domain evaluations. This is the first summand in Deep(x). The trace evaluations provided by the prover were the correct ones, i.e., they were \\(t(z)\\), \\(t(zg)\\), and \\(t(zg^2)\\). These are the remaining summands of the Deep(x).","breadcrumbs":"STARKs » Recap » Deep Composition Polynomial","id":"59","title":"Deep Composition Polynomial"},"6":{"body":"","breadcrumbs":"Plonk » Recap » The ideas and components","id":"6","title":"The ideas and components"},"60":{"body":"The prover needs to show that Deep was constructed correctly according to the formula above. To do this, the verifier will ask the prover to provide: An evaluation of H on z and x_0 Evaluations of the trace at the points \\(t(z)\\), \\(t(zg)\\), \\(t(zg^2)\\) and \\(t(x_0)\\) Where z is the same random, out of domain point used in the consistency check of the composition polynomial, and x_0 is a random point that belongs to the trace domain. With the values provided by the prover, the verifier can check both sides of the equation: Deep(x0​)=γ1​x0​−zH(x0​)−H(z)​+γ2​x0​−zt(x0​)−t(z)​+γ3​x0​−zgt(x0​)−t(zg)​+γ4​x0​−zg2t(x0​)−t(zg2)​ The prover also needs to show that the trace evaluation \\(t(x_0)\\) belongs to the trace. To achieve this, it needs to commit the merkle roots of t and the merkle proof of \\(t(x_0)\\).","breadcrumbs":"STARKs » Recap » Consistency check","id":"60","title":"Consistency check"},"61":{"body":"We summarize below the steps required in a STARK proof for both prover and verifier.","breadcrumbs":"STARKs » Recap » Summary","id":"61","title":"Summary"},"62":{"body":"Compute the trace polynomial t by interpolating the trace column over a set of \\(2^n\\)-th roots of unity \\({g^i : 0 \\leq i < 2^n}\\). Compute the boundary polynomial B. Compute the transition constraint polynomial C. Construct the composition polynomial H from B and C. Sample an out of domain point z and provide the evaluations \\(H(z)\\), \\(t(z)\\), \\(t(zg)\\), and \\(t(zg^2)\\) to the verifier. Sample a domain point x_0 and provide the evaluations \\(H(x_0)\\) and \\(t(x_0)\\) to the verifier. Construct the deep composition polynomial Deep(x) from H, t, and the evaluations from the item above. Do FRI on Deep(x) and provide the resulting FRI commitment to the verifier. Provide the merkle root of t and the merkle proof of \\(t(x_0)\\).","breadcrumbs":"STARKs » Recap » Prover side","id":"62","title":"Prover side"},"63":{"body":"Take the evaluations \\(H(z)\\), \\(H(x_0)\\), \\(t(z)\\), \\(t(zg)\\), \\(t(zg^2)\\) and \\(t(x_0)\\) the prover provided. Reconstruct the evaluations \\(B(z)\\) and \\(C(z)\\) from the trace evaluations we were given. Check that the claimed evaluation \\(H(z)\\) the prover gave us actually satisfies H(z)=β1​B(z)+β2​C(z) Check that the claimed evaluation \\(Deep(x_0)\\) the prover gave us actually satisfies Deep(x0​)=γ1​x0​−zH(x0​)−H(z)​+γ2​x0​−zt(x0​)−t(z)​+γ3​x0​−zgt(x0​)−t(zg)​+γ4​x0​−zg2t(x0​)−t(zg2)​ Using the merkle root and the merkle proof the prover provided, check that \\(t(x_0)\\) belongs to the trace. Take the provided FRI commitment and check that it verifies.","breadcrumbs":"STARKs » Recap » Verifier side","id":"63","title":"Verifier side"},"64":{"body":"The walkthrough above was for the fibonacci example which, because of its simplicity, allowed us to sweep under the rug a few more complexities that we'll have to tackle on the implementation side. They are:","breadcrumbs":"STARKs » Recap » Simplifications and Omissions","id":"64","title":"Simplifications and Omissions"},"65":{"body":"Our trace contained only one column, but in the general setting there can be multiple (the Cairo AIR has around 30). This means there isn't just one trace polynomial, but several; one for each column. This also means there are multiple boundary constraint polynomials. The general idea, however, remains the same. The deep composition polynomial H is now the sum of several terms containing the boundary constraint polynomials \\(B_1(x), \\dots, B_k(x)\\) (one per column), and each \\(B_i\\) is in turn constructed from the \\(i\\)-th trace polynomial \\(t_i(x)\\).","breadcrumbs":"STARKs » Recap » Multiple trace columns","id":"65","title":"Multiple trace columns"},"66":{"body":"Much in the same way, our fibonacci AIR had only one transition constraint, but there could be several. We will therefore have multiple transition constraint polynomials \\(C_1(x), \\dots, C_n(x)\\), each of which encodes a different relationship between rows that must be satisfied. Also, because there are multiple trace columns, a transition constraint can mix different trace polynomials. One such constraint could be C1​(x)=t1​(gx)−t2​(x) which means \"The first column on the next row has to be equal to the second column in the current row\". Again, even though this seems way more complex, the ideas remain the same. The composition polynomial H will now include a term for every \\(C_i(x)\\), and for each one the prover will have to provide out of domain evaluations of the trace polynomials at the appropriate values. In our example above, to perform the consistency check on \\(C_1(x)\\) the prover will have to provide the evaluations \\(t_1(zg)\\) and \\(t_2(z)\\).","breadcrumbs":"STARKs » Recap » Multiple transition constraints","id":"66","title":"Multiple transition constraints"},"67":{"body":"In the actual implementation, we won't commit to \\(H\\), but rather to a decomposition of \\(H\\) into an even term \\(H_1(x)\\) and an odd term \\(H_2(x)\\), which satisfy H(x)=H1​(x2)+xH2​(x2) This way, we don't commit to \\(H\\) but to \\(H_1\\) and \\(H_2\\). This is just an optimization at the code level; once again, the ideas remain exactly the same.","breadcrumbs":"STARKs » Recap » Composition polynomial decomposition","id":"67","title":"Composition polynomial decomposition"},"68":{"body":"We treated FRI as a black box entirely. However, there is one thing we do need to understand about it: low degree extensions. When applying FRI to a polynomial of degree \\(n\\), we need to provide evaluations of it over a domain with more than \\(n\\) points. In our case, the DEEP composition polynomial's degree is around the same as the trace's, which is, at most, \\(2^n - 1\\) (because it interpolates the trace containing \\(2^n\\) points). The domain we are going to choose to evaluate our DEEP polynomial on will be a set of higher roots of unity. In our fibonacci example, we will take a primitive \\(16\\)-th root of unity \\(\\omega\\). As a reminder, this means: \\(\\omega\\) is an \\(16\\)-th root of unity, i.e., \\(\\omega^{16} = 1\\). Every \\(16\\)-th root of unity is of the form \\(\\omega^i\\) for some \\(0 \\leq i \\leq 15\\). Additionally, we also take it so that \\(\\omega\\) satisfies \\(\\omega^2 = g\\) (\\(g\\) being the \\(8\\)-th primitive root of unity we used to construct t). The evaluation of \\(t\\) on the set \\({\\omega^i : 0 \\leq i \\leq 15}\\) is called a low degree extension (LDE) of \\(t\\). Notice this is not a new polynomial, they're evaluations of \\(t\\) on some set of points. Also note that, because \\(\\omega^2 = g\\), the LDE contains all the evaluations of \\(t\\) on the set of powers of \\(g\\). In fact, {t(ω2i):0≤i≤15}={t(gi):0≤i≤7} This will be extremely important when we get to implementation. For our LDE, we chose \\(16\\)-th roots of unity, but we could have chosen any other power of two greater than \\(8\\). In general, this choice is called the blowup factor, so that if the trace has \\(2^n\\) elements, a blowup factor of \\(b\\) means our LDE evaluates over the \\(2^{n} * b\\) roots of unity (\\(b\\) needs to be a power of two). The blowup factor is a parameter of the protocol related to its security.","breadcrumbs":"STARKs » Recap » FRI, low degree extensions and roots of unity","id":"68","title":"FRI, low degree extensions and roots of unity"},"69":{"body":"In this section, we start diving deeper before showing the formal protocol. If you haven't done so, we recommend reading the \"Recap\" section first. At a high level, the protocol works as follows. The starting point is a matrix T that encodes the trace of a valid execution of the program. This matrix needs to be in a particular format so that its correctness is equivalent to checking a finite number of polynomial equations on its rows. Transforming the execution to this matrix is what's called the arithmetization process. Then a single polynomial F is constructed that encodes the set of all the polynomial constraints. The satisfiability of all these constraints is equivalent to F being divisible by some public polynomial G. So the prover constructs H as the quotient F/G called the composition polynomial. Then the verifier chooses a random point z and challenges the prover to reveal the values F(z) and H(z). Then the verifier checks that H(z)=F(z)/G(z), which convinces him that the same relation holds at a level of polynomials and, in consequence, convinces the verifier that the private trace T of the prover is valid. In summary, at a very high level, the STARK protocol can be organized into three major parts: Arithmetization and commitment of execution trace. Construction and commitment of composition polynomial H. Opening of polynomials at random z.","breadcrumbs":"STARKs » Protocol overview » Protocol Overview","id":"69","title":"Protocol Overview"},"7":{"body":"For better clarity, we'll be using the following toy program throughout this recap. INPUT: x PRIVATE INPUT: e OUTPUT: e * x + x - 1 The observer would have noticed that this program could also be written as (e+1)∗x−1, which is more sensible. But the way it is written now serves us to better explain the arithmetization of PLONK. So we'll stick to it. The idea here is that the verifier holds some value x, say x=3. He gives it to the prover. She executes the program using her own chosen value e, and sends the output value, say 8, along with a proof π demonstrating correct execution of the program and obtaining the correct output. In the context of PLONK, both the inputs and outputs of the program are considered public inputs . This may sound odd, but it is because these are the inputs to the verification algorithm. This is the algorithm that takes, in this case, the tuple (3,8,π) and outputs Accept if the toy program was executed with input x=3, some private value e not revealed to the verifier, and out came 8. Otherwise it outputs Reject . PLONK can be used to delegate program executions to untrusted parties, but it can also be used as a proof of knowledge. Our program could be used by a prover to demostrate that she knows the multiplicative inverse of some value x in the finite field without revealing it. She would do it by sending the verifier the tuple (x,0,π), where π is the proof of the execution of our toy program. In our toy example this is pointless because inverting field elements is easily performed by any verifier. But change our program to the following and you get proofs of knowledge of the preimage of SHA256 digests. PRIVATE INPUT: e OUTPUT: SHA256(e) Here there's no input aside from the prover's private input. As we mentioned, the output h of the program is then part of the inputs to the verification algorithm. Which in this case just takes (h,π).","breadcrumbs":"Plonk » Recap » Programs. Our toy example","id":"7","title":"Programs. Our toy example"},"70":{"body":"As the Recap mentions, the trace is a table containing the system's state at every step. In this section, we will denote the trace as T. A trace can have several columns to store different aspects or features of a particular state at a specific moment. We will refer to the j-th column as Tj​. You can think of a trace as a matrix T where the entry Tij​ is the j-th element of the i-th state. Most proving systems' primary tool is polynomials over a finite field F. Each column Tj​ of the trace T will be interpreted as evaluations of such a polynomial tj​. Consequently, any information about the states must be encoded somehow as an element in F. To ease notation, we will assume here and in the protocol that the constraints encoding transition rules depend only on a state and the previous one. Everything can be easily generalized to transitions that depend on many preceding states. Then, constraints can be expressed as multivariate polynomials in 2m variables PkT​(X1​,…,Xm​,Y1​,…,Ym​) A transition from state i to state i+1 will be valid if and only if when we plug row i of T in the first m variables and row i+1 in the second m variables of PkT​, we get 0 for all k. In mathematical notation, this is PkT​(Ti,0​,…,Ti,m​,Ti+1,0​,…,Ti+1,m​)=0 for all k These are called transition constraints and check the trace's local properties, where local means relative to specific rows. There is another type of constraint, called boundary constraint , and denoted PjB​. These enforce parts of the trace to take particular values. It is helpful, for example, to verify the initial states. So far, these constraints can only express the local properties of the trace. There are situations where the global properties of the trace need to be checked for consistency. For example, a column may need to take all values in a range but not in any predefined way. Several methods exist to express these global properties as local by adding redundant columns. Usually, they need to involve randomness from the verifier to make sense, and they turn into an interactive protocol called Randomized AIR with Preprocessing .","breadcrumbs":"STARKs » Protocol overview » Arithmetization","id":"70","title":"Arithmetization"},"71":{"body":"To make interactions possible, a crucial cryptographic primitive is the Polynomial Commitment Scheme. This prevents the prover from changing the polynomials to adjust them to what the verifier expects. Such a scheme consists of the commit and the open protocols. STARK uses a univariate polynomial commitment scheme that internally combines a vector commitment scheme and a protocol called FRI. Let's begin with these two components and see how they build up the polynomial commitment scheme.","breadcrumbs":"STARKs » Protocol overview » Polynomial commitment scheme","id":"71","title":"Polynomial commitment scheme"},"72":{"body":"Given a vector Y=(y0​,…,yM​), commiting to Y means the following. The prover builds a Merkle tree out of it and sends its root to the verifier. The verifier can then ask the prover to reveal, or open , the value of the vector Y at some index i. The prover won't have any choice except to send the correct value. The verifier will expect the corresponding value yi​ and the authentication path to the tree's root to check its authenticity. The authentication path also encodes the vector's position i and its length M. The root of the Merkle tree is said to be the commitment of Y, and we denote it here by [Y].","breadcrumbs":"STARKs » Protocol overview » Vector commitments","id":"72","title":"Vector commitments"},"73":{"body":"In STARKs, all commited vectors are of the form Y=(p(d1​),…,p(dM​)) for some polynomial p and some fixed domain D=(d1​,…,dM​). The domain is always known to the prover and the verifier. It can be proved, as long as M is less than the total number of field elements, that every vector (y0​,…,yM​) is equal to (p(d1​),…,p(dM​)) for a unique polynomial p of degree at most M−1. This is called the Lagrange interpolation theorem. It means, there is a unique polynomial of degree at most M−1 such that p(di​)=yi​ for all i. And M−1 is an upper bound to the degree of p. It could be less. For example, the vector of all ones Y=(1,1,…,1) is the evaluation of the constant polynomial p=1, which has degree 0. Suppose the vector Y=(y1​,…,yM​) is the vector of evaluations of a polynomial p of degree strictly less than M−1. Suppose one party holds the vector Y and another party holds only the commitment [Y] of it. The FRI protocol is an efficient interactive protocol with which the former can convince the latter that the commitment they hold corresponds to the vector of evaluations of a polynomial p of degree strictly less than M. More precisely, the protocol depends on the following parameters Powers of two N=2n and M=2m with n":{":":{":":{"df":0,"docs":{},"n":{"df":0,"docs":{},"e":{"df":0,"docs":{},"w":{"df":2,"docs":{"44":{"tf":1.0},"45":{"tf":1.0}}}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}}}}}},"df":0,"docs":{}},"df":0,"docs":{}},"<":{"df":0,"docs":{},"f":{"df":0,"docs":{},"r":{"df":0,"docs":{},"f":{"df":0,"docs":{},"i":{"df":0,"docs":{},"e":{"df":0,"docs":{},"l":{"d":{"df":1,"docs":{"45":{"tf":1.0}}},"df":0,"docs":{}}}}}}}},"df":0,"docs":{}}}}}}}}}}},"df":0,"docs":{},"u":{"c":{"df":0,"docs":{},"t":{"df":30,"docs":{"112":{"tf":1.4142135623730951},"113":{"tf":1.0},"114":{"tf":2.0},"117":{"tf":1.0},"118":{"tf":1.0},"12":{"tf":1.0},"125":{"tf":1.0},"14":{"tf":1.4142135623730951},"142":{"tf":1.4142135623730951},"144":{"tf":1.0},"145":{"tf":1.0},"146":{"tf":2.449489742783178},"16":{"tf":1.4142135623730951},"20":{"tf":1.0},"21":{"tf":1.0},"41":{"tf":1.0},"53":{"tf":1.4142135623730951},"54":{"tf":1.4142135623730951},"55":{"tf":1.0},"56":{"tf":1.0},"58":{"tf":1.4142135623730951},"60":{"tf":1.0},"62":{"tf":1.4142135623730951},"65":{"tf":1.0},"68":{"tf":1.0},"69":{"tf":1.7320508075688772},"84":{"tf":1.4142135623730951},"85":{"tf":1.4142135623730951},"91":{"tf":1.0},"94":{"tf":1.0}}}},"df":0,"docs":{}}}}},"t":{"a":{"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"df":8,"docs":{"13":{"tf":1.0},"146":{"tf":1.0},"35":{"tf":1.0},"48":{"tf":1.0},"50":{"tf":1.0},"65":{"tf":1.4142135623730951},"68":{"tf":1.4142135623730951},"70":{"tf":1.0}}}}},"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"df":0,"docs":{},"t":{"df":1,"docs":{"133":{"tf":1.0}}}},"x":{"df":0,"docs":{},"t":{"df":5,"docs":{"109":{"tf":1.4142135623730951},"14":{"tf":1.0},"7":{"tf":1.0},"80":{"tf":1.0},"84":{"tf":1.0}}}}},"i":{"df":0,"docs":{},"n":{"df":0,"docs":{},"u":{"df":4,"docs":{"102":{"tf":1.0},"130":{"tf":1.0},"134":{"tf":1.0},"146":{"tf":2.0}}}}},"r":{"df":0,"docs":{},"i":{"df":0,"docs":{},"v":{"df":1,"docs":{"49":{"tf":1.0}}}},"o":{"df":0,"docs":{},"l":{"df":1,"docs":{"118":{"tf":1.0}}}}}},"v":{"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"df":0,"docs":{},"i":{"df":2,"docs":{"35":{"tf":1.0},"92":{"tf":1.0}}},"t":{"df":1,"docs":{"95":{"tf":1.0}}}},"r":{"df":0,"docs":{},"s":{"df":1,"docs":{"14":{"tf":1.4142135623730951}}},"t":{"df":2,"docs":{"14":{"tf":1.0},"4":{"tf":1.0}}}}},"i":{"df":0,"docs":{},"n":{"c":{"df":9,"docs":{"21":{"tf":1.4142135623730951},"53":{"tf":1.0},"55":{"tf":1.0},"57":{"tf":1.0},"58":{"tf":1.0},"69":{"tf":1.4142135623730951},"73":{"tf":1.0},"75":{"tf":1.0},"80":{"tf":1.0}}},"df":0,"docs":{}}}}},"o":{"df":0,"docs":{},"r":{"d":{"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"df":4,"docs":{"14":{"tf":1.4142135623730951},"73":{"tf":1.0},"79":{"tf":1.0},"92":{"tf":1.0}}}}},"df":0,"docs":{}}},"p":{"df":0,"docs":{},"i":{"df":3,"docs":{"142":{"tf":1.7320508075688772},"146":{"tf":1.7320508075688772},"35":{"tf":1.0}}},"r":{"df":0,"docs":{},"i":{"df":0,"docs":{},"m":{"df":1,"docs":{"124":{"tf":1.0}}}}}},"r":{"df":0,"docs":{},"e":{"df":1,"docs":{"144":{"tf":1.0}}},"r":{"df":0,"docs":{},"e":{"c":{"df":0,"docs":{},"t":{"df":6,"docs":{"151":{"tf":1.0},"50":{"tf":1.0},"59":{"tf":1.0},"69":{"tf":1.0},"7":{"tf":1.4142135623730951},"72":{"tf":1.0}},"l":{"df":0,"docs":{},"i":{"df":7,"docs":{"104":{"tf":1.0},"117":{"tf":1.0},"146":{"tf":1.0},"151":{"tf":1.0},"58":{"tf":1.4142135623730951},"60":{"tf":1.0},"84":{"tf":1.0}}}}}},"df":0,"docs":{},"s":{"df":0,"docs":{},"p":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"d":{"df":18,"docs":{"10":{"tf":2.0},"108":{"tf":1.0},"11":{"tf":1.4142135623730951},"13":{"tf":1.0},"134":{"tf":1.0},"136":{"tf":1.0},"14":{"tf":1.0},"142":{"tf":1.4142135623730951},"146":{"tf":2.23606797749979},"149":{"tf":1.7320508075688772},"16":{"tf":1.4142135623730951},"23":{"tf":1.0},"36":{"tf":1.4142135623730951},"4":{"tf":1.0},"52":{"tf":1.0},"72":{"tf":1.0},"73":{"tf":1.0},"75":{"tf":1.0}}},"df":0,"docs":{}}}}}}}},"s":{"df":0,"docs":{},"e":{"df":0,"docs":{},"t":{"_":{"df":0,"docs":{},"o":{"df":0,"docs":{},"f":{"df":0,"docs":{},"f":{"df":0,"docs":{},"s":{"df":0,"docs":{},"e":{"df":0,"docs":{},"t":{"df":1,"docs":{"109":{"tf":1.0}}}}}}}}},"df":3,"docs":{"109":{"tf":1.0},"125":{"tf":2.23606797749979},"91":{"tf":1.0}}}},"t":{"df":1,"docs":{"151":{"tf":1.0}},"l":{"df":0,"docs":{},"i":{"df":1,"docs":{"80":{"tf":1.0}}}}}},"u":{"df":0,"docs":{},"l":{"d":{"df":0,"docs":{},"n":{"'":{"df":0,"docs":{},"t":{"df":1,"docs":{"103":{"tf":1.0}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"p":{"df":0,"docs":{},"l":{"df":1,"docs":{"105":{"tf":1.0}}}},"r":{"df":0,"docs":{},"s":{"df":3,"docs":{"12":{"tf":1.0},"125":{"tf":1.0},"14":{"tf":1.0}}}}}},"p":{"df":0,"docs":{},"u":{"_":{"a":{"df":0,"docs":{},"i":{"df":0,"docs":{},"r":{"_":{"d":{"df":0,"docs":{},"e":{"df":0,"docs":{},"f":{"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"df":0,"docs":{},"i":{"df":0,"docs":{},"t":{"df":0,"docs":{},"i":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"1":{"0":{".":{"df":0,"docs":{},"h":{"df":1,"docs":{"127":{"tf":1.0}}},"i":{"df":0,"docs":{},"n":{"df":0,"docs":{},"l":{"df":1,"docs":{"127":{"tf":1.0}}}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}}}}}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":1,"docs":{"3":{"tf":2.0}}}},"r":{"a":{"df":0,"docs":{},"f":{"df":0,"docs":{},"t":{"df":1,"docs":{"79":{"tf":1.0}}}},"t":{"df":0,"docs":{},"e":{"df":1,"docs":{"1":{"tf":1.0}}}}},"df":0,"docs":{},"e":{"a":{"df":0,"docs":{},"t":{"df":5,"docs":{"108":{"tf":1.0},"146":{"tf":1.0},"44":{"tf":1.4142135623730951},"45":{"tf":1.0},"50":{"tf":1.0}},"i":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"df":1,"docs":{"34":{"tf":1.0}}}}}}},"df":0,"docs":{}},"i":{"df":0,"docs":{},"t":{"df":0,"docs":{},"i":{"c":{"df":1,"docs":{"146":{"tf":1.0}}},"df":0,"docs":{}}}},"u":{"c":{"df":0,"docs":{},"i":{"a":{"df":0,"docs":{},"l":{"df":1,"docs":{"71":{"tf":1.0}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"y":{"df":0,"docs":{},"p":{"df":0,"docs":{},"t":{"df":0,"docs":{},"o":{"df":1,"docs":{"1":{"tf":1.0}},"g":{"df":0,"docs":{},"r":{"a":{"df":0,"docs":{},"p":{"df":0,"docs":{},"h":{"df":3,"docs":{"19":{"tf":1.0},"4":{"tf":1.0},"71":{"tf":1.0}}}}},"df":0,"docs":{}}}}}}}},"s":{":":{":":{"c":{"df":0,"docs":{},"o":{"df":0,"docs":{},"m":{"df":0,"docs":{},"m":{"df":0,"docs":{},"i":{"df":0,"docs":{},"t":{"df":1,"docs":{"35":{"tf":3.0}}}}}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":1,"docs":{"35":{"tf":2.449489742783178}}},"u":{"df":0,"docs":{},"m":{"df":1,"docs":{"139":{"tf":1.0}},"u":{"df":0,"docs":{},"l":{"df":3,"docs":{"138":{"tf":1.4142135623730951},"139":{"tf":1.0},"146":{"tf":1.7320508075688772}}}}},"r":{"df":0,"docs":{},"r":{"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"df":0,"docs":{},"t":{"df":7,"docs":{"107":{"tf":2.23606797749979},"109":{"tf":1.0},"121":{"tf":1.0},"133":{"tf":1.0},"40":{"tf":1.0},"66":{"tf":1.0},"84":{"tf":1.0}}}}}},"v":{"df":2,"docs":{"38":{"tf":1.0},"44":{"tf":1.0}}}},"s":{"df":0,"docs":{},"t":{"df":0,"docs":{},"o":{"df":0,"docs":{},"m":{"df":1,"docs":{"12":{"tf":1.4142135623730951}}}}}}},"y":{"c":{"df":0,"docs":{},"l":{"df":4,"docs":{"127":{"tf":1.0},"128":{"tf":1.0},"144":{"tf":1.0},"146":{"tf":1.0}},"i":{"c":{"df":1,"docs":{"38":{"tf":1.0}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"d":{"0":{"df":1,"docs":{"149":{"tf":1.7320508075688772}},"​":{":":{"=":{"d":{"df":0,"docs":{},"l":{"d":{"df":0,"docs":{},"e":{"df":1,"docs":{"94":{"tf":1.0}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"1":{"df":1,"docs":{"149":{"tf":1.7320508075688772}}},"2":{"df":1,"docs":{"149":{"tf":1.7320508075688772}}},"3":{"df":1,"docs":{"149":{"tf":1.7320508075688772}}},"=":{"(":{"d":{"1":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"…":{",":{"d":{"df":0,"docs":{},"m":{"df":3,"docs":{"73":{"tf":1.4142135623730951},"75":{"tf":1.0},"80":{"tf":1.0}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{},"y":{"1":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"…":{",":{"df":0,"docs":{},"y":{"df":0,"docs":{},"l":{"df":1,"docs":{"90":{"tf":1.0}}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{},"{":{"1":{",":{"df":0,"docs":{},"η":{",":{"df":0,"docs":{},"η":{"2":{",":{"df":0,"docs":{},"…":{",":{"df":0,"docs":{},"η":{"3":{"df":0,"docs":{},"n":{"df":0,"docs":{},"−":{"1":{"df":1,"docs":{"14":{"tf":1.0}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{},"g":{"df":0,"docs":{},"i":{"df":0,"docs":{},"​":{"df":0,"docs":{},"}":{"df":0,"docs":{},"i":{"=":{"0":{"2":{"df":0,"docs":{},"n":{"df":0,"docs":{},"−":{"1":{"df":1,"docs":{"84":{"tf":1.0}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}}}}}},"a":{"df":0,"docs":{},"t":{"a":{"df":2,"docs":{"146":{"tf":1.4142135623730951},"32":{"tf":1.0}}},"df":0,"docs":{}}},"df":11,"docs":{"114":{"tf":1.0},"14":{"tf":1.0},"142":{"tf":1.4142135623730951},"146":{"tf":2.23606797749979},"147":{"tf":1.4142135623730951},"149":{"tf":1.0},"3":{"tf":1.4142135623730951},"73":{"tf":1.0},"76":{"tf":1.0},"82":{"tf":1.0},"84":{"tf":1.0}},"e":{"c":{"df":0,"docs":{},"e":{"df":0,"docs":{},"i":{"df":0,"docs":{},"v":{"df":1,"docs":{"83":{"tf":1.0}}}}},"o":{"d":{"df":3,"docs":{"142":{"tf":1.0},"146":{"tf":2.0},"147":{"tf":1.0}},"e":{"/":{"df":0,"docs":{},"o":{"df":0,"docs":{},"p":{"c":{"df":0,"docs":{},"o":{"d":{"df":1,"docs":{"131":{"tf":1.0}}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{},"m":{"df":0,"docs":{},"p":{"df":0,"docs":{},"o":{"df":0,"docs":{},"s":{"df":2,"docs":{"151":{"tf":1.0},"94":{"tf":1.4142135623730951}},"i":{"df":0,"docs":{},"t":{"df":4,"docs":{"113":{"tf":1.0},"116":{"tf":1.4142135623730951},"146":{"tf":1.0},"67":{"tf":1.4142135623730951}}}}}}}}}},"d":{"df":0,"docs":{},"i":{"c":{"df":2,"docs":{"146":{"tf":1.4142135623730951},"150":{"tf":1.0}}},"df":0,"docs":{}}},"df":0,"docs":{},"e":{"df":0,"docs":{},"p":{"(":{"df":0,"docs":{},"x":{")":{"=":{"df":0,"docs":{},"γ":{"1":{"df":0,"docs":{},"​":{"df":0,"docs":{},"x":{"df":0,"docs":{},"−":{"df":0,"docs":{},"z":{"df":0,"docs":{},"h":{"(":{"df":0,"docs":{},"x":{")":{"df":0,"docs":{},"−":{"df":0,"docs":{},"h":{"(":{"df":0,"docs":{},"z":{")":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"γ":{"2":{"df":0,"docs":{},"​":{"df":0,"docs":{},"x":{"df":0,"docs":{},"−":{"df":0,"docs":{},"z":{"df":0,"docs":{},"t":{"(":{"df":0,"docs":{},"x":{")":{"df":0,"docs":{},"−":{"df":0,"docs":{},"t":{"(":{"df":0,"docs":{},"z":{")":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"γ":{"3":{"df":0,"docs":{},"​":{"df":0,"docs":{},"x":{"df":0,"docs":{},"−":{"df":0,"docs":{},"z":{"df":0,"docs":{},"g":{"df":0,"docs":{},"t":{"(":{"df":0,"docs":{},"x":{")":{"df":0,"docs":{},"−":{"df":0,"docs":{},"t":{"(":{"df":0,"docs":{},"z":{"df":0,"docs":{},"g":{")":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"γ":{"4":{"df":0,"docs":{},"​":{"df":0,"docs":{},"x":{"df":0,"docs":{},"−":{"df":0,"docs":{},"z":{"df":0,"docs":{},"g":{"2":{"df":0,"docs":{},"t":{"(":{"df":0,"docs":{},"x":{")":{"df":0,"docs":{},"−":{"df":0,"docs":{},"t":{"(":{"df":0,"docs":{},"z":{"df":0,"docs":{},"g":{"2":{"df":1,"docs":{"59":{"tf":1.0}}},"df":0,"docs":{}}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"0":{"df":1,"docs":{"113":{"tf":1.0}},"​":{")":{"=":{"df":0,"docs":{},"γ":{"1":{"df":0,"docs":{},"​":{"df":0,"docs":{},"x":{"0":{"df":0,"docs":{},"​":{"df":0,"docs":{},"−":{"df":0,"docs":{},"z":{"df":0,"docs":{},"h":{"(":{"df":0,"docs":{},"x":{"0":{"df":0,"docs":{},"​":{")":{"df":0,"docs":{},"−":{"df":0,"docs":{},"h":{"(":{"df":0,"docs":{},"z":{")":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"γ":{"2":{"df":0,"docs":{},"​":{"df":0,"docs":{},"x":{"0":{"df":0,"docs":{},"​":{"df":0,"docs":{},"−":{"df":0,"docs":{},"z":{"df":0,"docs":{},"t":{"(":{"df":0,"docs":{},"x":{"0":{"df":0,"docs":{},"​":{")":{"df":0,"docs":{},"−":{"df":0,"docs":{},"t":{"(":{"df":0,"docs":{},"z":{")":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"γ":{"3":{"df":0,"docs":{},"​":{"df":0,"docs":{},"x":{"0":{"df":0,"docs":{},"​":{"df":0,"docs":{},"−":{"df":0,"docs":{},"z":{"df":0,"docs":{},"g":{"df":0,"docs":{},"t":{"(":{"df":0,"docs":{},"x":{"0":{"df":0,"docs":{},"​":{")":{"df":0,"docs":{},"−":{"df":0,"docs":{},"t":{"(":{"df":0,"docs":{},"z":{"df":0,"docs":{},"g":{")":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"γ":{"4":{"df":0,"docs":{},"​":{"df":0,"docs":{},"x":{"0":{"df":0,"docs":{},"​":{"df":0,"docs":{},"−":{"df":0,"docs":{},"z":{"df":0,"docs":{},"g":{"2":{"df":0,"docs":{},"t":{"(":{"df":0,"docs":{},"x":{"0":{"df":0,"docs":{},"​":{")":{"df":0,"docs":{},"−":{"df":0,"docs":{},"t":{"(":{"df":0,"docs":{},"z":{"df":0,"docs":{},"g":{"2":{"df":3,"docs":{"113":{"tf":1.0},"60":{"tf":1.0},"63":{"tf":1.0}}},"df":0,"docs":{}}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"_":{"0":{"df":1,"docs":{"63":{"tf":1.0}}},"df":0,"docs":{}},"df":3,"docs":{"112":{"tf":1.4142135623730951},"59":{"tf":1.4142135623730951},"62":{"tf":1.4142135623730951}}}},"df":13,"docs":{"100":{"tf":1.0},"112":{"tf":1.0},"119":{"tf":1.7320508075688772},"125":{"tf":1.0},"59":{"tf":1.4142135623730951},"60":{"tf":1.0},"62":{"tf":1.0},"65":{"tf":1.0},"68":{"tf":1.4142135623730951},"80":{"tf":1.0},"81":{"tf":1.4142135623730951},"95":{"tf":1.0},"97":{"tf":1.0}},"e":{"df":0,"docs":{},"r":{"df":2,"docs":{"103":{"tf":1.0},"69":{"tf":1.0}}}}}},"f":{"a":{"df":0,"docs":{},"u":{"df":0,"docs":{},"l":{"df":0,"docs":{},"t":{"df":1,"docs":{"9":{"tf":1.0}}}}}},"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"df":29,"docs":{"105":{"tf":1.7320508075688772},"108":{"tf":1.0},"109":{"tf":1.4142135623730951},"110":{"tf":1.0},"113":{"tf":1.0},"114":{"tf":1.0},"128":{"tf":1.0},"131":{"tf":1.0},"14":{"tf":1.7320508075688772},"142":{"tf":1.7320508075688772},"147":{"tf":2.449489742783178},"149":{"tf":1.0},"150":{"tf":2.0},"20":{"tf":1.0},"23":{"tf":1.0},"25":{"tf":1.0},"26":{"tf":1.4142135623730951},"28":{"tf":1.4142135623730951},"35":{"tf":1.4142135623730951},"5":{"tf":1.0},"50":{"tf":1.0},"54":{"tf":1.0},"55":{"tf":1.0},"59":{"tf":1.0},"8":{"tf":1.0},"90":{"tf":1.0},"94":{"tf":1.4142135623730951},"95":{"tf":1.0},"97":{"tf":1.7320508075688772}},"i":{"df":0,"docs":{},"t":{"df":3,"docs":{"14":{"tf":1.4142135623730951},"34":{"tf":1.0},"91":{"tf":1.0}}}}}}},"g":{"df":0,"docs":{},"r":{"df":0,"docs":{},"e":{"df":18,"docs":{"109":{"tf":1.0},"114":{"tf":1.0},"119":{"tf":1.0},"125":{"tf":1.0},"14":{"tf":1.4142135623730951},"146":{"tf":1.0},"20":{"tf":2.23606797749979},"26":{"tf":1.0},"3":{"tf":1.4142135623730951},"68":{"tf":2.23606797749979},"73":{"tf":3.0},"74":{"tf":1.4142135623730951},"75":{"tf":1.7320508075688772},"78":{"tf":2.0},"79":{"tf":1.7320508075688772},"80":{"tf":1.0},"81":{"tf":1.0},"91":{"tf":1.4142135623730951}}}}},"l":{"df":0,"docs":{},"e":{"df":0,"docs":{},"g":{"df":2,"docs":{"151":{"tf":1.0},"7":{"tf":1.0}}}},"i":{"c":{"df":1,"docs":{"16":{"tf":1.0}}},"df":0,"docs":{}},"v":{"df":1,"docs":{"4":{"tf":1.0}}}},"m":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"df":0,"docs":{},"s":{"df":0,"docs":{},"t":{"df":0,"docs":{},"r":{"df":2,"docs":{"13":{"tf":1.0},"7":{"tf":1.0}}}}}},"s":{"df":0,"docs":{},"t":{"df":0,"docs":{},"r":{"df":1,"docs":{"7":{"tf":1.0}}}}}}},"n":{"df":0,"docs":{},"o":{"df":0,"docs":{},"m":{"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"df":4,"docs":{"114":{"tf":1.0},"123":{"tf":1.0},"125":{"tf":1.4142135623730951},"54":{"tf":1.0}}}}},"t":{"df":18,"docs":{"14":{"tf":1.0},"147":{"tf":1.0},"150":{"tf":1.0},"19":{"tf":1.0},"36":{"tf":1.0},"5":{"tf":3.0},"53":{"tf":1.0},"70":{"tf":1.4142135623730951},"72":{"tf":1.0},"75":{"tf":1.0},"77":{"tf":1.0},"82":{"tf":1.0},"83":{"tf":1.0},"84":{"tf":1.0},"88":{"tf":1.0},"90":{"tf":2.0},"91":{"tf":1.4142135623730951},"95":{"tf":1.4142135623730951}}}}},"p":{"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"d":{"df":9,"docs":{"10":{"tf":1.0},"109":{"tf":1.0},"13":{"tf":1.4142135623730951},"146":{"tf":1.7320508075688772},"70":{"tf":1.4142135623730951},"73":{"tf":1.4142135623730951},"8":{"tf":1.0},"84":{"tf":1.4142135623730951},"92":{"tf":1.0}}},"df":0,"docs":{}}}},"r":{"df":0,"docs":{},"i":{"df":0,"docs":{},"v":{"df":5,"docs":{"142":{"tf":1.0},"146":{"tf":1.7320508075688772},"147":{"tf":1.0},"5":{"tf":1.0},"91":{"tf":1.0}}}}},"s":{"c":{"df":0,"docs":{},"r":{"df":0,"docs":{},"i":{"b":{"df":12,"docs":{"142":{"tf":1.0},"145":{"tf":1.0},"147":{"tf":1.0},"23":{"tf":1.4142135623730951},"37":{"tf":1.0},"40":{"tf":1.0},"74":{"tf":1.0},"77":{"tf":1.0},"80":{"tf":1.0},"86":{"tf":1.0},"87":{"tf":1.0},"95":{"tf":1.0}}},"df":0,"docs":{},"p":{"df":0,"docs":{},"t":{"df":3,"docs":{"142":{"tf":1.0},"16":{"tf":1.0},"82":{"tf":1.0}}}}}}},"df":0,"docs":{},"i":{"df":0,"docs":{},"g":{"df":0,"docs":{},"n":{"df":2,"docs":{"10":{"tf":1.0},"151":{"tf":1.0}}}},"r":{"df":2,"docs":{"146":{"tf":1.0},"20":{"tf":1.0}}}}},"t":{"a":{"df":0,"docs":{},"i":{"df":0,"docs":{},"l":{"df":15,"docs":{"103":{"tf":1.0},"114":{"tf":1.0},"127":{"tf":1.0},"133":{"tf":1.0},"134":{"tf":1.0},"138":{"tf":1.0},"139":{"tf":1.4142135623730951},"144":{"tf":1.0},"145":{"tf":1.0},"146":{"tf":1.0},"151":{"tf":1.0},"18":{"tf":1.0},"34":{"tf":1.0},"37":{"tf":1.4142135623730951},"75":{"tf":1.0}}}}},"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":0,"docs":{},"m":{"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"df":1,"docs":{"91":{"tf":1.0}},"i":{"df":0,"docs":{},"s":{"df":0,"docs":{},"t":{"df":1,"docs":{"137":{"tf":1.0}}}}}}}}}}},"v":{"df":0,"docs":{},"i":{"c":{"df":1,"docs":{"151":{"tf":1.4142135623730951}}},"df":0,"docs":{}}}},"i":{"a":{"df":0,"docs":{},"g":{"df":0,"docs":{},"r":{"a":{"df":0,"docs":{},"m":{"df":1,"docs":{"133":{"tf":1.0}}}},"df":0,"docs":{}}}},"d":{"df":0,"docs":{},"n":{"'":{"df":0,"docs":{},"t":{"df":1,"docs":{"134":{"tf":1.0}}}},"df":0,"docs":{}}},"df":0,"docs":{},"f":{"df":0,"docs":{},"f":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":15,"docs":{"118":{"tf":1.0},"14":{"tf":1.7320508075688772},"146":{"tf":1.0},"149":{"tf":1.0},"20":{"tf":1.0},"3":{"tf":1.0},"50":{"tf":1.4142135623730951},"52":{"tf":1.0},"53":{"tf":1.0},"56":{"tf":1.0},"66":{"tf":1.4142135623730951},"70":{"tf":1.0},"73":{"tf":1.0},"80":{"tf":1.0},"9":{"tf":1.0}}}}}},"g":{"df":0,"docs":{},"e":{"df":0,"docs":{},"s":{"df":0,"docs":{},"t":{"df":1,"docs":{"7":{"tf":1.0}}}}}},"r":{"df":0,"docs":{},"e":{"c":{"df":0,"docs":{},"t":{"df":0,"docs":{},"l":{"df":0,"docs":{},"i":{"df":3,"docs":{"127":{"tf":1.0},"14":{"tf":1.0},"146":{"tf":1.4142135623730951}}}}}},"df":0,"docs":{}}},"s":{"c":{"df":0,"docs":{},"u":{"df":0,"docs":{},"s":{"df":0,"docs":{},"s":{"df":3,"docs":{"34":{"tf":1.0},"43":{"tf":1.0},"56":{"tf":1.0}}}}}},"df":0,"docs":{},"j":{"df":0,"docs":{},"o":{"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"df":0,"docs":{},"t":{"df":1,"docs":{"90":{"tf":1.0}}}}}}},"t":{"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"df":0,"docs":{},"g":{"df":0,"docs":{},"u":{"df":0,"docs":{},"i":{"df":0,"docs":{},"s":{"df":0,"docs":{},"h":{"df":1,"docs":{"9":{"tf":1.0}}}}}}}}}}},"v":{"df":0,"docs":{},"e":{"df":4,"docs":{"103":{"tf":1.0},"46":{"tf":1.0},"69":{"tf":1.0},"81":{"tf":1.0}}},"i":{"d":{"df":7,"docs":{"100":{"tf":1.0},"123":{"tf":1.4142135623730951},"125":{"tf":1.7320508075688772},"146":{"tf":1.4142135623730951},"38":{"tf":1.0},"79":{"tf":1.0},"99":{"tf":1.0}}},"df":0,"docs":{},"s":{"df":4,"docs":{"125":{"tf":1.4142135623730951},"146":{"tf":1.0},"69":{"tf":1.0},"99":{"tf":1.0}}}}},"​":{"=":{"df":0,"docs":{},"h":{"df":0,"docs":{},"ω":{"df":0,"docs":{},"i":{"df":2,"docs":{"73":{"tf":1.0},"75":{"tf":1.0}}}}}},"df":0,"docs":{},"∈":{"d":{"df":1,"docs":{"73":{"tf":1.0}}},"df":0,"docs":{}},"−":{"df":0,"docs":{},"z":{"df":1,"docs":{"79":{"tf":1.0}}}}}},"k":{"df":0,"docs":{},"​":{":":{"=":{"(":{"d":{"0":{"2":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"…":{",":{"d":{"df":0,"docs":{},"l":{"df":0,"docs":{},"−":{"1":{"2":{"df":1,"docs":{"94":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"−":{"1":{"df":0,"docs":{},"​":{"=":{"(":{"d":{"0":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"…":{",":{"d":{"2":{"df":0,"docs":{},"l":{"df":0,"docs":{},"−":{"1":{"df":1,"docs":{"94":{"tf":1.0}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"l":{"d":{"df":0,"docs":{},"e":{"df":1,"docs":{"83":{"tf":1.0}},"​":{"=":{"(":{"df":0,"docs":{},"h":{",":{"df":0,"docs":{},"h":{"df":0,"docs":{},"ω":{",":{"df":0,"docs":{},"h":{"df":0,"docs":{},"ω":{"2":{",":{"df":0,"docs":{},"…":{",":{"df":0,"docs":{},"h":{"df":0,"docs":{},"ω":{"2":{"df":0,"docs":{},"n":{"+":{"df":0,"docs":{},"l":{"df":1,"docs":{"82":{"tf":1.0}},"−":{"1":{"df":1,"docs":{"91":{"tf":1.0}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}},"o":{"c":{"df":0,"docs":{},"u":{"df":0,"docs":{},"m":{"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"df":0,"docs":{},"t":{"df":3,"docs":{"0":{"tf":1.0},"126":{"tf":1.0},"46":{"tf":1.0}}}}}}}},"df":5,"docs":{"109":{"tf":1.0},"114":{"tf":1.0},"125":{"tf":1.7320508075688772},"146":{"tf":1.4142135623730951},"149":{"tf":1.0}},"e":{"df":0,"docs":{},"s":{"df":0,"docs":{},"n":{"'":{"df":0,"docs":{},"t":{"df":4,"docs":{"125":{"tf":1.0},"130":{"tf":1.0},"14":{"tf":1.0},"32":{"tf":1.0}}}},"df":0,"docs":{}}}},"m":{"a":{"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"df":24,"docs":{"101":{"tf":1.0},"112":{"tf":1.4142135623730951},"115":{"tf":1.0},"117":{"tf":1.4142135623730951},"119":{"tf":1.0},"125":{"tf":1.4142135623730951},"14":{"tf":2.0},"16":{"tf":1.0},"20":{"tf":1.0},"24":{"tf":1.0},"25":{"tf":1.0},"26":{"tf":1.0},"32":{"tf":1.4142135623730951},"35":{"tf":1.4142135623730951},"5":{"tf":1.0},"59":{"tf":1.4142135623730951},"60":{"tf":1.4142135623730951},"62":{"tf":1.4142135623730951},"66":{"tf":1.0},"68":{"tf":1.4142135623730951},"73":{"tf":1.7320508075688772},"84":{"tf":1.0},"91":{"tf":1.0},"94":{"tf":1.4142135623730951}}}}},"df":0,"docs":{}},"n":{"'":{"df":0,"docs":{},"t":{"df":9,"docs":{"111":{"tf":1.0},"114":{"tf":1.0},"116":{"tf":1.4142135623730951},"118":{"tf":1.0},"123":{"tf":1.4142135623730951},"125":{"tf":1.7320508075688772},"149":{"tf":1.0},"151":{"tf":1.0},"67":{"tf":1.0}}}},"df":0,"docs":{},"e":{"df":12,"docs":{"103":{"tf":1.0},"105":{"tf":1.0},"110":{"tf":1.0},"114":{"tf":1.4142135623730951},"118":{"tf":1.4142135623730951},"130":{"tf":1.0},"146":{"tf":1.4142135623730951},"151":{"tf":1.4142135623730951},"58":{"tf":1.0},"69":{"tf":1.0},"83":{"tf":1.0},"84":{"tf":1.0}}}},"t":{"df":2,"docs":{"65":{"tf":1.0},"66":{"tf":1.0}}},"w":{"df":0,"docs":{},"n":{"df":4,"docs":{"14":{"tf":1.4142135623730951},"16":{"tf":1.0},"21":{"tf":1.0},"35":{"tf":1.0}}}}},"s":{"df":1,"docs":{"94":{"tf":1.4142135623730951}},"t":{"_":{"a":{"d":{"d":{"df":0,"docs":{},"r":{"[":{"0":{"df":1,"docs":{"147":{"tf":1.0}}},"1":{"df":1,"docs":{"147":{"tf":1.0}}},"df":0,"docs":{}},"df":3,"docs":{"142":{"tf":1.0},"146":{"tf":1.7320508075688772},"147":{"tf":1.4142135623730951}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{},"r":{"df":0,"docs":{},"e":{"df":0,"docs":{},"g":{"df":1,"docs":{"146":{"tf":1.0}}}}}},"df":4,"docs":{"133":{"tf":2.0},"142":{"tf":1.0},"146":{"tf":2.23606797749979},"147":{"tf":1.4142135623730951}},"r":{"df":0,"docs":{},"e":{"df":0,"docs":{},"g":{"df":1,"docs":{"149":{"tf":1.0}}}}}},"​":{"=":{"(":{"1":{",":{"df":0,"docs":{},"g":{",":{"df":0,"docs":{},"…":{",":{"df":0,"docs":{},"g":{"2":{"df":0,"docs":{},"n":{"df":0,"docs":{},"−":{"1":{"df":1,"docs":{"91":{"tf":1.0}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{},"{":{"df":0,"docs":{},"g":{"df":0,"docs":{},"i":{"df":0,"docs":{},"}":{"df":0,"docs":{},"i":{"=":{"0":{"2":{"df":0,"docs":{},"n":{"df":0,"docs":{},"−":{"1":{"df":0,"docs":{},"​":{"df":0,"docs":{},"⊆":{"df":0,"docs":{},"f":{"df":1,"docs":{"82":{"tf":1.0}}}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}}}}},"df":0,"docs":{}}},"u":{"df":0,"docs":{},"e":{"df":2,"docs":{"13":{"tf":1.0},"4":{"tf":1.0}}},"m":{"df":0,"docs":{},"m":{"df":0,"docs":{},"i":{"df":5,"docs":{"133":{"tf":1.7320508075688772},"135":{"tf":1.7320508075688772},"146":{"tf":1.7320508075688772},"149":{"tf":1.0},"36":{"tf":1.7320508075688772}}}}},"r":{"df":0,"docs":{},"e":{"df":2,"docs":{"14":{"tf":1.0},"80":{"tf":1.0}}}}}},"df":0,"docs":{},"e":{"+":{"1":{")":{"df":0,"docs":{},"∗":{"df":0,"docs":{},"x":{"df":0,"docs":{},"−":{"1":{"df":1,"docs":{"7":{"tf":1.0}}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}},"=":{"2":{"df":1,"docs":{"9":{"tf":1.0}}},"df":0,"docs":{}},"a":{"c":{"df":0,"docs":{},"h":{"df":30,"docs":{"10":{"tf":1.0},"102":{"tf":1.0},"106":{"tf":1.0},"109":{"tf":1.7320508075688772},"114":{"tf":1.7320508075688772},"125":{"tf":1.0},"128":{"tf":1.0},"130":{"tf":1.4142135623730951},"131":{"tf":2.0},"133":{"tf":1.4142135623730951},"136":{"tf":1.0},"14":{"tf":1.0},"142":{"tf":1.4142135623730951},"146":{"tf":3.3166247903554},"147":{"tf":1.0},"149":{"tf":2.0},"150":{"tf":1.7320508075688772},"151":{"tf":2.23606797749979},"3":{"tf":1.0},"49":{"tf":1.0},"65":{"tf":1.4142135623730951},"66":{"tf":1.4142135623730951},"70":{"tf":1.0},"80":{"tf":1.7320508075688772},"82":{"tf":1.4142135623730951},"83":{"tf":1.0},"84":{"tf":1.0},"94":{"tf":1.4142135623730951},"95":{"tf":1.0},"99":{"tf":1.0}}}},"df":0,"docs":{},"r":{"df":0,"docs":{},"l":{"df":0,"docs":{},"i":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":2,"docs":{"59":{"tf":1.0},"80":{"tf":1.0}}}}}}},"s":{"df":1,"docs":{"70":{"tf":1.0}},"i":{"df":1,"docs":{"14":{"tf":1.0}},"l":{"df":0,"docs":{},"i":{"df":5,"docs":{"114":{"tf":1.0},"133":{"tf":1.0},"45":{"tf":1.0},"7":{"tf":1.0},"70":{"tf":1.0}}}}}}},"df":10,"docs":{"11":{"tf":1.0},"142":{"tf":1.4142135623730951},"146":{"tf":1.7320508075688772},"147":{"tf":1.4142135623730951},"34":{"tf":1.0},"35":{"tf":2.6457513110645907},"44":{"tf":2.6457513110645907},"45":{"tf":2.0},"7":{"tf":2.23606797749979},"9":{"tf":1.7320508075688772}},"f":{"df":0,"docs":{},"f":{"df":0,"docs":{},"e":{"c":{"df":0,"docs":{},"t":{"df":1,"docs":{"59":{"tf":1.0}}}},"df":0,"docs":{}},"i":{"c":{"df":0,"docs":{},"i":{"df":4,"docs":{"146":{"tf":1.0},"151":{"tf":1.0},"4":{"tf":1.0},"73":{"tf":1.0}}}},"df":0,"docs":{}}}},"i":{"df":1,"docs":{"123":{"tf":1.0}},"g":{"df":0,"docs":{},"h":{"df":0,"docs":{},"t":{"df":5,"docs":{"15":{"tf":1.0},"151":{"tf":1.0},"23":{"tf":1.0},"35":{"tf":1.4142135623730951},"8":{"tf":1.0}}}}}},"l":{"df":0,"docs":{},"e":{"df":0,"docs":{},"m":{"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"df":0,"docs":{},"t":{"df":30,"docs":{"101":{"tf":1.0},"107":{"tf":1.0},"121":{"tf":1.0},"123":{"tf":1.0},"125":{"tf":2.0},"128":{"tf":1.7320508075688772},"14":{"tf":5.477225575051661},"144":{"tf":1.0},"16":{"tf":1.0},"19":{"tf":2.449489742783178},"31":{"tf":1.0},"35":{"tf":1.7320508075688772},"41":{"tf":1.7320508075688772},"52":{"tf":1.0},"54":{"tf":1.0},"55":{"tf":1.0},"68":{"tf":1.0},"7":{"tf":1.0},"70":{"tf":1.4142135623730951},"73":{"tf":1.0},"74":{"tf":1.4142135623730951},"77":{"tf":1.0},"79":{"tf":1.0},"82":{"tf":1.0},"84":{"tf":1.0},"91":{"tf":1.0},"92":{"tf":2.0},"95":{"tf":1.4142135623730951},"98":{"tf":1.4142135623730951},"99":{"tf":1.0}}}}}}}},"m":{"b":{"df":0,"docs":{},"e":{"d":{"df":1,"docs":{"130":{"tf":1.0}}},"df":0,"docs":{}}},"df":0,"docs":{},"p":{"df":0,"docs":{},"h":{"a":{"df":0,"docs":{},"s":{"df":1,"docs":{"5":{"tf":1.0}}}},"df":0,"docs":{}},"l":{"df":0,"docs":{},"o":{"df":0,"docs":{},"y":{"df":2,"docs":{"100":{"tf":1.0},"115":{"tf":1.0}}}}},"t":{"df":0,"docs":{},"i":{"df":2,"docs":{"134":{"tf":1.0},"36":{"tf":1.0}}}}},"u":{"df":0,"docs":{},"l":{"df":1,"docs":{"118":{"tf":1.0}}}}},"n":{"a":{"b":{"df":0,"docs":{},"l":{"df":2,"docs":{"146":{"tf":1.0},"4":{"tf":1.0}}}},"df":0,"docs":{}},"c":{"a":{"df":0,"docs":{},"p":{"df":0,"docs":{},"s":{"df":0,"docs":{},"u":{"df":0,"docs":{},"l":{"df":1,"docs":{"109":{"tf":1.0}}}}}}},"df":0,"docs":{},"o":{"d":{"df":15,"docs":{"10":{"tf":1.0},"105":{"tf":1.0},"107":{"tf":1.0},"11":{"tf":1.0},"133":{"tf":1.0},"48":{"tf":1.0},"50":{"tf":1.0},"52":{"tf":1.0},"54":{"tf":1.0},"55":{"tf":1.0},"66":{"tf":1.0},"69":{"tf":1.4142135623730951},"70":{"tf":1.4142135623730951},"72":{"tf":1.0},"84":{"tf":1.0}}},"df":0,"docs":{}}},"d":{"df":12,"docs":{"103":{"tf":1.0},"109":{"tf":1.0},"116":{"tf":1.0},"12":{"tf":1.0},"13":{"tf":1.0},"130":{"tf":1.0},"134":{"tf":1.0},"144":{"tf":1.0},"40":{"tf":1.0},"46":{"tf":1.0},"8":{"tf":1.0},"95":{"tf":1.0}},"i":{"a":{"df":0,"docs":{},"n":{"df":1,"docs":{"41":{"tf":1.0}}}},"df":0,"docs":{}}},"df":0,"docs":{},"f":{"df":0,"docs":{},"o":{"df":0,"docs":{},"r":{"c":{"df":5,"docs":{"146":{"tf":1.0},"150":{"tf":1.4142135623730951},"151":{"tf":2.0},"49":{"tf":1.0},"70":{"tf":1.0}}},"df":0,"docs":{}}}},"g":{"a":{"df":0,"docs":{},"g":{"df":3,"docs":{"75":{"tf":1.0},"77":{"tf":1.0},"86":{"tf":1.0}}}},"df":0,"docs":{}},"h":{"a":{"df":0,"docs":{},"n":{"c":{"df":1,"docs":{"100":{"tf":1.0}}},"df":0,"docs":{}}},"df":0,"docs":{}},"o":{"df":0,"docs":{},"u":{"df":0,"docs":{},"g":{"df":0,"docs":{},"h":{"df":3,"docs":{"114":{"tf":1.4142135623730951},"146":{"tf":1.0},"49":{"tf":1.0}}}}}},"s":{"df":0,"docs":{},"u":{"df":0,"docs":{},"r":{"df":1,"docs":{"83":{"tf":1.0}}}}},"t":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":1,"docs":{"14":{"tf":1.0}}}},"i":{"df":0,"docs":{},"r":{"df":4,"docs":{"12":{"tf":1.0},"14":{"tf":1.0},"50":{"tf":1.0},"68":{"tf":1.0}}},"t":{"df":0,"docs":{},"i":{"df":1,"docs":{"104":{"tf":1.0}}}}},"r":{"df":0,"docs":{},"i":{"df":7,"docs":{"134":{"tf":1.0},"141":{"tf":1.0},"142":{"tf":2.0},"147":{"tf":2.0},"36":{"tf":1.0},"70":{"tf":1.0},"91":{"tf":1.0}}}}}},"q":{"df":0,"docs":{},"u":{"a":{"df":0,"docs":{},"l":{"df":16,"docs":{"10":{"tf":1.4142135623730951},"101":{"tf":1.0},"106":{"tf":1.7320508075688772},"11":{"tf":1.0},"13":{"tf":1.0},"14":{"tf":5.830951894845301},"142":{"tf":1.4142135623730951},"146":{"tf":2.23606797749979},"147":{"tf":1.0},"16":{"tf":1.4142135623730951},"33":{"tf":1.0},"52":{"tf":1.0},"66":{"tf":1.0},"73":{"tf":1.0},"78":{"tf":1.4142135623730951},"95":{"tf":1.4142135623730951}}},"t":{"df":12,"docs":{"10":{"tf":1.7320508075688772},"11":{"tf":1.0},"12":{"tf":1.0},"13":{"tf":1.0},"14":{"tf":3.0},"149":{"tf":1.4142135623730951},"16":{"tf":1.7320508075688772},"4":{"tf":1.4142135623730951},"58":{"tf":1.0},"60":{"tf":1.0},"69":{"tf":1.0},"95":{"tf":1.0}}}},"df":0,"docs":{},"i":{"df":0,"docs":{},"v":{"a":{"df":0,"docs":{},"l":{"df":6,"docs":{"14":{"tf":2.8284271247461903},"16":{"tf":1.0},"56":{"tf":1.0},"69":{"tf":1.4142135623730951},"80":{"tf":1.0},"84":{"tf":1.0}}}},"df":0,"docs":{}}}}},"s":{"df":0,"docs":{},"s":{"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"c":{"df":1,"docs":{"133":{"tf":1.0}}},"df":0,"docs":{},"t":{"df":0,"docs":{},"i":{"df":1,"docs":{"49":{"tf":1.0}}}}}}}},"t":{"c":{"df":2,"docs":{"134":{"tf":1.0},"50":{"tf":1.0}}},"df":0,"docs":{}},"v":{"a":{"df":0,"docs":{},"l":{"df":0,"docs":{},"u":{"a":{"df":0,"docs":{},"t":{"df":0,"docs":{},"o":{"df":0,"docs":{},"r":{".":{"df":0,"docs":{},"e":{"df":0,"docs":{},"v":{"a":{"df":0,"docs":{},"l":{"df":0,"docs":{},"u":{"df":1,"docs":{"114":{"tf":1.0}}}}},"df":0,"docs":{}}}},"df":0,"docs":{}}}}},"df":39,"docs":{"10":{"tf":1.4142135623730951},"11":{"tf":1.4142135623730951},"112":{"tf":2.0},"113":{"tf":2.6457513110645907},"114":{"tf":4.358898943540674},"115":{"tf":1.0},"116":{"tf":1.0},"117":{"tf":1.7320508075688772},"119":{"tf":1.0},"121":{"tf":1.7320508075688772},"123":{"tf":2.23606797749979},"125":{"tf":2.23606797749979},"13":{"tf":1.7320508075688772},"14":{"tf":1.4142135623730951},"149":{"tf":2.6457513110645907},"19":{"tf":1.4142135623730951},"28":{"tf":1.0},"35":{"tf":1.0},"52":{"tf":1.0},"58":{"tf":2.23606797749979},"59":{"tf":2.0},"60":{"tf":1.7320508075688772},"62":{"tf":1.7320508075688772},"63":{"tf":2.23606797749979},"66":{"tf":1.4142135623730951},"68":{"tf":2.449489742783178},"70":{"tf":1.0},"73":{"tf":1.7320508075688772},"74":{"tf":1.4142135623730951},"75":{"tf":1.0},"76":{"tf":1.0},"77":{"tf":1.4142135623730951},"78":{"tf":1.0},"79":{"tf":1.0},"80":{"tf":2.0},"85":{"tf":1.4142135623730951},"86":{"tf":1.0},"94":{"tf":1.0},"95":{"tf":2.0}}}}},"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"/":{"df":0,"docs":{},"o":{"d":{"d":{"df":2,"docs":{"113":{"tf":1.0},"116":{"tf":1.4142135623730951}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":10,"docs":{"101":{"tf":1.0},"114":{"tf":1.0},"119":{"tf":1.0},"146":{"tf":1.4142135623730951},"49":{"tf":1.0},"52":{"tf":1.0},"66":{"tf":1.0},"67":{"tf":1.0},"73":{"tf":1.0},"94":{"tf":1.0}}},"r":{"df":0,"docs":{},"y":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"df":1,"docs":{"103":{"tf":1.0}}}},"t":{"df":0,"docs":{},"h":{"df":4,"docs":{"146":{"tf":1.0},"16":{"tf":1.0},"21":{"tf":1.0},"70":{"tf":1.0}}}}}}},"i":{"d":{"df":1,"docs":{"14":{"tf":1.0}}},"df":0,"docs":{}},"o":{"df":0,"docs":{},"l":{"df":0,"docs":{},"u":{"df":0,"docs":{},"t":{"df":1,"docs":{"141":{"tf":1.0}}}}}}},"x":{"a":{"c":{"df":0,"docs":{},"t":{"df":2,"docs":{"146":{"tf":1.0},"95":{"tf":1.0}},"l":{"df":0,"docs":{},"i":{"df":4,"docs":{"114":{"tf":1.0},"20":{"tf":1.0},"21":{"tf":1.4142135623730951},"67":{"tf":1.0}}}}}},"df":0,"docs":{},"m":{"df":0,"docs":{},"p":{"df":0,"docs":{},"l":{"df":42,"docs":{"10":{"tf":1.4142135623730951},"100":{"tf":1.0},"101":{"tf":1.0},"103":{"tf":1.0},"104":{"tf":1.0},"108":{"tf":1.0},"109":{"tf":1.4142135623730951},"11":{"tf":1.0},"110":{"tf":1.0},"111":{"tf":1.0},"114":{"tf":1.4142135623730951},"117":{"tf":1.0},"12":{"tf":1.0},"124":{"tf":1.0},"13":{"tf":1.0},"130":{"tf":1.0},"134":{"tf":1.0},"135":{"tf":1.0},"14":{"tf":2.0},"146":{"tf":1.4142135623730951},"149":{"tf":1.7320508075688772},"150":{"tf":1.0},"151":{"tf":1.4142135623730951},"20":{"tf":1.0},"34":{"tf":1.0},"40":{"tf":1.0},"44":{"tf":1.4142135623730951},"48":{"tf":1.0},"49":{"tf":1.7320508075688772},"5":{"tf":1.0},"50":{"tf":2.23606797749979},"52":{"tf":1.0},"64":{"tf":1.0},"66":{"tf":1.0},"68":{"tf":1.0},"7":{"tf":1.4142135623730951},"70":{"tf":1.4142135623730951},"73":{"tf":1.0},"74":{"tf":1.4142135623730951},"84":{"tf":1.0},"9":{"tf":1.0},"95":{"tf":2.0}}}}}},"c":{"df":0,"docs":{},"e":{"df":0,"docs":{},"p":{"df":0,"docs":{},"t":{"df":3,"docs":{"142":{"tf":1.4142135623730951},"44":{"tf":1.0},"72":{"tf":1.0}}}}}},"df":0,"docs":{},"e":{"c":{"df":0,"docs":{},"u":{"df":0,"docs":{},"t":{"df":30,"docs":{"10":{"tf":1.7320508075688772},"108":{"tf":1.0},"11":{"tf":1.0},"110":{"tf":1.4142135623730951},"127":{"tf":1.0},"13":{"tf":2.0},"131":{"tf":1.4142135623730951},"133":{"tf":1.0},"14":{"tf":1.0},"141":{"tf":1.0},"142":{"tf":1.4142135623730951},"143":{"tf":1.0},"144":{"tf":2.23606797749979},"145":{"tf":1.0},"146":{"tf":3.605551275463989},"16":{"tf":1.7320508075688772},"22":{"tf":1.0},"34":{"tf":1.0},"4":{"tf":1.0},"43":{"tf":1.0},"48":{"tf":1.4142135623730951},"49":{"tf":1.0},"50":{"tf":2.449489742783178},"69":{"tf":1.7320508075688772},"7":{"tf":2.23606797749979},"8":{"tf":2.0},"83":{"tf":1.0},"9":{"tf":2.23606797749979},"91":{"tf":1.4142135623730951},"94":{"tf":1.4142135623730951}}}}},"df":0,"docs":{},"m":{"df":0,"docs":{},"p":{"df":0,"docs":{},"t":{"df":1,"docs":{"123":{"tf":1.4142135623730951}}}}},"r":{"c":{"df":0,"docs":{},"i":{"df":0,"docs":{},"s":{"df":1,"docs":{"40":{"tf":1.0}}}}},"df":0,"docs":{}}},"i":{"df":0,"docs":{},"s":{"df":0,"docs":{},"t":{"df":4,"docs":{"106":{"tf":1.0},"14":{"tf":2.6457513110645907},"149":{"tf":1.0},"70":{"tf":1.0}}}}},"p":{"a":{"df":0,"docs":{},"n":{"d":{"df":1,"docs":{"14":{"tf":1.0}}},"df":0,"docs":{}}},"df":0,"docs":{},"e":{"c":{"df":0,"docs":{},"t":{"df":7,"docs":{"146":{"tf":2.23606797749979},"151":{"tf":1.0},"71":{"tf":1.0},"72":{"tf":1.0},"75":{"tf":1.0},"79":{"tf":1.0},"85":{"tf":1.0}}}},"df":0,"docs":{},"n":{"df":0,"docs":{},"s":{"df":2,"docs":{"123":{"tf":1.0},"21":{"tf":1.0}}}}},"l":{"a":{"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"df":7,"docs":{"103":{"tf":1.0},"117":{"tf":1.0},"118":{"tf":1.0},"144":{"tf":1.0},"146":{"tf":1.7320508075688772},"19":{"tf":1.0},"7":{"tf":1.0}}}},"n":{"df":4,"docs":{"114":{"tf":1.0},"128":{"tf":1.0},"146":{"tf":1.4142135623730951},"51":{"tf":1.0}}}},"df":0,"docs":{},"i":{"c":{"df":0,"docs":{},"i":{"df":0,"docs":{},"t":{"df":1,"docs":{"95":{"tf":1.0}},"l":{"df":0,"docs":{},"i":{"df":2,"docs":{"114":{"tf":1.4142135623730951},"5":{"tf":1.0}}}}}}},"df":0,"docs":{}},"o":{"df":0,"docs":{},"i":{"df":0,"docs":{},"t":{"df":1,"docs":{"10":{"tf":1.0}}}}}},"o":{"df":0,"docs":{},"n":{"df":1,"docs":{"45":{"tf":1.0}},"e":{"df":0,"docs":{},"n":{"df":0,"docs":{},"t":{"_":{"b":{"df":0,"docs":{},"i":{"df":0,"docs":{},"t":{"df":1,"docs":{"45":{"tf":1.0}}}}},"df":0,"docs":{}},"df":0,"docs":{},"i":{"df":2,"docs":{"124":{"tf":1.0},"45":{"tf":1.0}}}}}}},"s":{"df":1,"docs":{"40":{"tf":1.0}}}},"r":{"df":0,"docs":{},"e":{"df":0,"docs":{},"s":{"df":0,"docs":{},"s":{"df":7,"docs":{"107":{"tf":1.0},"12":{"tf":1.0},"123":{"tf":1.4142135623730951},"4":{"tf":1.0},"48":{"tf":1.4142135623730951},"70":{"tf":1.7320508075688772},"83":{"tf":1.0}}}}}}},"t":{"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"d":{"df":9,"docs":{"121":{"tf":1.0},"14":{"tf":1.0},"142":{"tf":1.7320508075688772},"146":{"tf":1.0},"147":{"tf":1.0},"45":{"tf":1.0},"83":{"tf":1.4142135623730951},"91":{"tf":1.0},"94":{"tf":1.0}}},"df":0,"docs":{},"s":{"df":8,"docs":{"109":{"tf":1.0},"125":{"tf":1.0},"145":{"tf":1.0},"146":{"tf":1.4142135623730951},"49":{"tf":1.0},"68":{"tf":1.7320508075688772},"84":{"tf":1.0},"91":{"tf":1.0}}}},"r":{"df":0,"docs":{},"n":{"df":2,"docs":{"151":{"tf":1.0},"34":{"tf":1.0}}}}},"r":{"a":{"c":{"df":0,"docs":{},"t":{"df":1,"docs":{"32":{"tf":1.0}}}},"df":2,"docs":{"13":{"tf":1.7320508075688772},"146":{"tf":1.0}}},"df":0,"docs":{},"e":{"df":0,"docs":{},"m":{"df":3,"docs":{"50":{"tf":1.0},"68":{"tf":1.0},"79":{"tf":1.0}}}}}}}},"f":{"(":{"a":{")":{":":{"=":{"df":0,"docs":{},"p":{"(":{"a":{")":{"df":0,"docs":{},"q":{"(":{"a":{")":{"df":0,"docs":{},"−":{"1":{"df":1,"docs":{"90":{"tf":1.0}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":1,"docs":{"90":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},",":{"b":{")":{"=":{"(":{"a":{"df":0,"docs":{},"−":{"df":0,"docs":{},"y":{")":{"/":{"(":{"b":{"df":0,"docs":{},"−":{"df":0,"docs":{},"z":{"df":1,"docs":{"77":{"tf":1.0}}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}},"a":{"+":{"b":{"df":0,"docs":{},"−":{"1":{"df":1,"docs":{"74":{"tf":1.0}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"1":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"…":{",":{"a":{"df":0,"docs":{},"l":{"df":0,"docs":{},"​":{",":{"b":{")":{"=":{"df":0,"docs":{},"i":{"=":{"1":{"df":0,"docs":{},"∑":{"df":0,"docs":{},"l":{"df":0,"docs":{},"​":{"df":0,"docs":{},"γ":{"df":0,"docs":{},"i":{"df":0,"docs":{},"​":{"(":{"a":{"df":0,"docs":{},"i":{"df":0,"docs":{},"​":{"df":0,"docs":{},"−":{"df":0,"docs":{},"y":{"df":0,"docs":{},"i":{"df":0,"docs":{},"​":{")":{"/":{"(":{"b":{"df":0,"docs":{},"−":{"df":0,"docs":{},"z":{"df":0,"docs":{},"i":{"df":1,"docs":{"80":{"tf":1.0}}}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}}}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"d":{"df":1,"docs":{"90":{"tf":1.0}}},"df":0,"docs":{},"h":{")":{"=":{"0":{"df":1,"docs":{"16":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"p":{"(":{"d":{"1":{"df":0,"docs":{},"​":{")":{",":{"d":{"1":{"df":0,"docs":{},"​":{")":{",":{"df":0,"docs":{},"…":{",":{"df":0,"docs":{},"f":{"(":{"df":0,"docs":{},"p":{"(":{"d":{"df":0,"docs":{},"m":{"df":0,"docs":{},"​":{")":{",":{"d":{"df":0,"docs":{},"m":{"df":1,"docs":{"78":{"tf":1.0}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"1":{"df":0,"docs":{},"​":{"(":{"d":{"1":{"df":0,"docs":{},"​":{")":{",":{"df":0,"docs":{},"…":{",":{"df":0,"docs":{},"p":{"df":0,"docs":{},"l":{"df":0,"docs":{},"​":{"(":{"d":{"1":{"df":0,"docs":{},"​":{")":{",":{"d":{"1":{"df":0,"docs":{},"​":{")":{",":{"df":0,"docs":{},"…":{",":{"df":0,"docs":{},"f":{"(":{"df":0,"docs":{},"p":{"1":{"df":0,"docs":{},"​":{"(":{"d":{"df":0,"docs":{},"m":{"df":0,"docs":{},"​":{")":{",":{"df":0,"docs":{},"…":{",":{"df":0,"docs":{},"p":{"df":0,"docs":{},"l":{"df":0,"docs":{},"​":{"(":{"d":{"df":0,"docs":{},"m":{"df":0,"docs":{},"​":{")":{",":{"d":{"df":0,"docs":{},"m":{"df":1,"docs":{"80":{"tf":1.0}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"y":{"1":{"df":0,"docs":{},"​":{")":{",":{"df":0,"docs":{},"…":{",":{"df":0,"docs":{},"f":{"(":{"df":0,"docs":{},"y":{"df":0,"docs":{},"l":{"df":1,"docs":{"90":{"tf":1.0}}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},",":{"d":{"1":{"df":0,"docs":{},"​":{")":{",":{"df":0,"docs":{},"…":{",":{"df":0,"docs":{},"f":{"(":{"df":0,"docs":{},"y":{"df":0,"docs":{},"m":{"df":0,"docs":{},"​":{",":{"d":{"df":0,"docs":{},"m":{"df":1,"docs":{"74":{"tf":1.0}}}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"z":{")":{"=":{"df":0,"docs":{},"i":{"df":1,"docs":{"19":{"tf":1.0}}}},"df":0,"docs":{}},"df":1,"docs":{"69":{"tf":1.0}}}},"+":{"df":0,"docs":{},"g":{"]":{"1":{"df":0,"docs":{},"​":{"=":{"[":{"df":0,"docs":{},"f":{"]":{"1":{"df":0,"docs":{},"​":{"+":{"[":{"df":0,"docs":{},"g":{"]":{"1":{"df":1,"docs":{"19":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},",":{"df":0,"docs":{},"g":{"df":1,"docs":{"14":{"tf":1.0}}}},"/":{"df":0,"docs":{},"g":{"df":1,"docs":{"69":{"tf":1.0}}}},"0":{"df":1,"docs":{"131":{"tf":1.0}}},"1":{"5":{"df":0,"docs":{},"​":{"=":{"0":{"df":1,"docs":{"131":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{},"~":{"df":0,"docs":{},"​":{"=":{"0":{"df":1,"docs":{"131":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}},":":{"a":{"df":0,"docs":{},"→":{"df":0,"docs":{},"f":{"df":1,"docs":{"90":{"tf":1.4142135623730951}}}}},"df":0,"docs":{},"f":{"df":0,"docs":{},"×":{"d":{"df":0,"docs":{},"→":{"df":0,"docs":{},"f":{"df":1,"docs":{"74":{"tf":1.0}}}}},"df":0,"docs":{}}},"s":{"df":0,"docs":{},"e":{"df":0,"docs":{},"t":{"(":{"d":{")":{"df":0,"docs":{},"→":{"df":0,"docs":{},"f":{"df":1,"docs":{"90":{"tf":1.0}}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"=":{"a":{"(":{"df":0,"docs":{},"ζ":{")":{"df":0,"docs":{},"q":{"df":0,"docs":{},"l":{"df":0,"docs":{},"​":{"+":{"b":{"(":{"df":0,"docs":{},"ζ":{")":{"df":0,"docs":{},"q":{"df":0,"docs":{},"r":{"df":1,"docs":{"21":{"tf":1.0}}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{},"z":{"df":0,"docs":{},"h":{"df":0,"docs":{},"​":{"df":0,"docs":{},"t":{"df":1,"docs":{"16":{"tf":1.0}}}}}}},"]":{"1":{"df":3,"docs":{"19":{"tf":1.4142135623730951},"21":{"tf":1.0},"35":{"tf":1.0}}},"df":0,"docs":{}},"a":{"c":{"df":0,"docs":{},"e":{"b":{"df":0,"docs":{},"o":{"df":0,"docs":{},"o":{"df":0,"docs":{},"k":{"'":{"df":1,"docs":{"103":{"tf":1.0}}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"t":{"df":9,"docs":{"13":{"tf":1.0},"131":{"tf":1.0},"139":{"tf":1.0},"14":{"tf":2.8284271247461903},"146":{"tf":1.0},"149":{"tf":1.0},"16":{"tf":1.7320508075688772},"5":{"tf":1.0},"68":{"tf":1.0}},"o":{"df":0,"docs":{},"r":{"df":8,"docs":{"125":{"tf":1.0},"14":{"tf":1.7320508075688772},"146":{"tf":1.0},"28":{"tf":1.0},"3":{"tf":1.0},"68":{"tf":1.7320508075688772},"73":{"tf":1.0},"91":{"tf":2.0}}}}}},"df":0,"docs":{},"l":{"df":0,"docs":{},"s":{"df":1,"docs":{"104":{"tf":1.0}}}},"r":{"df":2,"docs":{"16":{"tf":1.0},"70":{"tf":1.0}}},"s":{"df":0,"docs":{},"h":{"df":0,"docs":{},"i":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"df":2,"docs":{"114":{"tf":1.0},"151":{"tf":1.0}}}}}},"t":{"df":2,"docs":{"123":{"tf":1.4142135623730951},"99":{"tf":1.0}},"e":{"df":0,"docs":{},"r":{"df":1,"docs":{"123":{"tf":1.0}}}}}}},"b":{"a":{"df":0,"docs":{},"t":{"c":{"df":0,"docs":{},"h":{"df":3,"docs":{"28":{"tf":1.0},"32":{"tf":1.7320508075688772},"33":{"tf":1.0}},"​":{"(":{"df":0,"docs":{},"ζ":{")":{"=":{"df":0,"docs":{},"t":{"df":0,"docs":{},"ˉ":{"+":{"df":0,"docs":{},"υ":{"df":0,"docs":{},"p":{"df":0,"docs":{},"ˉ":{"df":0,"docs":{},"​":{"df":0,"docs":{},"n":{"c":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"υ":{"2":{"a":{"df":0,"docs":{},"ˉ":{"+":{"df":0,"docs":{},"υ":{"3":{"b":{"df":0,"docs":{},"ˉ":{"+":{"df":0,"docs":{},"υ":{"4":{"c":{"df":0,"docs":{},"ˉ":{"+":{"df":0,"docs":{},"υ":{"5":{"df":0,"docs":{},"s":{"df":0,"docs":{},"ˉ":{"df":0,"docs":{},"σ":{"1":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"υ":{"6":{"df":0,"docs":{},"s":{"df":0,"docs":{},"ˉ":{"df":0,"docs":{},"σ":{"2":{"df":1,"docs":{"32":{"tf":1.0}}},"df":0,"docs":{}}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"]":{"1":{"df":1,"docs":{"32":{"tf":1.0}},"​":{"=":{"[":{"df":0,"docs":{},"t":{"df":0,"docs":{},"p":{"a":{"df":0,"docs":{},"r":{"df":0,"docs":{},"t":{"df":0,"docs":{},"i":{"a":{"df":0,"docs":{},"l":{"df":0,"docs":{},"​":{"]":{"1":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"υ":{"[":{"df":0,"docs":{},"p":{"df":0,"docs":{},"n":{"c":{"df":0,"docs":{},"​":{"]":{"1":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"υ":{"2":{"[":{"a":{"]":{"1":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"υ":{"3":{"[":{"b":{"]":{"1":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"υ":{"4":{"[":{"c":{"]":{"1":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"υ":{"5":{"[":{"df":0,"docs":{},"s":{"df":0,"docs":{},"σ":{"1":{"df":0,"docs":{},"​":{"]":{"1":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"υ":{"6":{"[":{"df":0,"docs":{},"s":{"df":0,"docs":{},"σ":{"2":{"df":0,"docs":{},"​":{"]":{"1":{"df":1,"docs":{"32":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":21,"docs":{"14":{"tf":2.6457513110645907},"141":{"tf":1.0},"142":{"tf":1.4142135623730951},"146":{"tf":1.7320508075688772},"147":{"tf":1.4142135623730951},"16":{"tf":1.4142135623730951},"19":{"tf":2.449489742783178},"21":{"tf":2.0},"35":{"tf":1.4142135623730951},"5":{"tf":1.7320508075688772},"69":{"tf":1.4142135623730951},"70":{"tf":1.4142135623730951},"73":{"tf":1.0},"74":{"tf":1.4142135623730951},"75":{"tf":1.0},"80":{"tf":1.0},"82":{"tf":1.0},"90":{"tf":1.7320508075688772},"91":{"tf":1.4142135623730951},"94":{"tf":2.0},"95":{"tf":1.4142135623730951}},"e":{"1":{"7":{":":{":":{"df":0,"docs":{},"n":{"df":0,"docs":{},"e":{"df":0,"docs":{},"w":{"(":{"1":{"df":1,"docs":{"108":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},":":{":":{"df":0,"docs":{},"f":{"df":0,"docs":{},"r":{"df":0,"docs":{},"o":{"df":0,"docs":{},"m":{"(":{"1":{"df":1,"docs":{"104":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"o":{"df":0,"docs":{},"n":{"df":1,"docs":{"104":{"tf":1.4142135623730951}}}}},"df":0,"docs":{}},"a":{"df":0,"docs":{},"t":{"df":0,"docs":{},"u":{"df":0,"docs":{},"r":{"df":1,"docs":{"70":{"tf":1.0}}}}}},"df":0,"docs":{},"e":{"d":{"df":1,"docs":{"34":{"tf":1.0}}},"df":0,"docs":{}},"t":{"c":{"df":0,"docs":{},"h":{"df":1,"docs":{"146":{"tf":1.4142135623730951}}}},"df":0,"docs":{}},"w":{"df":10,"docs":{"109":{"tf":1.0},"111":{"tf":1.0},"123":{"tf":1.4142135623730951},"124":{"tf":1.0},"13":{"tf":1.0},"14":{"tf":1.0},"16":{"tf":1.0},"32":{"tf":1.0},"37":{"tf":1.0},"64":{"tf":1.0}}}},"f":{"df":0,"docs":{},"t":{"df":4,"docs":{"121":{"tf":2.0},"2":{"tf":1.0},"3":{"tf":1.4142135623730951},"99":{"tf":1.4142135623730951}}}},"i":{"a":{"df":0,"docs":{},"t":{"df":7,"docs":{"118":{"tf":1.4142135623730951},"39":{"tf":1.0},"40":{"tf":1.0},"42":{"tf":2.0},"89":{"tf":1.0},"94":{"tf":1.0},"95":{"tf":1.0}}}},"b":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"a":{"c":{"c":{"df":0,"docs":{},"i":{"_":{"df":0,"docs":{},"t":{"df":0,"docs":{},"r":{"a":{"c":{"df":1,"docs":{"108":{"tf":1.0}},"e":{"(":{"[":{"df":0,"docs":{},"f":{"df":0,"docs":{},"e":{"1":{"7":{":":{":":{"df":0,"docs":{},"n":{"df":0,"docs":{},"e":{"df":0,"docs":{},"w":{"(":{"1":{"df":1,"docs":{"108":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"a":{"df":0,"docs":{},"i":{"df":0,"docs":{},"r":{"<":{"df":0,"docs":{},"f":{">":{">":{"(":{"&":{"df":0,"docs":{},"p":{"df":0,"docs":{},"r":{"df":0,"docs":{},"o":{"df":0,"docs":{},"o":{"df":0,"docs":{},"f":{"df":1,"docs":{"104":{"tf":1.0}}}}}}},"t":{"df":0,"docs":{},"r":{"a":{"c":{"df":1,"docs":{"104":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":18,"docs":{"104":{"tf":1.4142135623730951},"105":{"tf":1.4142135623730951},"106":{"tf":1.7320508075688772},"107":{"tf":1.0},"108":{"tf":1.0},"109":{"tf":1.0},"110":{"tf":1.0},"111":{"tf":1.0},"112":{"tf":1.0},"114":{"tf":1.7320508075688772},"117":{"tf":1.0},"49":{"tf":1.7320508075688772},"50":{"tf":1.4142135623730951},"51":{"tf":1.4142135623730951},"64":{"tf":1.0},"66":{"tf":1.0},"68":{"tf":1.0},"84":{"tf":1.0}},"p":{"df":0,"docs":{},"u":{"b":{"df":0,"docs":{},"l":{"df":0,"docs":{},"i":{"c":{"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"df":0,"docs":{},"p":{"df":0,"docs":{},"u":{"df":0,"docs":{},"t":{"df":1,"docs":{"104":{"tf":1.0}}}}}}}},"df":0,"docs":{}}}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":1,"docs":{"131":{"tf":1.0}},"e":{"df":0,"docs":{},"l":{"d":{"df":22,"docs":{"108":{"tf":1.0},"109":{"tf":1.0},"114":{"tf":1.0},"14":{"tf":4.58257569495584},"16":{"tf":1.0},"19":{"tf":1.4142135623730951},"35":{"tf":1.0},"36":{"tf":1.0},"41":{"tf":2.23606797749979},"44":{"tf":1.0},"5":{"tf":1.0},"52":{"tf":1.0},"7":{"tf":1.4142135623730951},"70":{"tf":1.0},"73":{"tf":1.0},"74":{"tf":1.4142135623730951},"82":{"tf":1.0},"90":{"tf":1.0},"91":{"tf":1.0},"95":{"tf":1.0},"98":{"tf":1.0},"99":{"tf":1.0}},"e":{"df":0,"docs":{},"l":{"df":0,"docs":{},"e":{"df":0,"docs":{},"m":{"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"df":0,"docs":{},"t":{":":{":":{"df":0,"docs":{},"f":{"df":0,"docs":{},"r":{"df":0,"docs":{},"o":{"df":0,"docs":{},"m":{"(":{"2":{"_":{"df":0,"docs":{},"u":{"6":{"4":{"df":1,"docs":{"35":{"tf":1.4142135623730951}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"3":{"_":{"df":0,"docs":{},"u":{"6":{"4":{"df":1,"docs":{"35":{"tf":1.4142135623730951}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":1,"docs":{"44":{"tf":1.0}}},"4":{"df":1,"docs":{"44":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}}}}}},"df":0,"docs":{}},"<":{"df":0,"docs":{},"f":{"df":1,"docs":{"35":{"tf":3.1622776601683795}}}},"df":0,"docs":{}}}}}}}}},"df":0,"docs":{}}},"f":{"df":0,"docs":{},"t":{"df":0,"docs":{},"h":{"df":1,"docs":{"52":{"tf":1.0}}}}},"l":{"df":0,"docs":{},"e":{"df":2,"docs":{"144":{"tf":2.23606797749979},"145":{"tf":1.0}}},"l":{"df":6,"docs":{"13":{"tf":1.0},"130":{"tf":2.449489742783178},"134":{"tf":2.0},"139":{"tf":1.0},"146":{"tf":2.23606797749979},"36":{"tf":1.7320508075688772}}}},"n":{"a":{"df":0,"docs":{},"l":{"df":8,"docs":{"13":{"tf":1.0},"135":{"tf":1.0},"144":{"tf":1.4142135623730951},"146":{"tf":2.0},"147":{"tf":1.0},"16":{"tf":1.0},"35":{"tf":1.4142135623730951},"44":{"tf":1.0}}}},"d":{"df":3,"docs":{"128":{"tf":1.4142135623730951},"3":{"tf":1.4142135623730951},"88":{"tf":1.0}}},"df":0,"docs":{},"e":{"df":1,"docs":{"12":{"tf":1.0}}},"i":{"df":0,"docs":{},"t":{"df":10,"docs":{"14":{"tf":1.0},"19":{"tf":1.0},"5":{"tf":1.0},"52":{"tf":1.0},"69":{"tf":1.0},"7":{"tf":1.0},"70":{"tf":1.0},"90":{"tf":1.0},"91":{"tf":1.0},"98":{"tf":1.0}}}}},"r":{"df":0,"docs":{},"s":{"df":0,"docs":{},"t":{"_":{"df":0,"docs":{},"r":{"df":0,"docs":{},"o":{"df":0,"docs":{},"w":{"[":{"0":{"df":2,"docs":{"107":{"tf":1.4142135623730951},"114":{"tf":1.0}}},"df":0,"docs":{}},"df":2,"docs":{"107":{"tf":1.0},"114":{"tf":1.0}}}}}},"df":29,"docs":{"105":{"tf":1.0},"107":{"tf":1.4142135623730951},"108":{"tf":1.0},"114":{"tf":1.4142135623730951},"128":{"tf":1.0},"13":{"tf":1.7320508075688772},"130":{"tf":1.0},"134":{"tf":1.0},"135":{"tf":1.0},"14":{"tf":1.4142135623730951},"145":{"tf":1.0},"146":{"tf":1.7320508075688772},"147":{"tf":2.0},"149":{"tf":1.4142135623730951},"20":{"tf":1.0},"23":{"tf":1.4142135623730951},"31":{"tf":1.0},"32":{"tf":1.4142135623730951},"42":{"tf":1.0},"46":{"tf":1.0},"49":{"tf":1.4142135623730951},"52":{"tf":1.4142135623730951},"59":{"tf":1.7320508075688772},"66":{"tf":1.0},"69":{"tf":1.0},"70":{"tf":1.0},"8":{"tf":1.0},"83":{"tf":1.0},"9":{"tf":1.0}},"l":{"df":0,"docs":{},"i":{"df":1,"docs":{"32":{"tf":1.0}}}}}}},"t":{"df":2,"docs":{"103":{"tf":1.0},"133":{"tf":1.0}}},"v":{"df":0,"docs":{},"e":{"df":1,"docs":{"12":{"tf":1.0}}}},"x":{"df":5,"docs":{"146":{"tf":1.0},"5":{"tf":1.0},"73":{"tf":1.0},"88":{"tf":1.0},"91":{"tf":1.0}}},"​":{"df":0,"docs":{},"~":{"df":0,"docs":{},"​":{"=":{"df":0,"docs":{},"∑":{"df":0,"docs":{},"j":{"=":{"df":0,"docs":{},"i":{"1":{"4":{"df":0,"docs":{},"​":{"2":{"df":0,"docs":{},"j":{"df":0,"docs":{},"−":{"df":0,"docs":{},"i":{"df":0,"docs":{},"f":{"df":0,"docs":{},"i":{"df":1,"docs":{"131":{"tf":1.0}}}}}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{},"−":{"2":{"df":0,"docs":{},"f":{"df":0,"docs":{},"~":{"df":0,"docs":{},"​":{"df":0,"docs":{},"i":{"+":{"1":{"df":0,"docs":{},"​":{")":{"(":{"df":0,"docs":{},"f":{"df":0,"docs":{},"i":{"df":0,"docs":{},"​":{"df":0,"docs":{},"~":{"df":0,"docs":{},"​":{"df":0,"docs":{},"−":{"2":{"df":0,"docs":{},"f":{"df":0,"docs":{},"~":{"df":0,"docs":{},"​":{"df":0,"docs":{},"i":{"+":{"1":{"df":0,"docs":{},"​":{"df":0,"docs":{},"−":{"1":{")":{"=":{"0":{"df":1,"docs":{"131":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}}}}},"df":0,"docs":{}}}}}}}},"df":0,"docs":{}},"=":{"df":0,"docs":{},"f":{"df":0,"docs":{},"i":{"df":1,"docs":{"131":{"tf":1.0}}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}}}},"df":0,"docs":{}}}},"∗":{"(":{"df":0,"docs":{},"f":{"df":0,"docs":{},"i":{"df":0,"docs":{},"​":{"df":0,"docs":{},"−":{"1":{")":{"=":{"0":{"df":1,"docs":{"131":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}}}},"df":0,"docs":{}}}},"l":{"a":{"df":0,"docs":{},"g":{"a":{"df":1,"docs":{"149":{"tf":1.0}}},"b":{"df":1,"docs":{"149":{"tf":1.4142135623730951}}},"df":6,"docs":{"131":{"tf":2.0},"142":{"tf":1.4142135623730951},"146":{"tf":3.0},"147":{"tf":1.4142135623730951},"149":{"tf":3.1622776601683795},"150":{"tf":2.0}}}},"df":0,"docs":{},"e":{"df":0,"docs":{},"x":{"df":0,"docs":{},"i":{"b":{"df":0,"docs":{},"l":{"df":2,"docs":{"12":{"tf":1.4142135623730951},"4":{"tf":1.0}}}},"df":0,"docs":{}}}},"o":{"df":0,"docs":{},"w":{"df":2,"docs":{"103":{"tf":1.0},"50":{"tf":1.0}}}}},"n":{"df":6,"docs":{"104":{"tf":1.0},"106":{"tf":1.0},"107":{"tf":1.0},"114":{"tf":1.0},"44":{"tf":1.0},"45":{"tf":1.4142135623730951}}},"o":{"c":{"df":0,"docs":{},"u":{"df":1,"docs":{"149":{"tf":1.0}}}},"df":0,"docs":{},"l":{"df":0,"docs":{},"l":{"df":0,"docs":{},"o":{"df":0,"docs":{},"w":{"df":49,"docs":{"10":{"tf":1.4142135623730951},"101":{"tf":1.0},"102":{"tf":1.0},"103":{"tf":1.0},"11":{"tf":1.4142135623730951},"111":{"tf":1.0},"113":{"tf":1.0},"114":{"tf":1.7320508075688772},"123":{"tf":1.0},"128":{"tf":1.0},"13":{"tf":1.4142135623730951},"137":{"tf":1.0},"14":{"tf":3.7416573867739413},"142":{"tf":1.4142135623730951},"146":{"tf":1.4142135623730951},"147":{"tf":1.7320508075688772},"151":{"tf":1.0},"16":{"tf":2.0},"19":{"tf":1.4142135623730951},"21":{"tf":1.0},"22":{"tf":1.0},"24":{"tf":1.0},"31":{"tf":1.0},"32":{"tf":1.4142135623730951},"35":{"tf":1.7320508075688772},"37":{"tf":1.0},"42":{"tf":1.0},"44":{"tf":1.4142135623730951},"45":{"tf":1.0},"48":{"tf":1.0},"49":{"tf":1.0},"5":{"tf":1.0},"50":{"tf":1.0},"52":{"tf":1.0},"54":{"tf":1.0},"55":{"tf":1.0},"56":{"tf":1.0},"59":{"tf":1.4142135623730951},"69":{"tf":1.0},"7":{"tf":1.4142135623730951},"72":{"tf":1.0},"73":{"tf":1.0},"75":{"tf":1.0},"84":{"tf":1.0},"88":{"tf":1.0},"91":{"tf":1.0},"92":{"tf":1.0},"94":{"tf":1.7320508075688772},"95":{"tf":2.23606797749979}}}}}},"r":{"c":{"df":2,"docs":{"13":{"tf":1.4142135623730951},"20":{"tf":1.0}}},"df":0,"docs":{},"m":{"a":{"df":0,"docs":{},"l":{"df":1,"docs":{"69":{"tf":1.0}}},"t":{"df":1,"docs":{"69":{"tf":1.0}}}},"df":12,"docs":{"100":{"tf":1.0},"108":{"tf":1.0},"123":{"tf":1.0},"14":{"tf":2.23606797749979},"146":{"tf":1.0},"35":{"tf":1.0},"52":{"tf":1.0},"68":{"tf":1.0},"73":{"tf":1.0},"9":{"tf":1.0},"92":{"tf":1.0},"99":{"tf":1.0}},"e":{"df":0,"docs":{},"r":{"df":2,"docs":{"19":{"tf":1.0},"73":{"tf":1.0}}}},"u":{"df":0,"docs":{},"l":{"a":{"df":2,"docs":{"58":{"tf":1.0},"60":{"tf":1.0}}},"df":0,"docs":{}}}}},"u":{"df":0,"docs":{},"n":{"d":{"df":8,"docs":{"127":{"tf":1.0},"130":{"tf":1.4142135623730951},"133":{"tf":1.0},"134":{"tf":1.4142135623730951},"137":{"tf":1.0},"138":{"tf":1.0},"38":{"tf":1.0},"4":{"tf":1.0}}},"df":0,"docs":{}},"r":{"df":2,"docs":{"21":{"tf":1.0},"56":{"tf":1.0}},"i":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":3,"docs":{"123":{"tf":1.0},"3":{"tf":1.4142135623730951},"99":{"tf":1.0}}}}}}}},"p":{"df":6,"docs":{"136":{"tf":1.4142135623730951},"141":{"tf":1.0},"142":{"tf":1.0},"144":{"tf":1.0},"146":{"tf":1.4142135623730951},"147":{"tf":1.0}}},"r":{"a":{"df":0,"docs":{},"m":{"df":0,"docs":{},"e":{".":{"df":0,"docs":{},"g":{"df":0,"docs":{},"e":{"df":0,"docs":{},"t":{"_":{"df":0,"docs":{},"r":{"df":0,"docs":{},"o":{"df":0,"docs":{},"w":{"(":{"0":{"df":2,"docs":{"107":{"tf":1.0},"114":{"tf":1.0}}},"1":{"df":2,"docs":{"107":{"tf":1.0},"114":{"tf":1.0}}},"2":{"df":2,"docs":{"107":{"tf":1.0},"114":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}}}}},":":{":":{"df":0,"docs":{},"r":{"df":0,"docs":{},"e":{"a":{"d":{"_":{"df":0,"docs":{},"f":{"df":0,"docs":{},"r":{"df":0,"docs":{},"o":{"df":0,"docs":{},"m":{"_":{"df":0,"docs":{},"t":{"df":0,"docs":{},"r":{"a":{"c":{"df":1,"docs":{"114":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":7,"docs":{"107":{"tf":1.4142135623730951},"109":{"tf":1.0},"113":{"tf":1.0},"114":{"tf":2.8284271247461903},"117":{"tf":2.23606797749979},"149":{"tf":1.7320508075688772},"84":{"tf":1.0}}}}},"df":1,"docs":{"38":{"tf":1.0}},"e":{"df":0,"docs":{},"s":{"df":0,"docs":{},"h":{"df":1,"docs":{"40":{"tf":1.0}}}}},"i":{"_":{"df":0,"docs":{},"n":{"df":0,"docs":{},"u":{"df":0,"docs":{},"m":{"b":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"_":{"df":0,"docs":{},"o":{"df":0,"docs":{},"f":{"_":{"df":0,"docs":{},"q":{"df":0,"docs":{},"u":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":0,"docs":{},"i":{"df":1,"docs":{"109":{"tf":1.0}}}}}}}},"df":0,"docs":{}}}},"df":0,"docs":{}}}},"df":0,"docs":{}}}}},"d":{"a":{"df":0,"docs":{},"y":{"df":1,"docs":{"81":{"tf":1.0}}}},"df":0,"docs":{}},"df":22,"docs":{"102":{"tf":1.0},"109":{"tf":1.0},"112":{"tf":1.4142135623730951},"113":{"tf":1.0},"119":{"tf":1.4142135623730951},"125":{"tf":1.7320508075688772},"57":{"tf":2.0},"59":{"tf":1.7320508075688772},"62":{"tf":1.4142135623730951},"63":{"tf":1.0},"68":{"tf":1.7320508075688772},"71":{"tf":1.0},"73":{"tf":2.0},"74":{"tf":2.0},"75":{"tf":1.0},"77":{"tf":1.4142135623730951},"78":{"tf":1.0},"80":{"tf":1.7320508075688772},"81":{"tf":1.7320508075688772},"91":{"tf":1.0},"94":{"tf":1.4142135623730951},"95":{"tf":1.0}},"e":{"df":0,"docs":{},"n":{"d":{"df":0,"docs":{},"l":{"df":0,"docs":{},"i":{"df":1,"docs":{"83":{"tf":1.0}}}}},"df":0,"docs":{}}}}},"u":{"df":0,"docs":{},"l":{"df":0,"docs":{},"l":{"df":2,"docs":{"10":{"tf":1.0},"131":{"tf":1.0}}}},"n":{"c":{"df":0,"docs":{},"t":{"df":0,"docs":{},"i":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"df":24,"docs":{"104":{"tf":1.4142135623730951},"108":{"tf":1.0},"110":{"tf":1.0},"111":{"tf":1.0},"113":{"tf":1.0},"114":{"tf":1.0},"118":{"tf":1.0},"142":{"tf":1.0},"146":{"tf":1.4142135623730951},"35":{"tf":1.7320508075688772},"40":{"tf":1.0},"45":{"tf":2.6457513110645907},"54":{"tf":1.0},"55":{"tf":1.0},"56":{"tf":1.0},"74":{"tf":1.7320508075688772},"77":{"tf":1.0},"80":{"tf":1.0},"84":{"tf":1.4142135623730951},"85":{"tf":1.0},"88":{"tf":1.0},"90":{"tf":1.7320508075688772},"91":{"tf":1.4142135623730951},"95":{"tf":1.0}}}}}}},"df":0,"docs":{}},"r":{"df":0,"docs":{},"t":{"df":0,"docs":{},"h":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":4,"docs":{"100":{"tf":1.0},"147":{"tf":1.0},"21":{"tf":1.0},"74":{"tf":1.0}},"m":{"df":0,"docs":{},"o":{"df":0,"docs":{},"r":{"df":1,"docs":{"13":{"tf":1.0}}}}}}}}}}}},"g":{"+":{"df":0,"docs":{},"ζ":{"df":0,"docs":{},"k":{"df":0,"docs":{},"​":{"df":0,"docs":{},"h":{"df":1,"docs":{"95":{"tf":1.4142135623730951}}}}}}},",":{"df":0,"docs":{},"h":{"df":1,"docs":{"95":{"tf":1.0}}}},"1":{"df":0,"docs":{},"p":{"df":0,"docs":{},"o":{"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"df":0,"docs":{},"t":{"df":1,"docs":{"35":{"tf":2.8284271247461903}}}}}}}},"3":{"df":1,"docs":{"124":{"tf":1.0}}},"=":{"df":0,"docs":{},"ω":{"b":{"df":1,"docs":{"91":{"tf":1.0}}},"df":0,"docs":{}}},"^":{"0":{"df":2,"docs":{"52":{"tf":1.0},"55":{"tf":1.0}}},"1":{"df":2,"docs":{"52":{"tf":1.0},"55":{"tf":1.0}}},"2":{"df":2,"docs":{"52":{"tf":1.4142135623730951},"55":{"tf":1.0}}},"3":{"df":2,"docs":{"52":{"tf":1.0},"55":{"tf":1.0}}},"4":{"df":2,"docs":{"52":{"tf":1.0},"55":{"tf":1.0}}},"5":{"df":2,"docs":{"52":{"tf":1.7320508075688772},"55":{"tf":1.0}}},"8":{"df":1,"docs":{"52":{"tf":1.0}}},"df":0,"docs":{},"i":{"df":2,"docs":{"52":{"tf":1.0},"62":{"tf":1.0}}}},"a":{"df":0,"docs":{},"m":{"df":0,"docs":{},"m":{"a":{"_":{"df":0,"docs":{},"i":{"df":1,"docs":{"59":{"tf":1.0}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"p":{"df":1,"docs":{"134":{"tf":1.0}}},"t":{"df":0,"docs":{},"e":{"df":8,"docs":{"10":{"tf":2.6457513110645907},"11":{"tf":1.0},"12":{"tf":2.23606797749979},"13":{"tf":1.0},"14":{"tf":1.0},"16":{"tf":1.4142135623730951},"20":{"tf":1.0},"9":{"tf":3.3166247903554}}},"h":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":1,"docs":{"126":{"tf":1.0}}}}}},"v":{"df":0,"docs":{},"e":{"df":2,"docs":{"113":{"tf":1.4142135623730951},"63":{"tf":1.4142135623730951}}}}},"df":13,"docs":{"124":{"tf":2.23606797749979},"125":{"tf":1.4142135623730951},"14":{"tf":2.449489742783178},"142":{"tf":1.4142135623730951},"146":{"tf":2.0},"147":{"tf":1.4142135623730951},"16":{"tf":1.0},"19":{"tf":2.0},"52":{"tf":2.23606797749979},"54":{"tf":1.0},"68":{"tf":2.0},"69":{"tf":1.0},"82":{"tf":1.0}},"e":{"df":0,"docs":{},"n":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":26,"docs":{"103":{"tf":1.0},"104":{"tf":1.4142135623730951},"108":{"tf":1.0},"118":{"tf":1.0},"119":{"tf":1.4142135623730951},"124":{"tf":2.0},"144":{"tf":1.4142135623730951},"146":{"tf":1.0},"149":{"tf":1.0},"34":{"tf":1.4142135623730951},"35":{"tf":1.4142135623730951},"4":{"tf":1.0},"44":{"tf":1.4142135623730951},"48":{"tf":1.0},"49":{"tf":1.0},"50":{"tf":1.7320508075688772},"52":{"tf":1.0},"65":{"tf":1.4142135623730951},"68":{"tf":1.0},"70":{"tf":1.0},"77":{"tf":1.0},"8":{"tf":1.0},"80":{"tf":1.4142135623730951},"82":{"tf":1.0},"84":{"tf":1.0},"90":{"tf":1.0}}}}},"t":{"_":{"df":0,"docs":{},"r":{"df":0,"docs":{},"o":{"df":0,"docs":{},"w":{"df":1,"docs":{"107":{"tf":1.0}}}}}},"df":4,"docs":{"118":{"tf":1.4142135623730951},"151":{"tf":1.0},"75":{"tf":1.0},"82":{"tf":1.0}}}},"i":{":":{"0":{"df":0,"docs":{},"≤":{"df":0,"docs":{},"i":{"<":{"2":{"df":0,"docs":{},"n":{"df":1,"docs":{"112":{"tf":1.0}}}},"df":0,"docs":{},"n":{"df":1,"docs":{"124":{"tf":1.0}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":1,"docs":{"125":{"tf":1.4142135623730951}},"l":{"df":0,"docs":{},"l":{"df":0,"docs":{},"e":{"df":0,"docs":{},"t":{"df":0,"docs":{},"t":{"'":{"df":1,"docs":{"103":{"tf":1.0}}},"df":0,"docs":{}}}}}},"v":{"df":0,"docs":{},"e":{"df":8,"docs":{"114":{"tf":1.0},"14":{"tf":1.0},"149":{"tf":1.4142135623730951},"151":{"tf":1.7320508075688772},"19":{"tf":1.0},"46":{"tf":1.0},"50":{"tf":1.0},"7":{"tf":1.0}},"n":{"df":14,"docs":{"114":{"tf":1.7320508075688772},"128":{"tf":1.0},"130":{"tf":1.0},"14":{"tf":1.0},"146":{"tf":1.0},"16":{"tf":1.4142135623730951},"23":{"tf":1.0},"36":{"tf":1.0},"63":{"tf":1.0},"72":{"tf":1.0},"75":{"tf":1.0},"76":{"tf":1.0},"90":{"tf":1.0},"92":{"tf":1.0}}}}},"z":{"a":{"df":1,"docs":{"103":{"tf":1.0}}},"df":0,"docs":{}}},"k":{"df":1,"docs":{"124":{"tf":1.0}}},"l":{"df":0,"docs":{},"o":{"b":{"a":{"df":0,"docs":{},"l":{"df":1,"docs":{"70":{"tf":1.4142135623730951}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"n":{"+":{"1":{"=":{"df":0,"docs":{},"g":{"df":0,"docs":{},"g":{"df":0,"docs":{},"n":{"+":{"2":{"=":{"df":0,"docs":{},"g":{"2":{"df":1,"docs":{"124":{"tf":1.0}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}},"=":{"1":{"df":1,"docs":{"124":{"tf":1.0}}},"df":0,"docs":{}},"a":{"df":0,"docs":{},"r":{"df":0,"docs":{},"k":{"df":1,"docs":{"34":{"tf":1.0}}}}},"df":0,"docs":{}},"o":{"a":{"df":0,"docs":{},"l":{"df":4,"docs":{"103":{"tf":1.0},"46":{"tf":1.0},"50":{"tf":1.0},"77":{"tf":1.0}}}},"df":17,"docs":{"103":{"tf":1.0},"104":{"tf":1.4142135623730951},"109":{"tf":1.0},"111":{"tf":1.4142135623730951},"114":{"tf":1.0},"118":{"tf":1.0},"124":{"tf":1.0},"14":{"tf":1.7320508075688772},"42":{"tf":1.0},"46":{"tf":1.0},"48":{"tf":1.0},"49":{"tf":1.0},"50":{"tf":1.0},"51":{"tf":1.0},"53":{"tf":1.0},"57":{"tf":1.0},"68":{"tf":1.0}},"l":{"d":{"b":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":0,"docs":{},"g":{"df":4,"docs":{"103":{"tf":1.0},"19":{"tf":1.0},"35":{"tf":1.0},"38":{"tf":1.0}}}}}},"df":0,"docs":{}},"df":0,"docs":{}},"o":{"d":{"df":4,"docs":{"14":{"tf":1.0},"46":{"tf":1.0},"50":{"tf":1.0},"81":{"tf":1.0}}},"df":0,"docs":{}},"t":{"df":0,"docs":{},"h":{"df":0,"docs":{},"i":{"c":{"df":1,"docs":{"95":{"tf":1.0}}},"df":0,"docs":{}}}}},"p":{"df":0,"docs":{},"u":{"df":1,"docs":{"1":{"tf":1.0}}}},"r":{"a":{"df":0,"docs":{},"n":{"df":0,"docs":{},"t":{"df":1,"docs":{"14":{"tf":1.0}}}}},"df":0,"docs":{},"e":{"a":{"df":0,"docs":{},"t":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":2,"docs":{"3":{"tf":1.0},"68":{"tf":1.0}}}}}},"df":0,"docs":{},"e":{"df":0,"docs":{},"k":{"df":1,"docs":{"95":{"tf":1.0}}}}},"i":{"df":0,"docs":{},"n":{"d":{"df":4,"docs":{"88":{"tf":1.0},"91":{"tf":1.0},"94":{"tf":1.0},"95":{"tf":1.0}},"i":{"df":0,"docs":{},"n":{"df":0,"docs":{},"g":{"_":{"df":0,"docs":{},"f":{"a":{"c":{"df":0,"docs":{},"t":{"df":0,"docs":{},"o":{"df":0,"docs":{},"r":{"df":1,"docs":{"88":{"tf":1.4142135623730951}}}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}}},"df":0,"docs":{}}},"o":{"df":0,"docs":{},"u":{"df":0,"docs":{},"p":{"df":3,"docs":{"142":{"tf":1.4142135623730951},"147":{"tf":1.0},"19":{"tf":2.23606797749979}}}}}}},"h":{"(":{"df":0,"docs":{},"h":{"(":{"df":0,"docs":{},"p":{"df":0,"docs":{},"r":{"df":0,"docs":{},"e":{"df":0,"docs":{},"f":{"df":0,"docs":{},"i":{"df":0,"docs":{},"x":{"df":1,"docs":{"88":{"tf":1.0}}}}}}}}},"df":0,"docs":{}},"x":{")":{"=":{"df":0,"docs":{},"h":{"1":{"df":0,"docs":{},"​":{"(":{"df":0,"docs":{},"x":{"2":{")":{"+":{"df":0,"docs":{},"x":{"df":0,"docs":{},"h":{"2":{"df":0,"docs":{},"​":{"(":{"df":0,"docs":{},"x":{"2":{"df":2,"docs":{"116":{"tf":1.0},"67":{"tf":1.0}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"β":{"1":{"df":0,"docs":{},"​":{"b":{"(":{"df":0,"docs":{},"x":{")":{"+":{"df":0,"docs":{},"β":{"2":{"df":0,"docs":{},"​":{"c":{"(":{"df":0,"docs":{},"x":{"df":1,"docs":{"56":{"tf":1.0}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"0":{"df":2,"docs":{"112":{"tf":1.0},"113":{"tf":1.0}}},"_":{"0":{"df":2,"docs":{"62":{"tf":1.0},"63":{"tf":1.0}}},"df":0,"docs":{}},"df":1,"docs":{"56":{"tf":1.0}}},"z":{")":{"=":{"df":0,"docs":{},"f":{"(":{"df":0,"docs":{},"z":{")":{"/":{"df":0,"docs":{},"g":{"(":{"df":0,"docs":{},"z":{"df":1,"docs":{"69":{"tf":1.0}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"β":{"1":{"df":0,"docs":{},"​":{"b":{"(":{"df":0,"docs":{},"z":{")":{"+":{"df":0,"docs":{},"β":{"2":{"df":0,"docs":{},"​":{"c":{"(":{"df":0,"docs":{},"z":{"df":3,"docs":{"113":{"tf":1.0},"58":{"tf":1.0},"63":{"tf":1.0}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":7,"docs":{"112":{"tf":1.0},"113":{"tf":1.4142135623730951},"59":{"tf":1.0},"62":{"tf":1.0},"63":{"tf":1.4142135623730951},"69":{"tf":1.0},"85":{"tf":1.0}}}},".":{"df":0,"docs":{},"t":{"df":0,"docs":{},"h":{"df":0,"docs":{},"i":{"df":1,"docs":{"14":{"tf":1.0}}}}}},"1":{"df":2,"docs":{"94":{"tf":1.4142135623730951},"95":{"tf":1.0}},"​":{"(":{"df":0,"docs":{},"z":{"2":{"df":1,"docs":{"94":{"tf":1.4142135623730951}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"2":{"df":2,"docs":{"94":{"tf":1.4142135623730951},"95":{"tf":1.0}},"​":{"(":{"df":0,"docs":{},"z":{"2":{"df":1,"docs":{"94":{"tf":1.4142135623730951}}},"df":0,"docs":{}}},"df":0,"docs":{}}},":":{"=":{"df":0,"docs":{},"k":{"df":0,"docs":{},"∑":{"df":0,"docs":{},"​":{"df":0,"docs":{},"β":{"df":0,"docs":{},"k":{"df":0,"docs":{},"t":{"df":0,"docs":{},"​":{"c":{"df":0,"docs":{},"k":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"j":{"df":0,"docs":{},"∑":{"df":0,"docs":{},"​":{"df":0,"docs":{},"β":{"df":0,"docs":{},"j":{"b":{"df":0,"docs":{},"​":{"b":{"df":0,"docs":{},"j":{"df":1,"docs":{"94":{"tf":1.0}}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}}}},"df":0,"docs":{}}}},"df":0,"docs":{}}}}}}}},"η":{"1":{"df":0,"docs":{},"z":{"2":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"z":{"df":0,"docs":{},"η":{"2":{"df":0,"docs":{},"z":{"2":{"df":1,"docs":{"95":{"tf":1.0}}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"=":{"df":0,"docs":{},"h":{"1":{"df":0,"docs":{},"​":{"(":{"df":0,"docs":{},"x":{"2":{")":{"+":{"df":0,"docs":{},"x":{"df":0,"docs":{},"h":{"2":{"df":0,"docs":{},"​":{"(":{"df":0,"docs":{},"x":{"2":{"df":1,"docs":{"94":{"tf":1.0}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"k":{"df":0,"docs":{},"∑":{"df":0,"docs":{},"​":{"df":0,"docs":{},"β":{"df":0,"docs":{},"k":{"df":0,"docs":{},"t":{"df":0,"docs":{},"​":{"c":{"df":0,"docs":{},"k":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"j":{"df":0,"docs":{},"∑":{"df":0,"docs":{},"​":{"df":0,"docs":{},"β":{"df":0,"docs":{},"j":{"b":{"df":0,"docs":{},"​":{"b":{"df":0,"docs":{},"j":{"df":1,"docs":{"95":{"tf":1.0}}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}}}},"df":0,"docs":{}}}},"df":0,"docs":{}}}}}}}},"s":{"df":0,"docs":{},"h":{"a":{"3":{"df":1,"docs":{"40":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}}},"{":{"1":{",":{"df":0,"docs":{},"ω":{",":{"df":0,"docs":{},"ω":{"2":{",":{"df":0,"docs":{},"…":{",":{"df":0,"docs":{},"ω":{"df":0,"docs":{},"k":{"df":0,"docs":{},"−":{"1":{"df":1,"docs":{"14":{"tf":1.4142135623730951}}},"df":0,"docs":{}}},"n":{"df":0,"docs":{},"−":{"1":{"df":3,"docs":{"14":{"tf":1.4142135623730951},"16":{"tf":1.0},"23":{"tf":1.0}}},"df":0,"docs":{}}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{},"h":{"0":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"…":{",":{"df":0,"docs":{},"h":{"df":0,"docs":{},"n":{"df":0,"docs":{},"​":{"df":0,"docs":{},"}":{"df":0,"docs":{},"⊂":{"df":0,"docs":{},"f":{"df":1,"docs":{"5":{"tf":1.0}}}}}}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"ω":{"df":0,"docs":{},"i":{":":{"0":{"df":0,"docs":{},"≤":{"df":0,"docs":{},"i":{"<":{"df":0,"docs":{},"n":{"df":1,"docs":{"14":{"tf":1.0}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"_":{"1":{"(":{"df":0,"docs":{},"x":{"df":1,"docs":{"67":{"tf":1.0}}}},"df":2,"docs":{"116":{"tf":1.7320508075688772},"67":{"tf":1.0}}},"2":{"(":{"df":0,"docs":{},"x":{"df":1,"docs":{"67":{"tf":1.0}}}},"df":2,"docs":{"116":{"tf":1.7320508075688772},"67":{"tf":1.0}}},"df":0,"docs":{}},"a":{"df":0,"docs":{},"l":{"df":0,"docs":{},"f":{"df":1,"docs":{"125":{"tf":1.0}}}},"n":{"d":{"c":{"df":0,"docs":{},"r":{"a":{"df":0,"docs":{},"f":{"df":0,"docs":{},"t":{"df":1,"docs":{"83":{"tf":1.0}}}}},"df":0,"docs":{}}},"df":4,"docs":{"114":{"tf":1.0},"117":{"tf":1.0},"52":{"tf":1.0},"56":{"tf":1.0}},"l":{"df":2,"docs":{"110":{"tf":1.0},"9":{"tf":1.0}}}},"df":0,"docs":{}},"p":{"df":0,"docs":{},"p":{"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"df":4,"docs":{"116":{"tf":1.0},"125":{"tf":1.0},"146":{"tf":1.0},"77":{"tf":1.0}}}},"i":{"df":1,"docs":{"35":{"tf":1.0}}}}},"r":{"d":{"df":2,"docs":{"14":{"tf":1.0},"20":{"tf":1.0}}},"df":0,"docs":{}},"s":{"df":0,"docs":{},"h":{"(":{"df":0,"docs":{},"m":{"df":0,"docs":{},"e":{"df":0,"docs":{},"s":{"df":0,"docs":{},"s":{"a":{"df":0,"docs":{},"g":{"df":0,"docs":{},"e":{"_":{"2":{"df":1,"docs":{"40":{"tf":1.7320508075688772}}},"3":{"df":1,"docs":{"40":{"tf":1.4142135623730951}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}}}}},"df":7,"docs":{"118":{"tf":1.0},"151":{"tf":1.0},"45":{"tf":1.0},"75":{"tf":1.7320508075688772},"88":{"tf":1.0},"91":{"tf":1.4142135623730951},"92":{"tf":1.0}},"e":{"df":0,"docs":{},"r":{"df":2,"docs":{"110":{"tf":1.0},"40":{"tf":2.6457513110645907}}}},"m":{"a":{"df":0,"docs":{},"p":{":":{":":{"df":0,"docs":{},"f":{"df":0,"docs":{},"r":{"df":0,"docs":{},"o":{"df":0,"docs":{},"m":{"(":{"[":{"(":{"df":0,"docs":{},"x":{"df":1,"docs":{"44":{"tf":1.0}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"v":{"df":0,"docs":{},"e":{"df":3,"docs":{"110":{"tf":1.0},"130":{"tf":1.0},"49":{"tf":1.0}},"n":{"'":{"df":0,"docs":{},"t":{"df":1,"docs":{"69":{"tf":1.0}}}},"df":0,"docs":{}}}}},"df":35,"docs":{"112":{"tf":1.4142135623730951},"113":{"tf":1.0},"114":{"tf":2.0},"116":{"tf":1.7320508075688772},"117":{"tf":1.0},"118":{"tf":1.0},"125":{"tf":2.23606797749979},"134":{"tf":1.0},"14":{"tf":2.8284271247461903},"147":{"tf":1.4142135623730951},"20":{"tf":2.23606797749979},"24":{"tf":1.0},"25":{"tf":1.0},"26":{"tf":1.0},"32":{"tf":1.4142135623730951},"35":{"tf":1.0},"53":{"tf":1.0},"56":{"tf":2.23606797749979},"57":{"tf":2.0},"58":{"tf":2.0},"59":{"tf":1.7320508075688772},"60":{"tf":1.0},"62":{"tf":1.4142135623730951},"65":{"tf":1.0},"66":{"tf":1.0},"67":{"tf":1.7320508075688772},"69":{"tf":1.4142135623730951},"7":{"tf":1.4142135623730951},"73":{"tf":1.0},"75":{"tf":1.0},"82":{"tf":1.0},"84":{"tf":2.449489742783178},"85":{"tf":2.0},"88":{"tf":1.0},"94":{"tf":1.4142135623730951}},"e":{"df":0,"docs":{},"l":{"df":0,"docs":{},"i":{"df":0,"docs":{},"o":{"df":1,"docs":{"42":{"tf":1.0}}}},"p":{"df":2,"docs":{"45":{"tf":1.0},"70":{"tf":1.0}},"e":{"df":0,"docs":{},"r":{"df":2,"docs":{"108":{"tf":1.0},"35":{"tf":1.0}}}}}},"n":{"c":{"df":2,"docs":{"125":{"tf":1.0},"130":{"tf":1.0}}},"df":0,"docs":{}},"r":{"df":0,"docs":{},"e":{"'":{"df":1,"docs":{"20":{"tf":1.0}}},"df":27,"docs":{"11":{"tf":1.0},"114":{"tf":1.0},"118":{"tf":1.0},"124":{"tf":1.0},"133":{"tf":1.4142135623730951},"135":{"tf":1.0},"137":{"tf":1.0},"14":{"tf":2.449489742783178},"142":{"tf":1.0},"151":{"tf":1.4142135623730951},"21":{"tf":1.0},"35":{"tf":1.4142135623730951},"4":{"tf":1.0},"40":{"tf":1.4142135623730951},"52":{"tf":1.0},"54":{"tf":1.0},"56":{"tf":1.0},"57":{"tf":1.0},"7":{"tf":1.4142135623730951},"70":{"tf":1.0},"72":{"tf":1.0},"75":{"tf":1.0},"88":{"tf":1.0},"9":{"tf":1.0},"90":{"tf":1.0},"94":{"tf":1.7320508075688772},"98":{"tf":1.0}}}},"u":{"df":0,"docs":{},"r":{"df":0,"docs":{},"i":{"df":0,"docs":{},"s":{"df":0,"docs":{},"t":{"df":4,"docs":{"118":{"tf":1.0},"40":{"tf":1.0},"42":{"tf":1.0},"89":{"tf":1.0}}}}}}}},"i":{"d":{"df":0,"docs":{},"e":{"df":1,"docs":{"19":{"tf":1.0}}}},"df":0,"docs":{},"g":{"df":0,"docs":{},"h":{"df":6,"docs":{"104":{"tf":1.0},"35":{"tf":1.0},"46":{"tf":1.0},"59":{"tf":1.0},"69":{"tf":1.4142135623730951},"82":{"tf":1.0}},"e":{"df":0,"docs":{},"r":{"df":4,"docs":{"125":{"tf":1.0},"146":{"tf":1.0},"20":{"tf":1.0},"68":{"tf":1.0}}}}}},"m":{"df":0,"docs":{},"s":{"df":0,"docs":{},"e":{"df":0,"docs":{},"l":{"df":0,"docs":{},"f":{"df":1,"docs":{"21":{"tf":1.4142135623730951}}}}}}}},"o":{"df":0,"docs":{},"l":{"d":{"df":20,"docs":{"10":{"tf":1.0},"107":{"tf":2.0},"108":{"tf":1.4142135623730951},"109":{"tf":1.7320508075688772},"11":{"tf":1.0},"123":{"tf":1.4142135623730951},"13":{"tf":1.0},"131":{"tf":1.0},"133":{"tf":1.0},"14":{"tf":2.23606797749979},"146":{"tf":1.4142135623730951},"16":{"tf":1.4142135623730951},"35":{"tf":1.0},"54":{"tf":1.0},"69":{"tf":1.0},"7":{"tf":1.0},"73":{"tf":2.23606797749979},"74":{"tf":2.0},"75":{"tf":1.0},"77":{"tf":1.0}}},"df":0,"docs":{},"e":{"df":3,"docs":{"130":{"tf":2.0},"134":{"tf":2.8284271247461903},"146":{"tf":2.6457513110645907}}}},"m":{"df":0,"docs":{},"o":{"df":0,"docs":{},"m":{"df":0,"docs":{},"o":{"df":0,"docs":{},"r":{"df":0,"docs":{},"p":{"df":0,"docs":{},"h":{"df":1,"docs":{"19":{"tf":1.0}}}}}}}}},"n":{"df":0,"docs":{},"e":{"df":0,"docs":{},"s":{"df":0,"docs":{},"t":{"df":4,"docs":{"118":{"tf":1.0},"20":{"tf":1.0},"77":{"tf":1.0},"78":{"tf":1.0}}}}}},"o":{"d":{"df":2,"docs":{"111":{"tf":1.0},"114":{"tf":1.0}}},"df":0,"docs":{}},"s":{"df":0,"docs":{},"t":{"df":1,"docs":{"0":{"tf":1.0}}}}},"u":{"df":0,"docs":{},"g":{"df":0,"docs":{},"e":{"df":2,"docs":{"123":{"tf":1.7320508075688772},"80":{"tf":1.0}}}}},"ω":{"df":0,"docs":{},"i":{":":{"0":{"df":0,"docs":{},"≤":{"df":0,"docs":{},"i":{"<":{"2":{"df":0,"docs":{},"n":{"df":1,"docs":{"125":{"tf":1.0}}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"∈":{"d":{"df":1,"docs":{"14":{"tf":1.4142135623730951}}},"df":0,"docs":{},"f":{"df":0,"docs":{},"∖":{"df":0,"docs":{},"{":{"df":0,"docs":{},"ω":{"df":0,"docs":{},"i":{"df":0,"docs":{},"}":{"df":0,"docs":{},"i":{"df":0,"docs":{},"≥":{"0":{"df":1,"docs":{"91":{"tf":1.0}}},"df":0,"docs":{}}}}}}}}},"h":{"df":2,"docs":{"14":{"tf":1.7320508075688772},"16":{"tf":1.0}}}}},"i":{"+":{"1":{"df":2,"docs":{"101":{"tf":1.0},"70":{"tf":1.4142135623730951}}},"2":{"df":0,"docs":{},"k":{"df":0,"docs":{},"−":{"1":{"df":1,"docs":{"101":{"tf":1.0}}},"df":0,"docs":{}}}},"df":0,"docs":{}},",":{"df":0,"docs":{},"j":{")":{",":{"df":0,"docs":{},"v":{"df":1,"docs":{"14":{"tf":1.4142135623730951}}}},"df":0,"docs":{},"∈":{"df":0,"docs":{},"i":{"df":1,"docs":{"14":{"tf":1.0}}}}},",":{"df":0,"docs":{},"k":{",":{"df":0,"docs":{},"l":{"df":4,"docs":{"11":{"tf":1.0},"13":{"tf":1.0},"14":{"tf":1.0},"16":{"tf":1.0}}}},"df":0,"docs":{}},"l":{"df":1,"docs":{"14":{"tf":1.0}}}},"df":1,"docs":{"14":{"tf":2.0}}}},".":{"df":6,"docs":{"107":{"tf":1.0},"114":{"tf":1.0},"125":{"tf":1.0},"52":{"tf":1.0},"59":{"tf":1.4142135623730951},"68":{"tf":1.0}}},":":{"0":{"df":0,"docs":{},"≤":{"df":0,"docs":{},"i":{"<":{"2":{"df":0,"docs":{},"n":{"+":{"1":{"df":1,"docs":{"125":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}},"=":{"0":{"df":0,"docs":{},"k":{"df":0,"docs":{},"−":{"1":{"df":0,"docs":{},"​":{"(":{"a":{"df":0,"docs":{},"i":{"df":1,"docs":{"14":{"tf":1.4142135623730951}},"​":{"+":{"df":0,"docs":{},"γ":{")":{"/":{"df":0,"docs":{},"∏":{"df":0,"docs":{},"i":{"=":{"0":{"df":0,"docs":{},"k":{"df":0,"docs":{},"−":{"1":{"df":0,"docs":{},"​":{"(":{"b":{"df":0,"docs":{},"i":{"df":1,"docs":{"14":{"tf":1.0}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"b":{"df":0,"docs":{},"i":{"df":1,"docs":{"14":{"tf":1.4142135623730951}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"∏":{"df":0,"docs":{},"k":{"df":0,"docs":{},"−":{"1":{"df":0,"docs":{},"​":{"(":{"a":{"df":0,"docs":{},"i":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"γ":{")":{"=":{"df":0,"docs":{},"i":{"=":{"0":{"df":0,"docs":{},"∏":{"df":0,"docs":{},"k":{"df":0,"docs":{},"−":{"1":{"df":0,"docs":{},"​":{"(":{"b":{"df":0,"docs":{},"i":{"df":1,"docs":{"14":{"tf":1.0}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"∑":{"df":0,"docs":{},"n":{"df":0,"docs":{},"​":{"df":0,"docs":{},"l":{"df":0,"docs":{},"i":{"df":0,"docs":{},"​":{"(":{"df":0,"docs":{},"ζ":{")":{"(":{"df":0,"docs":{},"p":{"df":0,"docs":{},"i":{")":{"df":0,"docs":{},"i":{"df":1,"docs":{"32":{"tf":1.0}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}}}}}},"1":{"df":1,"docs":{"101":{"tf":1.0}}},"df":0,"docs":{},"{":{"(":{"df":0,"docs":{},"i":{",":{"df":0,"docs":{},"j":{"df":1,"docs":{"14":{"tf":1.0}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"∣":{"df":0,"docs":{},"l":{"df":0,"docs":{},"j":{"df":1,"docs":{"142":{"tf":1.0}}}}}},">":{"df":0,"docs":{},"n":{"df":2,"docs":{"14":{"tf":1.0},"16":{"tf":1.0}}}},"b":{"df":0,"docs":{},"​":{"b":{"df":0,"docs":{},"i":{"df":0,"docs":{},"​":{"(":{"df":0,"docs":{},"x":{"df":1,"docs":{"114":{"tf":1.0}}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"d":{"df":0,"docs":{},"e":{"a":{"df":12,"docs":{"118":{"tf":1.0},"14":{"tf":1.0},"151":{"tf":1.0},"4":{"tf":1.0},"54":{"tf":1.0},"59":{"tf":1.0},"6":{"tf":1.0},"65":{"tf":1.0},"66":{"tf":1.0},"67":{"tf":1.0},"7":{"tf":1.0},"79":{"tf":1.0}}},"df":0,"docs":{},"n":{"df":0,"docs":{},"t":{"df":0,"docs":{},"i":{"df":0,"docs":{},"f":{"df":0,"docs":{},"i":{"df":1,"docs":{"52":{"tf":1.0}}}}}}}}},"df":0,"docs":{},"l":{"df":0,"docs":{},"u":{"df":0,"docs":{},"s":{"df":0,"docs":{},"t":{"df":0,"docs":{},"r":{"df":1,"docs":{"111":{"tf":1.0}}}}}}},"m":{"a":{"df":0,"docs":{},"g":{"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"df":1,"docs":{"149":{"tf":1.0}}}}}},"df":0,"docs":{},"m":{"df":0,"docs":{},"e":{"d":{"df":0,"docs":{},"i":{"df":1,"docs":{"113":{"tf":1.0}}}},"df":0,"docs":{}}},"p":{"df":0,"docs":{},"l":{"df":0,"docs":{},"e":{"df":0,"docs":{},"m":{"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"df":0,"docs":{},"t":{"df":18,"docs":{"103":{"tf":1.7320508075688772},"105":{"tf":1.0},"110":{"tf":1.0},"111":{"tf":1.0},"123":{"tf":1.0},"124":{"tf":1.0},"127":{"tf":1.0},"19":{"tf":1.0},"34":{"tf":1.4142135623730951},"36":{"tf":1.0},"37":{"tf":1.4142135623730951},"40":{"tf":1.0},"41":{"tf":1.0},"46":{"tf":1.0},"64":{"tf":1.0},"67":{"tf":1.0},"68":{"tf":1.0},"9":{"tf":1.0}}}}}}},"i":{"c":{"df":0,"docs":{},"i":{"df":0,"docs":{},"t":{"df":0,"docs":{},"l":{"df":0,"docs":{},"i":{"df":2,"docs":{"52":{"tf":1.0},"95":{"tf":1.0}}}}}}},"df":1,"docs":{"101":{"tf":1.0}}}},"o":{"df":0,"docs":{},"r":{"df":0,"docs":{},"t":{"df":10,"docs":{"10":{"tf":1.0},"105":{"tf":1.0},"124":{"tf":1.0},"134":{"tf":1.0},"14":{"tf":1.4142135623730951},"146":{"tf":1.7320508075688772},"16":{"tf":1.0},"68":{"tf":1.0},"73":{"tf":1.0},"92":{"tf":1.0}}}}},"r":{"df":0,"docs":{},"o":{"df":0,"docs":{},"v":{"df":3,"docs":{"123":{"tf":1.0},"151":{"tf":1.0},"21":{"tf":1.0}}}}}}},"n":{"c":{"df":0,"docs":{},"l":{"df":0,"docs":{},"u":{"d":{"df":7,"docs":{"125":{"tf":1.7320508075688772},"13":{"tf":1.0},"146":{"tf":1.0},"149":{"tf":1.0},"23":{"tf":1.0},"66":{"tf":1.0},"84":{"tf":1.0}}},"df":0,"docs":{}}},"o":{"df":0,"docs":{},"r":{"df":0,"docs":{},"p":{"df":0,"docs":{},"o":{"df":0,"docs":{},"r":{"df":2,"docs":{"13":{"tf":1.4142135623730951},"147":{"tf":1.4142135623730951}}}}}}},"r":{"df":0,"docs":{},"e":{"a":{"df":0,"docs":{},"s":{"df":3,"docs":{"147":{"tf":1.4142135623730951},"88":{"tf":1.0},"94":{"tf":1.4142135623730951}}}},"d":{"df":1,"docs":{"50":{"tf":1.0}}},"df":0,"docs":{},"m":{"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"df":0,"docs":{},"t":{"df":1,"docs":{"142":{"tf":1.0}}}}}}}}},"d":{"df":0,"docs":{},"e":{"df":2,"docs":{"57":{"tf":1.0},"59":{"tf":1.4142135623730951}},"p":{"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"d":{"df":4,"docs":{"11":{"tf":1.0},"13":{"tf":1.4142135623730951},"150":{"tf":1.0},"80":{"tf":1.0}}},"df":0,"docs":{}}}},"x":{"df":11,"docs":{"101":{"tf":2.0},"109":{"tf":1.0},"11":{"tf":2.23606797749979},"13":{"tf":1.4142135623730951},"14":{"tf":1.4142135623730951},"149":{"tf":1.0},"36":{"tf":1.0},"72":{"tf":1.0},"80":{"tf":1.0},"94":{"tf":1.0},"95":{"tf":1.4142135623730951}}}},"i":{"c":{"df":4,"docs":{"11":{"tf":1.0},"14":{"tf":1.0},"28":{"tf":1.0},"95":{"tf":1.0}}},"df":0,"docs":{}},"u":{"c":{"df":2,"docs":{"14":{"tf":1.0},"90":{"tf":1.4142135623730951}}},"df":0,"docs":{}}},"df":1,"docs":{"146":{"tf":1.0}},"e":{"df":0,"docs":{},"f":{"df":0,"docs":{},"f":{"df":0,"docs":{},"i":{"c":{"df":0,"docs":{},"i":{"df":1,"docs":{"84":{"tf":1.0}}}},"df":0,"docs":{}}}}},"f":{"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"df":0,"docs":{},"i":{"df":0,"docs":{},"t":{"df":1,"docs":{"146":{"tf":1.4142135623730951}}}}}},"o":{"df":0,"docs":{},"r":{"df":0,"docs":{},"m":{"df":10,"docs":{"119":{"tf":1.0},"126":{"tf":1.0},"142":{"tf":1.0},"144":{"tf":1.7320508075688772},"145":{"tf":1.0},"146":{"tf":1.4142135623730951},"19":{"tf":1.0},"52":{"tf":1.0},"70":{"tf":1.0},"9":{"tf":1.0}}}}}},"g":{"df":0,"docs":{},"r":{"df":0,"docs":{},"e":{"d":{"df":0,"docs":{},"i":{"df":2,"docs":{"151":{"tf":1.0},"44":{"tf":1.0}}}},"df":0,"docs":{}}}},"i":{"df":0,"docs":{},"t":{"df":0,"docs":{},"i":{"df":5,"docs":{"146":{"tf":1.0},"151":{"tf":1.0},"31":{"tf":1.4142135623730951},"70":{"tf":1.0},"94":{"tf":1.0}}}}},"n":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":2,"docs":{"19":{"tf":1.0},"73":{"tf":1.0}}}}},"p":{"df":0,"docs":{},"u":{"df":0,"docs":{},"t":{"df":24,"docs":{"104":{"tf":2.449489742783178},"106":{"tf":1.4142135623730951},"11":{"tf":1.0},"110":{"tf":1.0},"121":{"tf":1.4142135623730951},"13":{"tf":2.449489742783178},"14":{"tf":1.4142135623730951},"142":{"tf":1.0},"144":{"tf":1.0},"15":{"tf":1.4142135623730951},"151":{"tf":1.0},"16":{"tf":1.7320508075688772},"23":{"tf":1.7320508075688772},"26":{"tf":1.0},"3":{"tf":1.7320508075688772},"32":{"tf":1.4142135623730951},"34":{"tf":2.0},"35":{"tf":3.3166247903554},"36":{"tf":1.0},"42":{"tf":1.4142135623730951},"44":{"tf":2.449489742783178},"7":{"tf":3.1622776601683795},"9":{"tf":1.0},"95":{"tf":1.0}}}}},"s":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":0,"docs":{},"t":{"df":3,"docs":{"130":{"tf":1.0},"133":{"tf":1.4142135623730951},"134":{"tf":1.7320508075688772}}}}},"i":{"d":{"df":1,"docs":{"45":{"tf":1.0}}},"df":0,"docs":{},"g":{"df":0,"docs":{},"h":{"df":0,"docs":{},"t":{"df":1,"docs":{"103":{"tf":1.0}}}}}},"p":{"df":0,"docs":{},"e":{"c":{"df":0,"docs":{},"t":{"df":2,"docs":{"101":{"tf":1.0},"14":{"tf":1.0}}}},"df":0,"docs":{}}},"t":{"a":{"df":0,"docs":{},"n":{"c":{"df":5,"docs":{"13":{"tf":1.0},"14":{"tf":1.0},"16":{"tf":1.4142135623730951},"35":{"tf":2.6457513110645907},"8":{"tf":1.0}}},"df":0,"docs":{},"t":{"df":0,"docs":{},"i":{"df":4,"docs":{"106":{"tf":1.0},"109":{"tf":1.0},"110":{"tf":1.0},"35":{"tf":1.7320508075688772}}}}}},"df":3,"docs":{"142":{"tf":1.0},"146":{"tf":1.4142135623730951},"147":{"tf":1.4142135623730951}},"e":{"a":{"d":{"df":13,"docs":{"101":{"tf":1.0},"123":{"tf":1.0},"131":{"tf":1.0},"14":{"tf":1.0},"146":{"tf":1.0},"21":{"tf":1.0},"32":{"tf":1.0},"54":{"tf":1.0},"55":{"tf":1.0},"80":{"tf":1.4142135623730951},"84":{"tf":1.0},"9":{"tf":1.0},"92":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}},"r":{"df":0,"docs":{},"u":{"c":{"df":0,"docs":{},"t":{"df":8,"docs":{"130":{"tf":1.0},"131":{"tf":1.4142135623730951},"133":{"tf":2.449489742783178},"142":{"tf":1.4142135623730951},"146":{"tf":3.872983346207417},"147":{"tf":1.0},"151":{"tf":1.4142135623730951},"50":{"tf":1.0}}}},"df":0,"docs":{}}}}},"t":{"df":0,"docs":{},"e":{"df":0,"docs":{},"g":{"df":3,"docs":{"142":{"tf":1.0},"146":{"tf":1.0},"41":{"tf":1.4142135623730951}},"r":{"df":2,"docs":{"105":{"tf":1.0},"84":{"tf":1.0}}}},"n":{"df":0,"docs":{},"s":{"df":1,"docs":{"99":{"tf":1.0}}}},"r":{"a":{"c":{"df":0,"docs":{},"t":{"df":14,"docs":{"118":{"tf":1.4142135623730951},"127":{"tf":1.0},"128":{"tf":1.0},"137":{"tf":1.7320508075688772},"145":{"tf":1.0},"146":{"tf":1.0},"70":{"tf":1.0},"71":{"tf":1.0},"73":{"tf":1.0},"75":{"tf":1.0},"77":{"tf":1.0},"82":{"tf":1.0},"91":{"tf":1.0},"95":{"tf":1.0}}}},"df":0,"docs":{}},"df":0,"docs":{},"e":{"df":0,"docs":{},"s":{"df":0,"docs":{},"t":{"df":1,"docs":{"14":{"tf":1.0}}}}},"l":{"df":0,"docs":{},"e":{"a":{"df":0,"docs":{},"v":{"df":2,"docs":{"149":{"tf":1.0},"150":{"tf":1.0}}}},"df":0,"docs":{}}},"m":{"df":0,"docs":{},"e":{"d":{"df":0,"docs":{},"i":{"df":2,"docs":{"11":{"tf":1.0},"48":{"tf":1.0}}}},"df":0,"docs":{}}},"n":{"df":4,"docs":{"118":{"tf":1.4142135623730951},"40":{"tf":2.23606797749979},"71":{"tf":1.0},"94":{"tf":1.0}}},"p":{"df":0,"docs":{},"o":{"df":0,"docs":{},"l":{"df":26,"docs":{"112":{"tf":1.0},"114":{"tf":2.6457513110645907},"121":{"tf":1.4142135623730951},"123":{"tf":2.0},"125":{"tf":1.0},"14":{"tf":3.0},"16":{"tf":1.0},"20":{"tf":1.4142135623730951},"24":{"tf":1.0},"25":{"tf":1.0},"26":{"tf":1.0},"3":{"tf":1.7320508075688772},"32":{"tf":1.0},"35":{"tf":1.0},"36":{"tf":1.0},"5":{"tf":1.0},"52":{"tf":1.0},"54":{"tf":1.0},"62":{"tf":1.0},"68":{"tf":1.0},"73":{"tf":1.0},"82":{"tf":1.0},"83":{"tf":1.0},"84":{"tf":1.0},"91":{"tf":1.0},"94":{"tf":1.4142135623730951}}}},"r":{"df":0,"docs":{},"e":{"df":0,"docs":{},"t":{"df":2,"docs":{"41":{"tf":1.0},"70":{"tf":1.0}}}}}}}},"r":{"df":0,"docs":{},"o":{"d":{"df":0,"docs":{},"u":{"c":{"df":3,"docs":{"125":{"tf":1.0},"14":{"tf":1.0},"53":{"tf":1.0}},"t":{"df":1,"docs":{"0":{"tf":1.0}}}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"v":{"a":{"df":0,"docs":{},"l":{"df":0,"docs":{},"i":{"d":{"df":2,"docs":{"79":{"tf":1.0},"9":{"tf":1.0}}},"df":0,"docs":{}}},"r":{"df":0,"docs":{},"i":{"df":1,"docs":{"20":{"tf":1.0}}}}},"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":0,"docs":{},"s":{"df":5,"docs":{"123":{"tf":1.0},"146":{"tf":1.0},"45":{"tf":1.0},"7":{"tf":1.0},"98":{"tf":1.7320508075688772}}},"t":{"df":2,"docs":{"7":{"tf":1.0},"98":{"tf":1.0}}}}},"o":{"df":0,"docs":{},"l":{"df":0,"docs":{},"v":{"df":8,"docs":{"103":{"tf":1.0},"104":{"tf":1.0},"105":{"tf":1.0},"123":{"tf":1.4142135623730951},"149":{"tf":1.0},"21":{"tf":1.0},"53":{"tf":1.0},"70":{"tf":1.0}}}}}}},"o":{"df":1,"docs":{"151":{"tf":1.0}}},"s":{"c":{"df":0,"docs":{},"o":{"df":0,"docs":{},"m":{"df":0,"docs":{},"m":{"df":0,"docs":{},"i":{"df":0,"docs":{},"t":{"df":0,"docs":{},"m":{"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"df":0,"docs":{},"t":{"df":0,"docs":{},"s":{"c":{"df":0,"docs":{},"h":{"df":0,"docs":{},"e":{"df":0,"docs":{},"m":{"df":0,"docs":{},"e":{"<":{"df":0,"docs":{},"f":{"df":1,"docs":{"35":{"tf":1.7320508075688772}}}},"df":0,"docs":{}}}}}},"df":0,"docs":{}}}}}}}}}}}},"df":0,"docs":{},"f":{"df":0,"docs":{},"i":{"df":0,"docs":{},"e":{"df":0,"docs":{},"l":{"d":{"df":1,"docs":{"35":{"tf":2.23606797749979}}},"df":0,"docs":{}}}}},"n":{"'":{"df":0,"docs":{},"t":{"df":3,"docs":{"121":{"tf":1.0},"149":{"tf":1.0},"65":{"tf":1.0}}}},"df":0,"docs":{}},"r":{"a":{"df":0,"docs":{},"n":{"d":{"df":0,"docs":{},"o":{"df":0,"docs":{},"m":{"df":0,"docs":{},"f":{"df":0,"docs":{},"i":{"df":0,"docs":{},"e":{"df":0,"docs":{},"l":{"d":{"df":0,"docs":{},"e":{"df":0,"docs":{},"l":{"df":0,"docs":{},"e":{"df":0,"docs":{},"m":{"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"df":0,"docs":{},"t":{"df":0,"docs":{},"g":{"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"a":{"df":0,"docs":{},"t":{"df":0,"docs":{},"o":{"df":0,"docs":{},"r":{"<":{"df":0,"docs":{},"f":{"df":1,"docs":{"35":{"tf":1.0}}}},"df":0,"docs":{}}}}},"df":0,"docs":{}}}}}}}}}}}}}},"df":0,"docs":{}}}}}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"s":{"df":0,"docs":{},"u":{"df":2,"docs":{"146":{"tf":1.0},"151":{"tf":1.0}}}}},"t":{"'":{"df":15,"docs":{"107":{"tf":1.0},"11":{"tf":1.0},"124":{"tf":1.0},"125":{"tf":1.0},"14":{"tf":1.0},"146":{"tf":1.7320508075688772},"151":{"tf":1.0},"20":{"tf":1.0},"35":{"tf":1.0},"49":{"tf":1.4142135623730951},"50":{"tf":1.4142135623730951},"52":{"tf":1.0},"54":{"tf":1.0},"55":{"tf":1.0},"81":{"tf":1.0}}},"df":0,"docs":{},"e":{"df":0,"docs":{},"m":{"df":4,"docs":{"112":{"tf":1.0},"49":{"tf":1.0},"59":{"tf":1.4142135623730951},"62":{"tf":1.0}}},"r":{"df":2,"docs":{"102":{"tf":1.0},"114":{"tf":1.0}}}},"s":{"df":0,"docs":{},"e":{"df":0,"docs":{},"l":{"df":0,"docs":{},"f":{"df":3,"docs":{"10":{"tf":1.0},"14":{"tf":1.0},"58":{"tf":1.0}}}}}},"​":{"c":{"df":0,"docs":{},"i":{"df":0,"docs":{},"​":{"(":{"df":0,"docs":{},"x":{"df":1,"docs":{"114":{"tf":1.0}}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"∈":{"[":{"0":{",":{"2":{"df":0,"docs":{},"n":{"+":{"df":0,"docs":{},"k":{"df":1,"docs":{"92":{"tf":1.0}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"":{"=":{"df":0,"docs":{},"ω":{"df":0,"docs":{},"j":{"df":0,"docs":{},"k":{"1":{"df":0,"docs":{},"​":{"df":0,"docs":{},"":{"=":{"df":0,"docs":{},"ω":{"df":0,"docs":{},"l":{"df":0,"docs":{},"k":{"2":{"df":1,"docs":{"14":{"tf":1.0}}},"df":0,"docs":{}}}}},"df":0,"docs":{}}}},"df":0,"docs":{}}}}},"df":0,"docs":{}}},"j":{"=":{"1":{",":{"df":0,"docs":{},"…":{",":{"df":0,"docs":{},"m":{"df":4,"docs":{"91":{"tf":1.4142135623730951},"94":{"tf":1.4142135623730951},"95":{"tf":1.0},"97":{"tf":2.0}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{},"m":{"df":0,"docs":{},"′":{"+":{"1":{",":{"df":0,"docs":{},"…":{",":{"df":0,"docs":{},"m":{"df":0,"docs":{},"′":{"+":{"df":0,"docs":{},"m":{"df":2,"docs":{"94":{"tf":1.4142135623730951},"95":{"tf":1.0}}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}},"b":{"df":1,"docs":{"97":{"tf":1.4142135623730951}},"​":{":":{"=":{"df":0,"docs":{},"β":{"df":0,"docs":{},"j":{"+":{"df":0,"docs":{},"n":{"df":0,"docs":{},"t":{"df":0,"docs":{},"​":{"df":0,"docs":{},"−":{"1":{"df":1,"docs":{"97":{"tf":1.0}}},"df":0,"docs":{}}}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":9,"docs":{"106":{"tf":1.0},"14":{"tf":1.0},"147":{"tf":1.4142135623730951},"5":{"tf":1.0},"70":{"tf":1.4142135623730951},"83":{"tf":1.0},"85":{"tf":1.0},"92":{"tf":1.4142135623730951},"94":{"tf":1.4142135623730951}},"g":{"df":0,"docs":{},"z":{"df":1,"docs":{"95":{"tf":1.4142135623730951}}}},"o":{"b":{"df":3,"docs":{"114":{"tf":1.0},"118":{"tf":1.0},"20":{"tf":1.0}}},"df":0,"docs":{}},"u":{"df":0,"docs":{},"m":{"df":0,"docs":{},"p":{"df":2,"docs":{"146":{"tf":1.0},"50":{"tf":1.0}}}}},"z":{"df":1,"docs":{"95":{"tf":1.4142135623730951}}},"υ":{"df":0,"docs":{},"s":{"df":0,"docs":{},"​":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"t":{"df":0,"docs":{},"j":{"df":1,"docs":{"95":{"tf":1.0}}}}},"df":0,"docs":{}}}}},"​":{",":{"df":0,"docs":{},"γ":{"df":0,"docs":{},"j":{"df":1,"docs":{"97":{"tf":1.0}}}}},":":{"=":{"df":0,"docs":{},"α":{"df":0,"docs":{},"j":{"df":1,"docs":{"97":{"tf":1.0}}}}},"df":0,"docs":{}},"df":0,"docs":{}},"′":{"df":0,"docs":{},"​":{":":{"=":{"df":0,"docs":{},"α":{"df":0,"docs":{},"j":{"+":{"df":0,"docs":{},"m":{"df":0,"docs":{},"−":{"1":{"df":1,"docs":{"97":{"tf":1.0}}},"df":0,"docs":{}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"":{"=":{"df":0,"docs":{},"i":{"df":1,"docs":{"5":{"tf":1.0}}}},"df":0,"docs":{}}},"k":{"+":{"1":{"df":0,"docs":{},"υ":{"df":0,"docs":{},"s":{"2":{"df":0,"docs":{},"k":{"+":{"1":{"df":1,"docs":{"95":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"1":{"2":{"df":1,"docs":{"35":{"tf":1.0}}},"df":2,"docs":{"14":{"tf":1.0},"35":{"tf":1.4142135623730951}}},"2":{"df":2,"docs":{"14":{"tf":1.0},"35":{"tf":1.0}}},"<":{"df":0,"docs":{},"n":{"df":2,"docs":{"94":{"tf":1.0},"95":{"tf":1.0}},"−":{"1":{"df":1,"docs":{"95":{"tf":1.0}}},"df":0,"docs":{}}}},"=":{"0":{",":{"df":0,"docs":{},"…":{",":{"df":0,"docs":{},"n":{"df":0,"docs":{},"−":{"1":{"df":1,"docs":{"95":{"tf":1.0}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"1":{",":{"df":0,"docs":{},"…":{",":{"df":0,"docs":{},"n":{"df":2,"docs":{"94":{"tf":1.0},"95":{"tf":1.0}},"t":{"df":2,"docs":{"91":{"tf":1.4142135623730951},"97":{"tf":1.4142135623730951}}},"−":{"1":{"df":1,"docs":{"94":{"tf":1.0}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"3":{"df":1,"docs":{"101":{"tf":1.0}}},"df":0,"docs":{},"n":{"df":1,"docs":{"95":{"tf":1.0}}},"ω":{"0":{"df":1,"docs":{"14":{"tf":1.0}}},"df":0,"docs":{}}},">":{"1":{"df":1,"docs":{"102":{"tf":1.0}}},"df":0,"docs":{}},"a":{"df":0,"docs":{},"t":{"df":0,"docs":{},"e":{"df":3,"docs":{"19":{"tf":1.0},"35":{"tf":1.0},"38":{"tf":1.0}}}}},"df":6,"docs":{"101":{"tf":1.0},"124":{"tf":1.0},"14":{"tf":2.0},"147":{"tf":1.4142135623730951},"49":{"tf":1.0},"70":{"tf":1.4142135623730951}},"e":{"c":{"c":{"a":{"df":0,"docs":{},"k":{"2":{"5":{"6":{"(":{"df":0,"docs":{},"x":{"df":0,"docs":{},"∣":{"df":0,"docs":{},"∣":{"df":0,"docs":{},"i":{"df":2,"docs":{"94":{"tf":1.0},"95":{"tf":1.0}}}}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":1,"docs":{"151":{"tf":1.0}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{},"e":{"df":0,"docs":{},"p":{"df":5,"docs":{"118":{"tf":1.0},"124":{"tf":1.0},"20":{"tf":1.0},"45":{"tf":1.0},"50":{"tf":1.0}}}},"y":{"df":3,"docs":{"146":{"tf":1.0},"151":{"tf":1.0},"44":{"tf":1.0}}}},"i":{"df":0,"docs":{},"n":{"d":{"df":1,"docs":{"74":{"tf":1.0}}},"df":0,"docs":{}}},"n":{"df":0,"docs":{},"o":{"df":0,"docs":{},"w":{"df":14,"docs":{"114":{"tf":1.0},"119":{"tf":1.0},"146":{"tf":1.7320508075688772},"151":{"tf":1.0},"44":{"tf":1.0},"45":{"tf":1.0},"57":{"tf":1.0},"58":{"tf":1.0},"7":{"tf":1.0},"74":{"tf":1.0},"75":{"tf":1.0},"79":{"tf":1.0},"80":{"tf":1.0},"83":{"tf":1.0}},"l":{"df":0,"docs":{},"e":{"d":{"df":0,"docs":{},"g":{"df":3,"docs":{"20":{"tf":1.0},"4":{"tf":1.0},"7":{"tf":1.4142135623730951}}}},"df":0,"docs":{}}},"n":{"df":6,"docs":{"151":{"tf":1.0},"21":{"tf":1.0},"42":{"tf":1.0},"73":{"tf":1.0},"91":{"tf":1.0},"98":{"tf":1.0}}}}}},"t":{"df":1,"docs":{"97":{"tf":1.4142135623730951}},"​":{":":{"=":{"df":0,"docs":{},"β":{"df":0,"docs":{},"k":{"df":0,"docs":{},"−":{"1":{"df":1,"docs":{"97":{"tf":1.0}}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"z":{"df":0,"docs":{},"g":{":":{":":{"df":0,"docs":{},"n":{"df":0,"docs":{},"e":{"df":0,"docs":{},"w":{"(":{"df":0,"docs":{},"s":{"df":0,"docs":{},"r":{"df":2,"docs":{"35":{"tf":1.4142135623730951},"44":{"tf":1.0}}}}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":2,"docs":{"35":{"tf":2.23606797749979},"44":{"tf":1.4142135623730951}}}},"υ":{"df":0,"docs":{},"s":{"2":{"df":0,"docs":{},"k":{"df":1,"docs":{"102":{"tf":1.0}},"​":{"df":0,"docs":{},"​":{"df":0,"docs":{},"π":{"df":0,"docs":{},"k":{"df":0,"docs":{},"−":{"df":0,"docs":{},"υ":{"df":0,"docs":{},"s":{"2":{"df":0,"docs":{},"k":{"df":0,"docs":{},"​":{"df":0,"docs":{},"​":{"df":0,"docs":{},"​":{"=":{"df":0,"docs":{},"g":{"+":{"df":0,"docs":{},"υ":{"df":0,"docs":{},"s":{"2":{"df":0,"docs":{},"k":{"df":0,"docs":{},"​":{"df":0,"docs":{},"h":{"=":{"df":0,"docs":{},"g":{"df":0,"docs":{},"−":{"df":0,"docs":{},"υ":{"df":0,"docs":{},"s":{"2":{"df":0,"docs":{},"k":{"df":0,"docs":{},"​":{"df":0,"docs":{},"h":{"df":1,"docs":{"95":{"tf":1.0}}}}}},"df":0,"docs":{}}}}}},"df":0,"docs":{}}}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}}},"df":0,"docs":{}}}}}}}}}},"df":0,"docs":{}}},"−":{"1":{"df":3,"docs":{"14":{"tf":1.0},"94":{"tf":1.0},"95":{"tf":1.0}}},"df":0,"docs":{}},"≥":{"0":{"df":1,"docs":{"20":{"tf":1.0}}},"df":0,"docs":{}}},"l":{",":{"df":0,"docs":{},"r":{",":{"df":0,"docs":{},"o":{"df":2,"docs":{"10":{"tf":1.0},"9":{"tf":1.0}}}},"df":0,"docs":{}}},"=":{"df":0,"docs":{},"∣":{"d":{"df":0,"docs":{},"k":{"df":0,"docs":{},"−":{"1":{"df":0,"docs":{},"​":{"df":0,"docs":{},"∣":{"/":{"2":{"df":1,"docs":{"94":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"a":{"df":0,"docs":{},"g":{"df":0,"docs":{},"r":{"a":{"df":0,"docs":{},"n":{"df":0,"docs":{},"g":{"df":7,"docs":{"123":{"tf":1.0},"3":{"tf":1.7320508075688772},"32":{"tf":1.0},"35":{"tf":1.0},"5":{"tf":1.0},"73":{"tf":1.0},"99":{"tf":1.0}}}}},"df":0,"docs":{}}},"m":{"b":{"d":{"a":{"df":0,"docs":{},"w":{"df":0,"docs":{},"o":{"df":0,"docs":{},"r":{"df":0,"docs":{},"k":{"df":6,"docs":{"0":{"tf":1.0},"1":{"tf":1.7320508075688772},"103":{"tf":1.0},"19":{"tf":1.0},"45":{"tf":1.7320508075688772},"87":{"tf":1.0}},"s":{"_":{"c":{"df":0,"docs":{},"r":{"df":0,"docs":{},"y":{"df":0,"docs":{},"p":{"df":0,"docs":{},"t":{"df":0,"docs":{},"o":{"df":1,"docs":{"38":{"tf":1.0}}}}}}}},"df":0,"docs":{},"m":{"a":{"df":0,"docs":{},"t":{"df":0,"docs":{},"h":{":":{":":{"df":0,"docs":{},"e":{"df":0,"docs":{},"l":{"df":0,"docs":{},"l":{"df":0,"docs":{},"i":{"df":0,"docs":{},"p":{"df":0,"docs":{},"t":{"df":0,"docs":{},"i":{"c":{"_":{"c":{"df":0,"docs":{},"u":{"df":0,"docs":{},"r":{"df":0,"docs":{},"v":{"df":0,"docs":{},"e":{":":{":":{"df":0,"docs":{},"s":{"df":0,"docs":{},"h":{"df":0,"docs":{},"o":{"df":0,"docs":{},"r":{"df":0,"docs":{},"t":{"_":{"df":0,"docs":{},"w":{"df":0,"docs":{},"e":{"df":0,"docs":{},"i":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":0,"docs":{},"s":{"df":0,"docs":{},"t":{"df":0,"docs":{},"r":{"a":{"df":0,"docs":{},"s":{"df":0,"docs":{},"s":{":":{":":{"c":{"df":0,"docs":{},"u":{"df":0,"docs":{},"r":{"df":0,"docs":{},"v":{"df":0,"docs":{},"e":{"df":0,"docs":{},"s":{":":{":":{"b":{"df":0,"docs":{},"l":{"df":0,"docs":{},"s":{"1":{"2":{"_":{"3":{"8":{"1":{":":{":":{"d":{"df":0,"docs":{},"e":{"df":0,"docs":{},"f":{"a":{"df":0,"docs":{},"u":{"df":0,"docs":{},"l":{"df":0,"docs":{},"t":{"_":{"df":0,"docs":{},"t":{"df":0,"docs":{},"y":{"df":0,"docs":{},"p":{"df":0,"docs":{},"e":{"df":0,"docs":{},"s":{":":{":":{"df":0,"docs":{},"f":{"df":0,"docs":{},"r":{"df":0,"docs":{},"f":{"df":0,"docs":{},"i":{"df":0,"docs":{},"e":{"df":0,"docs":{},"l":{"d":{"df":2,"docs":{"44":{"tf":1.0},"45":{"tf":1.0}}},"df":0,"docs":{}}}}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}}}}},"df":0,"docs":{}}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}}}}}}}}},"df":0,"docs":{}}}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}}}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}},"p":{"df":0,"docs":{},"l":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"df":0,"docs":{},"k":{":":{":":{"c":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"df":0,"docs":{},"s":{"df":0,"docs":{},"t":{"df":0,"docs":{},"r":{"a":{"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"df":0,"docs":{},"t":{"_":{"df":0,"docs":{},"s":{"df":0,"docs":{},"y":{"df":0,"docs":{},"s":{"df":0,"docs":{},"t":{"df":0,"docs":{},"e":{"df":0,"docs":{},"m":{":":{":":{"c":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"df":0,"docs":{},"s":{"df":0,"docs":{},"t":{"df":0,"docs":{},"r":{"a":{"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"df":0,"docs":{},"t":{"df":0,"docs":{},"s":{"df":0,"docs":{},"y":{"df":0,"docs":{},"s":{"df":0,"docs":{},"t":{"df":0,"docs":{},"e":{"df":0,"docs":{},"m":{"df":2,"docs":{"44":{"tf":1.0},"45":{"tf":1.0}}}}}}}}}}}},"df":0,"docs":{}}}}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}}}}}},"df":0,"docs":{}}}}},"df":0,"docs":{}}}}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}}}}},"df":0,"docs":{}}}}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"n":{"d":{"df":1,"docs":{"14":{"tf":1.4142135623730951}}},"df":0,"docs":{},"g":{"df":0,"docs":{},"u":{"a":{"df":0,"docs":{},"g":{"df":1,"docs":{"50":{"tf":1.0}}}},"df":0,"docs":{}}}},"s":{"df":0,"docs":{},"t":{"df":14,"docs":{"10":{"tf":1.0},"107":{"tf":1.0},"109":{"tf":1.0},"119":{"tf":1.0},"13":{"tf":1.4142135623730951},"134":{"tf":1.0},"14":{"tf":2.0},"142":{"tf":1.4142135623730951},"146":{"tf":2.0},"147":{"tf":1.0},"151":{"tf":1.0},"16":{"tf":1.0},"40":{"tf":1.0},"9":{"tf":1.0}}}},"t":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":3,"docs":{"102":{"tf":1.0},"19":{"tf":1.4142135623730951},"87":{"tf":1.0}}}},"t":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":2,"docs":{"19":{"tf":1.0},"73":{"tf":1.0}}}}}},"y":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":2,"docs":{"102":{"tf":1.4142135623730951},"119":{"tf":1.4142135623730951}}}},"o":{"df":0,"docs":{},"u":{"df":0,"docs":{},"t":{"df":6,"docs":{"127":{"tf":1.7320508075688772},"142":{"tf":1.0},"146":{"tf":1.4142135623730951},"147":{"tf":1.0},"149":{"tf":1.0},"151":{"tf":1.0}}}}}}},"d":{"df":0,"docs":{},"e":{"_":{"d":{"df":0,"docs":{},"o":{"df":0,"docs":{},"m":{"a":{"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{".":{"df":0,"docs":{},"i":{"df":0,"docs":{},"t":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"(":{")":{".":{"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"df":0,"docs":{},"u":{"df":0,"docs":{},"m":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":1,"docs":{"114":{"tf":1.0}}}}}}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}}}},"df":0,"docs":{}}}},"df":0,"docs":{}}}},"df":0,"docs":{},"r":{"df":0,"docs":{},"o":{"df":0,"docs":{},"o":{"df":0,"docs":{},"t":{"df":0,"docs":{},"s":{"_":{"df":0,"docs":{},"o":{"df":0,"docs":{},"f":{"_":{"df":0,"docs":{},"u":{"df":0,"docs":{},"n":{"df":0,"docs":{},"i":{"df":0,"docs":{},"t":{"df":0,"docs":{},"y":{"_":{"c":{"df":0,"docs":{},"o":{"df":0,"docs":{},"s":{"df":0,"docs":{},"e":{"df":0,"docs":{},"t":{"df":1,"docs":{"114":{"tf":1.4142135623730951}}}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}}}}},"df":0,"docs":{}}}},"df":0,"docs":{}}}}}},"t":{"df":0,"docs":{},"r":{"a":{"c":{"df":1,"docs":{"114":{"tf":2.0}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":6,"docs":{"109":{"tf":1.4142135623730951},"114":{"tf":1.4142135623730951},"119":{"tf":1.0},"125":{"tf":1.7320508075688772},"149":{"tf":1.0},"68":{"tf":2.0}}}},"df":7,"docs":{"11":{"tf":1.0},"12":{"tf":1.0},"13":{"tf":1.0},"14":{"tf":1.0},"142":{"tf":1.0},"147":{"tf":1.4142135623730951},"80":{"tf":1.4142135623730951}},"e":{"a":{"d":{"df":3,"docs":{"151":{"tf":1.0},"94":{"tf":1.0},"95":{"tf":1.0}}},"df":0,"docs":{},"f":{"df":2,"docs":{"101":{"tf":1.4142135623730951},"92":{"tf":1.0}}},"r":{"df":0,"docs":{},"n":{"df":1,"docs":{"42":{"tf":1.0}}}},"v":{"df":7,"docs":{"101":{"tf":1.4142135623730951},"14":{"tf":1.0},"144":{"tf":1.0},"16":{"tf":1.0},"20":{"tf":1.0},"80":{"tf":1.0},"92":{"tf":1.0}}}},"df":0,"docs":{},"f":{"df":0,"docs":{},"t":{"df":8,"docs":{"109":{"tf":1.0},"11":{"tf":1.0},"134":{"tf":1.0},"16":{"tf":1.0},"20":{"tf":1.0},"59":{"tf":1.0},"88":{"tf":1.0},"9":{"tf":2.23606797749979}}}},"m":{"df":0,"docs":{},"m":{"a":{"df":1,"docs":{"14":{"tf":1.4142135623730951}}},"df":0,"docs":{}}},"n":{"df":0,"docs":{},"g":{"df":0,"docs":{},"h":{"df":0,"docs":{},"t":{"df":1,"docs":{"131":{"tf":1.0}}}},"t":{"df":0,"docs":{},"h":{"df":7,"docs":{"119":{"tf":1.0},"142":{"tf":1.4142135623730951},"146":{"tf":1.0},"147":{"tf":1.7320508075688772},"36":{"tf":1.0},"51":{"tf":1.0},"72":{"tf":1.0}}}}}},"q":{"df":3,"docs":{"52":{"tf":1.4142135623730951},"62":{"tf":1.0},"68":{"tf":2.0}}},"s":{"df":0,"docs":{},"s":{"df":6,"docs":{"14":{"tf":1.0},"146":{"tf":1.0},"151":{"tf":1.4142135623730951},"48":{"tf":1.0},"73":{"tf":2.6457513110645907},"82":{"tf":1.0}}}},"t":{"'":{"df":24,"docs":{"104":{"tf":1.0},"107":{"tf":1.0},"109":{"tf":1.0},"114":{"tf":1.0},"12":{"tf":1.0},"125":{"tf":1.0},"14":{"tf":2.23606797749979},"149":{"tf":2.23606797749979},"150":{"tf":1.4142135623730951},"151":{"tf":1.7320508075688772},"16":{"tf":1.0},"20":{"tf":1.0},"34":{"tf":1.0},"35":{"tf":1.0},"44":{"tf":1.0},"48":{"tf":1.0},"49":{"tf":1.0},"52":{"tf":1.0},"54":{"tf":1.0},"71":{"tf":1.0},"75":{"tf":1.0},"77":{"tf":1.0},"79":{"tf":1.0},"9":{"tf":1.0}}},"df":0,"docs":{},"t":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":4,"docs":{"146":{"tf":1.0},"149":{"tf":1.0},"5":{"tf":1.0},"95":{"tf":1.7320508075688772}}}}}},"v":{"df":0,"docs":{},"e":{"df":0,"docs":{},"l":{"df":6,"docs":{"104":{"tf":1.0},"46":{"tf":1.0},"59":{"tf":1.0},"67":{"tf":1.0},"69":{"tf":1.7320508075688772},"82":{"tf":1.0}}},"r":{"a":{"df":0,"docs":{},"g":{"df":1,"docs":{"83":{"tf":1.0}}}},"df":0,"docs":{}}}}},"i":{",":{"df":0,"docs":{},"j":{"df":0,"docs":{},"′":{"df":0,"docs":{},"′":{"df":0,"docs":{},"​":{"<":{"df":0,"docs":{},"m":{"a":{"d":{"d":{"df":0,"docs":{},"r":{",":{"df":0,"docs":{},"i":{"+":{"1":{",":{"df":0,"docs":{},"j":{"df":1,"docs":{"142":{"tf":1.0}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}}}},"b":{"df":0,"docs":{},"r":{"a":{"df":0,"docs":{},"r":{"df":0,"docs":{},"i":{"df":1,"docs":{"121":{"tf":1.0}}}}},"df":0,"docs":{}}},"df":3,"docs":{"146":{"tf":1.0},"32":{"tf":1.0},"5":{"tf":1.4142135623730951}},"m":{"b":{"df":1,"docs":{"151":{"tf":1.4142135623730951}}},"df":0,"docs":{}},"n":{"df":0,"docs":{},"e":{"a":{"df":0,"docs":{},"r":{"df":6,"docs":{"14":{"tf":1.0},"21":{"tf":1.7320508075688772},"28":{"tf":1.0},"32":{"tf":1.0},"84":{"tf":1.0},"85":{"tf":1.4142135623730951}}}},"df":1,"docs":{"35":{"tf":1.0}}},"k":{"df":1,"docs":{"151":{"tf":1.0}}}},"o":{"df":0,"docs":{},"r":{"df":1,"docs":{"103":{"tf":1.0}}}},"s":{"df":0,"docs":{},"t":{"df":4,"docs":{"114":{"tf":1.0},"119":{"tf":1.0},"146":{"tf":1.0},"59":{"tf":1.0}}}},"t":{"df":0,"docs":{},"t":{"df":0,"docs":{},"l":{"df":2,"docs":{"107":{"tf":1.0},"14":{"tf":1.0}}}}},"​":{"(":{"df":0,"docs":{},"h":{"df":0,"docs":{},"i":{"df":0,"docs":{},"​":{")":{"=":{"1":{"df":1,"docs":{"5":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"j":{"df":0,"docs":{},"​":{")":{"=":{"0":{"df":1,"docs":{"5":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}}},"o":{"c":{"a":{"df":0,"docs":{},"l":{"df":2,"docs":{"70":{"tf":2.0},"83":{"tf":1.0}}}},"df":0,"docs":{}},"df":0,"docs":{},"g":{"df":0,"docs":{},"i":{"c":{"df":5,"docs":{"146":{"tf":1.0},"151":{"tf":1.0},"16":{"tf":1.0},"36":{"tf":1.0},"4":{"tf":1.0}}},"df":0,"docs":{}}},"n":{"df":0,"docs":{},"g":{"df":2,"docs":{"114":{"tf":1.0},"73":{"tf":1.0}},"e":{"df":0,"docs":{},"r":{"df":2,"docs":{"13":{"tf":1.0},"146":{"tf":1.4142135623730951}}}}}},"o":{"df":0,"docs":{},"k":{"df":8,"docs":{"106":{"tf":1.0},"107":{"tf":1.0},"108":{"tf":1.0},"114":{"tf":1.0},"14":{"tf":1.0},"149":{"tf":2.23606797749979},"21":{"tf":1.0},"51":{"tf":1.0}}},"p":{"df":2,"docs":{"102":{"tf":1.0},"146":{"tf":2.0}}},"s":{"df":1,"docs":{"16":{"tf":1.0}}}},"s":{"df":0,"docs":{},"t":{"df":1,"docs":{"49":{"tf":1.0}}}},"t":{"df":2,"docs":{"50":{"tf":1.0},"53":{"tf":1.0}}},"w":{"df":5,"docs":{"125":{"tf":1.0},"35":{"tf":1.0},"68":{"tf":1.7320508075688772},"81":{"tf":1.0},"91":{"tf":1.0}},"e":{"df":0,"docs":{},"s":{"df":0,"docs":{},"t":{"df":1,"docs":{"3":{"tf":1.0}}}}}}},"p":{"df":0,"docs":{},"u":{"b":{"df":2,"docs":{"142":{"tf":1.0},"147":{"tf":1.0}}},"df":0,"docs":{}}},"u":{"c":{"df":0,"docs":{},"k":{"df":0,"docs":{},"i":{"df":0,"docs":{},"l":{"df":0,"docs":{},"i":{"df":1,"docs":{"146":{"tf":1.0}}}}}}},"df":0,"docs":{}},"×":{"3":{"df":1,"docs":{"141":{"tf":1.0}}},"df":0,"docs":{}},"′":{":":{"=":{"df":0,"docs":{},"⌈":{"df":0,"docs":{},"l":{"df":0,"docs":{},"p":{"df":0,"docs":{},"u":{"b":{"df":0,"docs":{},"​":{"/":{"4":{"df":1,"docs":{"142":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}}}},"df":0,"docs":{}},"df":0,"docs":{},"′":{"=":{"(":{"df":0,"docs":{},"l":{"0":{"df":0,"docs":{},"′":{"df":0,"docs":{},"′":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"l":{"1":{"df":0,"docs":{},"′":{"df":0,"docs":{},"′":{"df":0,"docs":{},"​":{",":{".":{".":{".":{",":{"df":0,"docs":{},"l":{"df":0,"docs":{},"j":{"df":0,"docs":{},"′":{"df":0,"docs":{},"′":{"df":0,"docs":{},"​":{")":{"df":0,"docs":{},"t":{"df":1,"docs":{"142":{"tf":1.0}}}},"df":0,"docs":{}}}}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"m":{",":{"df":0,"docs":{},"v":{")":{"=":{"(":{"0":{",":{"0":{"df":1,"docs":{"146":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":1,"docs":{"146":{"tf":1.0}}}},"/":{"2":{"df":1,"docs":{"133":{"tf":1.0}}},"df":0,"docs":{}},"0":{"df":0,"docs":{},"​":{"=":{"df":0,"docs":{},"r":{"0":{"df":1,"docs":{"151":{"tf":1.0}}},"df":0,"docs":{}}},"df":0,"docs":{}}},":":{"=":{"df":0,"docs":{},"m":{"df":0,"docs":{},"′":{"+":{"df":0,"docs":{},"m":{"df":1,"docs":{"91":{"tf":1.0}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"=":{"2":{"df":0,"docs":{},"m":{"df":3,"docs":{"73":{"tf":1.0},"75":{"tf":1.0},"80":{"tf":1.0}}}},"5":{"2":{"df":1,"docs":{"147":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{},"n":{"+":{"df":0,"docs":{},"k":{"df":1,"docs":{"20":{"tf":1.0}}}},"df":0,"docs":{}}},"^":{"df":0,"docs":{},"j":{"df":1,"docs":{"94":{"tf":1.0}}}},"a":{"c":{"df":0,"docs":{},"h":{"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"df":2,"docs":{"151":{"tf":1.7320508075688772},"50":{"tf":1.4142135623730951}}}}}},"d":{"d":{"df":0,"docs":{},"r":{",":{"df":0,"docs":{},"i":{"+":{"1":{",":{"df":0,"docs":{},"j":{"df":0,"docs":{},"​":{"df":0,"docs":{},"−":{"df":0,"docs":{},"m":{"a":{"d":{"d":{"df":0,"docs":{},"r":{",":{"df":0,"docs":{},"i":{",":{"df":0,"docs":{},"j":{"df":0,"docs":{},"​":{">":{"1":{"df":1,"docs":{"142":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}}}},"df":0,"docs":{}},"df":0,"docs":{}},",":{"df":0,"docs":{},"j":{"df":0,"docs":{},"​":{"<":{"df":0,"docs":{},"l":{"0":{",":{"df":0,"docs":{},"j":{"df":1,"docs":{"142":{"tf":1.0}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":1,"docs":{"142":{"tf":1.0}}}},"df":0,"docs":{},"e":{"df":1,"docs":{"130":{"tf":1.0}}}},"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"df":14,"docs":{"0":{"tf":1.0},"104":{"tf":1.4142135623730951},"111":{"tf":1.0},"113":{"tf":1.0},"128":{"tf":1.0},"129":{"tf":1.0},"133":{"tf":1.0},"145":{"tf":1.4142135623730951},"146":{"tf":1.7320508075688772},"21":{"tf":1.0},"4":{"tf":1.0},"44":{"tf":1.0},"45":{"tf":1.0},"94":{"tf":1.0}},"l":{"df":0,"docs":{},"i":{"df":1,"docs":{"34":{"tf":1.0}}}},"t":{"a":{"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"df":1,"docs":{"13":{"tf":1.0}}}}},"df":0,"docs":{}}}},"j":{"df":0,"docs":{},"o":{"df":0,"docs":{},"r":{"df":3,"docs":{"108":{"tf":1.0},"147":{"tf":1.4142135623730951},"69":{"tf":1.0}}}}},"k":{"df":0,"docs":{},"e":{"df":15,"docs":{"125":{"tf":1.0},"146":{"tf":1.7320508075688772},"149":{"tf":1.0},"151":{"tf":1.0},"20":{"tf":1.0},"45":{"tf":1.0},"48":{"tf":1.0},"56":{"tf":1.0},"70":{"tf":1.0},"71":{"tf":1.0},"77":{"tf":1.0},"80":{"tf":1.0},"83":{"tf":1.0},"89":{"tf":1.0},"95":{"tf":1.0}}}},"l":{"df":0,"docs":{},"f":{"df":0,"docs":{},"o":{"df":0,"docs":{},"r":{"df":0,"docs":{},"m":{"df":1,"docs":{"11":{"tf":1.0}}}}}},"i":{"c":{"df":0,"docs":{},"i":{"df":1,"docs":{"77":{"tf":1.0}}}},"df":0,"docs":{}}},"n":{"df":0,"docs":{},"i":{"df":6,"docs":{"108":{"tf":1.0},"109":{"tf":1.0},"146":{"tf":1.7320508075688772},"20":{"tf":1.0},"70":{"tf":1.0},"98":{"tf":1.0}},"p":{"df":0,"docs":{},"u":{"df":0,"docs":{},"l":{"df":1,"docs":{"118":{"tf":1.0}}}}}}},"p":{"df":3,"docs":{"101":{"tf":1.0},"113":{"tf":1.4142135623730951},"14":{"tf":2.23606797749979}}},"r":{"df":0,"docs":{},"k":{"df":1,"docs":{"3":{"tf":1.0}}}},"t":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":0,"docs":{},"i":{"df":1,"docs":{"144":{"tf":1.0}}}}},"h":{"/":{"df":0,"docs":{},"s":{"df":0,"docs":{},"r":{"c":{"/":{"df":0,"docs":{},"p":{"df":0,"docs":{},"o":{"df":0,"docs":{},"l":{"df":0,"docs":{},"y":{"df":0,"docs":{},"n":{"df":0,"docs":{},"o":{"df":0,"docs":{},"m":{"df":0,"docs":{},"i":{"a":{"df":0,"docs":{},"l":{".":{"df":0,"docs":{},"r":{"df":0,"docs":{},"s":{":":{"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"df":0,"docs":{},"t":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":0,"docs":{},"p":{"df":0,"docs":{},"o":{"df":0,"docs":{},"l":{"a":{"df":0,"docs":{},"t":{"df":0,"docs":{},"e":{"_":{"df":0,"docs":{},"f":{"df":0,"docs":{},"f":{"df":0,"docs":{},"t":{"df":1,"docs":{"3":{"tf":1.0}}}}}},"df":0,"docs":{}}}},"df":1,"docs":{"3":{"tf":1.0}}}}}}}}}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}}}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":2,"docs":{"1":{"tf":1.0},"146":{"tf":1.0}},"e":{"df":0,"docs":{},"m":{"a":{"df":0,"docs":{},"t":{"df":4,"docs":{"131":{"tf":1.0},"46":{"tf":1.0},"70":{"tf":1.0},"8":{"tf":1.0}}}},"df":0,"docs":{}}}},"r":{"df":0,"docs":{},"i":{"c":{"df":9,"docs":{"12":{"tf":1.0},"13":{"tf":2.0},"14":{"tf":1.4142135623730951},"147":{"tf":1.0},"16":{"tf":2.23606797749979},"34":{"tf":1.4142135623730951},"36":{"tf":1.4142135623730951},"8":{"tf":1.4142135623730951},"9":{"tf":1.4142135623730951}}},"df":0,"docs":{},"x":{"df":24,"docs":{"10":{"tf":2.0},"11":{"tf":2.23606797749979},"12":{"tf":1.7320508075688772},"13":{"tf":2.6457513110645907},"14":{"tf":2.0},"141":{"tf":1.0},"142":{"tf":2.23606797749979},"147":{"tf":2.8284271247461903},"16":{"tf":2.449489742783178},"20":{"tf":1.7320508075688772},"23":{"tf":1.4142135623730951},"26":{"tf":1.0},"32":{"tf":1.0},"34":{"tf":1.0},"35":{"tf":1.0},"36":{"tf":2.0},"5":{"tf":1.0},"69":{"tf":1.7320508075688772},"70":{"tf":1.0},"8":{"tf":1.4142135623730951},"83":{"tf":1.0},"9":{"tf":1.7320508075688772},"91":{"tf":2.0},"94":{"tf":1.4142135623730951}}}}},"t":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":3,"docs":{"14":{"tf":1.0},"146":{"tf":1.0},"16":{"tf":1.0}}}}}},"x":{"df":5,"docs":{"103":{"tf":1.0},"119":{"tf":1.0},"130":{"tf":1.0},"134":{"tf":1.0},"146":{"tf":1.0}},"i":{"df":0,"docs":{},"m":{"df":0,"docs":{},"u":{"df":0,"docs":{},"m":{"df":3,"docs":{"142":{"tf":1.0},"36":{"tf":1.0},"38":{"tf":1.4142135623730951}}}}}}}},"df":12,"docs":{"133":{"tf":1.4142135623730951},"14":{"tf":1.0},"141":{"tf":1.0},"142":{"tf":2.449489742783178},"146":{"tf":2.449489742783178},"20":{"tf":1.0},"5":{"tf":1.0},"70":{"tf":1.4142135623730951},"72":{"tf":1.0},"73":{"tf":1.7320508075688772},"75":{"tf":1.0},"91":{"tf":1.7320508075688772}},"e":{"a":{"df":0,"docs":{},"n":{"df":32,"docs":{"101":{"tf":1.0},"105":{"tf":1.0},"106":{"tf":1.0},"108":{"tf":1.0},"114":{"tf":1.0},"123":{"tf":1.0},"124":{"tf":1.4142135623730951},"127":{"tf":1.0},"131":{"tf":1.0},"133":{"tf":1.4142135623730951},"134":{"tf":1.0},"14":{"tf":1.7320508075688772},"142":{"tf":1.0},"146":{"tf":1.4142135623730951},"147":{"tf":1.0},"21":{"tf":1.0},"23":{"tf":1.0},"28":{"tf":1.0},"35":{"tf":1.0},"36":{"tf":1.0},"52":{"tf":1.0},"65":{"tf":1.4142135623730951},"66":{"tf":1.0},"68":{"tf":1.4142135623730951},"70":{"tf":1.0},"72":{"tf":1.0},"73":{"tf":1.0},"75":{"tf":1.0},"78":{"tf":1.0},"9":{"tf":1.0},"92":{"tf":1.0},"95":{"tf":1.0}}}},"c":{"df":0,"docs":{},"h":{"a":{"df":0,"docs":{},"n":{"df":1,"docs":{"4":{"tf":1.0}}}},"df":0,"docs":{}}},"df":0,"docs":{},"m":{"_":{"a":{"df":3,"docs":{"142":{"tf":2.0},"146":{"tf":1.0},"147":{"tf":1.4142135623730951}}},"df":0,"docs":{},"p":{"df":1,"docs":{"147":{"tf":1.0}}},"v":{"df":3,"docs":{"142":{"tf":1.7320508075688772},"146":{"tf":1.0},"147":{"tf":1.4142135623730951}}}},"df":3,"docs":{"133":{"tf":1.0},"134":{"tf":1.4142135623730951},"135":{"tf":1.0}},"o":{"df":0,"docs":{},"r":{"df":0,"docs":{},"i":{"df":11,"docs":{"127":{"tf":1.0},"133":{"tf":3.7416573867739413},"134":{"tf":2.8284271247461903},"135":{"tf":2.0},"141":{"tf":1.0},"142":{"tf":1.7320508075688772},"144":{"tf":2.23606797749979},"146":{"tf":5.5677643628300215},"147":{"tf":2.8284271247461903},"149":{"tf":1.0},"151":{"tf":3.1622776601683795}}},"y":{"_":{"c":{"df":0,"docs":{},"e":{"df":0,"docs":{},"l":{"df":1,"docs":{"134":{"tf":1.0}},"l":{".":{"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"df":0,"docs":{},"l":{"df":1,"docs":{"134":{"tf":1.0}}}}}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"n":{"df":0,"docs":{},"t":{"a":{"df":0,"docs":{},"l":{"df":1,"docs":{"50":{"tf":1.0}}}},"df":0,"docs":{},"i":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"df":14,"docs":{"142":{"tf":1.0},"146":{"tf":1.4142135623730951},"15":{"tf":1.0},"35":{"tf":1.0},"37":{"tf":1.0},"51":{"tf":1.0},"55":{"tf":1.0},"58":{"tf":1.0},"7":{"tf":1.0},"70":{"tf":1.0},"77":{"tf":1.0},"83":{"tf":1.0},"84":{"tf":1.0},"92":{"tf":1.0}}}}}}},"r":{"df":0,"docs":{},"g":{"df":1,"docs":{"12":{"tf":1.0}}},"k":{"df":0,"docs":{},"l":{"df":13,"docs":{"101":{"tf":1.4142135623730951},"110":{"tf":1.0},"112":{"tf":1.4142135623730951},"113":{"tf":1.4142135623730951},"119":{"tf":1.0},"60":{"tf":1.4142135623730951},"62":{"tf":1.4142135623730951},"63":{"tf":1.4142135623730951},"72":{"tf":1.4142135623730951},"76":{"tf":1.0},"80":{"tf":2.449489742783178},"91":{"tf":1.0},"92":{"tf":2.23606797749979}}}}},"s":{"df":0,"docs":{},"s":{"a":{"df":0,"docs":{},"g":{"df":2,"docs":{"40":{"tf":1.4142135623730951},"42":{"tf":1.0}},"e":{"_":{"1":{"df":1,"docs":{"40":{"tf":2.23606797749979}}},"2":{"df":1,"docs":{"40":{"tf":1.4142135623730951}}},"3":{"df":1,"docs":{"40":{"tf":1.0}}},"4":{"df":1,"docs":{"40":{"tf":1.4142135623730951}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"t":{"df":1,"docs":{"121":{"tf":1.0}},"h":{"df":0,"docs":{},"o":{"d":{"df":12,"docs":{"105":{"tf":1.0},"106":{"tf":1.4142135623730951},"107":{"tf":1.7320508075688772},"113":{"tf":1.0},"114":{"tf":2.23606797749979},"117":{"tf":1.0},"123":{"tf":1.0},"14":{"tf":1.4142135623730951},"3":{"tf":1.4142135623730951},"35":{"tf":1.4142135623730951},"40":{"tf":1.7320508075688772},"70":{"tf":1.0}}},"df":0,"docs":{}}}}},"i":{"+":{"1":{"df":0,"docs":{},"​":{"=":{"df":0,"docs":{},"m":{"df":0,"docs":{},"i":{"df":0,"docs":{},"​":{"+":{"1":{"df":1,"docs":{"151":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}}},"df":0,"docs":{}},",":{"df":0,"docs":{},"j":{"df":1,"docs":{"5":{"tf":1.0}}}},"d":{"d":{"df":0,"docs":{},"l":{"df":1,"docs":{"35":{"tf":1.0}}}},"df":0,"docs":{}},"df":1,"docs":{"151":{"tf":1.0}},"l":{"df":0,"docs":{},"l":{"df":0,"docs":{},"i":{"df":0,"docs":{},"s":{"df":0,"docs":{},"e":{"c":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"d":{"df":1,"docs":{"3":{"tf":1.0}}},"df":0,"docs":{}}}},"df":0,"docs":{}}}}}},"n":{"d":{"df":4,"docs":{"124":{"tf":1.0},"146":{"tf":1.0},"49":{"tf":1.0},"50":{"tf":1.0}}},"df":0,"docs":{},"i":{"df":0,"docs":{},"m":{"df":0,"docs":{},"u":{"df":0,"docs":{},"m":{"df":1,"docs":{"142":{"tf":1.0}}}}}},"u":{"df":0,"docs":{},"t":{"df":1,"docs":{"14":{"tf":1.0}}}}},"s":{"df":0,"docs":{},"s":{"df":4,"docs":{"128":{"tf":1.0},"130":{"tf":1.0},"146":{"tf":1.0},"151":{"tf":1.0}}}},"x":{"df":2,"docs":{"150":{"tf":1.0},"66":{"tf":1.0}}}},"j":{"df":1,"docs":{"94":{"tf":1.0}}},"m":{"df":0,"docs":{},"e":{"df":0,"docs":{},"m":{"df":1,"docs":{"147":{"tf":1.0}},"r":{"a":{"df":0,"docs":{},"p":{"2":{"df":1,"docs":{"147":{"tf":1.4142135623730951}}},"df":0,"docs":{}}},"df":0,"docs":{},"e":{"df":0,"docs":{},"p":{"df":0,"docs":{},"l":{"df":1,"docs":{"147":{"tf":1.0}},"s":{"df":0,"docs":{},"o":{"df":0,"docs":{},"r":{"df":0,"docs":{},"t":{"df":1,"docs":{"147":{"tf":1.4142135623730951}}}}}},"​":{"df":0,"docs":{},"∈":{"df":0,"docs":{},"f":{"2":{"df":0,"docs":{},"n":{"+":{"2":{"df":0,"docs":{},"×":{"2":{"df":1,"docs":{"147":{"tf":1.0}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}}}}}},"​":{"df":0,"docs":{},"∈":{"df":0,"docs":{},"f":{"2":{"df":0,"docs":{},"n":{"+":{"2":{"df":0,"docs":{},"×":{"2":{"df":1,"docs":{"147":{"tf":1.0}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}}}}},"o":{"d":{"df":0,"docs":{},"e":{"df":1,"docs":{"146":{"tf":1.4142135623730951}},"l":{"df":1,"docs":{"50":{"tf":1.0}}},"r":{"df":0,"docs":{},"n":{"df":1,"docs":{"42":{"tf":1.0}}}}},"i":{"df":0,"docs":{},"f":{"df":0,"docs":{},"i":{"df":4,"docs":{"102":{"tf":1.0},"146":{"tf":1.0},"45":{"tf":1.0},"59":{"tf":1.4142135623730951}}}}},"u":{"df":0,"docs":{},"l":{"df":1,"docs":{"38":{"tf":1.0}},"o":{"df":1,"docs":{"41":{"tf":1.0}}}}}},"df":0,"docs":{},"f":{"df":0,"docs":{},"f":{"df":0,"docs":{},"s":{"df":0,"docs":{},"e":{"df":0,"docs":{},"t":{"df":1,"docs":{"142":{"tf":1.4142135623730951}}}}}}},"m":{"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"df":0,"docs":{},"t":{"df":5,"docs":{"10":{"tf":1.0},"19":{"tf":1.0},"34":{"tf":1.0},"70":{"tf":1.0},"79":{"tf":1.0}}}}}},"r":{"df":0,"docs":{},"e":{"df":27,"docs":{"103":{"tf":1.0},"114":{"tf":1.7320508075688772},"118":{"tf":1.0},"12":{"tf":1.0},"130":{"tf":1.0},"133":{"tf":1.0},"135":{"tf":1.0},"14":{"tf":2.0},"146":{"tf":1.7320508075688772},"150":{"tf":1.0},"151":{"tf":2.0},"16":{"tf":1.0},"21":{"tf":1.0},"34":{"tf":1.0},"41":{"tf":1.4142135623730951},"42":{"tf":1.0},"45":{"tf":1.0},"64":{"tf":1.0},"66":{"tf":1.0},"68":{"tf":1.0},"7":{"tf":1.0},"73":{"tf":1.4142135623730951},"74":{"tf":1.0},"80":{"tf":1.0},"82":{"tf":1.0},"83":{"tf":1.0},"84":{"tf":1.4142135623730951}},"o":{"df":0,"docs":{},"v":{"df":1,"docs":{"14":{"tf":1.0}}}}}},"v":{"df":0,"docs":{},"e":{"df":4,"docs":{"128":{"tf":1.4142135623730951},"149":{"tf":1.0},"50":{"tf":1.0},"53":{"tf":1.0}},"m":{"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"df":0,"docs":{},"t":{"df":1,"docs":{"149":{"tf":1.0}}}}}}}}},"r":{"a":{"df":0,"docs":{},"n":{"df":0,"docs":{},"g":{"df":0,"docs":{},"e":{"c":{"df":0,"docs":{},"h":{"df":0,"docs":{},"e":{"c":{"df":0,"docs":{},"k":{"df":0,"docs":{},"r":{"a":{"df":0,"docs":{},"p":{"2":{"df":1,"docs":{"147":{"tf":1.4142135623730951}}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}}}},"df":0,"docs":{}}}},"p":{"2":{"df":3,"docs":{"147":{"tf":1.0},"83":{"tf":1.0},"94":{"tf":1.0}},"​":{"df":0,"docs":{},"∈":{"df":0,"docs":{},"f":{"2":{"df":0,"docs":{},"n":{"df":0,"docs":{},"×":{"df":0,"docs":{},"m":{"df":1,"docs":{"94":{"tf":1.0}}}}}},"df":0,"docs":{}}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"s":{"df":1,"docs":{"3":{"tf":4.0}}},"u":{"c":{"df":0,"docs":{},"h":{"df":6,"docs":{"116":{"tf":1.0},"123":{"tf":1.4142135623730951},"125":{"tf":1.0},"21":{"tf":1.0},"37":{"tf":1.0},"66":{"tf":1.0}}}},"df":0,"docs":{},"l":{"df":3,"docs":{"142":{"tf":1.0},"146":{"tf":1.7320508075688772},"147":{"tf":1.0}},"t":{"df":0,"docs":{},"i":{"df":1,"docs":{"139":{"tf":1.0}},"p":{"df":0,"docs":{},"l":{"df":16,"docs":{"10":{"tf":1.4142135623730951},"101":{"tf":1.0},"106":{"tf":1.0},"107":{"tf":1.0},"12":{"tf":1.0},"14":{"tf":1.0},"142":{"tf":1.4142135623730951},"149":{"tf":1.4142135623730951},"151":{"tf":1.0},"44":{"tf":1.0},"65":{"tf":1.7320508075688772},"66":{"tf":1.7320508075688772},"7":{"tf":1.0},"9":{"tf":1.0},"92":{"tf":1.0},"98":{"tf":1.0}},"i":{"df":4,"docs":{"125":{"tf":1.0},"14":{"tf":1.0},"45":{"tf":1.0},"52":{"tf":1.0}}}}},"v":{"a":{"df":0,"docs":{},"r":{"df":0,"docs":{},"i":{"df":2,"docs":{"70":{"tf":1.0},"84":{"tf":1.0}}}}},"df":0,"docs":{}}}}},"t":{"a":{"df":0,"docs":{},"t":{"df":1,"docs":{"118":{"tf":1.4142135623730951}}}},"df":3,"docs":{"114":{"tf":1.0},"44":{"tf":1.0},"45":{"tf":1.7320508075688772}}}},"′":{",":{"df":0,"docs":{},"v":{"df":1,"docs":{"146":{"tf":1.0}}}},"=":{"3":{"3":{"df":1,"docs":{"147":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{},"′":{"=":{"1":{"8":{"df":1,"docs":{"147":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"∈":{"df":0,"docs":{},"f":{"(":{"df":0,"docs":{},"l":{"+":{"df":0,"docs":{},"l":{"df":0,"docs":{},"′":{")":{"df":0,"docs":{},"×":{"3":{"3":{"df":1,"docs":{"142":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}}},"+":{"df":0,"docs":{},"l":{"df":0,"docs":{},"′":{"df":0,"docs":{},"′":{")":{"df":0,"docs":{},"×":{"3":{"3":{"df":1,"docs":{"142":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{},"l":{"df":0,"docs":{},"×":{"3":{"3":{"df":1,"docs":{"142":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"−":{"1":{"df":2,"docs":{"73":{"tf":2.23606797749979},"79":{"tf":1.0}}},"df":0,"docs":{}},"≥":{"df":0,"docs":{},"n":{"df":1,"docs":{"20":{"tf":1.0}}}}},"n":{"+":{"1":{"df":1,"docs":{"26":{"tf":1.0}}},"df":0,"docs":{},"m":{"+":{"1":{")":{"df":0,"docs":{},"×":{"3":{"df":1,"docs":{"14":{"tf":1.0}}},"5":{"df":1,"docs":{"14":{"tf":1.0}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"<":{"df":0,"docs":{},"m":{"df":3,"docs":{"73":{"tf":1.0},"75":{"tf":1.0},"80":{"tf":1.0}}}},"=":{"2":{"df":0,"docs":{},"n":{"df":3,"docs":{"73":{"tf":1.0},"75":{"tf":1.0},"80":{"tf":1.0}}}},"3":{"df":1,"docs":{"51":{"tf":1.0}}},"df":0,"docs":{},"n":{"+":{"df":0,"docs":{},"m":{"+":{"1":{"df":1,"docs":{"14":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"a":{"df":0,"docs":{},"i":{"df":0,"docs":{},"v":{"df":3,"docs":{"101":{"tf":1.0},"123":{"tf":1.0},"3":{"tf":1.0}}}},"m":{"df":0,"docs":{},"e":{"df":3,"docs":{"102":{"tf":1.0},"13":{"tf":1.0},"130":{"tf":1.0}}}},"r":{"df":0,"docs":{},"r":{"df":0,"docs":{},"o":{"df":0,"docs":{},"w":{"df":1,"docs":{"50":{"tf":1.0}}}}}},"t":{"df":0,"docs":{},"u":{"df":0,"docs":{},"r":{"df":2,"docs":{"101":{"tf":1.0},"84":{"tf":1.0}}}}}},"c":{"df":1,"docs":{"28":{"tf":1.0}}},"df":14,"docs":{"124":{"tf":2.23606797749979},"127":{"tf":1.0},"14":{"tf":2.449489742783178},"151":{"tf":1.0},"16":{"tf":1.0},"20":{"tf":1.0},"23":{"tf":1.4142135623730951},"32":{"tf":1.0},"35":{"tf":1.7320508075688772},"68":{"tf":1.4142135623730951},"75":{"tf":1.4142135623730951},"78":{"tf":1.4142135623730951},"79":{"tf":1.0},"91":{"tf":1.4142135623730951}},"e":{"a":{"df":0,"docs":{},"r":{"df":0,"docs":{},"e":{"df":0,"docs":{},"s":{"df":0,"docs":{},"t":{"df":1,"docs":{"142":{"tf":1.0}}}}}}},"c":{"df":0,"docs":{},"e":{"df":0,"docs":{},"s":{"df":0,"docs":{},"s":{"a":{"df":0,"docs":{},"r":{"df":0,"docs":{},"i":{"df":6,"docs":{"103":{"tf":1.0},"110":{"tf":1.0},"112":{"tf":1.0},"13":{"tf":1.0},"133":{"tf":1.0},"33":{"tf":1.0}},"l":{"df":0,"docs":{},"i":{"df":1,"docs":{"14":{"tf":1.0}}}}}}},"df":0,"docs":{}}}}},"df":0,"docs":{},"e":{"d":{"df":42,"docs":{"102":{"tf":1.4142135623730951},"105":{"tf":1.0},"107":{"tf":2.0},"109":{"tf":1.0},"111":{"tf":1.4142135623730951},"114":{"tf":2.0},"117":{"tf":1.4142135623730951},"118":{"tf":1.4142135623730951},"119":{"tf":1.4142135623730951},"123":{"tf":1.0},"125":{"tf":1.0},"130":{"tf":1.0},"133":{"tf":1.0},"14":{"tf":1.4142135623730951},"144":{"tf":1.0},"146":{"tf":3.3166247903554},"149":{"tf":1.0},"151":{"tf":1.7320508075688772},"19":{"tf":1.0},"20":{"tf":1.0},"21":{"tf":1.0},"32":{"tf":2.0},"34":{"tf":1.0},"35":{"tf":1.4142135623730951},"36":{"tf":1.4142135623730951},"44":{"tf":1.4142135623730951},"48":{"tf":1.0},"5":{"tf":1.0},"57":{"tf":1.0},"58":{"tf":1.4142135623730951},"59":{"tf":1.0},"60":{"tf":1.7320508075688772},"68":{"tf":1.7320508075688772},"69":{"tf":1.0},"70":{"tf":1.7320508075688772},"74":{"tf":1.4142135623730951},"75":{"tf":1.0},"8":{"tf":1.4142135623730951},"80":{"tf":1.7320508075688772},"82":{"tf":1.0},"88":{"tf":1.0},"9":{"tf":1.0}}},"df":0,"docs":{}},"g":{"df":0,"docs":{},"l":{"df":0,"docs":{},"i":{"df":0,"docs":{},"g":{"df":1,"docs":{"73":{"tf":1.0}}}}}},"v":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":1,"docs":{"95":{"tf":1.0}}}}},"w":{"_":{"df":0,"docs":{},"s":{"df":0,"docs":{},"i":{"df":0,"docs":{},"m":{"df":0,"docs":{},"p":{"df":0,"docs":{},"l":{"df":1,"docs":{"106":{"tf":1.0}}}}}}}},"df":10,"docs":{"106":{"tf":1.0},"13":{"tf":1.0},"14":{"tf":1.0},"142":{"tf":1.0},"146":{"tf":1.7320508075688772},"20":{"tf":1.4142135623730951},"59":{"tf":1.0},"68":{"tf":1.0},"83":{"tf":1.4142135623730951},"94":{"tf":1.0}}},"x":{"df":0,"docs":{},"t":{"df":15,"docs":{"10":{"tf":1.0},"102":{"tf":1.4142135623730951},"118":{"tf":1.0},"128":{"tf":1.0},"134":{"tf":1.4142135623730951},"14":{"tf":1.4142135623730951},"144":{"tf":1.0},"149":{"tf":1.0},"23":{"tf":1.0},"35":{"tf":1.0},"49":{"tf":1.0},"66":{"tf":1.0},"74":{"tf":1.0},"77":{"tf":1.0},"9":{"tf":1.0}}}}},"i":{"c":{"df":0,"docs":{},"e":{"df":1,"docs":{"14":{"tf":1.0}}}},"df":0,"docs":{}},"o":{"df":0,"docs":{},"n":{"c":{"df":2,"docs":{"119":{"tf":1.0},"88":{"tf":1.4142135623730951}}},"df":1,"docs":{"28":{"tf":1.4142135623730951}},"i":{"df":0,"docs":{},"n":{"df":0,"docs":{},"t":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"a":{"c":{"df":0,"docs":{},"t":{"df":1,"docs":{"89":{"tf":1.0}}}},"df":0,"docs":{}},"df":0,"docs":{}}}}}},"z":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":0,"docs":{},"o":{"df":2,"docs":{"73":{"tf":1.0},"75":{"tf":1.0}}}}}}},"t":{"a":{"df":0,"docs":{},"t":{"df":11,"docs":{"142":{"tf":1.0},"147":{"tf":1.0},"34":{"tf":1.0},"35":{"tf":1.0},"5":{"tf":1.4142135623730951},"70":{"tf":1.4142135623730951},"87":{"tf":1.0},"9":{"tf":1.0},"90":{"tf":1.4142135623730951},"92":{"tf":1.7320508075688772},"95":{"tf":1.4142135623730951}}}},"df":0,"docs":{},"e":{"df":14,"docs":{"104":{"tf":1.0},"125":{"tf":1.7320508075688772},"128":{"tf":1.0},"131":{"tf":1.0},"146":{"tf":1.0},"44":{"tf":1.0},"68":{"tf":1.0},"74":{"tf":1.0},"79":{"tf":1.0},"80":{"tf":1.4142135623730951},"84":{"tf":1.0},"92":{"tf":1.0},"95":{"tf":1.0},"96":{"tf":1.0}}},"h":{"df":1,"docs":{"114":{"tf":1.0}}},"i":{"c":{"df":8,"docs":{"10":{"tf":1.0},"114":{"tf":1.0},"118":{"tf":1.0},"13":{"tf":1.0},"14":{"tf":1.4142135623730951},"21":{"tf":1.0},"68":{"tf":1.0},"7":{"tf":1.0}}},"df":0,"docs":{}}},"w":{"df":17,"docs":{"11":{"tf":1.4142135623730951},"12":{"tf":1.0},"121":{"tf":1.0},"123":{"tf":1.0},"13":{"tf":1.4142135623730951},"14":{"tf":2.23606797749979},"146":{"tf":1.0},"149":{"tf":1.7320508075688772},"150":{"tf":1.0},"33":{"tf":1.0},"44":{"tf":1.0},"58":{"tf":1.0},"65":{"tf":1.0},"66":{"tf":1.0},"7":{"tf":1.0},"77":{"tf":1.0},"83":{"tf":1.0}}}},"u":{"df":0,"docs":{},"m":{"_":{"c":{"df":0,"docs":{},"o":{"df":0,"docs":{},"l":{"df":1,"docs":{"108":{"tf":1.7320508075688772}}}}},"df":0,"docs":{},"t":{"df":0,"docs":{},"r":{"a":{"df":0,"docs":{},"n":{"df":0,"docs":{},"s":{"df":0,"docs":{},"i":{"df":0,"docs":{},"t":{"df":0,"docs":{},"i":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"_":{"c":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"df":0,"docs":{},"s":{"df":0,"docs":{},"t":{"df":0,"docs":{},"r":{"a":{"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"df":0,"docs":{},"t":{"df":1,"docs":{"109":{"tf":1.4142135623730951}}}}}},"df":0,"docs":{}}}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}}}}}}},"df":0,"docs":{}}}},"b":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":25,"docs":{"106":{"tf":1.0},"109":{"tf":2.0},"118":{"tf":1.7320508075688772},"119":{"tf":1.0},"124":{"tf":1.4142135623730951},"125":{"tf":1.0},"128":{"tf":1.4142135623730951},"133":{"tf":1.0},"142":{"tf":1.0},"144":{"tf":1.0},"146":{"tf":1.4142135623730951},"147":{"tf":1.0},"149":{"tf":1.0},"151":{"tf":1.7320508075688772},"21":{"tf":1.0},"32":{"tf":1.0},"35":{"tf":1.0},"49":{"tf":2.23606797749979},"50":{"tf":1.0},"56":{"tf":1.0},"59":{"tf":1.0},"69":{"tf":1.0},"73":{"tf":1.4142135623730951},"88":{"tf":1.0},"91":{"tf":2.6457513110645907}}}}},"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":4,"docs":{"114":{"tf":1.0},"123":{"tf":1.0},"125":{"tf":1.4142135623730951},"55":{"tf":1.0}}}}}},"×":{"1":{"df":2,"docs":{"14":{"tf":1.0},"16":{"tf":1.4142135623730951}}},"3":{"df":3,"docs":{"14":{"tf":1.0},"16":{"tf":1.4142135623730951},"23":{"tf":1.0}}},"df":0,"docs":{}},"−":{"1":{")":{"df":0,"docs":{},"t":{"df":1,"docs":{"33":{"tf":1.0}}}},"df":7,"docs":{"14":{"tf":1.0},"20":{"tf":1.0},"73":{"tf":1.4142135623730951},"74":{"tf":1.4142135623730951},"78":{"tf":1.4142135623730951},"79":{"tf":1.0},"80":{"tf":1.0}}},"df":0,"docs":{}}},"o":{"b":{"df":0,"docs":{},"j":{"df":0,"docs":{},"e":{"c":{"df":0,"docs":{},"t":{"df":2,"docs":{"87":{"tf":1.0},"89":{"tf":1.0}}}},"df":0,"docs":{}}},"s":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":0,"docs":{},"v":{"df":3,"docs":{"13":{"tf":1.0},"151":{"tf":1.0},"7":{"tf":1.0}}}}}},"t":{"a":{"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"df":14,"docs":{"114":{"tf":1.4142135623730951},"124":{"tf":1.0},"14":{"tf":1.7320508075688772},"142":{"tf":1.7320508075688772},"146":{"tf":1.7320508075688772},"32":{"tf":1.0},"34":{"tf":1.0},"44":{"tf":1.4142135623730951},"7":{"tf":1.0},"83":{"tf":1.0},"84":{"tf":1.4142135623730951},"91":{"tf":1.0},"94":{"tf":1.7320508075688772},"95":{"tf":1.0}}}}},"df":0,"docs":{}},"v":{"df":0,"docs":{},"i":{"df":0,"docs":{},"o":{"df":0,"docs":{},"u":{"df":3,"docs":{"107":{"tf":1.0},"14":{"tf":1.0},"16":{"tf":1.0}}}}}}},"c":{"c":{"df":0,"docs":{},"u":{"df":0,"docs":{},"r":{"df":1,"docs":{"41":{"tf":1.0}},"r":{"df":1,"docs":{"14":{"tf":1.7320508075688772}}}}}},"df":0,"docs":{}},"d":{"d":{"df":4,"docs":{"119":{"tf":1.0},"67":{"tf":1.0},"7":{"tf":1.0},"94":{"tf":1.0}}},"df":0,"docs":{}},"df":4,"docs":{"11":{"tf":1.0},"12":{"tf":1.0},"13":{"tf":1.0},"14":{"tf":1.0}},"f":{"df":0,"docs":{},"f":{"0":{"df":1,"docs":{"130":{"tf":1.0}}},"1":{"df":1,"docs":{"130":{"tf":1.0}}},"2":{"df":1,"docs":{"130":{"tf":1.0}}},"_":{"d":{"df":0,"docs":{},"s":{"df":0,"docs":{},"t":{"df":4,"docs":{"130":{"tf":1.4142135623730951},"142":{"tf":1.0},"146":{"tf":1.7320508075688772},"147":{"tf":1.0}}}}},"df":0,"docs":{},"o":{"df":0,"docs":{},"p":{"0":{"df":4,"docs":{"130":{"tf":1.4142135623730951},"142":{"tf":1.0},"146":{"tf":1.7320508075688772},"147":{"tf":1.0}}},"1":{"df":4,"docs":{"130":{"tf":1.4142135623730951},"142":{"tf":1.0},"146":{"tf":1.7320508075688772},"147":{"tf":1.0}}},"df":0,"docs":{}}}},"df":0,"docs":{},"s":{"df":0,"docs":{},"e":{"df":0,"docs":{},"t":{"df":12,"docs":{"109":{"tf":1.0},"125":{"tf":2.23606797749979},"128":{"tf":1.0},"130":{"tf":2.23606797749979},"133":{"tf":3.4641016151377544},"134":{"tf":1.0},"136":{"tf":2.449489742783178},"139":{"tf":1.4142135623730951},"142":{"tf":1.4142135623730951},"146":{"tf":2.23606797749979},"147":{"tf":1.7320508075688772},"151":{"tf":1.0}},"s":{"/":{"df":0,"docs":{},"r":{"a":{"df":0,"docs":{},"n":{"df":0,"docs":{},"g":{"df":1,"docs":{"146":{"tf":1.0}}}}},"df":0,"docs":{}}},"_":{"b":{"df":1,"docs":{"147":{"tf":1.0}}},"df":0,"docs":{},"p":{"df":1,"docs":{"147":{"tf":1.0}}}},"df":0,"docs":{}}}}}}},"m":{"df":0,"docs":{},"e":{"df":0,"docs":{},"g":{"a":{"^":{"2":{"df":1,"docs":{"68":{"tf":1.4142135623730951}}},"df":0,"docs":{},"i":{"df":1,"docs":{"68":{"tf":1.4142135623730951}}},"{":{"1":{"6":{"df":1,"docs":{"68":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":2,"docs":{"35":{"tf":1.0},"68":{"tf":1.7320508075688772}}},"df":0,"docs":{}}},"i":{"df":0,"docs":{},"s":{"df":0,"docs":{},"s":{"df":1,"docs":{"64":{"tf":1.0}}}}}},"n":{"c":{"df":7,"docs":{"111":{"tf":1.0},"134":{"tf":1.0},"44":{"tf":1.0},"67":{"tf":1.0},"8":{"tf":1.0},"80":{"tf":1.4142135623730951},"98":{"tf":1.0}}},"df":47,"docs":{"10":{"tf":1.0},"101":{"tf":1.0},"102":{"tf":1.0},"105":{"tf":1.0},"106":{"tf":1.0},"107":{"tf":2.0},"109":{"tf":1.0},"11":{"tf":1.4142135623730951},"114":{"tf":2.0},"118":{"tf":1.0},"123":{"tf":1.0},"124":{"tf":1.0},"125":{"tf":1.4142135623730951},"13":{"tf":2.0},"130":{"tf":1.0},"131":{"tf":1.0},"132":{"tf":1.0},"133":{"tf":1.0},"135":{"tf":1.0},"136":{"tf":1.0},"14":{"tf":2.449489742783178},"142":{"tf":1.0},"145":{"tf":1.0},"146":{"tf":3.3166247903554},"149":{"tf":2.23606797749979},"151":{"tf":1.7320508075688772},"19":{"tf":1.0},"21":{"tf":1.0},"22":{"tf":1.0},"34":{"tf":1.0},"49":{"tf":2.23606797749979},"5":{"tf":1.0},"50":{"tf":1.0},"51":{"tf":1.0},"52":{"tf":1.4142135623730951},"57":{"tf":1.0},"59":{"tf":1.4142135623730951},"65":{"tf":2.0},"66":{"tf":1.7320508075688772},"68":{"tf":1.0},"70":{"tf":1.0},"73":{"tf":1.4142135623730951},"80":{"tf":2.0},"84":{"tf":2.0},"9":{"tf":1.7320508075688772},"92":{"tf":1.0},"99":{"tf":1.0}},"w":{"a":{"df":0,"docs":{},"r":{"d":{"df":1,"docs":{"52":{"tf":1.0}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"o":{"d":{"df":2,"docs":{"113":{"tf":1.0},"117":{"tf":1.4142135623730951}}},"df":0,"docs":{}},"p":{"0":{"_":{"a":{"d":{"d":{"df":0,"docs":{},"r":{"[":{"0":{"df":1,"docs":{"147":{"tf":1.0}}},"df":0,"docs":{}},"df":3,"docs":{"142":{"tf":1.0},"146":{"tf":2.0},"147":{"tf":1.4142135623730951}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{},"r":{"df":0,"docs":{},"e":{"df":0,"docs":{},"g":{"df":1,"docs":{"146":{"tf":1.0}}}}}},"df":4,"docs":{"133":{"tf":2.0},"142":{"tf":1.0},"146":{"tf":2.0},"147":{"tf":1.4142135623730951}},"r":{"df":0,"docs":{},"e":{"df":0,"docs":{},"g":{"df":1,"docs":{"149":{"tf":1.0}}}}}},"1":{"_":{"a":{"d":{"d":{"df":0,"docs":{},"r":{"[":{"0":{"df":1,"docs":{"147":{"tf":1.0}}},"df":0,"docs":{}},"df":3,"docs":{"142":{"tf":1.0},"146":{"tf":1.7320508075688772},"147":{"tf":1.4142135623730951}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{},"s":{"df":0,"docs":{},"r":{"c":{"df":1,"docs":{"146":{"tf":1.0}}},"df":0,"docs":{}}}},"df":4,"docs":{"133":{"tf":2.0},"142":{"tf":1.0},"146":{"tf":2.0},"147":{"tf":1.4142135623730951}}},"c":{"df":0,"docs":{},"o":{"d":{"df":2,"docs":{"146":{"tf":1.7320508075688772},"149":{"tf":1.0}}},"df":0,"docs":{}}},"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"(":{"a":{",":{"a":{"b":{"df":0,"docs":{},"i":{"df":1,"docs":{"92":{"tf":1.0}}}},"df":0,"docs":{}},"df":0,"docs":{},"i":{"df":2,"docs":{"92":{"tf":1.4142135623730951},"95":{"tf":1.0}}}},"df":0,"docs":{}},"df":0,"docs":{},"f":{"df":1,"docs":{"19":{"tf":1.0}}},"h":{"1":{"df":0,"docs":{},"​":{"(":{"d":{"df":0,"docs":{},"l":{"d":{"df":0,"docs":{},"e":{"df":0,"docs":{},"​":{")":{",":{"df":0,"docs":{},"υ":{"df":1,"docs":{"94":{"tf":1.0}}},"−":{"df":0,"docs":{},"υ":{"df":1,"docs":{"94":{"tf":1.0}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"2":{"df":0,"docs":{},"​":{"(":{"d":{"df":0,"docs":{},"l":{"d":{"df":0,"docs":{},"e":{"df":0,"docs":{},"​":{")":{",":{"df":0,"docs":{},"υ":{"df":1,"docs":{"94":{"tf":1.0}}},"−":{"df":0,"docs":{},"υ":{"df":1,"docs":{"94":{"tf":1.0}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"p":{"(":{"d":{")":{",":{"df":0,"docs":{},"h":{"df":0,"docs":{},"ω":{"df":0,"docs":{},"i":{"df":1,"docs":{"101":{"tf":1.0}}}}},"−":{"df":0,"docs":{},"h":{"df":0,"docs":{},"ω":{"df":0,"docs":{},"i":{"df":1,"docs":{"101":{"tf":1.0}}}}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{},"k":{"df":0,"docs":{},"​":{"(":{"d":{"df":0,"docs":{},"k":{"df":0,"docs":{},"​":{")":{",":{"df":0,"docs":{},"υ":{"df":0,"docs":{},"s":{"2":{"df":0,"docs":{},"k":{"df":1,"docs":{"94":{"tf":1.0}}}},"df":0,"docs":{}}},"−":{"df":0,"docs":{},"υ":{"df":0,"docs":{},"s":{"2":{"df":0,"docs":{},"k":{"df":1,"docs":{"94":{"tf":1.0}}}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"t":{"df":0,"docs":{},"j":{"df":0,"docs":{},"​":{"(":{"d":{"df":0,"docs":{},"l":{"d":{"df":0,"docs":{},"e":{"df":0,"docs":{},"​":{")":{",":{"df":0,"docs":{},"υ":{"df":2,"docs":{"94":{"tf":1.0},"95":{"tf":1.0}}},"−":{"df":0,"docs":{},"υ":{"df":1,"docs":{"94":{"tf":1.0}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":17,"docs":{"101":{"tf":1.4142135623730951},"102":{"tf":1.0},"119":{"tf":1.0},"19":{"tf":1.0},"21":{"tf":2.8284271247461903},"28":{"tf":1.4142135623730951},"33":{"tf":1.4142135623730951},"35":{"tf":1.4142135623730951},"69":{"tf":1.0},"71":{"tf":1.0},"72":{"tf":1.0},"75":{"tf":2.0},"77":{"tf":1.0},"80":{"tf":2.23606797749979},"86":{"tf":1.4142135623730951},"94":{"tf":1.0},"95":{"tf":1.7320508075688772}}},"r":{"a":{"df":0,"docs":{},"n":{"d":{"df":3,"docs":{"11":{"tf":1.0},"20":{"tf":1.0},"9":{"tf":1.4142135623730951}}},"df":0,"docs":{}}},"df":5,"docs":{"13":{"tf":1.0},"45":{"tf":2.0},"50":{"tf":1.0},"92":{"tf":2.0},"99":{"tf":1.0}}}},"s":{"_":{"df":0,"docs":{},"m":{"df":0,"docs":{},"u":{"df":0,"docs":{},"l":{"df":1,"docs":{"136":{"tf":1.4142135623730951}}}}}},"df":0,"docs":{}},"t":{"df":0,"docs":{},"i":{"df":0,"docs":{},"m":{"df":9,"docs":{"12":{"tf":1.0},"136":{"tf":1.0},"146":{"tf":1.4142135623730951},"21":{"tf":1.0},"67":{"tf":1.0},"80":{"tf":1.0},"94":{"tf":1.7320508075688772},"96":{"tf":1.0},"99":{"tf":1.0}}},"o":{"df":0,"docs":{},"n":{"df":4,"docs":{"104":{"tf":2.0},"109":{"tf":1.4142135623730951},"110":{"tf":1.0},"91":{"tf":1.0}}}}}}},"r":{"d":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"_":{"df":0,"docs":{},"r":{"_":{"df":0,"docs":{},"m":{"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"df":0,"docs":{},"u":{"df":0,"docs":{},"s":{"_":{"1":{"_":{"df":0,"docs":{},"r":{"df":0,"docs":{},"o":{"df":0,"docs":{},"o":{"df":0,"docs":{},"t":{"_":{"df":0,"docs":{},"u":{"df":0,"docs":{},"n":{"df":1,"docs":{"44":{"tf":1.0}}}}},"df":0,"docs":{}}}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}}}}},"df":0,"docs":{}}},"df":16,"docs":{"101":{"tf":2.0},"118":{"tf":1.0},"130":{"tf":1.0},"132":{"tf":1.0},"134":{"tf":1.0},"135":{"tf":1.0},"142":{"tf":1.4142135623730951},"146":{"tf":2.449489742783178},"147":{"tf":1.4142135623730951},"20":{"tf":1.0},"36":{"tf":1.0},"38":{"tf":1.7320508075688772},"41":{"tf":1.7320508075688772},"45":{"tf":1.0},"94":{"tf":1.4142135623730951},"95":{"tf":1.0}}}}},"df":0,"docs":{},"g":{"a":{"df":0,"docs":{},"n":{"df":2,"docs":{"149":{"tf":1.0},"69":{"tf":1.0}}}},"df":0,"docs":{}},"i":{"df":0,"docs":{},"g":{"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"df":6,"docs":{"13":{"tf":1.0},"146":{"tf":1.0},"19":{"tf":1.4142135623730951},"74":{"tf":1.0},"83":{"tf":1.0},"84":{"tf":1.0}}}}}}},"t":{"df":0,"docs":{},"h":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":4,"docs":{"107":{"tf":1.0},"109":{"tf":1.0},"149":{"tf":1.0},"41":{"tf":1.0}},"w":{"df":0,"docs":{},"i":{"df":0,"docs":{},"s":{"df":5,"docs":{"104":{"tf":1.0},"124":{"tf":1.0},"19":{"tf":1.0},"33":{"tf":1.0},"7":{"tf":1.0}}}}}}}}},"u":{"df":0,"docs":{},"t":{"c":{"df":0,"docs":{},"o":{"df":0,"docs":{},"m":{"df":1,"docs":{"118":{"tf":1.0}}}}},"df":16,"docs":{"101":{"tf":1.0},"112":{"tf":1.0},"115":{"tf":1.0},"117":{"tf":1.4142135623730951},"119":{"tf":1.0},"16":{"tf":1.0},"41":{"tf":1.0},"42":{"tf":1.0},"50":{"tf":1.0},"59":{"tf":1.4142135623730951},"60":{"tf":1.0},"62":{"tf":1.0},"66":{"tf":1.0},"7":{"tf":1.0},"72":{"tf":1.0},"79":{"tf":1.0}},"p":{"df":0,"docs":{},"u":{"df":0,"docs":{},"t":{"df":16,"docs":{"104":{"tf":1.0},"11":{"tf":1.0},"13":{"tf":1.7320508075688772},"146":{"tf":1.0},"151":{"tf":1.0},"16":{"tf":1.0},"19":{"tf":1.0},"32":{"tf":1.0},"33":{"tf":2.0},"34":{"tf":1.4142135623730951},"35":{"tf":2.0},"4":{"tf":1.0},"40":{"tf":1.4142135623730951},"7":{"tf":2.8284271247461903},"74":{"tf":1.0},"9":{"tf":2.449489742783178}}}}},"s":{"df":0,"docs":{},"i":{"d":{"df":1,"docs":{"20":{"tf":1.0}}},"df":0,"docs":{}}}}},"v":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":16,"docs":{"103":{"tf":1.0},"104":{"tf":1.4142135623730951},"109":{"tf":1.0},"111":{"tf":1.0},"112":{"tf":1.0},"125":{"tf":2.0},"131":{"tf":1.4142135623730951},"149":{"tf":1.4142135623730951},"44":{"tf":1.0},"62":{"tf":1.0},"68":{"tf":1.4142135623730951},"70":{"tf":1.0},"80":{"tf":1.0},"82":{"tf":1.4142135623730951},"83":{"tf":1.0},"84":{"tf":1.0}},"v":{"df":0,"docs":{},"i":{"df":0,"docs":{},"e":{"df":0,"docs":{},"w":{"df":2,"docs":{"69":{"tf":1.0},"92":{"tf":1.0}}}}}},"w":{"df":0,"docs":{},"h":{"df":0,"docs":{},"e":{"df":0,"docs":{},"l":{"df":0,"docs":{},"m":{"df":3,"docs":{"14":{"tf":2.6457513110645907},"19":{"tf":1.0},"85":{"tf":1.0}}}}}}}}}}},"p":{"(":{"1":{")":{"=":{"1":{"df":0,"docs":{},"p":{"(":{"df":0,"docs":{},"g":{")":{"=":{"1":{"df":1,"docs":{"54":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"d":{"1":{"df":0,"docs":{},"​":{")":{",":{"df":0,"docs":{},"…":{",":{"df":0,"docs":{},"p":{"(":{"d":{"df":0,"docs":{},"m":{"df":3,"docs":{"73":{"tf":1.4142135623730951},"76":{"tf":1.0},"80":{"tf":1.0}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{},"i":{"df":0,"docs":{},"​":{")":{"=":{"df":0,"docs":{},"y":{"df":0,"docs":{},"i":{"df":1,"docs":{"73":{"tf":1.0}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{},"h":{")":{",":{"df":0,"docs":{},"p":{"(":{"df":0,"docs":{},"h":{"df":0,"docs":{},"ω":{")":{",":{"df":0,"docs":{},"p":{"(":{"df":0,"docs":{},"h":{"df":0,"docs":{},"ω":{"2":{")":{",":{"df":0,"docs":{},"…":{",":{"df":0,"docs":{},"p":{"(":{"df":0,"docs":{},"h":{"df":0,"docs":{},"ω":{"2":{"df":0,"docs":{},"k":{"df":0,"docs":{},"−":{"1":{"df":1,"docs":{"101":{"tf":1.0}}},"df":0,"docs":{}}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{},"ω":{"df":0,"docs":{},"i":{"df":1,"docs":{"101":{"tf":1.4142135623730951}}},"σ":{"(":{"df":0,"docs":{},"i":{"df":1,"docs":{"101":{"tf":1.4142135623730951}}}},"df":0,"docs":{}}}},"x":{"df":1,"docs":{"54":{"tf":1.0}}},"z":{"df":2,"docs":{"77":{"tf":1.0},"78":{"tf":1.0}}},"ζ":{")":{"=":{"a":{"(":{"df":0,"docs":{},"ζ":{")":{"df":0,"docs":{},"q":{"df":0,"docs":{},"l":{"df":0,"docs":{},"​":{"(":{"df":0,"docs":{},"ζ":{")":{"+":{"b":{"(":{"df":0,"docs":{},"ζ":{")":{"df":0,"docs":{},"q":{"df":0,"docs":{},"r":{"df":1,"docs":{"21":{"tf":1.0}}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{},"p":{"df":0,"docs":{},"ˉ":{"df":0,"docs":{},"​":{"c":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"p":{"df":0,"docs":{},"ˉ":{"df":0,"docs":{},"​":{"df":0,"docs":{},"n":{"c":{"df":1,"docs":{"32":{"tf":1.0}}},"df":0,"docs":{}}}}}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"z":{"df":0,"docs":{},"h":{"df":0,"docs":{},"​":{"(":{"df":0,"docs":{},"ζ":{")":{"df":0,"docs":{},"t":{"df":1,"docs":{"21":{"tf":1.0}}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}},"−":{"df":0,"docs":{},"h":{"df":0,"docs":{},"ω":{"df":0,"docs":{},"i":{"df":1,"docs":{"101":{"tf":1.0}}},"σ":{"(":{"df":0,"docs":{},"i":{"df":1,"docs":{"101":{"tf":1.0}}}},"df":0,"docs":{}}}}}},",":{"a":{",":{"b":{",":{"df":0,"docs":{},"z":{"df":1,"docs":{"5":{"tf":1.0}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{},"q":{"df":0,"docs":{},"∈":{"df":0,"docs":{},"f":{"[":{"df":0,"docs":{},"x":{"df":1,"docs":{"90":{"tf":1.0}}}},"df":0,"docs":{}}}}},".":{"df":0,"docs":{},"i":{"df":1,"docs":{"32":{"tf":1.0}}}},"/":{"df":0,"docs":{},"q":{"df":1,"docs":{"90":{"tf":1.0}}}},"0":{"df":1,"docs":{"94":{"tf":1.0}}},"1":{"df":0,"docs":{},"​":{"(":{"d":{"1":{"df":0,"docs":{},"​":{")":{"df":0,"docs":{},"∣":{"df":0,"docs":{},"∣":{"df":0,"docs":{},"…":{"df":0,"docs":{},"∣":{"df":0,"docs":{},"∣":{"df":0,"docs":{},"p":{"df":0,"docs":{},"l":{"df":0,"docs":{},"​":{"(":{"d":{"1":{"df":0,"docs":{},"​":{")":{",":{"df":0,"docs":{},"…":{",":{"df":0,"docs":{},"p":{"df":0,"docs":{},"l":{"df":0,"docs":{},"​":{"(":{"d":{"df":0,"docs":{},"m":{"df":0,"docs":{},"​":{")":{"df":0,"docs":{},"∣":{"df":0,"docs":{},"∣":{"df":0,"docs":{},"…":{"df":0,"docs":{},"∣":{"df":0,"docs":{},"∣":{"df":0,"docs":{},"p":{"df":0,"docs":{},"n":{"df":0,"docs":{},"​":{"(":{"d":{"df":0,"docs":{},"m":{"df":1,"docs":{"80":{"tf":1.0}}}},"df":0,"docs":{}},"df":0,"docs":{}}}}}}}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}}}}}}}},"df":0,"docs":{}}},"df":0,"docs":{},"i":{"df":0,"docs":{},"​":{")":{",":{"df":0,"docs":{},"…":{",":{"df":0,"docs":{},"p":{"df":0,"docs":{},"l":{"df":0,"docs":{},"​":{"(":{"d":{"df":0,"docs":{},"i":{"df":1,"docs":{"80":{"tf":1.0}}}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}}},"df":0,"docs":{},"∣":{"df":0,"docs":{},"∣":{"df":0,"docs":{},"…":{"df":0,"docs":{},"∣":{"df":0,"docs":{},"∣":{"df":0,"docs":{},"p":{"df":0,"docs":{},"l":{"df":0,"docs":{},"​":{"(":{"d":{"df":0,"docs":{},"i":{"df":1,"docs":{"80":{"tf":1.0}}}},"df":0,"docs":{}},"df":0,"docs":{}}}}}}}}}},"df":0,"docs":{}}}},"df":0,"docs":{},"z":{"1":{"df":0,"docs":{},"​":{")":{",":{"df":0,"docs":{},"…":{",":{"df":0,"docs":{},"p":{"df":0,"docs":{},"k":{"df":0,"docs":{},"​":{"(":{"df":0,"docs":{},"z":{"df":0,"docs":{},"l":{"df":1,"docs":{"80":{"tf":1.0}}}}},"df":0,"docs":{}}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{},"p":{"2":{"df":0,"docs":{},"​":{"df":0,"docs":{},"p":{"3":{"df":0,"docs":{},"​":{"df":0,"docs":{},"​":{"=":{"a":{"df":0,"docs":{},"q":{"df":0,"docs":{},"l":{"df":0,"docs":{},"​":{"+":{"b":{"df":0,"docs":{},"q":{"df":0,"docs":{},"r":{"df":0,"docs":{},"​":{"+":{"a":{"b":{"df":0,"docs":{},"q":{"df":0,"docs":{},"m":{"df":0,"docs":{},"​":{"+":{"c":{"df":0,"docs":{},"q":{"df":0,"docs":{},"o":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"q":{"c":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"p":{"df":0,"docs":{},"i":{"=":{"(":{"a":{"+":{"df":0,"docs":{},"β":{"df":0,"docs":{},"x":{"+":{"df":0,"docs":{},"γ":{")":{"(":{"b":{"+":{"df":0,"docs":{},"β":{"df":0,"docs":{},"k":{"1":{"df":0,"docs":{},"​":{"df":0,"docs":{},"x":{"+":{"df":0,"docs":{},"γ":{")":{"(":{"c":{"+":{"df":0,"docs":{},"β":{"df":0,"docs":{},"k":{"2":{"df":0,"docs":{},"​":{"df":0,"docs":{},"x":{"+":{"df":0,"docs":{},"γ":{")":{"df":0,"docs":{},"z":{"df":0,"docs":{},"−":{"(":{"a":{"+":{"df":0,"docs":{},"β":{"df":0,"docs":{},"s":{"df":0,"docs":{},"σ":{"1":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"γ":{")":{"(":{"b":{"+":{"df":0,"docs":{},"β":{"df":0,"docs":{},"s":{"df":0,"docs":{},"σ":{"2":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"γ":{")":{"(":{"c":{"+":{"df":0,"docs":{},"β":{"df":0,"docs":{},"s":{"df":0,"docs":{},"σ":{"3":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"γ":{")":{"df":0,"docs":{},"z":{"(":{"df":0,"docs":{},"ω":{"df":0,"docs":{},"x":{")":{"=":{"(":{"df":0,"docs":{},"z":{"df":0,"docs":{},"−":{"1":{")":{"df":0,"docs":{},"l":{"1":{"df":1,"docs":{"26":{"tf":1.0}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}}},"df":0,"docs":{}}}},"=":{"1":{"df":1,"docs":{"73":{"tf":1.0}}},"a":{"df":0,"docs":{},"q":{"df":0,"docs":{},"l":{"df":0,"docs":{},"​":{"+":{"b":{"df":0,"docs":{},"q":{"df":0,"docs":{},"r":{"df":0,"docs":{},"​":{"+":{"a":{"b":{"df":0,"docs":{},"q":{"df":0,"docs":{},"m":{"df":1,"docs":{"21":{"tf":1.0}}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{},"p":{"1":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"α":{"df":0,"docs":{},"p":{"2":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"α":{"2":{"df":0,"docs":{},"p":{"3":{"df":1,"docs":{"26":{"tf":1.0}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"t":{"df":0,"docs":{},"z":{"df":0,"docs":{},"h":{"df":1,"docs":{"26":{"tf":1.0}}}}},"{":{"df":0,"docs":{},"p":{"1":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"…":{",":{"df":0,"docs":{},"p":{"df":0,"docs":{},"l":{"df":1,"docs":{"80":{"tf":1.0}}},"n":{"df":1,"docs":{"80":{"tf":1.0}}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"^":{"df":0,"docs":{},"​":{"df":0,"docs":{},"n":{"c":{"1":{"df":0,"docs":{},"​":{"]":{"1":{"df":0,"docs":{},"​":{"[":{"df":0,"docs":{},"p":{"^":{"df":0,"docs":{},"​":{"df":0,"docs":{},"n":{"c":{"2":{"df":0,"docs":{},"​":{"]":{"1":{"df":0,"docs":{},"​":{"[":{"df":0,"docs":{},"p":{"^":{"df":0,"docs":{},"​":{"df":0,"docs":{},"n":{"c":{"3":{"df":0,"docs":{},"​":{"]":{"1":{"df":0,"docs":{},"​":{"df":0,"docs":{},"​":{"=":{"a":{"df":0,"docs":{},"ˉ":{"[":{"df":0,"docs":{},"q":{"df":0,"docs":{},"l":{"df":0,"docs":{},"​":{"]":{"1":{"df":0,"docs":{},"​":{"+":{"b":{"df":0,"docs":{},"ˉ":{"[":{"df":0,"docs":{},"q":{"df":0,"docs":{},"r":{"df":0,"docs":{},"​":{"]":{"1":{"df":0,"docs":{},"​":{"+":{"(":{"a":{"df":0,"docs":{},"ˉ":{"b":{"df":0,"docs":{},"ˉ":{")":{"[":{"df":0,"docs":{},"q":{"df":0,"docs":{},"m":{"df":0,"docs":{},"​":{"]":{"1":{"df":0,"docs":{},"​":{"+":{"c":{"df":0,"docs":{},"ˉ":{"[":{"df":0,"docs":{},"q":{"df":0,"docs":{},"o":{"df":0,"docs":{},"​":{"]":{"1":{"df":0,"docs":{},"​":{"+":{"[":{"df":0,"docs":{},"q":{"c":{"df":0,"docs":{},"​":{"]":{"1":{"df":0,"docs":{},"​":{"=":{"(":{"a":{"df":0,"docs":{},"ˉ":{"+":{"df":0,"docs":{},"β":{"df":0,"docs":{},"ζ":{"+":{"df":0,"docs":{},"γ":{")":{"(":{"b":{"df":0,"docs":{},"ˉ":{"+":{"df":0,"docs":{},"β":{"df":0,"docs":{},"k":{"1":{"df":0,"docs":{},"​":{"df":0,"docs":{},"ζ":{"+":{"df":0,"docs":{},"γ":{")":{"(":{"c":{"df":0,"docs":{},"ˉ":{"+":{"df":0,"docs":{},"β":{"df":0,"docs":{},"k":{"2":{"df":0,"docs":{},"​":{"df":0,"docs":{},"ζ":{"+":{"df":0,"docs":{},"γ":{")":{"[":{"df":0,"docs":{},"z":{"]":{"1":{"df":0,"docs":{},"​":{"df":0,"docs":{},"−":{"(":{"a":{"df":0,"docs":{},"ˉ":{"+":{"df":0,"docs":{},"β":{"df":0,"docs":{},"s":{"df":0,"docs":{},"ˉ":{"df":0,"docs":{},"σ":{"1":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"γ":{")":{"(":{"b":{"df":0,"docs":{},"ˉ":{"+":{"df":0,"docs":{},"β":{"df":0,"docs":{},"s":{"df":0,"docs":{},"ˉ":{"df":0,"docs":{},"σ":{"2":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"γ":{")":{"df":0,"docs":{},"β":{"df":0,"docs":{},"z":{"df":0,"docs":{},"ˉ":{"df":0,"docs":{},"ω":{"df":0,"docs":{},"​":{"[":{"df":0,"docs":{},"s":{"df":0,"docs":{},"σ":{"3":{"df":0,"docs":{},"​":{"]":{"1":{"df":0,"docs":{},"​":{"=":{"df":0,"docs":{},"l":{"1":{"df":0,"docs":{},"​":{"(":{"df":0,"docs":{},"ζ":{")":{"[":{"df":0,"docs":{},"z":{"]":{"1":{"df":1,"docs":{"32":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}}}}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{},"p":{"^":{"df":0,"docs":{},"​":{"df":0,"docs":{},"n":{"c":{"2":{"df":0,"docs":{},"​":{"df":0,"docs":{},"p":{"^":{"df":0,"docs":{},"​":{"df":0,"docs":{},"n":{"c":{"3":{"df":0,"docs":{},"​":{"df":0,"docs":{},"​":{"=":{"a":{"df":0,"docs":{},"ˉ":{"df":0,"docs":{},"q":{"df":0,"docs":{},"l":{"df":0,"docs":{},"​":{"+":{"b":{"df":0,"docs":{},"ˉ":{"df":0,"docs":{},"q":{"df":0,"docs":{},"r":{"df":0,"docs":{},"​":{"+":{"a":{"df":0,"docs":{},"ˉ":{"b":{"df":0,"docs":{},"ˉ":{"df":0,"docs":{},"q":{"df":0,"docs":{},"m":{"df":0,"docs":{},"​":{"+":{"c":{"df":0,"docs":{},"ˉ":{"df":0,"docs":{},"q":{"df":0,"docs":{},"o":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"q":{"c":{"df":0,"docs":{},"​":{"=":{"(":{"a":{"df":0,"docs":{},"ˉ":{"+":{"df":0,"docs":{},"β":{"df":0,"docs":{},"ζ":{"+":{"df":0,"docs":{},"γ":{")":{"(":{"b":{"df":0,"docs":{},"ˉ":{"+":{"df":0,"docs":{},"β":{"df":0,"docs":{},"k":{"1":{"df":0,"docs":{},"​":{"df":0,"docs":{},"ζ":{"+":{"df":0,"docs":{},"γ":{")":{"(":{"c":{"df":0,"docs":{},"ˉ":{"+":{"df":0,"docs":{},"β":{"df":0,"docs":{},"k":{"2":{"df":0,"docs":{},"​":{"df":0,"docs":{},"ζ":{"+":{"df":0,"docs":{},"γ":{")":{"df":0,"docs":{},"z":{"df":0,"docs":{},"−":{"(":{"a":{"df":0,"docs":{},"ˉ":{"+":{"df":0,"docs":{},"β":{"df":0,"docs":{},"s":{"df":0,"docs":{},"ˉ":{"df":0,"docs":{},"σ":{"1":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"γ":{")":{"(":{"b":{"df":0,"docs":{},"ˉ":{"+":{"df":0,"docs":{},"β":{"df":0,"docs":{},"s":{"df":0,"docs":{},"ˉ":{"df":0,"docs":{},"σ":{"2":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"γ":{")":{"df":0,"docs":{},"β":{"df":0,"docs":{},"z":{"df":0,"docs":{},"ˉ":{"df":0,"docs":{},"ω":{"df":0,"docs":{},"​":{"df":0,"docs":{},"s":{"df":0,"docs":{},"σ":{"3":{"df":0,"docs":{},"​":{"=":{"df":0,"docs":{},"l":{"1":{"df":0,"docs":{},"​":{"(":{"df":0,"docs":{},"ζ":{")":{"df":0,"docs":{},"z":{"df":1,"docs":{"28":{"tf":1.0}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}}}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"_":{"df":0,"docs":{},"n":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"_":{"c":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"df":0,"docs":{},"s":{"df":0,"docs":{},"t":{"a":{"df":0,"docs":{},"n":{"df":0,"docs":{},"t":{"_":{"df":0,"docs":{},"z":{"df":0,"docs":{},"e":{"df":0,"docs":{},"t":{"a":{"df":1,"docs":{"35":{"tf":1.0}}},"df":0,"docs":{}}}}},"df":0,"docs":{}}}},"df":1,"docs":{"35":{"tf":1.0}}}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"a":{"c":{"df":0,"docs":{},"k":{"a":{"df":0,"docs":{},"g":{"df":1,"docs":{"38":{"tf":1.0}}}},"df":0,"docs":{}}},"d":{"df":5,"docs":{"101":{"tf":1.0},"121":{"tf":1.0},"142":{"tf":1.0},"146":{"tf":2.0},"36":{"tf":1.7320508075688772}}},"df":0,"docs":{},"i":{"df":0,"docs":{},"r":{"df":9,"docs":{"102":{"tf":1.0},"135":{"tf":1.0},"14":{"tf":3.4641016151377544},"146":{"tf":2.23606797749979},"151":{"tf":1.0},"3":{"tf":1.0},"38":{"tf":1.0},"92":{"tf":1.0},"95":{"tf":1.0}}}},"p":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":3,"docs":{"146":{"tf":1.4142135623730951},"16":{"tf":1.0},"4":{"tf":1.0}}}},"i":{"df":0,"docs":{},"n":{"df":0,"docs":{},"i":{"df":1,"docs":{"103":{"tf":1.0}}}}}},"r":{"a":{"df":0,"docs":{},"m":{"df":0,"docs":{},"e":{"df":0,"docs":{},"t":{"df":7,"docs":{"109":{"tf":1.7320508075688772},"128":{"tf":1.0},"68":{"tf":1.0},"73":{"tf":1.4142135623730951},"75":{"tf":1.0},"80":{"tf":1.4142135623730951},"91":{"tf":1.0}}}}}},"df":0,"docs":{},"t":{"df":22,"docs":{"114":{"tf":1.0},"117":{"tf":1.0},"123":{"tf":1.0},"13":{"tf":1.0},"14":{"tf":1.0},"146":{"tf":1.0},"147":{"tf":1.0},"149":{"tf":1.0},"151":{"tf":1.0},"16":{"tf":1.0},"28":{"tf":1.0},"32":{"tf":1.0},"35":{"tf":1.7320508075688772},"50":{"tf":1.0},"53":{"tf":1.0},"58":{"tf":1.0},"69":{"tf":1.0},"7":{"tf":1.0},"70":{"tf":1.0},"73":{"tf":1.0},"9":{"tf":1.0},"94":{"tf":1.0}},"i":{"a":{"df":0,"docs":{},"l":{"df":1,"docs":{"28":{"tf":1.7320508075688772}}}},"c":{"df":0,"docs":{},"u":{"df":0,"docs":{},"l":{"a":{"df":0,"docs":{},"r":{"df":14,"docs":{"10":{"tf":1.4142135623730951},"11":{"tf":1.0},"13":{"tf":1.4142135623730951},"134":{"tf":1.0},"146":{"tf":1.4142135623730951},"21":{"tf":1.0},"22":{"tf":1.0},"28":{"tf":1.0},"35":{"tf":1.4142135623730951},"36":{"tf":1.0},"43":{"tf":1.0},"69":{"tf":1.0},"70":{"tf":1.4142135623730951},"8":{"tf":1.4142135623730951}}}},"df":0,"docs":{}}}},"df":5,"docs":{"19":{"tf":1.4142135623730951},"4":{"tf":1.0},"7":{"tf":1.0},"73":{"tf":1.4142135623730951},"74":{"tf":1.0}}}}},"s":{"df":0,"docs":{},"s":{"df":10,"docs":{"11":{"tf":1.0},"110":{"tf":1.4142135623730951},"114":{"tf":1.4142135623730951},"117":{"tf":1.0},"144":{"tf":1.0},"33":{"tf":1.0},"40":{"tf":2.0},"50":{"tf":1.4142135623730951},"57":{"tf":1.0},"75":{"tf":1.0}}}},"t":{"df":0,"docs":{},"h":{"df":7,"docs":{"101":{"tf":1.7320508075688772},"102":{"tf":1.0},"35":{"tf":1.0},"72":{"tf":1.4142135623730951},"80":{"tf":1.4142135623730951},"92":{"tf":1.4142135623730951},"95":{"tf":1.7320508075688772}}}}},"c":{"[":{"0":{"df":1,"docs":{"147":{"tf":1.0}}},"1":{"df":1,"docs":{"147":{"tf":1.0}}},"df":0,"docs":{}},"_":{"df":0,"docs":{},"j":{"df":0,"docs":{},"n":{"df":0,"docs":{},"z":{"df":1,"docs":{"146":{"tf":1.0}}}}},"u":{"df":0,"docs":{},"p":{"d":{"a":{"df":0,"docs":{},"t":{"df":1,"docs":{"146":{"tf":1.0}}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":8,"docs":{"133":{"tf":1.0},"136":{"tf":1.4142135623730951},"141":{"tf":1.0},"142":{"tf":1.0},"144":{"tf":1.0},"146":{"tf":2.23606797749979},"147":{"tf":1.4142135623730951},"19":{"tf":1.4142135623730951}},"i":{"df":1,"docs":{"142":{"tf":1.0}},"​":{",":{"a":{"df":0,"docs":{},"p":{"df":0,"docs":{},"i":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"f":{"df":0,"docs":{},"p":{"df":0,"docs":{},"i":{"df":1,"docs":{"142":{"tf":1.0}}}}}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":14,"docs":{"101":{"tf":1.0},"147":{"tf":2.0},"21":{"tf":2.6457513110645907},"28":{"tf":1.0},"32":{"tf":1.4142135623730951},"33":{"tf":1.0},"54":{"tf":1.4142135623730951},"73":{"tf":2.6457513110645907},"75":{"tf":2.8284271247461903},"76":{"tf":2.0},"77":{"tf":1.4142135623730951},"78":{"tf":1.4142135623730951},"79":{"tf":1.7320508075688772},"80":{"tf":2.449489742783178}},"e":{"df":0,"docs":{},"r":{"df":3,"docs":{"107":{"tf":1.0},"65":{"tf":1.0},"9":{"tf":1.0}},"f":{"df":0,"docs":{},"o":{"df":0,"docs":{},"r":{"df":0,"docs":{},"m":{"df":10,"docs":{"100":{"tf":1.0},"109":{"tf":1.0},"123":{"tf":1.0},"125":{"tf":1.4142135623730951},"151":{"tf":1.0},"35":{"tf":1.0},"66":{"tf":1.0},"7":{"tf":1.0},"82":{"tf":1.0},"99":{"tf":1.0}}}}}},"m":{"df":0,"docs":{},"u":{"df":0,"docs":{},"t":{"df":6,"docs":{"101":{"tf":1.4142135623730951},"138":{"tf":1.0},"139":{"tf":1.0},"14":{"tf":2.0},"146":{"tf":2.0},"147":{"tf":1.4142135623730951}}}}}}},"h":{"a":{"df":0,"docs":{},"n":{"df":0,"docs":{},"t":{"df":0,"docs":{},"o":{"df":0,"docs":{},"m":{"d":{"a":{"df":0,"docs":{},"t":{"a":{"<":{"df":0,"docs":{},"f":{"df":1,"docs":{"35":{"tf":1.4142135623730951}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":1,"docs":{"35":{"tf":1.4142135623730951}}}}}},"s":{"df":0,"docs":{},"e":{"df":4,"docs":{"127":{"tf":1.0},"22":{"tf":1.0},"35":{"tf":1.0},"94":{"tf":1.4142135623730951}}}}},"df":0,"docs":{}},"i":{")":{"df":0,"docs":{},"i":{"df":0,"docs":{},"​":{"=":{"0":{"df":2,"docs":{"14":{"tf":1.0},"16":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":7,"docs":{"13":{"tf":2.0},"14":{"tf":1.4142135623730951},"16":{"tf":2.449489742783178},"26":{"tf":1.4142135623730951},"32":{"tf":2.449489742783178},"36":{"tf":1.0},"80":{"tf":1.0}},"e":{"c":{"df":2,"docs":{"114":{"tf":1.0},"116":{"tf":1.0}}},"df":0,"docs":{}},"t":{"df":0,"docs":{},"f":{"a":{"df":0,"docs":{},"l":{"df":1,"docs":{"42":{"tf":1.0}}}},"df":0,"docs":{}}},"​":{":":{"=":{"df":0,"docs":{},"j":{"=":{"0":{"df":0,"docs":{},"∏":{"df":0,"docs":{},"i":{"df":0,"docs":{},"​":{"df":0,"docs":{},"z":{"df":0,"docs":{},"−":{"(":{"a":{"df":0,"docs":{},"i":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"α":{"df":0,"docs":{},"v":{"df":0,"docs":{},"i":{"df":0,"docs":{},"​":{")":{"df":0,"docs":{},"z":{"df":0,"docs":{},"−":{"(":{"a":{"df":0,"docs":{},"i":{"df":0,"docs":{},"′":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"α":{"df":0,"docs":{},"v":{"df":0,"docs":{},"i":{"df":1,"docs":{"147":{"tf":1.0}}}}}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}}}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"′":{"df":0,"docs":{},"​":{":":{"=":{"df":0,"docs":{},"j":{"=":{"0":{"df":0,"docs":{},"∏":{"df":0,"docs":{},"i":{"df":0,"docs":{},"​":{"df":0,"docs":{},"z":{"df":0,"docs":{},"′":{"df":0,"docs":{},"−":{"b":{"df":0,"docs":{},"i":{"df":0,"docs":{},"​":{"df":0,"docs":{},"z":{"df":0,"docs":{},"′":{"df":0,"docs":{},"−":{"b":{"df":0,"docs":{},"i":{"df":1,"docs":{"147":{"tf":1.0}}}},"df":0,"docs":{}}}}}}},"df":0,"docs":{}}}}}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"j":{"b":{"df":2,"docs":{"70":{"tf":1.0},"91":{"tf":1.0}}},"df":0,"docs":{}},"k":{"+":{"1":{"df":0,"docs":{},"​":{"(":{"df":0,"docs":{},"υ":{"2":{"df":0,"docs":{},"k":{"+":{"1":{"df":1,"docs":{"102":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":3,"docs":{"102":{"tf":1.4142135623730951},"94":{"tf":1.0},"95":{"tf":1.0}},"t":{"df":2,"docs":{"70":{"tf":1.0},"91":{"tf":1.0}},"​":{"(":{"df":0,"docs":{},"t":{"df":0,"docs":{},"i":{",":{"0":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"…":{",":{"df":0,"docs":{},"t":{"df":0,"docs":{},"i":{",":{"df":0,"docs":{},"m":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"t":{"df":0,"docs":{},"i":{"+":{"1":{",":{"0":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"…":{",":{"df":0,"docs":{},"t":{"df":0,"docs":{},"i":{"+":{"1":{",":{"df":0,"docs":{},"m":{"df":0,"docs":{},"​":{")":{"=":{"0":{"df":1,"docs":{"70":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"x":{"1":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"…":{",":{"df":0,"docs":{},"x":{"df":0,"docs":{},"m":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"y":{"1":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"…":{",":{"df":0,"docs":{},"y":{"df":0,"docs":{},"m":{"df":1,"docs":{"70":{"tf":1.0}}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"​":{"(":{"df":0,"docs":{},"υ":{"2":{"df":0,"docs":{},"k":{"+":{"1":{"df":1,"docs":{"102":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},":":{"=":{"df":0,"docs":{},"p":{"df":0,"docs":{},"k":{"df":0,"docs":{},"−":{"1":{"df":0,"docs":{},"o":{"d":{"d":{"df":0,"docs":{},"​":{"(":{"df":0,"docs":{},"x":{")":{"+":{"df":0,"docs":{},"ζ":{"df":0,"docs":{},"k":{"df":0,"docs":{},"−":{"1":{"df":0,"docs":{},"​":{"df":0,"docs":{},"p":{"df":0,"docs":{},"k":{"df":0,"docs":{},"−":{"1":{"df":0,"docs":{},"e":{"df":0,"docs":{},"v":{"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"df":0,"docs":{},"​":{"(":{"df":0,"docs":{},"x":{"df":1,"docs":{"94":{"tf":1.0}}}},"df":0,"docs":{}}}}}}},"df":0,"docs":{}}}}}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"]":{":":{"=":{"c":{"df":0,"docs":{},"o":{"df":0,"docs":{},"m":{"df":0,"docs":{},"m":{"df":0,"docs":{},"i":{"df":0,"docs":{},"t":{"(":{"df":0,"docs":{},"p":{"df":0,"docs":{},"k":{"df":0,"docs":{},"​":{"(":{"d":{"df":0,"docs":{},"k":{"df":1,"docs":{"94":{"tf":1.0}}}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}}}}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"−":{"1":{"df":1,"docs":{"94":{"tf":1.0}},"​":{"=":{"df":0,"docs":{},"p":{"df":0,"docs":{},"k":{"df":0,"docs":{},"−":{"1":{"df":0,"docs":{},"o":{"d":{"d":{"df":0,"docs":{},"​":{"(":{"df":0,"docs":{},"x":{"2":{")":{"+":{"df":0,"docs":{},"x":{"df":0,"docs":{},"p":{"df":0,"docs":{},"k":{"df":0,"docs":{},"−":{"1":{"df":0,"docs":{},"e":{"df":0,"docs":{},"v":{"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"df":0,"docs":{},"​":{"(":{"df":0,"docs":{},"x":{"2":{"df":1,"docs":{"94":{"tf":1.0}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}}}},"df":0,"docs":{}}}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"l":{"a":{"c":{"df":0,"docs":{},"e":{"df":3,"docs":{"123":{"tf":1.4142135623730951},"146":{"tf":1.0},"151":{"tf":1.0}}}},"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"df":2,"docs":{"127":{"tf":1.4142135623730951},"149":{"tf":1.0}}}},"y":{"df":1,"docs":{"16":{"tf":1.0}}}},"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"df":0,"docs":{},"t":{"df":0,"docs":{},"i":{"df":1,"docs":{"130":{"tf":1.0}}}}}},"o":{"df":0,"docs":{},"n":{"df":0,"docs":{},"k":{"df":9,"docs":{"12":{"tf":1.4142135623730951},"14":{"tf":1.4142135623730951},"19":{"tf":1.0},"21":{"tf":1.4142135623730951},"34":{"tf":1.4142135623730951},"4":{"tf":1.7320508075688772},"44":{"tf":1.0},"7":{"tf":1.7320508075688772},"8":{"tf":1.0}}}}},"u":{"df":0,"docs":{},"g":{"df":1,"docs":{"70":{"tf":1.0}}}}},"n":{"c":{"df":0,"docs":{},"​":{"]":{"1":{"df":1,"docs":{"32":{"tf":1.4142135623730951}},"​":{"=":{"[":{"df":0,"docs":{},"p":{"df":0,"docs":{},"n":{"c":{"1":{"df":0,"docs":{},"​":{"]":{"1":{"df":0,"docs":{},"​":{"+":{"[":{"df":0,"docs":{},"p":{"df":0,"docs":{},"n":{"c":{"2":{"df":0,"docs":{},"​":{"]":{"1":{"df":0,"docs":{},"​":{"+":{"[":{"df":0,"docs":{},"p":{"df":0,"docs":{},"n":{"c":{"3":{"df":0,"docs":{},"​":{"]":{"1":{"df":1,"docs":{"32":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{},"t":{"df":0,"docs":{},"p":{"a":{"df":0,"docs":{},"r":{"df":0,"docs":{},"t":{"df":0,"docs":{},"i":{"a":{"df":0,"docs":{},"l":{"df":0,"docs":{},"​":{"df":0,"docs":{},"​":{"=":{"df":0,"docs":{},"p":{"df":0,"docs":{},"n":{"c":{"1":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"α":{"df":0,"docs":{},"p":{"df":0,"docs":{},"n":{"c":{"2":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"α":{"2":{"df":0,"docs":{},"p":{"df":0,"docs":{},"n":{"c":{"3":{"df":0,"docs":{},"​":{"=":{"df":0,"docs":{},"t":{"df":0,"docs":{},"l":{"df":0,"docs":{},"o":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"ζ":{"df":0,"docs":{},"n":{"+":{"2":{"df":0,"docs":{},"t":{"df":0,"docs":{},"m":{"df":0,"docs":{},"i":{"d":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"ζ":{"2":{"(":{"df":0,"docs":{},"n":{"+":{"2":{")":{"df":0,"docs":{},"t":{"df":0,"docs":{},"h":{"df":0,"docs":{},"i":{"df":1,"docs":{"28":{"tf":1.0}}}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}}}},"df":0,"docs":{}}}}},"df":0,"docs":{}}}}},"df":1,"docs":{"94":{"tf":1.4142135623730951}},"​":{"df":0,"docs":{},"∈":{"df":0,"docs":{},"f":{"df":1,"docs":{"94":{"tf":1.0}}}}},"−":{"1":{"df":1,"docs":{"146":{"tf":1.0}},"​":{"=":{"1":{"df":1,"docs":{"146":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"o":{"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"df":0,"docs":{},"t":{"df":23,"docs":{"101":{"tf":1.0},"112":{"tf":1.4142135623730951},"114":{"tf":2.8284271247461903},"115":{"tf":1.0},"117":{"tf":1.0},"123":{"tf":1.7320508075688772},"133":{"tf":1.0},"142":{"tf":1.0},"149":{"tf":1.0},"19":{"tf":1.0},"3":{"tf":1.0},"40":{"tf":1.0},"49":{"tf":1.0},"58":{"tf":1.4142135623730951},"59":{"tf":1.0},"60":{"tf":1.7320508075688772},"62":{"tf":1.4142135623730951},"68":{"tf":1.7320508075688772},"69":{"tf":1.4142135623730951},"80":{"tf":1.0},"85":{"tf":1.0},"88":{"tf":1.0},"95":{"tf":1.0}},"e":{"df":0,"docs":{},"r":{"df":3,"docs":{"142":{"tf":1.4142135623730951},"146":{"tf":2.449489742783178},"147":{"tf":1.4142135623730951}}}},"l":{"df":0,"docs":{},"e":{"df":0,"docs":{},"s":{"df":0,"docs":{},"s":{"df":1,"docs":{"7":{"tf":1.0}}}}}}}}},"l":{"df":0,"docs":{},"i":{"df":1,"docs":{"119":{"tf":1.0}}},"y":{"df":0,"docs":{},"l":{"df":0,"docs":{},"o":{"df":0,"docs":{},"g":{"a":{"df":0,"docs":{},"r":{"df":0,"docs":{},"i":{"df":0,"docs":{},"t":{"df":0,"docs":{},"h":{"df":0,"docs":{},"m":{"df":1,"docs":{"81":{"tf":1.0}}}}}}}},"df":0,"docs":{}}}},"n":{"df":0,"docs":{},"o":{"df":0,"docs":{},"m":{"df":0,"docs":{},"i":{"a":{"df":0,"docs":{},"l":{"'":{"df":3,"docs":{"19":{"tf":1.0},"3":{"tf":1.0},"68":{"tf":1.0}}},"<":{"df":0,"docs":{},"f":{"df":0,"docs":{},"i":{"df":0,"docs":{},"e":{"df":0,"docs":{},"l":{"d":{"df":0,"docs":{},"e":{"df":0,"docs":{},"l":{"df":0,"docs":{},"e":{"df":0,"docs":{},"m":{"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"df":0,"docs":{},"t":{"<":{"df":0,"docs":{},"f":{"df":1,"docs":{"35":{"tf":2.8284271247461903}}}},"df":0,"docs":{}}}}}}}}},"df":0,"docs":{}}}}}},"df":0,"docs":{}}},"df":70,"docs":{"100":{"tf":1.4142135623730951},"101":{"tf":1.0},"102":{"tf":1.0},"112":{"tf":2.23606797749979},"113":{"tf":1.7320508075688772},"114":{"tf":3.3166247903554},"116":{"tf":1.0},"117":{"tf":1.4142135623730951},"119":{"tf":1.7320508075688772},"121":{"tf":1.0},"123":{"tf":3.4641016151377544},"124":{"tf":1.0},"125":{"tf":2.6457513110645907},"14":{"tf":5.830951894845301},"149":{"tf":1.0},"15":{"tf":1.0},"16":{"tf":3.0},"19":{"tf":3.1622776601683795},"20":{"tf":3.1622776601683795},"21":{"tf":2.0},"23":{"tf":1.0},"24":{"tf":1.4142135623730951},"25":{"tf":1.4142135623730951},"26":{"tf":1.0},"28":{"tf":1.4142135623730951},"3":{"tf":2.23606797749979},"32":{"tf":1.0},"35":{"tf":3.605551275463989},"4":{"tf":1.4142135623730951},"42":{"tf":1.0},"48":{"tf":1.7320508075688772},"5":{"tf":2.6457513110645907},"52":{"tf":2.23606797749979},"53":{"tf":2.23606797749979},"54":{"tf":2.449489742783178},"55":{"tf":2.0},"56":{"tf":1.7320508075688772},"57":{"tf":1.7320508075688772},"58":{"tf":1.0},"59":{"tf":3.0},"60":{"tf":1.0},"62":{"tf":2.23606797749979},"65":{"tf":2.23606797749979},"66":{"tf":2.0},"67":{"tf":1.0},"68":{"tf":1.7320508075688772},"69":{"tf":2.8284271247461903},"70":{"tf":1.7320508075688772},"71":{"tf":2.23606797749979},"73":{"tf":2.6457513110645907},"74":{"tf":1.7320508075688772},"75":{"tf":3.0},"76":{"tf":1.0},"77":{"tf":1.0},"78":{"tf":1.4142135623730951},"79":{"tf":1.4142135623730951},"8":{"tf":1.4142135623730951},"80":{"tf":3.7416573867739413},"81":{"tf":1.0},"82":{"tf":1.0},"83":{"tf":1.0},"84":{"tf":4.123105625617661},"85":{"tf":1.0},"86":{"tf":1.0},"90":{"tf":1.4142135623730951},"91":{"tf":1.4142135623730951},"94":{"tf":2.449489742783178},"95":{"tf":2.0},"97":{"tf":1.0},"99":{"tf":1.0}}}}}}}},"o":{"df":0,"docs":{},"l":{"df":5,"docs":{"130":{"tf":1.0},"133":{"tf":1.0},"134":{"tf":1.4142135623730951},"135":{"tf":1.0},"138":{"tf":1.4142135623730951}}}},"p":{"df":0,"docs":{},"u":{"df":0,"docs":{},"l":{"a":{"df":0,"docs":{},"r":{"df":2,"docs":{"19":{"tf":1.0},"4":{"tf":1.0}}}},"df":0,"docs":{}}}},"s":{"df":0,"docs":{},"e":{"df":0,"docs":{},"i":{"d":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"df":1,"docs":{"151":{"tf":1.0}}}}},"df":0,"docs":{}}},"i":{"df":0,"docs":{},"t":{"df":4,"docs":{"101":{"tf":1.4142135623730951},"14":{"tf":1.4142135623730951},"151":{"tf":1.7320508075688772},"72":{"tf":1.0}}}},"s":{"df":0,"docs":{},"i":{"b":{"df":0,"docs":{},"l":{"df":6,"docs":{"114":{"tf":1.0},"149":{"tf":1.4142135623730951},"36":{"tf":1.0},"38":{"tf":1.0},"50":{"tf":1.0},"71":{"tf":1.0}}}},"df":0,"docs":{}}}},"w":{"(":{"df":0,"docs":{},"s":{"df":0,"docs":{},"y":{"df":0,"docs":{},"s":{"df":0,"docs":{},"t":{"df":0,"docs":{},"e":{"df":0,"docs":{},"m":{"df":1,"docs":{"45":{"tf":1.0}}}}}}}},"x":{"df":1,"docs":{"45":{"tf":1.0}}}},"df":1,"docs":{"45":{"tf":1.0}},"e":{"df":0,"docs":{},"r":{"df":15,"docs":{"121":{"tf":1.0},"124":{"tf":1.4142135623730951},"125":{"tf":1.0},"142":{"tf":1.0},"146":{"tf":1.0},"149":{"tf":1.0},"28":{"tf":1.4142135623730951},"36":{"tf":1.0},"38":{"tf":1.4142135623730951},"52":{"tf":1.0},"68":{"tf":1.7320508075688772},"73":{"tf":1.0},"75":{"tf":1.0},"95":{"tf":1.0},"97":{"tf":1.0}}}}}},"r":{"a":{"c":{"df":0,"docs":{},"t":{"df":0,"docs":{},"i":{"c":{"df":1,"docs":{"149":{"tf":1.0}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{},"e":{"c":{"df":0,"docs":{},"e":{"d":{"df":4,"docs":{"49":{"tf":1.0},"52":{"tf":1.0},"70":{"tf":1.0},"84":{"tf":1.0}}},"df":0,"docs":{}},"i":{"df":0,"docs":{},"s":{"df":6,"docs":{"14":{"tf":1.0},"22":{"tf":1.0},"34":{"tf":1.0},"73":{"tf":1.4142135623730951},"74":{"tf":1.0},"87":{"tf":1.0}}}},"o":{"df":0,"docs":{},"m":{"df":0,"docs":{},"p":{"df":0,"docs":{},"u":{"df":0,"docs":{},"t":{"df":2,"docs":{"34":{"tf":1.0},"56":{"tf":1.0}}}}}}}},"d":{"df":0,"docs":{},"e":{"df":0,"docs":{},"f":{"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"df":1,"docs":{"70":{"tf":1.0}}}}}},"i":{"c":{"df":0,"docs":{},"t":{"df":1,"docs":{"118":{"tf":1.0}}}},"df":0,"docs":{}}},"df":0,"docs":{},"f":{"df":0,"docs":{},"i":{"df":0,"docs":{},"x":{"df":1,"docs":{"88":{"tf":1.0}}}}},"i":{"df":0,"docs":{},"m":{"a":{"df":0,"docs":{},"g":{"df":1,"docs":{"7":{"tf":1.0}}}},"df":0,"docs":{}}},"p":{"df":0,"docs":{},"r":{"df":0,"docs":{},"o":{"c":{"df":0,"docs":{},"e":{"df":0,"docs":{},"s":{"df":0,"docs":{},"s":{"df":6,"docs":{"15":{"tf":1.4142135623730951},"23":{"tf":1.0},"35":{"tf":1.7320508075688772},"42":{"tf":1.0},"70":{"tf":1.0},"83":{"tf":1.0}}}}}},"df":0,"docs":{}}}},"t":{"df":0,"docs":{},"t":{"df":0,"docs":{},"i":{"df":5,"docs":{"113":{"tf":1.0},"132":{"tf":1.0},"14":{"tf":1.0},"151":{"tf":1.0},"37":{"tf":1.0}}}}},"v":{"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"df":0,"docs":{},"t":{"df":2,"docs":{"42":{"tf":1.0},"71":{"tf":1.0}}}}},"i":{"df":0,"docs":{},"o":{"df":0,"docs":{},"u":{"df":10,"docs":{"107":{"tf":1.7320508075688772},"109":{"tf":1.0},"11":{"tf":1.0},"14":{"tf":2.6457513110645907},"145":{"tf":1.0},"149":{"tf":1.4142135623730951},"45":{"tf":1.0},"70":{"tf":1.0},"84":{"tf":1.0},"86":{"tf":1.0}}}}}}},"i":{"df":0,"docs":{},"m":{"a":{"df":0,"docs":{},"r":{"df":0,"docs":{},"i":{"df":1,"docs":{"70":{"tf":1.0}}}}},"df":0,"docs":{},"e":{"df":2,"docs":{"14":{"tf":1.0},"41":{"tf":1.0}}},"i":{"df":0,"docs":{},"t":{"df":17,"docs":{"114":{"tf":1.0},"124":{"tf":3.7416573867739413},"125":{"tf":1.7320508075688772},"14":{"tf":2.23606797749979},"16":{"tf":1.0},"23":{"tf":1.0},"34":{"tf":1.0},"35":{"tf":1.0},"36":{"tf":1.0},"38":{"tf":1.0},"5":{"tf":1.0},"52":{"tf":1.4142135623730951},"68":{"tf":1.4142135623730951},"71":{"tf":1.0},"73":{"tf":1.0},"75":{"tf":1.0},"91":{"tf":1.0}}}}},"o":{"df":0,"docs":{},"r":{"df":1,"docs":{"91":{"tf":1.0}},"i":{"df":1,"docs":{"101":{"tf":1.0}}}}},"v":{"a":{"df":0,"docs":{},"t":{"df":5,"docs":{"34":{"tf":1.0},"35":{"tf":1.7320508075688772},"44":{"tf":1.7320508075688772},"69":{"tf":1.0},"7":{"tf":2.0}}}},"df":0,"docs":{}}},"o":{"b":{"a":{"b":{"df":0,"docs":{},"l":{"df":5,"docs":{"111":{"tf":1.0},"14":{"tf":2.6457513110645907},"19":{"tf":1.0},"41":{"tf":1.0},"85":{"tf":1.0}}}},"df":0,"docs":{}},"df":0,"docs":{},"l":{"df":0,"docs":{},"e":{"df":0,"docs":{},"m":{"df":4,"docs":{"125":{"tf":1.4142135623730951},"149":{"tf":1.0},"21":{"tf":1.0},"50":{"tf":1.0}}}}}},"c":{"df":0,"docs":{},"e":{"df":2,"docs":{"33":{"tf":1.0},"80":{"tf":1.0}},"s":{"df":0,"docs":{},"s":{"df":9,"docs":{"14":{"tf":1.0},"151":{"tf":1.0},"20":{"tf":1.4142135623730951},"4":{"tf":1.0},"40":{"tf":1.0},"69":{"tf":1.0},"77":{"tf":1.0},"8":{"tf":1.0},"94":{"tf":1.0}}}}}},"d":{"df":0,"docs":{},"u":{"c":{"df":5,"docs":{"141":{"tf":1.0},"19":{"tf":1.4142135623730951},"20":{"tf":1.0},"75":{"tf":1.0},"8":{"tf":1.0}},"t":{"df":6,"docs":{"123":{"tf":1.4142135623730951},"138":{"tf":1.4142135623730951},"139":{"tf":1.4142135623730951},"14":{"tf":1.4142135623730951},"144":{"tf":1.0},"146":{"tf":2.449489742783178}}}},"df":0,"docs":{}}},"df":0,"docs":{},"g":{"df":0,"docs":{},"r":{"a":{"df":0,"docs":{},"m":{"df":25,"docs":{"10":{"tf":1.0},"12":{"tf":1.4142135623730951},"127":{"tf":1.0},"13":{"tf":2.23606797749979},"133":{"tf":1.4142135623730951},"14":{"tf":1.0},"141":{"tf":1.0},"142":{"tf":1.0},"144":{"tf":1.7320508075688772},"146":{"tf":2.0},"147":{"tf":1.0},"16":{"tf":1.0},"23":{"tf":1.0},"34":{"tf":1.4142135623730951},"35":{"tf":1.4142135623730951},"4":{"tf":1.0},"43":{"tf":1.0},"44":{"tf":1.0},"45":{"tf":1.4142135623730951},"50":{"tf":2.0},"69":{"tf":1.0},"7":{"tf":3.4641016151377544},"8":{"tf":1.4142135623730951},"9":{"tf":1.7320508075688772},"91":{"tf":1.4142135623730951}}}},"df":0,"docs":{},"e":{"df":0,"docs":{},"s":{"df":0,"docs":{},"s":{"df":1,"docs":{"0":{"tf":1.0}}}}}}},"o":{"df":0,"docs":{},"f":{"<":{"df":0,"docs":{},"f":{"df":1,"docs":{"35":{"tf":1.0}}}},"_":{"df":0,"docs":{},"o":{"df":0,"docs":{},"p":{"df":0,"docs":{},"t":{"df":2,"docs":{"104":{"tf":1.4142135623730951},"110":{"tf":1.4142135623730951}},"i":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"df":0,"docs":{},"s":{")":{".":{"df":0,"docs":{},"u":{"df":0,"docs":{},"n":{"df":0,"docs":{},"w":{"df":0,"docs":{},"r":{"a":{"df":0,"docs":{},"p":{"df":1,"docs":{"104":{"tf":1.0}}}},"df":0,"docs":{}}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}}}}}}},"df":39,"docs":{"102":{"tf":1.4142135623730951},"104":{"tf":3.0},"109":{"tf":1.4142135623730951},"110":{"tf":2.0},"112":{"tf":1.0},"113":{"tf":1.0},"119":{"tf":2.0},"13":{"tf":1.0},"14":{"tf":1.0},"144":{"tf":1.0},"146":{"tf":1.7320508075688772},"16":{"tf":1.0},"19":{"tf":1.4142135623730951},"21":{"tf":2.449489742783178},"22":{"tf":1.0},"28":{"tf":1.4142135623730951},"29":{"tf":1.4142135623730951},"32":{"tf":1.0},"33":{"tf":1.4142135623730951},"34":{"tf":1.4142135623730951},"35":{"tf":2.8284271247461903},"4":{"tf":1.0},"42":{"tf":1.0},"44":{"tf":1.7320508075688772},"50":{"tf":2.0},"57":{"tf":1.0},"59":{"tf":1.0},"60":{"tf":1.0},"61":{"tf":1.0},"62":{"tf":1.0},"63":{"tf":1.0},"7":{"tf":2.0},"77":{"tf":1.0},"8":{"tf":1.0},"80":{"tf":1.4142135623730951},"88":{"tf":1.0},"91":{"tf":1.0},"94":{"tf":1.4142135623730951},"95":{"tf":1.4142135623730951}},"o":{"df":0,"docs":{},"p":{"df":0,"docs":{},"t":{"df":1,"docs":{"109":{"tf":1.4142135623730951}},"i":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"df":0,"docs":{},"s":{":":{":":{"d":{"df":0,"docs":{},"e":{"df":0,"docs":{},"f":{"a":{"df":0,"docs":{},"u":{"df":0,"docs":{},"l":{"df":0,"docs":{},"t":{"_":{"df":0,"docs":{},"t":{"df":0,"docs":{},"e":{"df":0,"docs":{},"s":{"df":0,"docs":{},"t":{"_":{"df":0,"docs":{},"o":{"df":0,"docs":{},"p":{"df":0,"docs":{},"t":{"df":1,"docs":{"104":{"tf":1.0}}}}}},"df":0,"docs":{}}}}}},"df":0,"docs":{}}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}}}}}}}},"p":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":0,"docs":{},"l":{"df":0,"docs":{},"i":{"df":2,"docs":{"146":{"tf":1.0},"85":{"tf":1.0}}}},"t":{"df":0,"docs":{},"i":{"df":8,"docs":{"14":{"tf":1.0},"146":{"tf":1.0},"19":{"tf":1.4142135623730951},"20":{"tf":1.0},"52":{"tf":1.0},"70":{"tf":2.0},"73":{"tf":1.0},"84":{"tf":1.0}}}}}},"o":{"df":0,"docs":{},"r":{"df":0,"docs":{},"t":{"df":1,"docs":{"73":{"tf":1.0}}}}}},"t":{"df":0,"docs":{},"o":{"c":{"df":0,"docs":{},"o":{"df":0,"docs":{},"l":{"df":37,"docs":{"101":{"tf":1.4142135623730951},"102":{"tf":1.0},"109":{"tf":1.7320508075688772},"118":{"tf":1.4142135623730951},"123":{"tf":1.0},"14":{"tf":1.0},"142":{"tf":1.0},"16":{"tf":1.7320508075688772},"17":{"tf":1.0},"20":{"tf":1.4142135623730951},"34":{"tf":1.0},"37":{"tf":1.0},"4":{"tf":1.0},"57":{"tf":1.4142135623730951},"58":{"tf":1.0},"59":{"tf":1.0},"68":{"tf":1.0},"69":{"tf":2.0},"70":{"tf":1.4142135623730951},"71":{"tf":1.4142135623730951},"73":{"tf":2.449489742783178},"74":{"tf":1.0},"75":{"tf":2.23606797749979},"77":{"tf":1.4142135623730951},"78":{"tf":1.0},"80":{"tf":1.7320508075688772},"82":{"tf":1.4142135623730951},"83":{"tf":1.0},"84":{"tf":1.4142135623730951},"85":{"tf":1.0},"86":{"tf":1.4142135623730951},"87":{"tf":1.4142135623730951},"88":{"tf":1.4142135623730951},"89":{"tf":1.0},"92":{"tf":1.0},"93":{"tf":1.0},"94":{"tf":1.0}}}}},"df":0,"docs":{}}},"v":{"a":{"b":{"df":0,"docs":{},"l":{"df":1,"docs":{"149":{"tf":1.4142135623730951}}}},"df":0,"docs":{}},"df":0,"docs":{},"e":{"(":{"&":{"df":0,"docs":{},"t":{"df":0,"docs":{},"r":{"a":{"c":{"df":0,"docs":{},"e":{"_":{"df":0,"docs":{},"t":{"df":1,"docs":{"110":{"tf":1.0}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}},":":{":":{"<":{"df":0,"docs":{},"f":{"df":1,"docs":{"104":{"tf":1.0}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":34,"docs":{"102":{"tf":1.0},"103":{"tf":1.0},"104":{"tf":1.4142135623730951},"105":{"tf":1.0},"108":{"tf":1.0},"109":{"tf":1.0},"110":{"tf":1.7320508075688772},"111":{"tf":1.0},"113":{"tf":1.0},"114":{"tf":1.4142135623730951},"118":{"tf":1.0},"138":{"tf":1.0},"14":{"tf":1.0},"144":{"tf":1.0},"146":{"tf":1.7320508075688772},"151":{"tf":1.7320508075688772},"16":{"tf":1.0},"19":{"tf":1.4142135623730951},"23":{"tf":1.4142135623730951},"34":{"tf":1.0},"35":{"tf":1.0},"4":{"tf":1.0},"42":{"tf":1.0},"43":{"tf":1.0},"44":{"tf":1.0},"46":{"tf":1.0},"48":{"tf":1.0},"49":{"tf":1.0},"50":{"tf":1.0},"70":{"tf":1.0},"73":{"tf":1.4142135623730951},"74":{"tf":1.0},"84":{"tf":1.0},"85":{"tf":1.0}},"r":{"'":{"df":2,"docs":{"35":{"tf":1.0},"7":{"tf":1.0}}},".":{"df":0,"docs":{},"p":{"df":0,"docs":{},"r":{"df":0,"docs":{},"o":{"df":0,"docs":{},"v":{"df":1,"docs":{"35":{"tf":1.4142135623730951}},"e":{"(":{"&":{"df":0,"docs":{},"w":{"df":0,"docs":{},"i":{"df":0,"docs":{},"t":{"df":1,"docs":{"44":{"tf":1.0}}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}}}}},":":{":":{"df":0,"docs":{},"n":{"df":0,"docs":{},"e":{"df":0,"docs":{},"w":{"(":{"df":0,"docs":{},"k":{"df":0,"docs":{},"z":{"df":0,"docs":{},"g":{".":{"c":{"df":0,"docs":{},"l":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"df":2,"docs":{"35":{"tf":1.4142135623730951},"44":{"tf":1.0}}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"<":{"df":0,"docs":{},"f":{"df":1,"docs":{"35":{"tf":1.0}}}},"df":60,"docs":{"101":{"tf":1.0},"102":{"tf":1.4142135623730951},"103":{"tf":1.0},"104":{"tf":1.4142135623730951},"111":{"tf":1.4142135623730951},"112":{"tf":1.0},"113":{"tf":2.0},"117":{"tf":1.4142135623730951},"118":{"tf":2.8284271247461903},"126":{"tf":1.4142135623730951},"127":{"tf":1.4142135623730951},"13":{"tf":1.4142135623730951},"131":{"tf":1.0},"144":{"tf":1.4142135623730951},"146":{"tf":1.7320508075688772},"147":{"tf":1.4142135623730951},"151":{"tf":1.4142135623730951},"20":{"tf":2.0},"21":{"tf":2.449489742783178},"23":{"tf":1.0},"31":{"tf":1.0},"32":{"tf":1.0},"34":{"tf":1.0},"35":{"tf":2.23606797749979},"44":{"tf":1.4142135623730951},"45":{"tf":1.0},"46":{"tf":1.4142135623730951},"50":{"tf":1.4142135623730951},"51":{"tf":1.0},"53":{"tf":1.0},"56":{"tf":1.0},"57":{"tf":1.0},"58":{"tf":2.0},"59":{"tf":2.0},"60":{"tf":2.0},"61":{"tf":1.0},"62":{"tf":1.0},"63":{"tf":2.0},"66":{"tf":1.4142135623730951},"69":{"tf":1.7320508075688772},"7":{"tf":1.4142135623730951},"71":{"tf":1.0},"72":{"tf":1.7320508075688772},"73":{"tf":1.4142135623730951},"74":{"tf":1.0},"75":{"tf":2.449489742783178},"77":{"tf":2.23606797749979},"78":{"tf":1.0},"79":{"tf":2.0},"80":{"tf":1.7320508075688772},"82":{"tf":1.0},"83":{"tf":2.23606797749979},"84":{"tf":2.0},"85":{"tf":1.0},"86":{"tf":1.0},"88":{"tf":1.0},"91":{"tf":2.23606797749979},"94":{"tf":1.0},"95":{"tf":1.0},"97":{"tf":1.0}}}},"i":{"d":{"df":19,"docs":{"103":{"tf":1.0},"109":{"tf":1.0},"112":{"tf":2.0},"113":{"tf":1.7320508075688772},"117":{"tf":1.0},"118":{"tf":1.0},"125":{"tf":1.0},"34":{"tf":1.0},"48":{"tf":1.0},"50":{"tf":1.4142135623730951},"57":{"tf":1.0},"58":{"tf":1.0},"59":{"tf":1.7320508075688772},"60":{"tf":1.4142135623730951},"62":{"tf":2.0},"63":{"tf":1.7320508075688772},"66":{"tf":1.4142135623730951},"68":{"tf":1.0},"80":{"tf":1.0}}},"df":0,"docs":{}}}}},"u":{"b":{"_":{"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"df":0,"docs":{},"p":{"df":0,"docs":{},"u":{"df":0,"docs":{},"t":{"df":2,"docs":{"104":{"tf":1.7320508075688772},"110":{"tf":1.4142135623730951}}}}}}}},"df":4,"docs":{"133":{"tf":1.4142135623730951},"135":{"tf":1.4142135623730951},"35":{"tf":6.928203230275509},"45":{"tf":1.0}},"l":{"c":{"df":1,"docs":{"135":{"tf":1.0}}},"df":0,"docs":{},"i":{"c":{"_":{"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"df":0,"docs":{},"p":{"df":0,"docs":{},"u":{"df":0,"docs":{},"t":{"df":2,"docs":{"35":{"tf":2.449489742783178},"44":{"tf":1.7320508075688772}}}}}}}},"df":26,"docs":{"104":{"tf":2.0},"106":{"tf":1.4142135623730951},"110":{"tf":1.0},"13":{"tf":2.0},"133":{"tf":2.23606797749979},"135":{"tf":1.4142135623730951},"14":{"tf":1.4142135623730951},"142":{"tf":1.0},"144":{"tf":1.4142135623730951},"146":{"tf":2.8284271247461903},"147":{"tf":1.0},"151":{"tf":1.0},"16":{"tf":1.7320508075688772},"23":{"tf":1.4142135623730951},"26":{"tf":1.0},"32":{"tf":1.4142135623730951},"34":{"tf":1.4142135623730951},"35":{"tf":1.4142135623730951},"36":{"tf":1.0},"42":{"tf":1.0},"44":{"tf":2.0},"58":{"tf":1.0},"69":{"tf":1.0},"7":{"tf":1.0},"94":{"tf":1.0},"95":{"tf":1.0}},"l":{"df":0,"docs":{},"i":{"df":2,"docs":{"144":{"tf":1.0},"19":{"tf":1.0}}}}},"df":0,"docs":{},"s":{"df":0,"docs":{},"h":{"df":1,"docs":{"22":{"tf":1.0}}}}}}},"df":0,"docs":{},"r":{"df":0,"docs":{},"p":{"df":0,"docs":{},"o":{"df":0,"docs":{},"s":{"df":4,"docs":{"149":{"tf":1.0},"34":{"tf":1.0},"50":{"tf":1.4142135623730951},"56":{"tf":1.0}}}}}},"t":{"df":7,"docs":{"10":{"tf":1.0},"13":{"tf":1.0},"14":{"tf":1.4142135623730951},"147":{"tf":1.0},"151":{"tf":1.0},"45":{"tf":1.0},"9":{"tf":1.4142135623730951}}}},"ˉ":{"df":0,"docs":{},"​":{"c":{"df":0,"docs":{},"​":{":":{"=":{"df":0,"docs":{},"p":{"df":0,"docs":{},"i":{"(":{"df":0,"docs":{},"ζ":{")":{"+":{"df":0,"docs":{},"α":{"df":0,"docs":{},"z":{"df":0,"docs":{},"ˉ":{"df":0,"docs":{},"ω":{"df":0,"docs":{},"​":{"(":{"c":{"df":0,"docs":{},"ˉ":{"+":{"df":0,"docs":{},"γ":{")":{"(":{"a":{"df":0,"docs":{},"ˉ":{"+":{"df":0,"docs":{},"β":{"df":0,"docs":{},"s":{"df":0,"docs":{},"ˉ":{"df":0,"docs":{},"σ":{"1":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"γ":{")":{"(":{"b":{"df":0,"docs":{},"ˉ":{"+":{"df":0,"docs":{},"β":{"df":0,"docs":{},"s":{"df":0,"docs":{},"ˉ":{"df":0,"docs":{},"σ":{"2":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"γ":{")":{"df":0,"docs":{},"−":{"df":0,"docs":{},"α":{"2":{"df":0,"docs":{},"l":{"1":{"df":1,"docs":{"32":{"tf":1.0}}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{},"n":{"c":{"df":1,"docs":{"32":{"tf":1.0}},"​":{":":{"=":{"df":0,"docs":{},"p":{"df":0,"docs":{},"n":{"c":{"df":1,"docs":{"28":{"tf":1.0}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"∈":{"df":0,"docs":{},"f":{"[":{"df":0,"docs":{},"x":{"df":1,"docs":{"90":{"tf":1.0}}}},"df":0,"docs":{}}},"−":{"df":0,"docs":{},"y":{"=":{"(":{"df":0,"docs":{},"x":{"df":0,"docs":{},"−":{"df":0,"docs":{},"z":{")":{"df":0,"docs":{},"q":{"df":1,"docs":{"78":{"tf":1.0}}}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"q":{"(":{"d":{"1":{"df":0,"docs":{},"​":{")":{",":{"df":0,"docs":{},"…":{",":{"df":0,"docs":{},"q":{"(":{"d":{"df":0,"docs":{},"m":{"df":1,"docs":{"78":{"tf":1.0}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},",":{"df":0,"docs":{},"v":{",":{"df":0,"docs":{},"t":{",":{"df":0,"docs":{},"p":{"df":0,"docs":{},"i":{"df":1,"docs":{"36":{"tf":1.0}}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"c":{"_":{"1":{"df":1,"docs":{"35":{"tf":1.0}}},"df":0,"docs":{}},"df":4,"docs":{"10":{"tf":2.23606797749979},"12":{"tf":1.0},"13":{"tf":2.0},"35":{"tf":1.0}}},"df":15,"docs":{"10":{"tf":1.4142135623730951},"11":{"tf":1.0},"12":{"tf":1.0},"13":{"tf":1.7320508075688772},"14":{"tf":1.0},"16":{"tf":1.4142135623730951},"34":{"tf":1.4142135623730951},"36":{"tf":1.0},"74":{"tf":1.0},"78":{"tf":1.4142135623730951},"8":{"tf":1.0},"80":{"tf":1.4142135623730951},"9":{"tf":1.0},"90":{"tf":1.0},"91":{"tf":1.0}},"k":{"df":0,"docs":{},"t":{"df":0,"docs":{},"​":{"(":{"df":0,"docs":{},"x":{")":{"=":{"df":0,"docs":{},"p":{"df":0,"docs":{},"k":{"df":0,"docs":{},"t":{"df":0,"docs":{},"​":{"(":{"df":0,"docs":{},"t":{"1":{"df":0,"docs":{},"​":{"(":{"df":0,"docs":{},"x":{")":{",":{"df":0,"docs":{},"…":{",":{"df":0,"docs":{},"t":{"df":0,"docs":{},"m":{"df":0,"docs":{},"​":{"(":{"df":0,"docs":{},"x":{")":{",":{"df":0,"docs":{},"t":{"1":{"df":0,"docs":{},"​":{"(":{"df":0,"docs":{},"g":{"df":0,"docs":{},"x":{")":{",":{"df":0,"docs":{},"…":{",":{"df":0,"docs":{},"t":{"df":0,"docs":{},"m":{"df":0,"docs":{},"​":{"(":{"df":0,"docs":{},"ω":{"df":0,"docs":{},"x":{"df":1,"docs":{"84":{"tf":1.0}}}}},"df":0,"docs":{}}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"l":{"_":{"1":{"df":1,"docs":{"35":{"tf":1.0}}},"df":0,"docs":{}},"df":5,"docs":{"10":{"tf":2.23606797749979},"12":{"tf":1.0},"13":{"tf":1.4142135623730951},"21":{"tf":2.0},"35":{"tf":1.0}},"​":{"(":{"df":0,"docs":{},"ζ":{")":{",":{"df":0,"docs":{},"q":{"df":0,"docs":{},"r":{"df":1,"docs":{"21":{"tf":1.0}}}}},"df":0,"docs":{}},"df":0,"docs":{}}},",":{"df":0,"docs":{},"q":{"df":0,"docs":{},"r":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"q":{"df":0,"docs":{},"m":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"q":{"df":0,"docs":{},"o":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"q":{"c":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"s":{"df":0,"docs":{},"σ":{"1":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"s":{"df":0,"docs":{},"σ":{"2":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"s":{"df":0,"docs":{},"σ":{"3":{"df":2,"docs":{"15":{"tf":1.0},"16":{"tf":1.0}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}},"df":0,"docs":{}}},"o":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"q":{"df":0,"docs":{},"m":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"q":{"c":{"df":1,"docs":{"10":{"tf":1.0}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}},"df":0,"docs":{}}}}},"df":0,"docs":{}}}}},"]":{"1":{"df":1,"docs":{"21":{"tf":1.0}},"​":{",":{"[":{"df":0,"docs":{},"q":{"df":0,"docs":{},"r":{"df":0,"docs":{},"​":{"]":{"1":{"df":0,"docs":{},"​":{",":{"[":{"df":0,"docs":{},"q":{"df":0,"docs":{},"m":{"df":0,"docs":{},"​":{"]":{"1":{"df":0,"docs":{},"​":{",":{"[":{"df":0,"docs":{},"q":{"df":0,"docs":{},"o":{"df":0,"docs":{},"​":{"]":{"1":{"df":0,"docs":{},"​":{",":{"[":{"df":0,"docs":{},"q":{"c":{"df":0,"docs":{},"​":{"]":{"1":{"df":0,"docs":{},"​":{",":{"[":{"df":0,"docs":{},"s":{"df":0,"docs":{},"σ":{"1":{"df":0,"docs":{},"​":{"]":{"1":{"df":0,"docs":{},"​":{",":{"[":{"df":0,"docs":{},"s":{"df":0,"docs":{},"σ":{"2":{"df":0,"docs":{},"​":{"]":{"1":{"df":0,"docs":{},"​":{",":{"[":{"df":0,"docs":{},"s":{"df":0,"docs":{},"σ":{"3":{"df":0,"docs":{},"​":{"]":{"1":{"df":1,"docs":{"22":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"m":{"_":{"1":{"df":1,"docs":{"35":{"tf":1.0}}},"df":0,"docs":{}},"df":4,"docs":{"10":{"tf":2.23606797749979},"12":{"tf":1.0},"13":{"tf":1.4142135623730951},"35":{"tf":1.0}}},"o":{"_":{"1":{"df":1,"docs":{"35":{"tf":1.0}}},"df":0,"docs":{}},"df":4,"docs":{"10":{"tf":2.23606797749979},"12":{"tf":1.0},"13":{"tf":1.4142135623730951},"35":{"tf":1.0}}},"r":{"_":{"1":{"df":1,"docs":{"35":{"tf":1.0}}},"df":0,"docs":{}},"df":5,"docs":{"10":{"tf":2.23606797749979},"12":{"tf":1.0},"13":{"tf":1.4142135623730951},"21":{"tf":2.0},"35":{"tf":1.0}},"​":{"]":{"1":{"df":1,"docs":{"21":{"tf":1.4142135623730951}}},"df":0,"docs":{}},"df":0,"docs":{}}},"u":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":0,"docs":{},"i":{"df":4,"docs":{"109":{"tf":1.0},"119":{"tf":1.0},"91":{"tf":1.0},"94":{"tf":1.0}}}},"s":{"df":0,"docs":{},"t":{"df":0,"docs":{},"i":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"df":1,"docs":{"14":{"tf":1.0}}}}}}}},"i":{"c":{"df":0,"docs":{},"k":{"df":1,"docs":{"131":{"tf":1.0}}}},"df":0,"docs":{},"t":{"df":1,"docs":{"146":{"tf":1.0}}}},"o":{"df":0,"docs":{},"t":{"df":0,"docs":{},"i":{"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"df":0,"docs":{},"t":{"df":2,"docs":{"35":{"tf":1.7320508075688772},"69":{"tf":1.0}}}}}}}}}},"r":{"0":{"df":1,"docs":{"151":{"tf":1.4142135623730951}}},"a":{"d":{"df":0,"docs":{},"i":{"df":0,"docs":{},"x":{"df":1,"docs":{"121":{"tf":1.0}}}}},"df":0,"docs":{},"n":{"d":{"df":0,"docs":{},"o":{"df":0,"docs":{},"m":{"_":{"df":0,"docs":{},"g":{"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":1,"docs":{"35":{"tf":2.23606797749979}}}}}}}},"df":19,"docs":{"118":{"tf":1.4142135623730951},"14":{"tf":2.8284271247461903},"16":{"tf":2.0},"20":{"tf":1.7320508075688772},"24":{"tf":1.0},"25":{"tf":1.0},"26":{"tf":1.0},"3":{"tf":1.0},"35":{"tf":1.0},"58":{"tf":1.0},"60":{"tf":1.4142135623730951},"69":{"tf":1.4142135623730951},"70":{"tf":1.4142135623730951},"79":{"tf":1.0},"83":{"tf":1.0},"84":{"tf":1.0},"85":{"tf":1.0},"94":{"tf":1.4142135623730951},"95":{"tf":1.4142135623730951}},"l":{"df":0,"docs":{},"i":{"df":3,"docs":{"118":{"tf":1.0},"59":{"tf":1.0},"85":{"tf":1.0}}}}}}},"df":0,"docs":{},"g":{"df":9,"docs":{"130":{"tf":1.4142135623730951},"134":{"tf":1.0},"138":{"tf":1.0},"14":{"tf":1.4142135623730951},"144":{"tf":1.0},"146":{"tf":2.23606797749979},"147":{"tf":1.4142135623730951},"151":{"tf":2.449489742783178},"70":{"tf":1.0}},"e":{"_":{"c":{"df":0,"docs":{},"h":{"df":0,"docs":{},"e":{"c":{"df":0,"docs":{},"k":{"_":{"c":{"df":0,"docs":{},"e":{"df":0,"docs":{},"l":{"df":0,"docs":{},"l":{".":{"df":0,"docs":{},"h":{"df":1,"docs":{"130":{"tf":1.0}}},"i":{"df":0,"docs":{},"n":{"df":0,"docs":{},"l":{"df":1,"docs":{"130":{"tf":1.0}}}}}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"p":{"2":{"df":1,"docs":{"83":{"tf":1.0}}},"df":3,"docs":{"147":{"tf":1.0},"91":{"tf":1.7320508075688772},"94":{"tf":1.0}}},"t":{"df":0,"docs":{},"i":{"df":0,"docs":{},"o":{"df":2,"docs":{"149":{"tf":1.0},"151":{"tf":1.0}},"n":{"df":3,"docs":{"54":{"tf":1.0},"55":{"tf":1.0},"56":{"tf":1.0}}}}}},"w":{"df":2,"docs":{"144":{"tf":1.0},"40":{"tf":1.0}}}},"c":{"1":{"6":{"/":{"df":0,"docs":{},"s":{"df":0,"docs":{},"o":{"df":0,"docs":{},"r":{"df":0,"docs":{},"t":{"df":1,"docs":{"132":{"tf":1.0}}}}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":5,"docs":{"130":{"tf":1.4142135623730951},"131":{"tf":1.0},"132":{"tf":1.0},"138":{"tf":1.4142135623730951},"146":{"tf":1.0}},"m":{"a":{"df":0,"docs":{},"x":{"df":1,"docs":{"146":{"tf":1.0}}}},"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"df":1,"docs":{"146":{"tf":1.0}}}}}},"df":10,"docs":{"11":{"tf":1.0},"12":{"tf":1.0},"13":{"tf":1.0},"14":{"tf":1.0},"142":{"tf":2.6457513110645907},"35":{"tf":1.7320508075688772},"38":{"tf":1.0},"9":{"tf":1.0},"91":{"tf":1.0},"92":{"tf":1.4142135623730951}},"e":{"a":{"c":{"df":0,"docs":{},"h":{"df":1,"docs":{"146":{"tf":1.0}}}},"d":{"df":2,"docs":{"111":{"tf":1.4142135623730951},"69":{"tf":1.0}},"e":{"df":0,"docs":{},"r":{"df":1,"docs":{"125":{"tf":1.0}}}},"i":{"df":1,"docs":{"146":{"tf":1.0}}}},"df":0,"docs":{},"l":{"df":4,"docs":{"128":{"tf":1.7320508075688772},"135":{"tf":1.0},"146":{"tf":1.0},"149":{"tf":1.0}},"i":{"df":0,"docs":{},"t":{"df":0,"docs":{},"i":{"df":1,"docs":{"146":{"tf":1.0}}}}},"l":{"df":0,"docs":{},"i":{"df":4,"docs":{"116":{"tf":1.0},"123":{"tf":1.0},"14":{"tf":1.0},"50":{"tf":1.0}}}}},"r":{"df":0,"docs":{},"r":{"a":{"df":0,"docs":{},"n":{"df":0,"docs":{},"g":{"df":1,"docs":{"14":{"tf":1.0}}}}},"df":0,"docs":{}}},"s":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"df":6,"docs":{"117":{"tf":1.0},"123":{"tf":1.0},"146":{"tf":1.0},"20":{"tf":1.0},"50":{"tf":1.0},"56":{"tf":1.0}}}}}},"c":{"a":{"df":0,"docs":{},"l":{"c":{"df":0,"docs":{},"u":{"df":0,"docs":{},"l":{"df":1,"docs":{"146":{"tf":1.0}}}}},"df":7,"docs":{"111":{"tf":1.0},"125":{"tf":1.0},"14":{"tf":2.0},"16":{"tf":1.0},"54":{"tf":1.0},"91":{"tf":1.0},"95":{"tf":1.0}}},"p":{"df":13,"docs":{"103":{"tf":1.0},"105":{"tf":1.0},"111":{"tf":1.0},"116":{"tf":1.0},"34":{"tf":1.4142135623730951},"35":{"tf":1.0},"36":{"tf":1.0},"46":{"tf":1.0},"47":{"tf":1.0},"69":{"tf":1.0},"7":{"tf":1.0},"70":{"tf":1.0},"84":{"tf":1.0}}}},"df":0,"docs":{},"e":{"df":0,"docs":{},"i":{"df":0,"docs":{},"v":{"df":5,"docs":{"114":{"tf":1.0},"21":{"tf":1.0},"77":{"tf":1.0},"79":{"tf":1.0},"95":{"tf":1.0}}}}},"o":{"df":0,"docs":{},"g":{"df":0,"docs":{},"n":{"df":1,"docs":{"16":{"tf":1.0}}}},"m":{"df":0,"docs":{},"m":{"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"d":{"df":1,"docs":{"69":{"tf":1.0}}},"df":0,"docs":{}}}}},"n":{"df":0,"docs":{},"s":{"df":0,"docs":{},"t":{"df":0,"docs":{},"r":{"df":0,"docs":{},"u":{"c":{"df":0,"docs":{},"t":{"df":8,"docs":{"102":{"tf":1.4142135623730951},"112":{"tf":1.0},"113":{"tf":1.0},"114":{"tf":1.4142135623730951},"115":{"tf":1.0},"117":{"tf":1.0},"63":{"tf":1.0},"95":{"tf":1.0}}}},"df":0,"docs":{}}}}}},"v":{"df":3,"docs":{"114":{"tf":1.7320508075688772},"124":{"tf":1.0},"95":{"tf":1.0}}}},"u":{"df":0,"docs":{},"r":{"df":0,"docs":{},"s":{"df":2,"docs":{"14":{"tf":1.0},"25":{"tf":1.0}}}}}},"d":{"df":0,"docs":{},"u":{"c":{"df":6,"docs":{"101":{"tf":1.0},"12":{"tf":1.0},"14":{"tf":1.7320508075688772},"144":{"tf":1.0},"21":{"tf":1.4142135623730951},"80":{"tf":1.7320508075688772}}},"df":0,"docs":{},"n":{"d":{"df":2,"docs":{"102":{"tf":1.0},"70":{"tf":1.0}}},"df":0,"docs":{}}}},"df":4,"docs":{"136":{"tf":1.4142135623730951},"142":{"tf":1.4142135623730951},"146":{"tf":2.449489742783178},"147":{"tf":1.4142135623730951}},"f":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":11,"docs":{"126":{"tf":1.0},"130":{"tf":1.0},"134":{"tf":1.0},"139":{"tf":1.0},"146":{"tf":1.0},"3":{"tf":1.0},"70":{"tf":1.0},"81":{"tf":1.0},"83":{"tf":1.0},"87":{"tf":1.0},"95":{"tf":2.0}}}},"l":{"df":0,"docs":{},"e":{"c":{"df":0,"docs":{},"t":{"df":1,"docs":{"133":{"tf":1.0}}}},"df":0,"docs":{}}},"o":{"df":0,"docs":{},"r":{"df":0,"docs":{},"m":{"df":0,"docs":{},"u":{"df":0,"docs":{},"l":{"df":1,"docs":{"16":{"tf":1.0}}}}}}}},"g":{"a":{"df":0,"docs":{},"r":{"d":{"df":1,"docs":{"126":{"tf":1.0}}},"df":0,"docs":{}}},"df":1,"docs":{"146":{"tf":1.4142135623730951}},"i":{"df":0,"docs":{},"s":{"df":0,"docs":{},"t":{"df":8,"docs":{"127":{"tf":1.0},"133":{"tf":1.0},"136":{"tf":1.7320508075688772},"141":{"tf":1.0},"144":{"tf":1.4142135623730951},"146":{"tf":1.4142135623730951},"149":{"tf":1.0},"50":{"tf":1.0}},"e":{"df":0,"docs":{},"r":{"df":0,"docs":{},"s":{"df":0,"docs":{},"t":{"df":1,"docs":{"146":{"tf":1.0}}}}}}}}}},"j":{"df":0,"docs":{},"e":{"c":{"df":0,"docs":{},"t":{"df":4,"docs":{"19":{"tf":1.0},"33":{"tf":1.0},"7":{"tf":1.0},"92":{"tf":1.0}}}},"df":0,"docs":{}}},"l":{"a":{"df":0,"docs":{},"t":{"df":6,"docs":{"109":{"tf":2.0},"13":{"tf":1.0},"136":{"tf":1.0},"146":{"tf":1.0},"68":{"tf":1.0},"69":{"tf":1.0}},"i":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"df":0,"docs":{},"s":{"df":0,"docs":{},"h":{"df":0,"docs":{},"i":{"df":0,"docs":{},"p":{"df":4,"docs":{"107":{"tf":1.0},"151":{"tf":1.0},"53":{"tf":1.0},"66":{"tf":1.0}}}}}}}}}}},"df":2,"docs":{"146":{"tf":1.0},"70":{"tf":1.0}},"e":{"df":0,"docs":{},"v":{"df":2,"docs":{"139":{"tf":1.0},"87":{"tf":1.0}}}},"i":{"df":2,"docs":{"34":{"tf":1.0},"4":{"tf":1.0}}},"o":{"c":{"df":1,"docs":{"144":{"tf":1.0}}},"df":0,"docs":{}}},"m":{"a":{"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"df":6,"docs":{"125":{"tf":1.0},"130":{"tf":1.0},"59":{"tf":1.0},"65":{"tf":1.0},"66":{"tf":1.0},"67":{"tf":1.0}}}}},"df":0,"docs":{},"e":{"df":0,"docs":{},"m":{"b":{"df":1,"docs":{"114":{"tf":1.4142135623730951}}},"df":0,"docs":{}}},"i":{"df":0,"docs":{},"n":{"d":{"df":1,"docs":{"68":{"tf":1.0}}},"df":0,"docs":{}}},"o":{"df":0,"docs":{},"v":{"df":2,"docs":{"118":{"tf":1.0},"13":{"tf":1.0}}}}},"p":{"df":0,"docs":{},"e":{"a":{"df":0,"docs":{},"t":{"df":2,"docs":{"135":{"tf":1.0},"83":{"tf":1.0}}}},"df":0,"docs":{}},"h":{"df":0,"docs":{},"r":{"a":{"df":0,"docs":{},"s":{"df":1,"docs":{"14":{"tf":1.0}}}},"df":0,"docs":{}}},"l":{"a":{"c":{"df":3,"docs":{"135":{"tf":1.4142135623730951},"28":{"tf":1.0},"98":{"tf":1.0}}},"df":0,"docs":{},"y":{"df":1,"docs":{"95":{"tf":1.0}}}},"df":0,"docs":{}},"r":{"df":0,"docs":{},"e":{"df":0,"docs":{},"s":{"df":7,"docs":{"10":{"tf":2.23606797749979},"106":{"tf":1.0},"127":{"tf":1.4142135623730951},"146":{"tf":1.7320508075688772},"41":{"tf":1.0},"44":{"tf":1.0},"82":{"tf":1.0}},"e":{"df":0,"docs":{},"n":{"df":0,"docs":{},"t":{"df":6,"docs":{"101":{"tf":1.7320508075688772},"12":{"tf":1.0},"128":{"tf":1.0},"146":{"tf":1.7320508075688772},"4":{"tf":1.4142135623730951},"48":{"tf":1.4142135623730951}}}}}}}}},"q":{"df":0,"docs":{},"u":{"df":0,"docs":{},"i":{"df":0,"docs":{},"r":{"df":8,"docs":{"105":{"tf":1.0},"109":{"tf":1.0},"114":{"tf":1.0},"121":{"tf":1.0},"134":{"tf":1.0},"151":{"tf":1.0},"53":{"tf":1.0},"61":{"tf":1.0}}}}}},"s":{"_":{"df":0,"docs":{},"l":{"df":0,"docs":{},"o":{"df":0,"docs":{},"g":{"df":1,"docs":{"146":{"tf":1.4142135623730951}}}}}},"c":{"df":0,"docs":{},"u":{"df":1,"docs":{"14":{"tf":1.0}}}},"df":0,"docs":{},"e":{"df":0,"docs":{},"t":{"df":1,"docs":{"40":{"tf":1.0}}}},"h":{"a":{"df":0,"docs":{},"p":{"df":1,"docs":{"147":{"tf":1.7320508075688772}}}},"df":0,"docs":{}},"o":{"df":0,"docs":{},"u":{"df":0,"docs":{},"r":{"c":{"df":1,"docs":{"42":{"tf":1.0}}},"df":0,"docs":{}}}},"p":{"df":0,"docs":{},"e":{"c":{"df":0,"docs":{},"t":{"df":9,"docs":{"109":{"tf":1.0},"14":{"tf":2.449489742783178},"142":{"tf":1.0},"144":{"tf":1.0},"146":{"tf":1.4142135623730951},"147":{"tf":1.0},"16":{"tf":1.0},"3":{"tf":1.0},"32":{"tf":1.0}}}},"df":0,"docs":{}},"o":{"df":0,"docs":{},"n":{"df":0,"docs":{},"s":{"df":1,"docs":{"13":{"tf":1.0}}}}}},"t":{"df":2,"docs":{"114":{"tf":1.4142135623730951},"84":{"tf":1.0}}},"u":{"df":0,"docs":{},"l":{"df":0,"docs":{},"t":{"_":{"df":0,"docs":{},"t":{"df":0,"docs":{},"i":{"df":0,"docs":{},"m":{"df":0,"docs":{},"e":{"df":0,"docs":{},"s":{"_":{"b":{"a":{"df":0,"docs":{},"s":{"df":1,"docs":{"45":{"tf":1.4142135623730951}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}}}}},"df":23,"docs":{"11":{"tf":1.0},"112":{"tf":1.0},"13":{"tf":1.0},"135":{"tf":1.0},"14":{"tf":1.4142135623730951},"142":{"tf":1.4142135623730951},"146":{"tf":1.0},"147":{"tf":1.7320508075688772},"151":{"tf":1.4142135623730951},"16":{"tf":1.4142135623730951},"3":{"tf":1.0},"40":{"tf":1.4142135623730951},"41":{"tf":1.0},"45":{"tf":2.449489742783178},"5":{"tf":1.0},"56":{"tf":1.0},"62":{"tf":1.0},"73":{"tf":1.0},"75":{"tf":1.0},"77":{"tf":1.4142135623730951},"8":{"tf":1.0},"84":{"tf":1.4142135623730951},"9":{"tf":1.4142135623730951}}}}}},"t":{"df":0,"docs":{},"u":{"df":0,"docs":{},"r":{"df":0,"docs":{},"n":{"df":9,"docs":{"106":{"tf":1.0},"107":{"tf":1.4142135623730951},"114":{"tf":1.7320508075688772},"146":{"tf":1.0},"35":{"tf":1.4142135623730951},"40":{"tf":1.0},"74":{"tf":1.0},"80":{"tf":1.0},"92":{"tf":1.7320508075688772}}}}}},"u":{"df":0,"docs":{},"s":{"df":1,"docs":{"151":{"tf":1.0}}}},"v":{"df":0,"docs":{},"e":{"a":{"df":0,"docs":{},"l":{"df":6,"docs":{"19":{"tf":1.0},"20":{"tf":1.0},"69":{"tf":1.0},"7":{"tf":1.4142135623730951},"72":{"tf":1.0},"80":{"tf":1.0}}}},"df":0,"docs":{},"r":{"df":0,"docs":{},"s":{"df":1,"docs":{"101":{"tf":2.23606797749979}}}}},"o":{"df":0,"docs":{},"l":{"df":0,"docs":{},"v":{"df":1,"docs":{"104":{"tf":1.0}}}}}}},"i":{"d":{"df":1,"docs":{"14":{"tf":1.0}}},"df":0,"docs":{},"g":{"df":0,"docs":{},"h":{"df":0,"docs":{},"t":{"df":6,"docs":{"11":{"tf":1.0},"118":{"tf":1.0},"121":{"tf":1.0},"14":{"tf":1.0},"16":{"tf":1.0},"9":{"tf":2.23606797749979}}}}}},"m":{"a":{"df":0,"docs":{},"x":{"df":1,"docs":{"142":{"tf":1.4142135623730951}}}},"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"df":1,"docs":{"142":{"tf":1.4142135623730951}}}}},"n":{"df":0,"docs":{},"g":{"df":1,"docs":{"118":{"tf":1.0}}}},"o":{"df":0,"docs":{},"o":{"df":0,"docs":{},"t":{"df":29,"docs":{"112":{"tf":1.4142135623730951},"113":{"tf":1.0},"114":{"tf":1.4142135623730951},"119":{"tf":1.4142135623730951},"123":{"tf":2.449489742783178},"124":{"tf":4.0},"125":{"tf":2.8284271247461903},"14":{"tf":3.0},"16":{"tf":1.0},"23":{"tf":1.0},"34":{"tf":1.0},"36":{"tf":1.0},"38":{"tf":1.0},"5":{"tf":1.0},"52":{"tf":2.0},"54":{"tf":1.7320508075688772},"60":{"tf":1.0},"62":{"tf":1.4142135623730951},"63":{"tf":1.0},"68":{"tf":2.8284271247461903},"72":{"tf":1.7320508075688772},"73":{"tf":1.0},"75":{"tf":1.0},"76":{"tf":1.0},"80":{"tf":1.0},"82":{"tf":1.4142135623730951},"90":{"tf":1.0},"91":{"tf":1.0},"92":{"tf":2.0}}}},"u":{"df":0,"docs":{},"n":{"d":{"df":14,"docs":{"24":{"tf":1.0},"25":{"tf":1.0},"26":{"tf":1.0},"27":{"tf":1.0},"28":{"tf":1.0},"35":{"tf":2.23606797749979},"37":{"tf":1.0},"82":{"tf":1.7320508075688772},"83":{"tf":1.7320508075688772},"84":{"tf":1.7320508075688772},"85":{"tf":1.4142135623730951},"86":{"tf":1.7320508075688772},"91":{"tf":1.0},"94":{"tf":3.1622776601683795}}},"df":0,"docs":{}}},"w":{"df":32,"docs":{"10":{"tf":3.1622776601683795},"104":{"tf":1.0},"106":{"tf":1.7320508075688772},"107":{"tf":2.23606797749979},"108":{"tf":1.4142135623730951},"109":{"tf":1.7320508075688772},"119":{"tf":1.0},"12":{"tf":1.0},"125":{"tf":1.0},"127":{"tf":1.7320508075688772},"128":{"tf":2.0},"13":{"tf":3.1622776601683795},"130":{"tf":2.23606797749979},"133":{"tf":3.605551275463989},"134":{"tf":2.449489742783178},"136":{"tf":2.449489742783178},"139":{"tf":1.4142135623730951},"142":{"tf":2.449489742783178},"147":{"tf":2.6457513110645907},"149":{"tf":2.449489742783178},"20":{"tf":1.0},"23":{"tf":1.0},"36":{"tf":2.449489742783178},"49":{"tf":1.7320508075688772},"5":{"tf":1.0},"52":{"tf":2.0},"66":{"tf":1.7320508075688772},"69":{"tf":1.0},"70":{"tf":1.7320508075688772},"84":{"tf":1.4142135623730951},"9":{"tf":1.0},"91":{"tf":1.4142135623730951}}}},"u":{"df":0,"docs":{},"f":{"df":0,"docs":{},"f":{"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"df":0,"docs":{},"i":{"'":{"df":1,"docs":{"100":{"tf":1.4142135623730951}}},"df":0,"docs":{}}}}}},"g":{"df":1,"docs":{"64":{"tf":1.0}}},"l":{"df":0,"docs":{},"e":{"df":4,"docs":{"100":{"tf":1.4142135623730951},"48":{"tf":1.4142135623730951},"70":{"tf":1.0},"85":{"tf":1.0}}}},"n":{"df":5,"docs":{"144":{"tf":1.0},"44":{"tf":1.0},"80":{"tf":1.7320508075688772},"86":{"tf":1.0},"94":{"tf":1.0}},"n":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":1,"docs":{"104":{"tf":1.0}}}}}}},"−":{"1":{"df":1,"docs":{"38":{"tf":1.0}}},"df":0,"docs":{}}},"s":{"1":{"_":{"1":{"df":1,"docs":{"35":{"tf":1.0}}},"df":0,"docs":{},"l":{"a":{"df":0,"docs":{},"g":{"df":0,"docs":{},"r":{"a":{"df":0,"docs":{},"n":{"df":0,"docs":{},"g":{"df":1,"docs":{"35":{"tf":1.0}}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"z":{"df":0,"docs":{},"e":{"df":0,"docs":{},"t":{"a":{"df":1,"docs":{"35":{"tf":1.0}}},"df":0,"docs":{}}}}},"df":1,"docs":{"35":{"tf":1.0}}},"2":{"_":{"1":{"df":1,"docs":{"35":{"tf":1.0}}},"df":0,"docs":{},"l":{"a":{"df":0,"docs":{},"g":{"df":0,"docs":{},"r":{"a":{"df":0,"docs":{},"n":{"df":0,"docs":{},"g":{"df":1,"docs":{"35":{"tf":1.0}}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"z":{"df":0,"docs":{},"e":{"df":0,"docs":{},"t":{"a":{"df":1,"docs":{"35":{"tf":1.0}}},"df":0,"docs":{}}}}},"df":1,"docs":{"35":{"tf":1.0}},"k":{"df":1,"docs":{"102":{"tf":1.4142135623730951}}}},"3":{"_":{"1":{"df":1,"docs":{"35":{"tf":1.0}}},"df":0,"docs":{},"l":{"a":{"df":0,"docs":{},"g":{"df":0,"docs":{},"r":{"a":{"df":0,"docs":{},"n":{"df":0,"docs":{},"g":{"df":1,"docs":{"35":{"tf":1.0}}}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":1,"docs":{"35":{"tf":1.0}}},"=":{"0":{",":{"df":0,"docs":{},"…":{",":{"df":0,"docs":{},"q":{"df":0,"docs":{},"−":{"1":{"df":2,"docs":{"94":{"tf":1.0},"95":{"tf":1.7320508075688772}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"_":{"df":0,"docs":{},"σ":{"1":{"df":1,"docs":{"35":{"tf":1.0}}},"2":{"df":1,"docs":{"35":{"tf":1.0}}},"df":0,"docs":{}}},"a":{"df":0,"docs":{},"k":{"df":0,"docs":{},"e":{"df":1,"docs":{"21":{"tf":1.0}}}},"m":{"df":0,"docs":{},"e":{"df":33,"docs":{"10":{"tf":1.0},"101":{"tf":1.0},"104":{"tf":1.0},"110":{"tf":1.0},"115":{"tf":1.0},"118":{"tf":2.0},"12":{"tf":1.0},"124":{"tf":1.0},"125":{"tf":1.0},"132":{"tf":1.0},"133":{"tf":1.0},"135":{"tf":1.0},"14":{"tf":1.7320508075688772},"146":{"tf":1.7320508075688772},"149":{"tf":2.23606797749979},"151":{"tf":1.0},"31":{"tf":1.0},"36":{"tf":1.0},"52":{"tf":1.0},"54":{"tf":1.0},"55":{"tf":1.0},"59":{"tf":1.0},"60":{"tf":1.0},"65":{"tf":1.0},"66":{"tf":1.4142135623730951},"67":{"tf":1.0},"68":{"tf":1.0},"69":{"tf":1.0},"75":{"tf":1.0},"80":{"tf":1.0},"84":{"tf":1.7320508075688772},"95":{"tf":1.0},"97":{"tf":1.0}}},"p":{"df":0,"docs":{},"l":{"df":19,"docs":{"112":{"tf":1.4142135623730951},"118":{"tf":1.7320508075688772},"147":{"tf":1.0},"20":{"tf":1.0},"24":{"tf":1.0},"25":{"tf":1.4142135623730951},"26":{"tf":1.4142135623730951},"27":{"tf":1.0},"28":{"tf":1.0},"32":{"tf":2.0},"56":{"tf":1.0},"59":{"tf":1.0},"62":{"tf":1.4142135623730951},"83":{"tf":1.4142135623730951},"84":{"tf":1.0},"89":{"tf":1.0},"94":{"tf":2.6457513110645907},"95":{"tf":2.6457513110645907},"97":{"tf":2.0}}}}},"t":{"df":0,"docs":{},"i":{"df":0,"docs":{},"s":{"df":0,"docs":{},"f":{"df":0,"docs":{},"i":{"df":28,"docs":{"10":{"tf":1.0},"11":{"tf":1.0},"113":{"tf":1.4142135623730951},"114":{"tf":1.0},"116":{"tf":1.0},"12":{"tf":1.0},"124":{"tf":1.0},"130":{"tf":1.0},"14":{"tf":1.0},"146":{"tf":1.0},"19":{"tf":1.0},"20":{"tf":1.4142135623730951},"48":{"tf":1.0},"49":{"tf":1.4142135623730951},"52":{"tf":1.7320508075688772},"53":{"tf":1.4142135623730951},"54":{"tf":1.7320508075688772},"55":{"tf":1.4142135623730951},"56":{"tf":1.0},"57":{"tf":1.0},"59":{"tf":1.0},"63":{"tf":1.4142135623730951},"66":{"tf":1.0},"67":{"tf":1.0},"68":{"tf":1.0},"69":{"tf":1.0},"75":{"tf":1.0},"84":{"tf":1.4142135623730951}}}}}}},"v":{"df":0,"docs":{},"e":{"df":2,"docs":{"151":{"tf":1.0},"21":{"tf":1.0}}}},"w":{"df":3,"docs":{"10":{"tf":1.0},"14":{"tf":1.0},"16":{"tf":1.0}}},"y":{"df":1,"docs":{"56":{"tf":1.0}}}},"c":{"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"a":{"df":0,"docs":{},"r":{"df":0,"docs":{},"i":{"df":0,"docs":{},"o":{"df":1,"docs":{"100":{"tf":1.0}}}}}},"df":0,"docs":{}}},"h":{"df":0,"docs":{},"e":{"df":0,"docs":{},"m":{"df":0,"docs":{},"e":{"df":11,"docs":{"19":{"tf":1.7320508075688772},"35":{"tf":1.7320508075688772},"38":{"tf":1.7320508075688772},"44":{"tf":1.0},"71":{"tf":2.449489742783178},"74":{"tf":1.0},"75":{"tf":1.4142135623730951},"81":{"tf":1.0},"82":{"tf":1.0},"86":{"tf":1.0},"92":{"tf":1.0}}}}},"w":{"a":{"df":0,"docs":{},"r":{"df":0,"docs":{},"t":{"df":0,"docs":{},"z":{"df":1,"docs":{"14":{"tf":1.4142135623730951}}}}}},"df":0,"docs":{}}}},"df":2,"docs":{"92":{"tf":1.4142135623730951},"95":{"tf":1.7320508075688772}},"e":{"c":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"d":{"_":{"df":0,"docs":{},"r":{"df":0,"docs":{},"o":{"df":0,"docs":{},"w":{"[":{"0":{"df":2,"docs":{"107":{"tf":1.4142135623730951},"114":{"tf":1.0}}},"df":0,"docs":{}},"df":2,"docs":{"107":{"tf":1.0},"114":{"tf":1.0}}}}}},"df":13,"docs":{"11":{"tf":1.0},"13":{"tf":1.0},"14":{"tf":1.7320508075688772},"145":{"tf":1.0},"147":{"tf":1.0},"149":{"tf":1.4142135623730951},"3":{"tf":1.0},"49":{"tf":1.0},"59":{"tf":1.0},"66":{"tf":1.0},"70":{"tf":1.0},"9":{"tf":1.0},"91":{"tf":1.0}}},"df":0,"docs":{}}},"r":{"df":0,"docs":{},"e":{"df":0,"docs":{},"t":{"df":2,"docs":{"20":{"tf":1.0},"21":{"tf":1.0}}}}},"t":{"df":0,"docs":{},"i":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"df":34,"docs":{"103":{"tf":1.0},"11":{"tf":1.0},"111":{"tf":1.0},"117":{"tf":1.0},"126":{"tf":1.0},"130":{"tf":1.0},"131":{"tf":1.0},"138":{"tf":1.0},"139":{"tf":1.0},"14":{"tf":2.6457513110645907},"142":{"tf":1.0},"144":{"tf":1.0},"145":{"tf":1.0},"146":{"tf":4.0},"19":{"tf":1.0},"21":{"tf":1.0},"23":{"tf":1.0},"34":{"tf":1.4142135623730951},"37":{"tf":1.0},"4":{"tf":1.0},"43":{"tf":1.0},"45":{"tf":1.0},"46":{"tf":1.0},"49":{"tf":1.0},"69":{"tf":1.4142135623730951},"70":{"tf":1.0},"74":{"tf":1.0},"77":{"tf":1.0},"80":{"tf":1.0},"82":{"tf":1.0},"84":{"tf":1.0},"87":{"tf":1.0},"9":{"tf":1.0},"95":{"tf":1.0}}}}}},"u":{"df":0,"docs":{},"r":{"df":5,"docs":{"109":{"tf":1.4142135623730951},"151":{"tf":1.0},"68":{"tf":1.0},"73":{"tf":1.0},"91":{"tf":1.0}}}}},"df":0,"docs":{},"e":{"df":15,"docs":{"101":{"tf":1.0},"134":{"tf":1.0},"14":{"tf":2.0},"149":{"tf":1.0},"151":{"tf":1.0},"16":{"tf":1.4142135623730951},"20":{"tf":1.0},"3":{"tf":1.4142135623730951},"34":{"tf":1.0},"71":{"tf":1.0},"75":{"tf":1.0},"77":{"tf":1.0},"80":{"tf":1.0},"9":{"tf":1.7320508075688772},"98":{"tf":1.0}},"m":{"df":2,"docs":{"20":{"tf":1.0},"66":{"tf":1.0}},"i":{"df":0,"docs":{},"n":{"df":0,"docs":{},"g":{"df":0,"docs":{},"l":{"df":0,"docs":{},"i":{"df":1,"docs":{"20":{"tf":1.0}}}}}}}},"n":{"df":7,"docs":{"10":{"tf":1.0},"130":{"tf":1.0},"131":{"tf":1.0},"133":{"tf":1.0},"14":{"tf":1.0},"149":{"tf":1.0},"16":{"tf":1.0}}}},"l":{"df":0,"docs":{},"f":{".":{"a":{"df":0,"docs":{},"i":{"df":0,"docs":{},"r":{".":{"c":{"df":0,"docs":{},"o":{"df":0,"docs":{},"m":{"df":0,"docs":{},"p":{"df":0,"docs":{},"u":{"df":0,"docs":{},"t":{"df":0,"docs":{},"e":{"_":{"df":0,"docs":{},"t":{"df":0,"docs":{},"r":{"a":{"df":0,"docs":{},"n":{"df":0,"docs":{},"s":{"df":0,"docs":{},"i":{"df":0,"docs":{},"t":{"df":0,"docs":{},"i":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"(":{"&":{"df":0,"docs":{},"f":{"df":0,"docs":{},"r":{"a":{"df":0,"docs":{},"m":{"df":1,"docs":{"114":{"tf":1.0}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}}}}}}}},"df":0,"docs":{}}}},"df":0,"docs":{}}}}}},"n":{"df":0,"docs":{},"t":{"df":0,"docs":{},"e":{"df":0,"docs":{},"x":{"df":0,"docs":{},"t":{"(":{")":{".":{"df":0,"docs":{},"t":{"df":0,"docs":{},"r":{"a":{"df":0,"docs":{},"n":{"df":0,"docs":{},"s":{"df":0,"docs":{},"i":{"df":0,"docs":{},"t":{"df":0,"docs":{},"i":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"_":{"df":0,"docs":{},"o":{"df":0,"docs":{},"f":{"df":0,"docs":{},"f":{"df":0,"docs":{},"s":{"df":0,"docs":{},"e":{"df":0,"docs":{},"t":{"df":1,"docs":{"114":{"tf":1.0}}}}}}}}},"df":0,"docs":{}}}}}}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{},"p":{"df":0,"docs":{},"u":{"b":{"_":{"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"df":0,"docs":{},"p":{"df":0,"docs":{},"u":{"df":0,"docs":{},"t":{"df":0,"docs":{},"s":{".":{"a":{"0":{".":{"c":{"df":0,"docs":{},"l":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"df":1,"docs":{"106":{"tf":1.0}}}}}},"df":0,"docs":{}},"df":0,"docs":{}},"1":{".":{"c":{"df":0,"docs":{},"l":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"df":1,"docs":{"106":{"tf":1.0}}}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}},":":{":":{"df":0,"docs":{},"r":{"a":{"df":0,"docs":{},"p":{"c":{"df":0,"docs":{},"h":{"a":{"df":0,"docs":{},"l":{"df":0,"docs":{},"l":{"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"df":0,"docs":{},"g":{"df":2,"docs":{"106":{"tf":1.0},"107":{"tf":1.0}}}}}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":3,"docs":{"106":{"tf":1.0},"107":{"tf":1.0},"114":{"tf":1.0}}}},"n":{"d":{"df":13,"docs":{"101":{"tf":1.4142135623730951},"102":{"tf":1.0},"147":{"tf":1.4142135623730951},"21":{"tf":2.0},"7":{"tf":1.4142135623730951},"72":{"tf":1.4142135623730951},"75":{"tf":1.7320508075688772},"77":{"tf":1.0},"79":{"tf":1.4142135623730951},"80":{"tf":1.4142135623730951},"83":{"tf":1.0},"84":{"tf":1.0},"94":{"tf":1.0}}},"df":0,"docs":{},"s":{"df":4,"docs":{"19":{"tf":1.0},"45":{"tf":1.0},"70":{"tf":1.0},"77":{"tf":1.0}},"i":{"b":{"df":0,"docs":{},"l":{"df":1,"docs":{"7":{"tf":1.0}}}},"df":0,"docs":{}}},"t":{"df":1,"docs":{"91":{"tf":1.0}}}},"q":{"df":0,"docs":{},"u":{"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"c":{"df":5,"docs":{"105":{"tf":1.0},"16":{"tf":1.0},"49":{"tf":1.7320508075688772},"51":{"tf":1.0},"9":{"tf":1.0}}},"df":0,"docs":{}}}}},"r":{"df":0,"docs":{},"v":{"df":4,"docs":{"101":{"tf":1.0},"7":{"tf":1.0},"83":{"tf":1.0},"95":{"tf":1.0}}}},"t":{"(":{"d":{"df":1,"docs":{"90":{"tf":1.0}}},"df":0,"docs":{}},"df":24,"docs":{"106":{"tf":1.4142135623730951},"112":{"tf":1.0},"119":{"tf":1.0},"124":{"tf":1.4142135623730951},"125":{"tf":2.0},"14":{"tf":6.48074069840786},"142":{"tf":1.7320508075688772},"149":{"tf":1.0},"150":{"tf":1.4142135623730951},"151":{"tf":1.0},"3":{"tf":1.0},"4":{"tf":1.0},"45":{"tf":1.0},"62":{"tf":1.0},"65":{"tf":1.0},"68":{"tf":2.0},"69":{"tf":1.0},"73":{"tf":1.0},"8":{"tf":1.4142135623730951},"80":{"tf":1.4142135623730951},"82":{"tf":1.7320508075688772},"83":{"tf":1.0},"90":{"tf":1.4142135623730951},"92":{"tf":1.0}},"u":{"df":0,"docs":{},"p":{"(":{"&":{"c":{"df":0,"docs":{},"o":{"df":0,"docs":{},"m":{"df":0,"docs":{},"m":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"_":{"df":0,"docs":{},"p":{"df":0,"docs":{},"r":{"df":0,"docs":{},"e":{"df":0,"docs":{},"p":{"df":0,"docs":{},"r":{"df":0,"docs":{},"o":{"c":{"df":0,"docs":{},"e":{"df":0,"docs":{},"s":{"df":0,"docs":{},"s":{"df":0,"docs":{},"e":{"d":{"_":{"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"df":0,"docs":{},"p":{"df":0,"docs":{},"u":{"df":0,"docs":{},"t":{"df":1,"docs":{"35":{"tf":1.4142135623730951}}}}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}}}},"df":0,"docs":{}}}}}}}},"df":1,"docs":{"44":{"tf":1.0}}}}}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":4,"docs":{"22":{"tf":1.4142135623730951},"34":{"tf":1.0},"35":{"tf":1.4142135623730951},"44":{"tf":1.0}}}}},"v":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":7,"docs":{"10":{"tf":1.0},"124":{"tf":1.0},"45":{"tf":1.0},"65":{"tf":1.4142135623730951},"66":{"tf":1.0},"70":{"tf":1.4142135623730951},"80":{"tf":1.4142135623730951}}}}}},"h":{"a":{"2":{"5":{"6":{"(":{"df":1,"docs":{"7":{"tf":1.0}}},"df":1,"docs":{"7":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{},"h":{"a":{"df":0,"docs":{},"r":{"df":1,"docs":{"103":{"tf":1.0}}}},"df":0,"docs":{}},"m":{"df":0,"docs":{},"i":{"df":0,"docs":{},"r":{"df":7,"docs":{"118":{"tf":1.4142135623730951},"39":{"tf":1.0},"40":{"tf":1.0},"42":{"tf":2.0},"89":{"tf":1.0},"94":{"tf":1.0},"95":{"tf":1.0}}}}},"p":{"df":0,"docs":{},"e":{"df":2,"docs":{"73":{"tf":1.0},"9":{"tf":1.0}}}},"r":{"df":0,"docs":{},"e":{"df":4,"docs":{"104":{"tf":1.0},"150":{"tf":2.0},"151":{"tf":1.4142135623730951},"19":{"tf":1.0}}}}},"df":0,"docs":{},"o":{"df":0,"docs":{},"w":{"c":{"a":{"df":0,"docs":{},"s":{"df":1,"docs":{"12":{"tf":1.0}}}},"df":0,"docs":{}},"df":11,"docs":{"114":{"tf":1.0},"14":{"tf":1.0},"53":{"tf":1.0},"54":{"tf":1.7320508075688772},"55":{"tf":1.0},"56":{"tf":1.0},"57":{"tf":1.0},"58":{"tf":1.0},"59":{"tf":2.0},"60":{"tf":1.4142135623730951},"69":{"tf":1.0}},"n":{"df":1,"docs":{"149":{"tf":1.0}}}}},"u":{"df":0,"docs":{},"f":{"df":0,"docs":{},"f":{"df":0,"docs":{},"l":{"df":1,"docs":{"79":{"tf":1.0}}}}}}},"i":{"d":{"df":0,"docs":{},"e":{"df":9,"docs":{"112":{"tf":1.0},"113":{"tf":1.0},"146":{"tf":1.0},"58":{"tf":1.0},"60":{"tf":1.0},"62":{"tf":1.0},"63":{"tf":1.0},"64":{"tf":1.0},"80":{"tf":1.0}}}},"df":0,"docs":{},"g":{"df":0,"docs":{},"n":{"a":{"df":0,"docs":{},"t":{"df":0,"docs":{},"u":{"df":0,"docs":{},"r":{"df":1,"docs":{"151":{"tf":1.0}}}}}},"df":0,"docs":{}}},"m":{"df":0,"docs":{},"i":{"df":0,"docs":{},"l":{"a":{"df":0,"docs":{},"r":{"df":5,"docs":{"114":{"tf":1.0},"14":{"tf":1.4142135623730951},"146":{"tf":1.0},"151":{"tf":1.0},"85":{"tf":1.0}},"l":{"df":0,"docs":{},"i":{"df":5,"docs":{"13":{"tf":1.0},"139":{"tf":1.0},"14":{"tf":1.4142135623730951},"80":{"tf":1.0},"92":{"tf":1.0}}}}}},"df":0,"docs":{}}},"p":{"df":0,"docs":{},"l":{"df":8,"docs":{"110":{"tf":1.0},"117":{"tf":1.0},"118":{"tf":1.0},"128":{"tf":1.0},"20":{"tf":1.0},"44":{"tf":1.4142135623730951},"45":{"tf":1.0},"49":{"tf":1.0}},"e":{"_":{"df":0,"docs":{},"f":{"df":0,"docs":{},"i":{"b":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"a":{"c":{"c":{"df":0,"docs":{},"i":{":":{":":{"df":0,"docs":{},"f":{"df":0,"docs":{},"i":{"b":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"a":{"c":{"c":{"df":0,"docs":{},"i":{"_":{"df":0,"docs":{},"t":{"df":0,"docs":{},"r":{"a":{"c":{"df":0,"docs":{},"e":{"(":{"[":{"df":0,"docs":{},"f":{"df":0,"docs":{},"e":{":":{":":{"df":0,"docs":{},"f":{"df":0,"docs":{},"r":{"df":0,"docs":{},"o":{"df":0,"docs":{},"m":{"(":{"1":{"df":1,"docs":{"104":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}}},"df":0,"docs":{},"r":{"df":2,"docs":{"14":{"tf":1.0},"149":{"tf":1.0}}}},"i":{"c":{"df":2,"docs":{"21":{"tf":1.0},"64":{"tf":1.0}}},"df":4,"docs":{"106":{"tf":1.0},"109":{"tf":1.0},"114":{"tf":1.0},"125":{"tf":1.0}},"f":{"df":2,"docs":{"149":{"tf":1.0},"64":{"tf":1.0}},"i":{"df":1,"docs":{"56":{"tf":1.0}}}}}}},"u":{"df":0,"docs":{},"l":{"df":0,"docs":{},"t":{"a":{"df":0,"docs":{},"n":{"df":3,"docs":{"101":{"tf":1.0},"59":{"tf":1.0},"80":{"tf":1.0}}}},"df":0,"docs":{}}}}},"n":{"df":0,"docs":{},"g":{"df":0,"docs":{},"l":{"df":14,"docs":{"101":{"tf":1.4142135623730951},"12":{"tf":1.7320508075688772},"128":{"tf":1.0},"146":{"tf":1.0},"16":{"tf":1.0},"28":{"tf":1.0},"33":{"tf":1.0},"35":{"tf":1.0},"5":{"tf":1.0},"69":{"tf":1.0},"80":{"tf":1.4142135623730951},"84":{"tf":1.0},"92":{"tf":1.0},"97":{"tf":1.4142135623730951}}}}},"t":{"df":1,"docs":{"13":{"tf":1.0}},"e":{"df":1,"docs":{"0":{"tf":1.0}}},"u":{"a":{"df":0,"docs":{},"t":{"df":2,"docs":{"14":{"tf":1.0},"70":{"tf":1.0}}}},"df":0,"docs":{}}},"z":{"df":0,"docs":{},"e":{"df":9,"docs":{"12":{"tf":1.0},"121":{"tf":1.4142135623730951},"14":{"tf":1.0},"141":{"tf":1.0},"147":{"tf":2.6457513110645907},"23":{"tf":1.0},"3":{"tf":2.0},"36":{"tf":1.0},"80":{"tf":1.0}}}}},"k":{"df":0,"docs":{},"e":{"df":0,"docs":{},"t":{"c":{"df":0,"docs":{},"h":{"df":1,"docs":{"79":{"tf":1.0}}}},"df":0,"docs":{}}},"i":{"df":0,"docs":{},"p":{"df":1,"docs":{"102":{"tf":1.0}}}}},"l":{"df":0,"docs":{},"i":{"df":0,"docs":{},"g":{"df":0,"docs":{},"h":{"df":0,"docs":{},"t":{"df":0,"docs":{},"l":{"df":0,"docs":{},"i":{"df":1,"docs":{"59":{"tf":1.0}}}}}}}},"o":{"df":0,"docs":{},"w":{"df":2,"docs":{"3":{"tf":1.0},"98":{"tf":1.0}}}}},"m":{"a":{"df":0,"docs":{},"l":{"df":0,"docs":{},"l":{"df":2,"docs":{"151":{"tf":1.0},"74":{"tf":1.0}},"e":{"df":0,"docs":{},"r":{"df":2,"docs":{"123":{"tf":1.0},"142":{"tf":1.0}}},"s":{"df":0,"docs":{},"t":{"df":1,"docs":{"142":{"tf":1.0}}}}}}}},"df":0,"docs":{}},"o":{"df":0,"docs":{},"l":{"df":0,"docs":{},"u":{"df":0,"docs":{},"t":{"df":4,"docs":{"101":{"tf":1.0},"14":{"tf":1.0},"4":{"tf":1.0},"44":{"tf":1.0}}}},"v":{"df":2,"docs":{"146":{"tf":1.0},"95":{"tf":1.0}}}},"m":{"df":0,"docs":{},"e":{"df":0,"docs":{},"h":{"df":0,"docs":{},"o":{"df":0,"docs":{},"w":{"df":2,"docs":{"114":{"tf":1.0},"70":{"tf":1.0}}}}},"o":{"df":0,"docs":{},"n":{"df":1,"docs":{"114":{"tf":1.0}}}},"t":{"df":0,"docs":{},"h":{"df":6,"docs":{"130":{"tf":1.0},"14":{"tf":1.4142135623730951},"146":{"tf":2.23606797749979},"45":{"tf":1.0},"50":{"tf":1.0},"95":{"tf":1.0}}},"i":{"df":0,"docs":{},"m":{"df":2,"docs":{"48":{"tf":1.0},"52":{"tf":1.0}}}}},"w":{"df":0,"docs":{},"h":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":1,"docs":{"21":{"tf":1.0}}}}}}}},"p":{"df":0,"docs":{},"h":{"df":0,"docs":{},"i":{"df":0,"docs":{},"s":{"df":0,"docs":{},"t":{"df":1,"docs":{"12":{"tf":1.0}}}}}}},"r":{"df":0,"docs":{},"t":{"df":6,"docs":{"132":{"tf":1.4142135623730951},"135":{"tf":1.7320508075688772},"138":{"tf":1.0},"14":{"tf":1.0},"147":{"tf":2.23606797749979},"75":{"tf":1.0}}}},"u":{"df":0,"docs":{},"n":{"d":{"df":3,"docs":{"7":{"tf":1.0},"79":{"tf":1.4142135623730951},"88":{"tf":1.0}}},"df":0,"docs":{}},"r":{"c":{"df":3,"docs":{"118":{"tf":1.0},"127":{"tf":1.0},"34":{"tf":1.0}}},"df":0,"docs":{}}}},"p":{"a":{"c":{"df":0,"docs":{},"e":{"df":1,"docs":{"149":{"tf":1.0}}}},"df":0,"docs":{}},"df":0,"docs":{},"e":{"a":{"df":0,"docs":{},"k":{"df":1,"docs":{"16":{"tf":1.0}}}},"c":{"df":0,"docs":{},"i":{"a":{"df":0,"docs":{},"l":{"df":2,"docs":{"120":{"tf":1.0},"151":{"tf":1.0}}}},"df":0,"docs":{},"f":{"df":14,"docs":{"100":{"tf":1.0},"106":{"tf":1.0},"108":{"tf":1.0},"109":{"tf":1.0},"114":{"tf":1.0},"13":{"tf":1.4142135623730951},"146":{"tf":1.0},"151":{"tf":1.7320508075688772},"19":{"tf":1.0},"49":{"tf":1.0},"70":{"tf":1.4142135623730951},"73":{"tf":1.0},"91":{"tf":1.0},"94":{"tf":1.0}},"i":{"df":7,"docs":{"105":{"tf":1.0},"106":{"tf":1.0},"107":{"tf":1.4142135623730951},"109":{"tf":1.4142135623730951},"128":{"tf":1.0},"146":{"tf":1.0},"44":{"tf":1.0}}}}}},"df":0,"docs":{}},"l":{"df":0,"docs":{},"i":{"df":0,"docs":{},"t":{"df":2,"docs":{"151":{"tf":1.0},"82":{"tf":1.0}}}}}},"q":{"df":0,"docs":{},"u":{"a":{"df":0,"docs":{},"r":{"df":1,"docs":{"45":{"tf":1.0}}},"s":{"df":0,"docs":{},"h":{"df":2,"docs":{"12":{"tf":1.0},"14":{"tf":1.0}}}}},"df":0,"docs":{}}},"r":{"c":{"df":1,"docs":{"146":{"tf":1.0}}},"df":2,"docs":{"35":{"tf":1.4142135623730951},"44":{"tf":1.0}}},"t":{"a":{"c":{"df":0,"docs":{},"k":{"df":2,"docs":{"14":{"tf":1.0},"147":{"tf":1.7320508075688772}}}},"df":0,"docs":{},"g":{"df":0,"docs":{},"e":{"df":1,"docs":{"145":{"tf":1.4142135623730951}}}},"n":{"d":{"df":2,"docs":{"13":{"tf":1.0},"28":{"tf":1.0}}},"df":0,"docs":{}},"r":{"df":0,"docs":{},"k":{"df":19,"docs":{"103":{"tf":1.0},"104":{"tf":1.0},"109":{"tf":1.7320508075688772},"123":{"tf":1.0},"142":{"tf":1.0},"46":{"tf":1.4142135623730951},"47":{"tf":1.0},"49":{"tf":1.0},"50":{"tf":1.4142135623730951},"51":{"tf":1.0},"61":{"tf":1.0},"69":{"tf":1.0},"71":{"tf":1.0},"73":{"tf":1.0},"74":{"tf":1.4142135623730951},"75":{"tf":1.0},"80":{"tf":1.0},"84":{"tf":1.0},"87":{"tf":1.4142135623730951}},"w":{"a":{"df":0,"docs":{},"r":{"df":1,"docs":{"103":{"tf":1.0}},"e":{"'":{"df":1,"docs":{"126":{"tf":1.0}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"t":{"df":12,"docs":{"109":{"tf":1.0},"14":{"tf":1.4142135623730951},"149":{"tf":1.0},"151":{"tf":1.0},"16":{"tf":1.0},"40":{"tf":1.0},"50":{"tf":1.0},"69":{"tf":1.4142135623730951},"8":{"tf":1.0},"82":{"tf":1.0},"94":{"tf":1.0},"95":{"tf":1.0}}}},"t":{"df":0,"docs":{},"e":{"df":12,"docs":{"118":{"tf":1.7320508075688772},"127":{"tf":2.0},"133":{"tf":1.0},"144":{"tf":1.4142135623730951},"146":{"tf":1.7320508075688772},"149":{"tf":1.0},"40":{"tf":2.23606797749979},"48":{"tf":1.0},"70":{"tf":3.0},"84":{"tf":1.0},"88":{"tf":1.7320508075688772},"94":{"tf":1.0}},"m":{"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"df":0,"docs":{},"t":{"df":5,"docs":{"11":{"tf":1.4142135623730951},"14":{"tf":1.0},"146":{"tf":1.4142135623730951},"34":{"tf":1.0},"84":{"tf":1.0}}}}}}}}},"df":0,"docs":{},"e":{"df":0,"docs":{},"p":{"df":27,"docs":{"102":{"tf":1.0},"107":{"tf":1.7320508075688772},"111":{"tf":1.0},"113":{"tf":1.0},"127":{"tf":2.0},"128":{"tf":1.4142135623730951},"130":{"tf":1.7320508075688772},"131":{"tf":1.0},"133":{"tf":4.123105625617661},"136":{"tf":2.449489742783178},"139":{"tf":1.4142135623730951},"144":{"tf":1.0},"146":{"tf":2.449489742783178},"147":{"tf":1.4142135623730951},"149":{"tf":1.4142135623730951},"16":{"tf":1.0},"31":{"tf":1.0},"32":{"tf":1.0},"48":{"tf":1.0},"49":{"tf":1.0},"51":{"tf":2.0},"52":{"tf":1.0},"53":{"tf":1.4142135623730951},"57":{"tf":1.0},"61":{"tf":1.0},"70":{"tf":1.0},"95":{"tf":2.0}}}},"i":{"c":{"df":0,"docs":{},"k":{"df":1,"docs":{"7":{"tf":1.0}}}},"df":0,"docs":{},"l":{"df":0,"docs":{},"l":{"df":3,"docs":{"0":{"tf":1.0},"125":{"tf":1.0},"58":{"tf":1.0}}}}},"o":{"df":0,"docs":{},"n":{"df":0,"docs":{},"e":{"df":4,"docs":{"126":{"tf":1.4142135623730951},"127":{"tf":1.7320508075688772},"130":{"tf":1.0},"131":{"tf":1.0}}}},"r":{"df":0,"docs":{},"e":{"df":5,"docs":{"136":{"tf":1.0},"146":{"tf":1.4142135623730951},"151":{"tf":1.0},"35":{"tf":2.0},"70":{"tf":1.0}}}}},"r":{"a":{"df":0,"docs":{},"i":{"df":0,"docs":{},"g":{"df":0,"docs":{},"h":{"df":0,"docs":{},"t":{"df":0,"docs":{},"f":{"df":0,"docs":{},"o":{"df":0,"docs":{},"r":{"df":0,"docs":{},"w":{"a":{"df":0,"docs":{},"r":{"d":{"df":4,"docs":{"132":{"tf":1.0},"14":{"tf":1.4142135623730951},"146":{"tf":1.0},"151":{"tf":1.0}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}}}}}},"t":{"df":0,"docs":{},"e":{"df":0,"docs":{},"g":{"df":0,"docs":{},"i":{"df":1,"docs":{"40":{"tf":1.0}}}}}}},"df":0,"docs":{},"i":{"c":{"df":0,"docs":{},"t":{"df":0,"docs":{},"l":{"df":0,"docs":{},"i":{"df":1,"docs":{"73":{"tf":1.4142135623730951}}}}}},"df":0,"docs":{},"n":{"df":0,"docs":{},"g":{"df":3,"docs":{"41":{"tf":1.0},"88":{"tf":1.7320508075688772},"91":{"tf":1.0}}}}},"o":{"df":0,"docs":{},"n":{"df":0,"docs":{},"g":{"df":3,"docs":{"42":{"tf":1.0},"94":{"tf":1.0},"95":{"tf":1.0}}}}},"u":{"c":{"df":0,"docs":{},"t":{"df":8,"docs":{"106":{"tf":1.4142135623730951},"107":{"tf":1.0},"108":{"tf":1.0},"109":{"tf":1.4142135623730951},"110":{"tf":1.0},"114":{"tf":1.4142135623730951},"118":{"tf":1.0},"35":{"tf":3.3166247903554}},"u":{"df":0,"docs":{},"r":{"df":2,"docs":{"146":{"tf":1.0},"83":{"tf":1.0}}}}}},"df":0,"docs":{}}},"u":{"df":0,"docs":{},"f":{"df":0,"docs":{},"f":{"df":1,"docs":{"56":{"tf":1.0}}}}}},"u":{"b":{"c":{"df":0,"docs":{},"o":{"df":0,"docs":{},"l":{"df":0,"docs":{},"u":{"df":0,"docs":{},"m":{"df":0,"docs":{},"n":{"df":4,"docs":{"146":{"tf":1.4142135623730951},"148":{"tf":1.0},"150":{"tf":1.7320508075688772},"151":{"tf":1.0}}}}}}}},"df":2,"docs":{"133":{"tf":1.7320508075688772},"134":{"tf":1.0}},"g":{"df":0,"docs":{},"r":{"df":0,"docs":{},"o":{"df":0,"docs":{},"u":{"df":0,"docs":{},"p":{"df":1,"docs":{"38":{"tf":1.0}}}}}}},"m":{"a":{"df":0,"docs":{},"t":{"df":0,"docs":{},"r":{"df":0,"docs":{},"i":{"df":0,"docs":{},"x":{"df":2,"docs":{"142":{"tf":1.7320508075688772},"147":{"tf":1.7320508075688772}}}}}}},"df":0,"docs":{}},"p":{"df":0,"docs":{},"r":{"df":0,"docs":{},"o":{"df":0,"docs":{},"g":{"df":0,"docs":{},"r":{"a":{"df":0,"docs":{},"m":{"df":1,"docs":{"151":{"tf":1.4142135623730951}}}},"df":0,"docs":{}}}}}},"s":{"c":{"df":0,"docs":{},"r":{"df":0,"docs":{},"i":{"df":0,"docs":{},"p":{"df":0,"docs":{},"t":{"df":1,"docs":{"28":{"tf":1.4142135623730951}}}}}}},"df":0,"docs":{},"e":{"c":{"df":0,"docs":{},"t":{"df":1,"docs":{"146":{"tf":1.4142135623730951}}}},"df":0,"docs":{},"q":{"df":0,"docs":{},"u":{"df":3,"docs":{"11":{"tf":1.0},"114":{"tf":1.0},"40":{"tf":1.0}}}},"t":{"df":3,"docs":{"128":{"tf":1.4142135623730951},"150":{"tf":1.0},"90":{"tf":1.4142135623730951}}}}},"t":{"a":{"b":{"df":0,"docs":{},"l":{"df":1,"docs":{"127":{"tf":1.0}}}},"df":0,"docs":{}},"df":0,"docs":{},"r":{"a":{"c":{"df":0,"docs":{},"t":{"df":1,"docs":{"9":{"tf":1.4142135623730951}}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"c":{"c":{"df":0,"docs":{},"e":{"df":1,"docs":{"78":{"tf":1.0}}}},"df":0,"docs":{},"h":{"df":23,"docs":{"100":{"tf":1.0},"109":{"tf":1.0},"11":{"tf":1.0},"124":{"tf":1.4142135623730951},"13":{"tf":1.0},"14":{"tf":3.3166247903554},"144":{"tf":1.0},"151":{"tf":1.0},"16":{"tf":2.23606797749979},"19":{"tf":1.0},"20":{"tf":1.7320508075688772},"26":{"tf":1.0},"44":{"tf":1.0},"45":{"tf":1.4142135623730951},"5":{"tf":1.0},"66":{"tf":1.0},"70":{"tf":1.0},"71":{"tf":1.0},"73":{"tf":1.7320508075688772},"79":{"tf":1.0},"88":{"tf":1.0},"94":{"tf":1.7320508075688772},"95":{"tf":1.0}}}},"df":0,"docs":{},"f":{"df":0,"docs":{},"f":{"df":0,"docs":{},"i":{"df":0,"docs":{},"x":{"df":1,"docs":{"35":{"tf":1.0}}}}}},"m":{"df":6,"docs":{"114":{"tf":1.0},"125":{"tf":1.0},"19":{"tf":1.0},"49":{"tf":1.0},"52":{"tf":1.0},"65":{"tf":1.0}},"m":{"a":{"df":0,"docs":{},"n":{"d":{"df":1,"docs":{"59":{"tf":1.4142135623730951}}},"df":0,"docs":{}},"r":{"df":1,"docs":{"61":{"tf":1.0}},"i":{"df":8,"docs":{"131":{"tf":1.0},"138":{"tf":1.0},"14":{"tf":1.0},"146":{"tf":1.0},"150":{"tf":1.0},"61":{"tf":1.0},"69":{"tf":1.0},"81":{"tf":1.0}}}}},"df":0,"docs":{}}},"p":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":0,"docs":{},"s":{"c":{"df":0,"docs":{},"r":{"df":0,"docs":{},"i":{"df":0,"docs":{},"p":{"df":0,"docs":{},"t":{"df":1,"docs":{"95":{"tf":1.4142135623730951}}}}}}},"df":0,"docs":{}}}},"p":{"df":0,"docs":{},"o":{"df":0,"docs":{},"r":{"df":0,"docs":{},"t":{"df":3,"docs":{"107":{"tf":1.0},"121":{"tf":1.0},"34":{"tf":1.0}}}},"s":{"df":5,"docs":{"14":{"tf":2.23606797749979},"45":{"tf":1.0},"73":{"tf":1.4142135623730951},"74":{"tf":1.4142135623730951},"79":{"tf":1.0}}}}}},"r":{"df":0,"docs":{},"e":{"df":2,"docs":{"125":{"tf":1.0},"151":{"tf":1.0}}}}},"w":{"df":0,"docs":{},"e":{"df":0,"docs":{},"e":{"df":0,"docs":{},"p":{"df":1,"docs":{"64":{"tf":1.0}}}}}},"y":{"df":0,"docs":{},"m":{"b":{"df":0,"docs":{},"o":{"df":0,"docs":{},"l":{"df":3,"docs":{"36":{"tf":1.0},"5":{"tf":2.0},"9":{"tf":1.0}}}}},"df":0,"docs":{},"m":{"df":0,"docs":{},"e":{"df":0,"docs":{},"t":{"df":0,"docs":{},"r":{"df":1,"docs":{"73":{"tf":1.0}}}}}}},"s":{"df":0,"docs":{},"t":{"df":0,"docs":{},"e":{"df":0,"docs":{},"m":{"'":{"df":1,"docs":{"70":{"tf":1.0}}},".":{"a":{"df":0,"docs":{},"s":{"df":0,"docs":{},"s":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":0,"docs":{},"t":{"_":{"df":0,"docs":{},"e":{"df":0,"docs":{},"q":{"(":{"&":{"df":0,"docs":{},"i":{"df":2,"docs":{"44":{"tf":1.0},"45":{"tf":1.0}}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}}}}}},"df":0,"docs":{},"i":{"df":0,"docs":{},"f":{"_":{"df":0,"docs":{},"e":{"df":0,"docs":{},"l":{"df":0,"docs":{},"s":{"df":0,"docs":{},"e":{"(":{"&":{"df":0,"docs":{},"e":{"df":0,"docs":{},"x":{"df":0,"docs":{},"p":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"df":0,"docs":{},"t":{"_":{"b":{"df":0,"docs":{},"i":{"df":0,"docs":{},"t":{"df":0,"docs":{},"s":{"[":{"df":0,"docs":{},"i":{"df":1,"docs":{"45":{"tf":1.0}}}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}}}}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}}}},"df":0,"docs":{}}},"m":{"df":0,"docs":{},"u":{"df":0,"docs":{},"l":{"(":{"&":{"df":0,"docs":{},"r":{"df":0,"docs":{},"e":{"df":0,"docs":{},"s":{"df":0,"docs":{},"u":{"df":0,"docs":{},"l":{"df":0,"docs":{},"t":{"df":1,"docs":{"45":{"tf":1.4142135623730951}}}}}}}},"x":{"df":1,"docs":{"44":{"tf":1.0}}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"n":{"df":0,"docs":{},"e":{"df":0,"docs":{},"w":{"_":{"c":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"df":0,"docs":{},"s":{"df":0,"docs":{},"t":{"a":{"df":0,"docs":{},"n":{"df":0,"docs":{},"t":{"(":{"df":0,"docs":{},"f":{"df":0,"docs":{},"i":{"df":0,"docs":{},"e":{"df":0,"docs":{},"l":{"d":{"df":0,"docs":{},"e":{"df":0,"docs":{},"l":{"df":0,"docs":{},"e":{"df":0,"docs":{},"m":{"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"df":0,"docs":{},"t":{":":{":":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"df":1,"docs":{"45":{"tf":1.0}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}}}}}}},"df":0,"docs":{}}}}}},"df":0,"docs":{}}}},"df":0,"docs":{}}}}}},"df":0,"docs":{},"p":{"df":0,"docs":{},"u":{"b":{"df":0,"docs":{},"l":{"df":0,"docs":{},"i":{"c":{"_":{"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"df":0,"docs":{},"p":{"df":0,"docs":{},"u":{"df":0,"docs":{},"t":{"df":2,"docs":{"44":{"tf":1.4142135623730951},"45":{"tf":1.4142135623730951}}}}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"u":{"3":{"2":{"(":{"&":{"df":0,"docs":{},"e":{"df":0,"docs":{},"x":{"df":0,"docs":{},"p":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"df":1,"docs":{"45":{"tf":1.0}}}}}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"v":{"a":{"df":0,"docs":{},"r":{"df":0,"docs":{},"i":{"df":2,"docs":{"44":{"tf":1.0},"45":{"tf":1.0}}}}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"p":{"df":0,"docs":{},"u":{"b":{"df":0,"docs":{},"l":{"df":0,"docs":{},"i":{"c":{"_":{"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"df":0,"docs":{},"p":{"df":0,"docs":{},"u":{"df":0,"docs":{},"t":{"_":{"df":0,"docs":{},"v":{"a":{"df":0,"docs":{},"l":{"df":0,"docs":{},"u":{"df":0,"docs":{},"e":{"df":0,"docs":{},"s":{"(":{"&":{"a":{"df":0,"docs":{},"s":{"df":0,"docs":{},"s":{"df":0,"docs":{},"i":{"df":0,"docs":{},"g":{"df":0,"docs":{},"n":{"df":1,"docs":{"44":{"tf":1.0}}}}}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"s":{"df":0,"docs":{},"o":{"df":0,"docs":{},"l":{"df":0,"docs":{},"v":{"df":0,"docs":{},"e":{"(":{"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"df":0,"docs":{},"p":{"df":0,"docs":{},"u":{"df":0,"docs":{},"t":{"df":0,"docs":{},"s":{")":{".":{"df":0,"docs":{},"u":{"df":0,"docs":{},"n":{"df":0,"docs":{},"w":{"df":0,"docs":{},"r":{"a":{"df":0,"docs":{},"p":{"df":1,"docs":{"44":{"tf":1.0}}}},"df":0,"docs":{}}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}}}}}},"df":0,"docs":{}}}}}}},"df":17,"docs":{"103":{"tf":1.0},"104":{"tf":1.0},"118":{"tf":1.0},"119":{"tf":1.0},"144":{"tf":1.0},"146":{"tf":1.0},"151":{"tf":1.0},"4":{"tf":1.4142135623730951},"42":{"tf":1.0},"43":{"tf":1.0},"44":{"tf":2.8284271247461903},"45":{"tf":2.23606797749979},"46":{"tf":1.0},"48":{"tf":1.4142135623730951},"49":{"tf":1.0},"70":{"tf":1.0},"95":{"tf":1.0}}}}}}},"σ":{"1":{"df":1,"docs":{"14":{"tf":1.0}},"​":{"]":{"1":{"df":0,"docs":{},"​":{",":{"[":{"df":0,"docs":{},"s":{"df":0,"docs":{},"σ":{"2":{"df":0,"docs":{},"​":{"]":{"1":{"df":0,"docs":{},"​":{",":{"[":{"df":0,"docs":{},"s":{"df":0,"docs":{},"σ":{"3":{"]":{"1":{"df":0,"docs":{},"​":{",":{"[":{"df":0,"docs":{},"q":{"df":0,"docs":{},"l":{"df":0,"docs":{},"​":{"]":{"1":{"df":0,"docs":{},"​":{",":{"[":{"df":0,"docs":{},"q":{"df":0,"docs":{},"r":{"df":0,"docs":{},"​":{"]":{"1":{"df":0,"docs":{},"​":{",":{"[":{"df":0,"docs":{},"q":{"df":0,"docs":{},"m":{"df":0,"docs":{},"​":{"]":{"1":{"df":0,"docs":{},"​":{",":{"[":{"df":0,"docs":{},"q":{"df":0,"docs":{},"o":{"df":0,"docs":{},"​":{"]":{"1":{"df":0,"docs":{},"​":{",":{"[":{"df":0,"docs":{},"q":{"c":{"df":0,"docs":{},"​":{"]":{"1":{"df":1,"docs":{"24":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{},"​":{"]":{"1":{"df":0,"docs":{},"​":{",":{"[":{"df":0,"docs":{},"q":{"df":0,"docs":{},"l":{"df":0,"docs":{},"​":{"]":{"1":{"df":0,"docs":{},"​":{",":{"[":{"df":0,"docs":{},"q":{"df":0,"docs":{},"r":{"df":0,"docs":{},"​":{"]":{"1":{"df":0,"docs":{},"​":{",":{"[":{"df":0,"docs":{},"q":{"df":0,"docs":{},"m":{"df":0,"docs":{},"​":{"]":{"1":{"df":0,"docs":{},"​":{",":{"[":{"df":0,"docs":{},"q":{"df":0,"docs":{},"o":{"df":0,"docs":{},"​":{"]":{"1":{"df":0,"docs":{},"​":{",":{"[":{"df":0,"docs":{},"q":{"c":{"df":0,"docs":{},"​":{"]":{"1":{"df":1,"docs":{"31":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"2":{"df":1,"docs":{"14":{"tf":1.0}}},"3":{"df":1,"docs":{"14":{"tf":1.0}}},"df":0,"docs":{},"i":{"df":1,"docs":{"35":{"tf":1.0}}}},"​":{":":{"=":{"df":0,"docs":{},"h":{"df":0,"docs":{},"ω":{"df":0,"docs":{},"ι":{"df":2,"docs":{"94":{"tf":1.0},"95":{"tf":1.0}}}}}},"df":0,"docs":{}},"df":0,"docs":{},"∈":{"[":{"0":{",":{"2":{"df":0,"docs":{},"n":{"+":{"df":0,"docs":{},"l":{"df":0,"docs":{},"−":{"1":{"df":2,"docs":{"94":{"tf":1.0},"95":{"tf":1.0}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}},"t":{"(":{"1":{")":{"df":0,"docs":{},"−":{"df":0,"docs":{},"p":{"(":{"1":{")":{"=":{"1":{"df":0,"docs":{},"−":{"1":{"=":{"0":{"df":1,"docs":{"54":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{},"g":{")":{"df":0,"docs":{},"−":{"df":0,"docs":{},"p":{"(":{"df":0,"docs":{},"g":{")":{"=":{"1":{"df":0,"docs":{},"−":{"1":{"=":{"0":{"df":1,"docs":{"54":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"^":{"0":{"df":2,"docs":{"52":{"tf":1.0},"54":{"tf":1.0}}},"df":0,"docs":{}},"df":2,"docs":{"52":{"tf":1.0},"54":{"tf":1.0}},"i":{")":{"=":{"a":{"df":0,"docs":{},"i":{"df":1,"docs":{"52":{"tf":1.0}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"x":{")":{"'":{"df":1,"docs":{"114":{"tf":1.0}}},"df":0,"docs":{}},"0":{"df":2,"docs":{"112":{"tf":1.4142135623730951},"113":{"tf":1.4142135623730951}},"​":{")":{"df":0,"docs":{},"t":{"(":{"df":0,"docs":{},"x":{"0":{"df":0,"docs":{},"​":{"df":0,"docs":{},"g":{")":{"df":0,"docs":{},"t":{"(":{"df":0,"docs":{},"x":{"0":{"df":0,"docs":{},"​":{"df":0,"docs":{},"g":{"2":{"df":1,"docs":{"114":{"tf":1.0}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{},"g":{"2":{")":{"df":0,"docs":{},"−":{"df":0,"docs":{},"t":{"(":{"df":0,"docs":{},"x":{"0":{"df":0,"docs":{},"​":{"df":0,"docs":{},"g":{")":{"df":0,"docs":{},"−":{"df":0,"docs":{},"t":{"(":{"df":0,"docs":{},"x":{"0":{"df":1,"docs":{"114":{"tf":1.0}}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"_":{"0":{"df":3,"docs":{"60":{"tf":1.7320508075688772},"62":{"tf":1.4142135623730951},"63":{"tf":1.4142135623730951}}},"df":0,"docs":{}},"df":3,"docs":{"35":{"tf":1.7320508075688772},"52":{"tf":1.4142135623730951},"54":{"tf":1.0}},"g":{"2":{")":{"df":0,"docs":{},"−":{"df":0,"docs":{},"t":{"(":{"df":0,"docs":{},"x":{"df":0,"docs":{},"g":{")":{"df":0,"docs":{},"−":{"df":0,"docs":{},"t":{"(":{"df":0,"docs":{},"x":{"df":1,"docs":{"114":{"tf":1.0}}}},"df":0,"docs":{}}}},"df":0,"docs":{}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":1,"docs":{"52":{"tf":1.0}}}},"z":{"df":7,"docs":{"112":{"tf":1.0},"117":{"tf":1.0},"58":{"tf":1.0},"59":{"tf":1.0},"60":{"tf":1.0},"62":{"tf":1.0},"63":{"tf":1.0}},"g":{"2":{"df":2,"docs":{"112":{"tf":1.0},"117":{"tf":1.0}}},"^":{"2":{"df":5,"docs":{"58":{"tf":1.0},"59":{"tf":1.0},"60":{"tf":1.0},"62":{"tf":1.0},"63":{"tf":1.0}}},"df":0,"docs":{}},"df":7,"docs":{"112":{"tf":1.0},"117":{"tf":1.0},"58":{"tf":1.0},"59":{"tf":1.0},"60":{"tf":1.0},"62":{"tf":1.0},"63":{"tf":1.0}}}},"ω":{"0":{")":{"df":0,"docs":{},"t":{"(":{"df":0,"docs":{},"ω":{"1":{")":{"df":0,"docs":{},"…":{"df":0,"docs":{},"t":{"(":{"df":0,"docs":{},"ω":{"1":{"5":{"df":1,"docs":{"114":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"2":{")":{"df":0,"docs":{},"t":{"(":{"df":0,"docs":{},"ω":{"4":{"df":1,"docs":{"114":{"tf":1.0}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{},"g":{"2":{")":{"df":0,"docs":{},"−":{"df":0,"docs":{},"t":{"(":{"df":0,"docs":{},"ω":{"0":{"df":0,"docs":{},"g":{")":{"df":0,"docs":{},"−":{"df":0,"docs":{},"t":{"(":{"df":0,"docs":{},"ω":{"0":{")":{"df":0,"docs":{},"t":{"(":{"df":0,"docs":{},"ω":{"df":0,"docs":{},"g":{"2":{")":{"df":0,"docs":{},"−":{"df":0,"docs":{},"t":{"(":{"df":0,"docs":{},"ω":{"df":0,"docs":{},"g":{")":{"df":0,"docs":{},"−":{"df":0,"docs":{},"t":{"(":{"df":0,"docs":{},"ω":{")":{"df":0,"docs":{},"t":{"(":{"df":0,"docs":{},"ω":{"2":{"df":0,"docs":{},"g":{"2":{")":{"df":0,"docs":{},"−":{"df":0,"docs":{},"t":{"(":{"df":0,"docs":{},"ω":{"2":{"df":0,"docs":{},"g":{")":{"df":0,"docs":{},"−":{"df":0,"docs":{},"t":{"(":{"df":0,"docs":{},"ω":{"2":{")":{"df":0,"docs":{},"⋮":{"df":0,"docs":{},"t":{"(":{"df":0,"docs":{},"ω":{"1":{"5":{"df":0,"docs":{},"g":{"2":{")":{"df":0,"docs":{},"−":{"df":0,"docs":{},"t":{"(":{"df":0,"docs":{},"ω":{"1":{"5":{"df":0,"docs":{},"g":{")":{"df":0,"docs":{},"−":{"df":0,"docs":{},"t":{"(":{"df":0,"docs":{},"ω":{"1":{"5":{"df":1,"docs":{"114":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"2":{"df":0,"docs":{},"i":{")":{":":{"0":{"df":0,"docs":{},"≤":{"df":0,"docs":{},"i":{"df":0,"docs":{},"≤":{"1":{"5":{"df":0,"docs":{},"}":{"=":{"df":0,"docs":{},"{":{"df":0,"docs":{},"t":{"(":{"df":0,"docs":{},"g":{"df":0,"docs":{},"i":{")":{":":{"0":{"df":0,"docs":{},"≤":{"df":0,"docs":{},"i":{"df":0,"docs":{},"≤":{"7":{"df":1,"docs":{"68":{"tf":1.0}}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"4":{")":{"df":0,"docs":{},"−":{"df":0,"docs":{},"t":{"(":{"df":0,"docs":{},"ω":{"2":{")":{"df":0,"docs":{},"−":{"df":0,"docs":{},"t":{"(":{"df":0,"docs":{},"ω":{"0":{")":{"df":0,"docs":{},"t":{"(":{"df":0,"docs":{},"ω":{"5":{")":{"df":0,"docs":{},"−":{"df":0,"docs":{},"t":{"(":{"df":0,"docs":{},"ω":{"3":{")":{"df":0,"docs":{},"−":{"df":0,"docs":{},"t":{"(":{"df":0,"docs":{},"ω":{")":{"df":0,"docs":{},"t":{"(":{"df":0,"docs":{},"ω":{"6":{")":{"df":0,"docs":{},"−":{"df":0,"docs":{},"t":{"(":{"df":0,"docs":{},"ω":{"4":{")":{"df":0,"docs":{},"−":{"df":0,"docs":{},"t":{"(":{"df":0,"docs":{},"ω":{"2":{")":{"df":0,"docs":{},"⋮":{"df":0,"docs":{},"t":{"(":{"df":0,"docs":{},"ω":{"3":{")":{"df":0,"docs":{},"−":{"df":0,"docs":{},"t":{"(":{"df":0,"docs":{},"ω":{")":{"df":0,"docs":{},"−":{"df":0,"docs":{},"t":{"(":{"df":0,"docs":{},"ω":{"1":{"5":{"df":1,"docs":{"114":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"0":{",":{"df":0,"docs":{},"i":{"df":1,"docs":{"20":{"tf":1.0}}},"j":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"s":{"df":0,"docs":{},"σ":{"1":{"df":0,"docs":{},"​":{"(":{"df":0,"docs":{},"ω":{"df":0,"docs":{},"i":{")":{"df":0,"docs":{},"β":{"+":{"df":0,"docs":{},"γ":{")":{"(":{"df":0,"docs":{},"t":{"0":{",":{"df":0,"docs":{},"j":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"s":{"df":0,"docs":{},"σ":{"2":{"df":0,"docs":{},"​":{"(":{"df":0,"docs":{},"ω":{"df":0,"docs":{},"i":{")":{"df":0,"docs":{},"β":{"+":{"df":0,"docs":{},"γ":{")":{"(":{"df":0,"docs":{},"t":{"0":{",":{"df":0,"docs":{},"j":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"s":{"df":0,"docs":{},"σ":{"3":{"df":0,"docs":{},"​":{"(":{"df":0,"docs":{},"ω":{"df":0,"docs":{},"i":{")":{"df":0,"docs":{},"β":{"+":{"df":0,"docs":{},"γ":{")":{":":{"0":{"df":0,"docs":{},"≤":{"df":0,"docs":{},"i":{"<":{"df":0,"docs":{},"n":{"df":1,"docs":{"14":{"tf":1.0}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"ω":{"df":0,"docs":{},"i":{"df":0,"docs":{},"β":{"+":{"df":0,"docs":{},"γ":{")":{"(":{"df":0,"docs":{},"t":{"1":{",":{"df":0,"docs":{},"j":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"ω":{"df":0,"docs":{},"i":{"df":0,"docs":{},"k":{"1":{"df":0,"docs":{},"​":{"df":0,"docs":{},"β":{"+":{"df":0,"docs":{},"γ":{")":{"(":{"df":0,"docs":{},"t":{"2":{",":{"df":0,"docs":{},"j":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"ω":{"df":0,"docs":{},"i":{"df":0,"docs":{},"k":{"2":{"df":0,"docs":{},"​":{"df":0,"docs":{},"β":{"+":{"df":0,"docs":{},"γ":{")":{":":{"0":{"df":0,"docs":{},"≤":{"df":0,"docs":{},"i":{"<":{"df":0,"docs":{},"n":{"df":1,"docs":{"14":{"tf":1.0}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}}},"df":0,"docs":{}}}},"df":4,"docs":{"136":{"tf":1.0},"142":{"tf":1.0},"146":{"tf":2.0},"147":{"tf":1.0}}},"1":{"df":5,"docs":{"136":{"tf":1.0},"142":{"tf":1.0},"146":{"tf":1.7320508075688772},"147":{"tf":1.0},"16":{"tf":1.0}}},"2":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"t":{"3":{"df":1,"docs":{"16":{"tf":1.0}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"=":{"df":0,"docs":{},"t":{"df":0,"docs":{},"l":{"df":0,"docs":{},"o":{"df":0,"docs":{},"′":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"x":{"df":0,"docs":{},"n":{"+":{"2":{"df":0,"docs":{},"t":{"df":0,"docs":{},"m":{"df":0,"docs":{},"i":{"d":{"df":0,"docs":{},"′":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"x":{"2":{"(":{"df":0,"docs":{},"n":{"+":{"2":{")":{"df":0,"docs":{},"t":{"df":0,"docs":{},"h":{"df":0,"docs":{},"i":{"df":1,"docs":{"26":{"tf":1.0}}}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}}}}}},"_":{"1":{"(":{"df":0,"docs":{},"z":{"df":0,"docs":{},"g":{"df":1,"docs":{"66":{"tf":1.0}}}}},"df":0,"docs":{}},"2":{"(":{"df":0,"docs":{},"z":{"df":1,"docs":{"66":{"tf":1.0}}}},"df":0,"docs":{}},"df":0,"docs":{},"h":{"df":0,"docs":{},"i":{"_":{"1":{"df":1,"docs":{"35":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}}},"i":{"(":{"df":0,"docs":{},"x":{"df":1,"docs":{"65":{"tf":1.0}}}},"df":0,"docs":{}},"l":{"df":0,"docs":{},"o":{"_":{"1":{"df":1,"docs":{"35":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}}},"m":{"df":0,"docs":{},"i":{"d":{"_":{"1":{"df":1,"docs":{"35":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"z":{"df":0,"docs":{},"e":{"df":0,"docs":{},"t":{"a":{"df":1,"docs":{"35":{"tf":1.0}}},"df":0,"docs":{}}}}},"a":{"b":{"df":0,"docs":{},"l":{"df":9,"docs":{"108":{"tf":1.7320508075688772},"119":{"tf":1.0},"127":{"tf":1.0},"137":{"tf":1.0},"145":{"tf":1.0},"48":{"tf":1.0},"49":{"tf":1.4142135623730951},"70":{"tf":1.0},"9":{"tf":1.0}}}},"c":{"df":0,"docs":{},"k":{"df":0,"docs":{},"l":{"df":1,"docs":{"64":{"tf":1.0}}}}},"df":0,"docs":{},"k":{"df":0,"docs":{},"e":{"df":23,"docs":{"104":{"tf":1.4142135623730951},"113":{"tf":2.0},"123":{"tf":1.0},"13":{"tf":1.4142135623730951},"14":{"tf":2.0},"149":{"tf":1.0},"151":{"tf":1.4142135623730951},"19":{"tf":1.7320508075688772},"20":{"tf":1.7320508075688772},"34":{"tf":1.0},"41":{"tf":1.0},"44":{"tf":1.0},"51":{"tf":1.0},"52":{"tf":1.4142135623730951},"56":{"tf":1.0},"63":{"tf":1.4142135623730951},"68":{"tf":1.4142135623730951},"7":{"tf":1.4142135623730951},"70":{"tf":1.4142135623730951},"74":{"tf":1.0},"8":{"tf":1.0},"80":{"tf":1.0},"9":{"tf":1.0}},"n":{"df":1,"docs":{"20":{"tf":1.0}}}}},"l":{"df":0,"docs":{},"k":{"df":4,"docs":{"105":{"tf":1.0},"107":{"tf":1.0},"116":{"tf":1.0},"52":{"tf":1.4142135623730951}}}}},"df":27,"docs":{"11":{"tf":1.0},"112":{"tf":1.7320508075688772},"114":{"tf":1.4142135623730951},"13":{"tf":1.0},"14":{"tf":1.7320508075688772},"142":{"tf":1.4142135623730951},"147":{"tf":1.4142135623730951},"16":{"tf":2.449489742783178},"20":{"tf":1.4142135623730951},"23":{"tf":1.0},"24":{"tf":1.0},"26":{"tf":1.0},"28":{"tf":1.0},"32":{"tf":1.7320508075688772},"34":{"tf":1.4142135623730951},"35":{"tf":1.4142135623730951},"36":{"tf":1.4142135623730951},"52":{"tf":1.7320508075688772},"60":{"tf":1.0},"62":{"tf":1.7320508075688772},"68":{"tf":2.23606797749979},"69":{"tf":1.4142135623730951},"70":{"tf":2.0},"83":{"tf":1.4142135623730951},"84":{"tf":1.7320508075688772},"91":{"tf":1.7320508075688772},"94":{"tf":1.0}},"e":{"c":{"df":0,"docs":{},"h":{"df":0,"docs":{},"n":{"df":0,"docs":{},"i":{"c":{"df":1,"docs":{"52":{"tf":1.0}}},"df":0,"docs":{},"q":{"df":0,"docs":{},"u":{"df":1,"docs":{"88":{"tf":1.0}}}}}}}},"df":0,"docs":{},"l":{"df":0,"docs":{},"l":{"df":3,"docs":{"109":{"tf":1.0},"125":{"tf":1.0},"49":{"tf":1.0}}}},"m":{"df":0,"docs":{},"p":{"df":0,"docs":{},"o":{"df":0,"docs":{},"r":{"a":{"df":0,"docs":{},"r":{"df":0,"docs":{},"i":{"df":3,"docs":{"142":{"tf":1.0},"146":{"tf":1.4142135623730951},"147":{"tf":1.0}},"l":{"df":0,"docs":{},"i":{"df":1,"docs":{"34":{"tf":1.4142135623730951}}}}}}},"df":0,"docs":{}}}}},"r":{"df":0,"docs":{},"m":{"df":14,"docs":{"114":{"tf":1.4142135623730951},"123":{"tf":1.0},"125":{"tf":1.0},"128":{"tf":1.0},"14":{"tf":1.0},"144":{"tf":1.4142135623730951},"16":{"tf":1.0},"20":{"tf":1.0},"52":{"tf":1.4142135623730951},"65":{"tf":1.0},"66":{"tf":1.0},"67":{"tf":1.4142135623730951},"80":{"tf":1.0},"85":{"tf":1.0}},"i":{"df":0,"docs":{},"n":{"df":0,"docs":{},"o":{"df":0,"docs":{},"l":{"df":0,"docs":{},"o":{"df":0,"docs":{},"g":{"df":1,"docs":{"34":{"tf":1.0}}}}}}}}}},"s":{"df":0,"docs":{},"t":{"_":{"c":{"df":0,"docs":{},"o":{"df":0,"docs":{},"m":{"df":0,"docs":{},"m":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"_":{"df":0,"docs":{},"p":{"df":0,"docs":{},"r":{"df":0,"docs":{},"e":{"df":0,"docs":{},"p":{"df":0,"docs":{},"r":{"df":0,"docs":{},"o":{"c":{"df":0,"docs":{},"e":{"df":0,"docs":{},"s":{"df":0,"docs":{},"s":{"df":0,"docs":{},"e":{"d":{"_":{"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"df":0,"docs":{},"p":{"df":0,"docs":{},"u":{"df":0,"docs":{},"t":{"_":{"2":{"df":1,"docs":{"35":{"tf":1.4142135623730951}}},"df":0,"docs":{}},"df":0,"docs":{}}}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}}}},"df":0,"docs":{}}}}}}}},"df":0,"docs":{}}}}}}},"df":0,"docs":{},"p":{"df":0,"docs":{},"r":{"df":0,"docs":{},"o":{"df":0,"docs":{},"v":{"df":0,"docs":{},"e":{"_":{"df":0,"docs":{},"f":{"df":0,"docs":{},"i":{"b":{"df":1,"docs":{"104":{"tf":1.0}}},"df":0,"docs":{}}}},"df":0,"docs":{}}}}}},"s":{"df":0,"docs":{},"r":{"df":0,"docs":{},"s":{"(":{"c":{"df":0,"docs":{},"o":{"df":0,"docs":{},"m":{"df":0,"docs":{},"m":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{".":{"df":0,"docs":{},"n":{"df":1,"docs":{"44":{"tf":1.0}}}},"_":{"df":0,"docs":{},"p":{"df":0,"docs":{},"r":{"df":0,"docs":{},"e":{"df":0,"docs":{},"p":{"df":0,"docs":{},"r":{"df":0,"docs":{},"o":{"c":{"df":0,"docs":{},"e":{"df":0,"docs":{},"s":{"df":0,"docs":{},"s":{"df":0,"docs":{},"e":{"d":{"_":{"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"df":0,"docs":{},"p":{"df":0,"docs":{},"u":{"df":0,"docs":{},"t":{".":{"df":0,"docs":{},"n":{"df":1,"docs":{"35":{"tf":1.4142135623730951}}}},"df":0,"docs":{}}}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}}}},"df":0,"docs":{}}}}}}}},"df":0,"docs":{}}}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"w":{"df":0,"docs":{},"i":{"df":0,"docs":{},"t":{"df":0,"docs":{},"n":{"df":0,"docs":{},"e":{"df":0,"docs":{},"s":{"df":0,"docs":{},"s":{"_":{"2":{"(":{"df":0,"docs":{},"x":{"df":1,"docs":{"35":{"tf":1.7320508075688772}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}}}}}}},"df":5,"docs":{"104":{"tf":1.0},"114":{"tf":1.0},"34":{"tf":1.0},"35":{"tf":1.4142135623730951},"81":{"tf":1.0}},"h":{"a":{"df":0,"docs":{},"s":{"df":0,"docs":{},"h":{"df":1,"docs":{"110":{"tf":1.0}}}}},"df":0,"docs":{}},"r":{"a":{"df":0,"docs":{},"n":{"d":{"df":0,"docs":{},"o":{"df":0,"docs":{},"m":{"df":0,"docs":{},"f":{"df":0,"docs":{},"i":{"df":0,"docs":{},"e":{"df":0,"docs":{},"l":{"d":{"df":0,"docs":{},"g":{"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":2,"docs":{"35":{"tf":1.4142135623730951},"44":{"tf":1.0}}}}}}}},"df":0,"docs":{}}}}}}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}},"h":{"a":{"df":0,"docs":{},"n":{"df":0,"docs":{},"k":{"df":2,"docs":{"103":{"tf":1.0},"81":{"tf":1.0}}}},"t":{"'":{"df":10,"docs":{"142":{"tf":1.4142135623730951},"146":{"tf":1.0},"147":{"tf":1.0},"151":{"tf":1.0},"16":{"tf":1.0},"46":{"tf":1.0},"54":{"tf":1.0},"79":{"tf":1.0},"9":{"tf":1.0},"95":{"tf":1.0}}},"df":0,"docs":{}}},"df":17,"docs":{"112":{"tf":1.0},"123":{"tf":1.0},"124":{"tf":2.8284271247461903},"125":{"tf":1.7320508075688772},"14":{"tf":3.0},"142":{"tf":1.0},"16":{"tf":1.0},"23":{"tf":1.0},"35":{"tf":1.0},"49":{"tf":1.4142135623730951},"52":{"tf":1.7320508075688772},"62":{"tf":1.0},"65":{"tf":1.0},"68":{"tf":2.23606797749979},"70":{"tf":1.7320508075688772},"91":{"tf":1.0},"92":{"tf":2.0}},"e":{"df":0,"docs":{},"o":{"df":0,"docs":{},"r":{"df":0,"docs":{},"e":{"df":0,"docs":{},"m":{"df":1,"docs":{"73":{"tf":1.0}}}}}},"r":{"df":0,"docs":{},"e":{"'":{"df":6,"docs":{"102":{"tf":1.0},"116":{"tf":1.0},"14":{"tf":1.0},"146":{"tf":1.0},"22":{"tf":1.0},"7":{"tf":1.0}}},"df":0,"docs":{},"f":{"df":0,"docs":{},"o":{"df":0,"docs":{},"r":{"df":8,"docs":{"101":{"tf":1.0},"12":{"tf":1.0},"20":{"tf":1.0},"36":{"tf":1.0},"38":{"tf":1.0},"41":{"tf":1.0},"66":{"tf":1.0},"94":{"tf":1.0}}}}}}},"y":{"'":{"df":0,"docs":{},"r":{"df":2,"docs":{"3":{"tf":1.0},"68":{"tf":1.0}}}},"df":0,"docs":{}}},"i":{"df":1,"docs":{"26":{"tf":1.0}},"n":{"df":0,"docs":{},"g":{"df":16,"docs":{"104":{"tf":1.0},"109":{"tf":1.4142135623730951},"111":{"tf":1.0},"113":{"tf":1.0},"124":{"tf":1.0},"14":{"tf":1.7320508075688772},"16":{"tf":1.0},"21":{"tf":1.0},"49":{"tf":1.4142135623730951},"50":{"tf":1.0},"52":{"tf":1.0},"53":{"tf":1.0},"56":{"tf":1.0},"58":{"tf":1.0},"59":{"tf":1.0},"68":{"tf":1.0}}},"k":{"df":4,"docs":{"118":{"tf":1.4142135623730951},"151":{"tf":1.4142135623730951},"52":{"tf":1.0},"70":{"tf":1.0}}}},"r":{"d":{"_":{"df":0,"docs":{},"r":{"df":0,"docs":{},"o":{"df":0,"docs":{},"w":{"[":{"0":{"df":1,"docs":{"107":{"tf":1.0}}},"df":0,"docs":{}},"df":2,"docs":{"107":{"tf":1.0},"114":{"tf":1.0}}}}}},"df":1,"docs":{"13":{"tf":1.0}}},"df":0,"docs":{}}},"o":{"df":0,"docs":{},"s":{"df":0,"docs":{},"e":{"df":3,"docs":{"102":{"tf":1.0},"14":{"tf":1.0},"3":{"tf":1.0}}}},"u":{"df":0,"docs":{},"g":{"df":0,"docs":{},"h":{"df":6,"docs":{"114":{"tf":1.0},"118":{"tf":1.0},"49":{"tf":1.0},"52":{"tf":1.0},"57":{"tf":1.0},"66":{"tf":1.0}}}}}},"r":{"df":0,"docs":{},"e":{"df":0,"docs":{},"e":{"df":16,"docs":{"107":{"tf":1.0},"12":{"tf":1.0},"128":{"tf":1.0},"130":{"tf":1.4142135623730951},"141":{"tf":1.0},"142":{"tf":1.4142135623730951},"144":{"tf":1.4142135623730951},"149":{"tf":1.0},"150":{"tf":1.0},"21":{"tf":1.4142135623730951},"3":{"tf":1.0},"44":{"tf":1.0},"58":{"tf":1.0},"69":{"tf":1.0},"84":{"tf":1.0},"9":{"tf":1.0}}}},"o":{"df":0,"docs":{},"u":{"df":0,"docs":{},"g":{"df":0,"docs":{},"h":{"df":8,"docs":{"105":{"tf":1.0},"106":{"tf":1.0},"111":{"tf":1.0},"113":{"tf":1.0},"4":{"tf":1.0},"46":{"tf":1.0},"48":{"tf":1.4142135623730951},"51":{"tf":1.0}},"o":{"df":0,"docs":{},"u":{"df":0,"docs":{},"t":{"df":4,"docs":{"118":{"tf":1.7320508075688772},"124":{"tf":1.4142135623730951},"49":{"tf":1.0},"7":{"tf":1.0}}}}}}}}}},"u":{"df":3,"docs":{"57":{"tf":1.0},"58":{"tf":1.0},"83":{"tf":1.0}}}},"i":{",":{"df":0,"docs":{},"j":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"η":{"3":{"df":0,"docs":{},"i":{"+":{"df":0,"docs":{},"j":{"df":0,"docs":{},"β":{"+":{"df":0,"docs":{},"γ":{":":{"(":{"df":0,"docs":{},"i":{",":{"df":0,"docs":{},"j":{")":{"df":0,"docs":{},"∈":{"df":0,"docs":{},"i":{"df":1,"docs":{"14":{"tf":1.0}}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"k":{"+":{"df":0,"docs":{},"l":{"df":0,"docs":{},"β":{"+":{"df":0,"docs":{},"γ":{":":{"(":{"df":0,"docs":{},"i":{",":{"df":0,"docs":{},"j":{")":{"df":0,"docs":{},"∈":{"df":0,"docs":{},"i":{",":{"df":0,"docs":{},"σ":{"(":{"(":{"df":0,"docs":{},"i":{",":{"df":0,"docs":{},"j":{")":{")":{"=":{"(":{"df":0,"docs":{},"k":{",":{"df":0,"docs":{},"l":{"df":1,"docs":{"14":{"tf":1.0}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"=":{"df":0,"docs":{},"t":{"df":0,"docs":{},"j":{"df":0,"docs":{},"​":{"(":{"df":0,"docs":{},"g":{"df":0,"docs":{},"i":{"df":2,"docs":{"83":{"tf":1.0},"84":{"tf":1.0}}}}},"df":0,"docs":{}}},"k":{",":{"df":0,"docs":{},"l":{"df":4,"docs":{"11":{"tf":1.0},"13":{"tf":1.0},"14":{"tf":1.0},"16":{"tf":1.0}}}},"df":0,"docs":{}},"σ":{"(":{"(":{"df":0,"docs":{},"i":{",":{"df":0,"docs":{},"j":{"df":1,"docs":{"14":{"tf":1.0}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}}},"df":1,"docs":{"16":{"tf":1.0}},"j":{"df":1,"docs":{"70":{"tf":1.0}}},"m":{"df":0,"docs":{},"e":{"df":10,"docs":{"101":{"tf":1.0},"118":{"tf":2.0},"133":{"tf":1.0},"146":{"tf":1.4142135623730951},"149":{"tf":1.0},"22":{"tf":1.0},"3":{"tf":1.4142135623730951},"45":{"tf":1.0},"59":{"tf":1.0},"80":{"tf":2.0}}}}},"j":{"df":5,"docs":{"70":{"tf":1.7320508075688772},"83":{"tf":1.4142135623730951},"84":{"tf":1.0},"94":{"tf":1.7320508075688772},"95":{"tf":2.6457513110645907}},"​":{"(":{"df":0,"docs":{},"g":{"df":0,"docs":{},"i":{")":{"=":{"df":0,"docs":{},"m":{"^":{"df":0,"docs":{},"i":{",":{"df":0,"docs":{},"j":{"df":1,"docs":{"94":{"tf":1.0}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"t":{"df":0,"docs":{},"i":{",":{"df":0,"docs":{},"j":{"df":1,"docs":{"94":{"tf":1.0}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}},"z":{"df":3,"docs":{"85":{"tf":1.0},"94":{"tf":1.4142135623730951},"95":{"tf":1.0}}}},"z":{"df":3,"docs":{"85":{"tf":1.0},"94":{"tf":1.4142135623730951},"95":{"tf":1.4142135623730951}}}},",":{"df":0,"docs":{},"τ":{"df":0,"docs":{},"j":{"df":0,"docs":{},"z":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"τ":{"df":0,"docs":{},"j":{"df":0,"docs":{},"g":{"df":0,"docs":{},"z":{"df":0,"docs":{},"​":{":":{"0":{"df":0,"docs":{},"≤":{"df":0,"docs":{},"j":{"<":{"df":0,"docs":{},"m":{"df":0,"docs":{},"}":{",":{"df":0,"docs":{},"h":{"1":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"η":{"1":{"df":0,"docs":{},"z":{"2":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"h":{"2":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"η":{"2":{"df":0,"docs":{},"z":{"2":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"{":{"df":0,"docs":{},"p":{"df":0,"docs":{},"k":{"df":0,"docs":{},"​":{":":{"1":{"df":0,"docs":{},"≤":{"df":0,"docs":{},"k":{"<":{"df":0,"docs":{},"n":{"df":0,"docs":{},"}":{",":{"df":0,"docs":{},"π":{",":{"df":0,"docs":{},"y":{",":{"df":0,"docs":{},"{":{"(":{"df":0,"docs":{},"τ":{"df":0,"docs":{},"j":{"df":0,"docs":{},"υ":{"df":0,"docs":{},"s":{"df":0,"docs":{},"​":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"t":{"df":0,"docs":{},"j":{"df":0,"docs":{},"​":{")":{":":{"0":{"df":0,"docs":{},"≤":{"df":0,"docs":{},"j":{"<":{"df":0,"docs":{},"m":{",":{"0":{"df":0,"docs":{},"≤":{"df":0,"docs":{},"s":{"<":{"df":0,"docs":{},"q":{"df":0,"docs":{},"}":{",":{"df":0,"docs":{},"{":{"(":{"df":0,"docs":{},"η":{"1":{"df":0,"docs":{},"υ":{"df":0,"docs":{},"s":{"df":0,"docs":{},"​":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"h":{"1":{"df":0,"docs":{},"​":{")":{":":{"0":{"df":0,"docs":{},"≤":{"df":0,"docs":{},"s":{"<":{"df":0,"docs":{},"q":{"df":0,"docs":{},"}":{"df":0,"docs":{},"{":{"(":{"df":0,"docs":{},"η":{"2":{"df":0,"docs":{},"υ":{"df":0,"docs":{},"s":{"df":0,"docs":{},"​":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"h":{"2":{"df":0,"docs":{},"​":{")":{":":{"0":{"df":0,"docs":{},"≤":{"df":0,"docs":{},"s":{"<":{"df":0,"docs":{},"q":{"df":0,"docs":{},"}":{",":{"df":0,"docs":{},"{":{"(":{"df":0,"docs":{},"π":{"df":0,"docs":{},"k":{"df":0,"docs":{},"υ":{"df":0,"docs":{},"s":{"2":{"df":0,"docs":{},"k":{"df":0,"docs":{},"​":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"p":{"df":0,"docs":{},"k":{"df":0,"docs":{},"​":{")":{":":{"1":{"df":0,"docs":{},"≤":{"df":0,"docs":{},"k":{"<":{"df":0,"docs":{},"n":{",":{"0":{"df":0,"docs":{},"≤":{"df":0,"docs":{},"s":{"<":{"df":0,"docs":{},"q":{"df":0,"docs":{},"}":{",":{"df":0,"docs":{},"{":{"(":{"df":0,"docs":{},"τ":{"df":0,"docs":{},"j":{"df":0,"docs":{},"−":{"df":0,"docs":{},"υ":{"df":0,"docs":{},"s":{"df":0,"docs":{},"​":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"t":{"df":0,"docs":{},"j":{"df":0,"docs":{},"′":{"df":0,"docs":{},"​":{")":{":":{"0":{"df":0,"docs":{},"≤":{"df":0,"docs":{},"j":{"<":{"df":0,"docs":{},"m":{",":{"0":{"df":0,"docs":{},"≤":{"df":0,"docs":{},"s":{"<":{"df":0,"docs":{},"q":{"df":0,"docs":{},"}":{",":{"df":0,"docs":{},"{":{"(":{"df":0,"docs":{},"η":{"1":{"df":0,"docs":{},"−":{"df":0,"docs":{},"υ":{"df":0,"docs":{},"s":{"df":0,"docs":{},"​":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"h":{"1":{"df":0,"docs":{},"′":{"df":0,"docs":{},"​":{")":{":":{"0":{"df":0,"docs":{},"≤":{"df":0,"docs":{},"s":{"<":{"df":0,"docs":{},"q":{"df":0,"docs":{},"}":{"df":0,"docs":{},"{":{"(":{"df":0,"docs":{},"η":{"2":{"df":0,"docs":{},"−":{"df":0,"docs":{},"υ":{"df":0,"docs":{},"s":{"df":0,"docs":{},"​":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"h":{"2":{"df":0,"docs":{},"′":{"df":0,"docs":{},"​":{")":{":":{"0":{"df":0,"docs":{},"≤":{"df":0,"docs":{},"s":{"<":{"df":0,"docs":{},"q":{"df":0,"docs":{},"}":{",":{"df":0,"docs":{},"{":{"(":{"df":0,"docs":{},"π":{"df":0,"docs":{},"k":{"df":0,"docs":{},"−":{"df":0,"docs":{},"υ":{"df":0,"docs":{},"s":{"2":{"df":0,"docs":{},"k":{"df":0,"docs":{},"​":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"p":{"df":0,"docs":{},"k":{"df":0,"docs":{},"′":{"df":0,"docs":{},"​":{")":{":":{"1":{"df":0,"docs":{},"≤":{"df":0,"docs":{},"k":{"<":{"df":0,"docs":{},"n":{",":{"0":{"df":0,"docs":{},"≤":{"df":0,"docs":{},"s":{"<":{"df":0,"docs":{},"q":{"df":1,"docs":{"95":{"tf":1.0}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}}}},"df":0,"docs":{}}}}},"df":0,"docs":{}}}}}}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}}}},"df":0,"docs":{}}}}}}}}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}}}}},"df":0,"docs":{}}}}}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}}}}}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}}}}}},"df":0,"docs":{}}}}}},"]":{",":{"df":0,"docs":{},"t":{"df":0,"docs":{},"j":{"df":0,"docs":{},"​":{"(":{"df":0,"docs":{},"z":{")":{",":{"df":0,"docs":{},"t":{"df":0,"docs":{},"j":{"df":0,"docs":{},"​":{"(":{"df":0,"docs":{},"g":{"df":0,"docs":{},"z":{")":{":":{"0":{"df":0,"docs":{},"≤":{"df":0,"docs":{},"j":{"<":{"df":0,"docs":{},"m":{"df":0,"docs":{},"}":{",":{"[":{"df":0,"docs":{},"h":{"1":{"df":0,"docs":{},"​":{"]":{",":{"df":0,"docs":{},"h":{"1":{"df":0,"docs":{},"​":{"(":{"df":0,"docs":{},"z":{"2":{")":{",":{"[":{"df":0,"docs":{},"h":{"2":{"df":0,"docs":{},"​":{"]":{",":{"df":0,"docs":{},"h":{"2":{"df":0,"docs":{},"​":{"(":{"df":0,"docs":{},"z":{"2":{")":{",":{"df":0,"docs":{},"{":{"[":{"df":0,"docs":{},"p":{"df":0,"docs":{},"k":{"df":0,"docs":{},"​":{"]":{":":{"1":{"df":0,"docs":{},"≤":{"df":0,"docs":{},"k":{"<":{"df":0,"docs":{},"n":{"df":0,"docs":{},"}":{",":{"df":0,"docs":{},"p":{"df":0,"docs":{},"n":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"y":{",":{"df":0,"docs":{},"{":{"df":0,"docs":{},"o":{"df":0,"docs":{},"p":{"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"(":{"df":0,"docs":{},"t":{"df":0,"docs":{},"j":{"df":0,"docs":{},"​":{"(":{"d":{"df":0,"docs":{},"l":{"d":{"df":0,"docs":{},"e":{"df":0,"docs":{},"​":{")":{",":{"df":0,"docs":{},"υ":{"df":0,"docs":{},"s":{"df":0,"docs":{},"​":{")":{":":{"0":{"df":0,"docs":{},"≤":{"df":0,"docs":{},"j":{"<":{"df":0,"docs":{},"m":{",":{"0":{"df":0,"docs":{},"≤":{"df":0,"docs":{},"s":{"<":{"df":0,"docs":{},"q":{"df":0,"docs":{},"}":{",":{"df":0,"docs":{},"{":{"df":0,"docs":{},"o":{"df":0,"docs":{},"p":{"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"(":{"df":0,"docs":{},"h":{"1":{"df":0,"docs":{},"​":{"(":{"d":{"df":0,"docs":{},"l":{"d":{"df":0,"docs":{},"e":{"df":0,"docs":{},"​":{")":{",":{"df":0,"docs":{},"υ":{"df":0,"docs":{},"s":{"df":0,"docs":{},"​":{")":{":":{"0":{"df":0,"docs":{},"≤":{"df":0,"docs":{},"s":{"<":{"df":0,"docs":{},"q":{"df":0,"docs":{},"}":{",":{"df":0,"docs":{},"{":{"df":0,"docs":{},"o":{"df":0,"docs":{},"p":{"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"(":{"df":0,"docs":{},"h":{"2":{"df":0,"docs":{},"​":{"(":{"d":{"df":0,"docs":{},"l":{"d":{"df":0,"docs":{},"e":{"df":0,"docs":{},"​":{")":{",":{"df":0,"docs":{},"υ":{"df":0,"docs":{},"s":{"df":0,"docs":{},"​":{")":{":":{"0":{"df":0,"docs":{},"≤":{"df":0,"docs":{},"s":{"<":{"df":0,"docs":{},"q":{"df":0,"docs":{},"}":{",":{"df":0,"docs":{},"{":{"df":0,"docs":{},"o":{"df":0,"docs":{},"p":{"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"(":{"df":0,"docs":{},"p":{"df":0,"docs":{},"k":{"df":0,"docs":{},"​":{"(":{"d":{"df":0,"docs":{},"k":{"df":0,"docs":{},"​":{")":{",":{"df":0,"docs":{},"υ":{"df":0,"docs":{},"s":{"2":{"df":0,"docs":{},"k":{"df":0,"docs":{},"​":{")":{":":{"1":{"df":0,"docs":{},"≤":{"df":0,"docs":{},"k":{"<":{"df":0,"docs":{},"n":{",":{"0":{"df":0,"docs":{},"≤":{"df":0,"docs":{},"s":{"<":{"df":0,"docs":{},"q":{"df":0,"docs":{},"}":{",":{"df":0,"docs":{},"{":{"df":0,"docs":{},"o":{"df":0,"docs":{},"p":{"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"(":{"df":0,"docs":{},"t":{"df":0,"docs":{},"j":{"df":0,"docs":{},"​":{"(":{"d":{"df":0,"docs":{},"l":{"d":{"df":0,"docs":{},"e":{"df":0,"docs":{},"​":{")":{",":{"df":0,"docs":{},"−":{"df":0,"docs":{},"υ":{"df":0,"docs":{},"s":{"df":0,"docs":{},"​":{")":{":":{"0":{"df":0,"docs":{},"≤":{"df":0,"docs":{},"j":{"<":{"df":0,"docs":{},"m":{",":{"0":{"df":0,"docs":{},"≤":{"df":0,"docs":{},"s":{"<":{"df":0,"docs":{},"q":{"df":0,"docs":{},"}":{",":{"df":0,"docs":{},"{":{"df":0,"docs":{},"o":{"df":0,"docs":{},"p":{"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"(":{"df":0,"docs":{},"h":{"1":{"df":0,"docs":{},"​":{"(":{"d":{"df":0,"docs":{},"l":{"d":{"df":0,"docs":{},"e":{"df":0,"docs":{},"​":{")":{",":{"df":0,"docs":{},"−":{"df":0,"docs":{},"υ":{"df":0,"docs":{},"s":{"df":0,"docs":{},"​":{")":{":":{"0":{"df":0,"docs":{},"≤":{"df":0,"docs":{},"s":{"<":{"df":0,"docs":{},"q":{"df":0,"docs":{},"}":{",":{"df":0,"docs":{},"{":{"df":0,"docs":{},"o":{"df":0,"docs":{},"p":{"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"(":{"df":0,"docs":{},"h":{"2":{"df":0,"docs":{},"​":{"(":{"d":{"df":0,"docs":{},"l":{"d":{"df":0,"docs":{},"e":{"df":0,"docs":{},"​":{")":{",":{"df":0,"docs":{},"−":{"df":0,"docs":{},"υ":{"df":0,"docs":{},"s":{"df":0,"docs":{},"​":{")":{":":{"0":{"df":0,"docs":{},"≤":{"df":0,"docs":{},"s":{"<":{"df":0,"docs":{},"q":{"df":0,"docs":{},"}":{",":{"df":0,"docs":{},"{":{"df":0,"docs":{},"o":{"df":0,"docs":{},"p":{"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"(":{"df":0,"docs":{},"p":{"df":0,"docs":{},"k":{"df":0,"docs":{},"​":{"(":{"d":{"df":0,"docs":{},"k":{"df":0,"docs":{},"​":{")":{",":{"df":0,"docs":{},"−":{"df":0,"docs":{},"υ":{"df":0,"docs":{},"s":{"2":{"df":0,"docs":{},"k":{"df":0,"docs":{},"​":{")":{":":{"1":{"df":0,"docs":{},"≤":{"df":0,"docs":{},"k":{"<":{"df":0,"docs":{},"n":{",":{"0":{"df":0,"docs":{},"≤":{"df":0,"docs":{},"s":{"<":{"df":0,"docs":{},"q":{"df":1,"docs":{"94":{"tf":1.0}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}}}}}}},"df":0,"docs":{}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}}}},"df":0,"docs":{}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}}}},"df":0,"docs":{}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}}}}}}},"df":0,"docs":{}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}}}}}}},"df":0,"docs":{}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}}}},"df":0,"docs":{}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}}}},"df":0,"docs":{}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}}}}}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}},"df":0,"docs":{}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}}},":":{"=":{"c":{"df":0,"docs":{},"o":{"df":0,"docs":{},"m":{"df":0,"docs":{},"m":{"df":0,"docs":{},"i":{"df":0,"docs":{},"t":{"(":{"df":0,"docs":{},"t":{"df":0,"docs":{},"j":{"df":0,"docs":{},"​":{"(":{"d":{"df":0,"docs":{},"l":{"df":1,"docs":{"94":{"tf":1.4142135623730951}}}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}}}}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"l":{"df":0,"docs":{},"o":{"df":0,"docs":{},"​":{"]":{"1":{"df":0,"docs":{},"​":{",":{"[":{"df":0,"docs":{},"t":{"df":0,"docs":{},"m":{"df":0,"docs":{},"i":{"d":{"df":0,"docs":{},"​":{"]":{"1":{"df":0,"docs":{},"​":{",":{"[":{"df":0,"docs":{},"t":{"df":0,"docs":{},"h":{"df":0,"docs":{},"i":{"df":0,"docs":{},"​":{"]":{"1":{"df":2,"docs":{"26":{"tf":1.0},"32":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}}}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{},"t":{"df":0,"docs":{},"m":{"df":0,"docs":{},"i":{"d":{"df":0,"docs":{},"​":{"df":0,"docs":{},"t":{"df":0,"docs":{},"h":{"df":0,"docs":{},"i":{"df":0,"docs":{},"​":{"df":0,"docs":{},"​":{"=":{"df":0,"docs":{},"t":{"df":0,"docs":{},"l":{"df":0,"docs":{},"o":{"df":0,"docs":{},"′":{"df":0,"docs":{},"​":{"+":{"b":{"1":{"0":{"df":0,"docs":{},"​":{"df":0,"docs":{},"x":{"df":0,"docs":{},"n":{"+":{"2":{"=":{"df":0,"docs":{},"t":{"df":0,"docs":{},"m":{"df":0,"docs":{},"i":{"d":{"df":0,"docs":{},"′":{"df":0,"docs":{},"​":{"df":0,"docs":{},"−":{"b":{"1":{"0":{"df":0,"docs":{},"​":{"+":{"b":{"1":{"1":{"df":0,"docs":{},"​":{"df":0,"docs":{},"x":{"df":0,"docs":{},"n":{"+":{"2":{"=":{"df":0,"docs":{},"t":{"df":0,"docs":{},"h":{"df":0,"docs":{},"i":{"df":0,"docs":{},"′":{"df":0,"docs":{},"​":{"df":0,"docs":{},"−":{"b":{"1":{"1":{"df":1,"docs":{"26":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}}}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}}}}},"df":0,"docs":{}}}}}}}},"df":0,"docs":{}}}}},"′":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"t":{"df":0,"docs":{},"m":{"df":0,"docs":{},"i":{"d":{"df":1,"docs":{"26":{"tf":1.0}}},"df":0,"docs":{}}}}},"df":0,"docs":{}}}}},"m":{"df":0,"docs":{},"p":{"0":{"df":1,"docs":{"136":{"tf":1.0}}},"1":{"df":1,"docs":{"136":{"tf":1.0}}},"df":0,"docs":{}},"′":{"+":{"1":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"…":{",":{"df":0,"docs":{},"t":{"df":0,"docs":{},"m":{"df":0,"docs":{},"′":{"+":{"df":0,"docs":{},"m":{"df":1,"docs":{"94":{"tf":1.0}}}},"df":0,"docs":{}}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"o":{"df":0,"docs":{},"g":{"df":0,"docs":{},"e":{"df":0,"docs":{},"t":{"df":0,"docs":{},"h":{"df":1,"docs":{"147":{"tf":1.0}}}}}},"o":{"df":0,"docs":{},"l":{"df":5,"docs":{"19":{"tf":1.0},"50":{"tf":1.0},"70":{"tf":1.0},"8":{"tf":1.0},"9":{"tf":1.0}}}},"p":{"df":1,"docs":{"151":{"tf":1.0}}},"t":{"a":{"df":0,"docs":{},"l":{"df":6,"docs":{"133":{"tf":1.0},"135":{"tf":1.4142135623730951},"144":{"tf":1.0},"147":{"tf":1.0},"73":{"tf":1.0},"91":{"tf":1.0}}}},"df":0,"docs":{}},"w":{"a":{"df":0,"docs":{},"r":{"d":{"df":1,"docs":{"50":{"tf":1.0}}},"df":0,"docs":{}}},"df":0,"docs":{}},"y":{"df":3,"docs":{"34":{"tf":1.0},"7":{"tf":2.23606797749979},"9":{"tf":1.0}}}},"p":{"a":{"df":0,"docs":{},"r":{"df":0,"docs":{},"t":{"df":0,"docs":{},"i":{"a":{"df":0,"docs":{},"l":{"df":0,"docs":{},"​":{"(":{"df":0,"docs":{},"ζ":{")":{"=":{"df":0,"docs":{},"t":{"df":1,"docs":{"28":{"tf":1.0}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"+":{"df":0,"docs":{},"υ":{"df":0,"docs":{},"p":{"df":0,"docs":{},"n":{"c":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"υ":{"2":{"a":{"+":{"df":0,"docs":{},"υ":{"3":{"b":{"+":{"df":0,"docs":{},"υ":{"4":{"c":{"+":{"df":0,"docs":{},"υ":{"5":{"df":0,"docs":{},"s":{"df":0,"docs":{},"σ":{"1":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"υ":{"6":{"df":0,"docs":{},"s":{"df":0,"docs":{},"σ":{"2":{"df":1,"docs":{"28":{"tf":1.0}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}},"]":{"1":{"df":1,"docs":{"32":{"tf":1.0}},"​":{"=":{"[":{"df":0,"docs":{},"t":{"df":0,"docs":{},"l":{"df":0,"docs":{},"o":{"df":0,"docs":{},"​":{"]":{"1":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"ζ":{"df":0,"docs":{},"n":{"+":{"2":{"[":{"df":0,"docs":{},"t":{"df":0,"docs":{},"m":{"df":0,"docs":{},"i":{"d":{"df":0,"docs":{},"​":{"]":{"1":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"ζ":{"2":{"(":{"df":0,"docs":{},"n":{"+":{"2":{")":{"[":{"df":0,"docs":{},"t":{"df":0,"docs":{},"h":{"df":0,"docs":{},"i":{"df":0,"docs":{},"​":{"]":{"1":{"df":1,"docs":{"32":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}}}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"r":{"a":{"c":{"df":0,"docs":{},"e":{"'":{"df":2,"docs":{"68":{"tf":1.0},"70":{"tf":1.0}}},".":{"c":{"df":0,"docs":{},"l":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"df":1,"docs":{"108":{"tf":1.0}}}}}},"df":0,"docs":{}},"_":{"c":{"df":0,"docs":{},"o":{"df":0,"docs":{},"l":{"df":0,"docs":{},"u":{"df":0,"docs":{},"m":{"df":0,"docs":{},"n":{"df":1,"docs":{"109":{"tf":1.4142135623730951}}}}}}}},"df":0,"docs":{},"t":{"a":{"b":{"df":0,"docs":{},"l":{"df":0,"docs":{},"e":{".":{"df":0,"docs":{},"n":{"_":{"c":{"df":0,"docs":{},"o":{"df":0,"docs":{},"l":{"df":1,"docs":{"109":{"tf":1.0}}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":1,"docs":{"108":{"tf":1.4142135623730951}}}},"df":63,"docs":{"10":{"tf":1.7320508075688772},"104":{"tf":1.7320508075688772},"105":{"tf":1.4142135623730951},"107":{"tf":1.0},"108":{"tf":2.6457513110645907},"109":{"tf":2.449489742783178},"11":{"tf":1.0},"110":{"tf":1.0},"112":{"tf":1.7320508075688772},"113":{"tf":1.7320508075688772},"114":{"tf":1.4142135623730951},"117":{"tf":1.0},"119":{"tf":1.7320508075688772},"12":{"tf":1.0},"123":{"tf":1.4142135623730951},"125":{"tf":2.0},"127":{"tf":2.449489742783178},"128":{"tf":1.0},"129":{"tf":1.0},"13":{"tf":1.0},"130":{"tf":1.7320508075688772},"131":{"tf":1.0},"133":{"tf":1.7320508075688772},"134":{"tf":1.0},"137":{"tf":1.4142135623730951},"14":{"tf":1.0},"141":{"tf":1.0},"142":{"tf":1.4142135623730951},"143":{"tf":1.0},"144":{"tf":2.0},"145":{"tf":1.7320508075688772},"146":{"tf":3.0},"147":{"tf":1.0},"149":{"tf":2.0},"16":{"tf":1.0},"20":{"tf":1.4142135623730951},"23":{"tf":1.0},"34":{"tf":1.0},"35":{"tf":1.0},"48":{"tf":1.4142135623730951},"49":{"tf":1.7320508075688772},"50":{"tf":1.7320508075688772},"51":{"tf":1.4142135623730951},"52":{"tf":3.0},"53":{"tf":1.0},"54":{"tf":1.4142135623730951},"58":{"tf":1.7320508075688772},"59":{"tf":1.4142135623730951},"60":{"tf":2.0},"62":{"tf":1.4142135623730951},"63":{"tf":1.4142135623730951},"65":{"tf":2.0},"66":{"tf":1.7320508075688772},"68":{"tf":1.4142135623730951},"69":{"tf":1.7320508075688772},"70":{"tf":2.8284271247461903},"8":{"tf":1.0},"83":{"tf":2.449489742783178},"84":{"tf":2.23606797749979},"9":{"tf":2.0},"91":{"tf":2.23606797749979},"94":{"tf":2.0},"95":{"tf":1.0}},"t":{"df":1,"docs":{"108":{"tf":2.23606797749979}}}}},"d":{"df":0,"docs":{},"i":{"df":0,"docs":{},"t":{"df":1,"docs":{"149":{"tf":1.0}}}},"u":{"c":{"df":1,"docs":{"14":{"tf":1.0}}},"df":0,"docs":{}}},"df":0,"docs":{},"i":{"df":0,"docs":{},"t":{"df":1,"docs":{"105":{"tf":1.0}}}},"n":{"df":0,"docs":{},"s":{"c":{"df":0,"docs":{},"r":{"df":0,"docs":{},"i":{"df":0,"docs":{},"p":{"df":0,"docs":{},"t":{"df":16,"docs":{"113":{"tf":1.0},"118":{"tf":1.7320508075688772},"147":{"tf":1.0},"24":{"tf":1.4142135623730951},"25":{"tf":1.4142135623730951},"26":{"tf":1.4142135623730951},"27":{"tf":1.4142135623730951},"28":{"tf":1.0},"31":{"tf":1.4142135623730951},"32":{"tf":2.0},"40":{"tf":2.23606797749979},"42":{"tf":1.0},"88":{"tf":1.0},"89":{"tf":1.4142135623730951},"94":{"tf":4.123105625617661},"95":{"tf":3.872983346207417}}}}}}},"df":0,"docs":{},"f":{"df":0,"docs":{},"o":{"df":0,"docs":{},"r":{"df":0,"docs":{},"m":{"df":7,"docs":{"123":{"tf":1.0},"3":{"tf":1.0},"4":{"tf":1.0},"69":{"tf":1.0},"74":{"tf":1.4142135623730951},"79":{"tf":1.4142135623730951},"99":{"tf":1.0}}}}}},"i":{"df":0,"docs":{},"t":{"df":20,"docs":{"105":{"tf":1.4142135623730951},"107":{"tf":2.0},"109":{"tf":2.23606797749979},"112":{"tf":1.0},"113":{"tf":1.0},"114":{"tf":1.7320508075688772},"125":{"tf":1.0},"146":{"tf":1.0},"149":{"tf":1.0},"49":{"tf":1.0},"50":{"tf":1.0},"53":{"tf":1.0},"55":{"tf":2.0},"56":{"tf":1.0},"58":{"tf":1.0},"62":{"tf":1.0},"66":{"tf":2.0},"70":{"tf":2.0},"84":{"tf":1.7320508075688772},"91":{"tf":1.4142135623730951}},"i":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"_":{"d":{"df":0,"docs":{},"e":{"df":0,"docs":{},"g":{"df":0,"docs":{},"r":{"df":0,"docs":{},"e":{"df":1,"docs":{"109":{"tf":1.4142135623730951}}}}}},"i":{"df":0,"docs":{},"v":{"df":0,"docs":{},"i":{"df":0,"docs":{},"s":{"df":0,"docs":{},"o":{"df":0,"docs":{},"r":{"df":1,"docs":{"114":{"tf":1.0}}}}}}}}},"df":0,"docs":{},"e":{"df":0,"docs":{},"x":{"df":0,"docs":{},"e":{"df":0,"docs":{},"m":{"df":0,"docs":{},"p":{"df":0,"docs":{},"t":{"df":2,"docs":{"109":{"tf":1.4142135623730951},"114":{"tf":1.0}}}}}}}},"o":{"df":0,"docs":{},"f":{"df":0,"docs":{},"f":{"df":0,"docs":{},"s":{"df":0,"docs":{},"e":{"df":0,"docs":{},"t":{"df":1,"docs":{"109":{"tf":1.4142135623730951}}}}}}}}},"df":0,"docs":{}}}}}},"l":{"a":{"df":0,"docs":{},"t":{"df":1,"docs":{"52":{"tf":1.0}}}},"df":0,"docs":{}},"p":{"a":{"df":0,"docs":{},"r":{"df":1,"docs":{"81":{"tf":1.0}}}},"df":0,"docs":{}}}},"v":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":0,"docs":{},"s":{"df":1,"docs":{"80":{"tf":1.0}}}}}}},"df":0,"docs":{},"e":{"a":{"df":0,"docs":{},"t":{"df":3,"docs":{"151":{"tf":1.7320508075688772},"57":{"tf":1.0},"68":{"tf":1.0}}}},"df":0,"docs":{},"e":{"'":{"df":1,"docs":{"72":{"tf":1.0}}},"df":6,"docs":{"101":{"tf":1.4142135623730951},"72":{"tf":1.4142135623730951},"76":{"tf":1.0},"80":{"tf":2.449489742783178},"91":{"tf":1.0},"92":{"tf":2.23606797749979}}}},"i":{"c":{"df":0,"docs":{},"k":{"df":8,"docs":{"115":{"tf":1.0},"123":{"tf":1.0},"130":{"tf":1.0},"14":{"tf":1.7320508075688772},"146":{"tf":1.0},"18":{"tf":1.0},"21":{"tf":1.4142135623730951},"98":{"tf":1.0}}}},"df":5,"docs":{"111":{"tf":1.0},"14":{"tf":1.0},"16":{"tf":1.0},"79":{"tf":1.0},"80":{"tf":1.0}},"v":{"df":0,"docs":{},"i":{"a":{"df":0,"docs":{},"l":{"df":2,"docs":{"14":{"tf":1.0},"146":{"tf":1.4142135623730951}}}},"df":0,"docs":{}}}},"u":{"df":0,"docs":{},"e":{"df":6,"docs":{"10":{"tf":1.0},"104":{"tf":1.0},"14":{"tf":1.4142135623730951},"146":{"tf":1.0},"21":{"tf":1.0},"84":{"tf":1.0}}}}},"u":{"df":0,"docs":{},"p":{"df":0,"docs":{},"l":{"df":2,"docs":{"14":{"tf":1.7320508075688772},"7":{"tf":1.4142135623730951}}}},"r":{"df":0,"docs":{},"n":{"df":4,"docs":{"14":{"tf":1.0},"65":{"tf":1.0},"70":{"tf":1.0},"79":{"tf":1.0}}}}},"w":{"df":0,"docs":{},"i":{"d":{"d":{"df":0,"docs":{},"l":{"df":1,"docs":{"3":{"tf":1.0}}}},"df":0,"docs":{}},"df":0,"docs":{}},"o":{"df":35,"docs":{"101":{"tf":1.0},"104":{"tf":1.0},"105":{"tf":1.0},"107":{"tf":1.0},"108":{"tf":1.0},"109":{"tf":1.4142135623730951},"116":{"tf":1.0},"121":{"tf":1.0},"123":{"tf":1.0},"128":{"tf":1.0},"13":{"tf":2.23606797749979},"133":{"tf":1.0},"14":{"tf":3.0},"142":{"tf":1.0},"145":{"tf":1.0},"146":{"tf":2.23606797749979},"16":{"tf":1.0},"32":{"tf":1.0},"36":{"tf":1.0},"38":{"tf":1.4142135623730951},"40":{"tf":1.0},"44":{"tf":1.4142135623730951},"48":{"tf":1.0},"49":{"tf":1.7320508075688772},"52":{"tf":2.0},"59":{"tf":1.0},"68":{"tf":1.4142135623730951},"71":{"tf":1.0},"73":{"tf":1.0},"74":{"tf":1.0},"75":{"tf":1.0},"8":{"tf":1.0},"84":{"tf":1.4142135623730951},"9":{"tf":1.4142135623730951},"90":{"tf":1.0}}}},"y":{"df":0,"docs":{},"p":{"df":0,"docs":{},"e":{"df":3,"docs":{"10":{"tf":1.0},"52":{"tf":1.0},"70":{"tf":1.0}}},"i":{"c":{"df":1,"docs":{"106":{"tf":1.4142135623730951}}},"df":0,"docs":{}}}},"ˉ":{"=":{"df":0,"docs":{},"t":{"df":1,"docs":{"28":{"tf":1.0}}}},"df":0,"docs":{}},"∈":{"df":0,"docs":{},"f":{"2":{"df":0,"docs":{},"n":{"df":0,"docs":{},"×":{"3":{"3":{"df":1,"docs":{"142":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}}},"u":{"=":{"6":{"df":1,"docs":{"9":{"tf":1.0}}},"df":0,"docs":{}},"df":2,"docs":{"11":{"tf":1.0},"9":{"tf":1.4142135623730951}},"l":{"df":0,"docs":{},"t":{"df":0,"docs":{},"i":{"df":0,"docs":{},"m":{"df":1,"docs":{"50":{"tf":1.0}}}}}},"n":{"b":{"a":{"df":0,"docs":{},"l":{"df":1,"docs":{"146":{"tf":1.4142135623730951}}}},"df":0,"docs":{}},"c":{"df":0,"docs":{},"h":{"a":{"df":0,"docs":{},"n":{"df":0,"docs":{},"g":{"df":1,"docs":{"20":{"tf":1.0}}}}},"df":0,"docs":{}}},"d":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":3,"docs":{"111":{"tf":1.0},"114":{"tf":1.0},"64":{"tf":1.0}},"l":{"df":0,"docs":{},"i":{"df":2,"docs":{"40":{"tf":1.0},"90":{"tf":1.0}}}},"s":{"df":0,"docs":{},"t":{"a":{"df":0,"docs":{},"n":{"d":{"df":7,"docs":{"111":{"tf":1.0},"14":{"tf":1.0},"151":{"tf":1.0},"46":{"tf":1.0},"50":{"tf":1.0},"68":{"tf":1.0},"8":{"tf":1.0}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}}},"df":0,"docs":{},"i":{"df":0,"docs":{},"q":{"df":0,"docs":{},"u":{"df":4,"docs":{"14":{"tf":1.4142135623730951},"20":{"tf":1.0},"73":{"tf":1.7320508075688772},"75":{"tf":1.0}}}},"t":{"df":0,"docs":{},"i":{"df":20,"docs":{"112":{"tf":1.0},"114":{"tf":1.4142135623730951},"123":{"tf":2.23606797749979},"124":{"tf":3.3166247903554},"125":{"tf":2.23606797749979},"14":{"tf":3.0},"16":{"tf":1.0},"23":{"tf":1.0},"34":{"tf":1.0},"35":{"tf":1.0},"36":{"tf":1.0},"38":{"tf":1.0},"5":{"tf":1.0},"52":{"tf":1.7320508075688772},"62":{"tf":1.0},"68":{"tf":2.8284271247461903},"73":{"tf":1.0},"75":{"tf":1.0},"82":{"tf":1.4142135623730951},"91":{"tf":1.0}}}},"v":{"a":{"df":0,"docs":{},"r":{"df":0,"docs":{},"i":{"df":4,"docs":{"71":{"tf":1.0},"75":{"tf":1.0},"83":{"tf":1.0},"84":{"tf":1.4142135623730951}}}}},"df":0,"docs":{}}},"l":{"df":0,"docs":{},"i":{"df":0,"docs":{},"k":{"df":1,"docs":{"79":{"tf":1.0}}}}},"o":{"df":0,"docs":{},"r":{"d":{"df":1,"docs":{"146":{"tf":1.0}}},"df":0,"docs":{}}},"p":{"a":{"c":{"df":0,"docs":{},"k":{"df":1,"docs":{"142":{"tf":1.0}}}},"df":0,"docs":{}},"df":0,"docs":{},"r":{"df":0,"docs":{},"e":{"d":{"df":0,"docs":{},"i":{"c":{"df":0,"docs":{},"t":{"df":2,"docs":{"56":{"tf":1.0},"79":{"tf":1.0}}}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"t":{"df":0,"docs":{},"i":{"df":0,"docs":{},"l":{"df":7,"docs":{"121":{"tf":1.0},"130":{"tf":1.0},"134":{"tf":1.0},"135":{"tf":1.0},"142":{"tf":1.0},"146":{"tf":1.0},"94":{"tf":1.0}}}},"r":{"df":0,"docs":{},"u":{"df":0,"docs":{},"s":{"df":0,"docs":{},"t":{"df":2,"docs":{"4":{"tf":1.0},"7":{"tf":1.0}}}}}}},"u":{"df":0,"docs":{},"s":{"df":4,"docs":{"130":{"tf":2.23606797749979},"134":{"tf":2.23606797749979},"139":{"tf":1.0},"146":{"tf":1.7320508075688772}}}}},"p":{"d":{"a":{"df":0,"docs":{},"t":{"df":5,"docs":{"11":{"tf":1.0},"13":{"tf":1.4142135623730951},"136":{"tf":1.4142135623730951},"146":{"tf":1.4142135623730951},"40":{"tf":1.4142135623730951}}}},"df":0,"docs":{}},"df":8,"docs":{"10":{"tf":1.0},"12":{"tf":1.0},"121":{"tf":1.0},"13":{"tf":1.4142135623730951},"130":{"tf":1.0},"16":{"tf":1.4142135623730951},"71":{"tf":1.0},"84":{"tf":1.0}},"p":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":1,"docs":{"73":{"tf":1.0}}}}}},"s":{"a":{"df":0,"docs":{},"g":{"df":1,"docs":{"35":{"tf":1.0}}}},"df":65,"docs":{"101":{"tf":1.4142135623730951},"102":{"tf":1.4142135623730951},"104":{"tf":1.0},"106":{"tf":1.0},"107":{"tf":1.0},"108":{"tf":1.0},"109":{"tf":2.0},"110":{"tf":1.0},"111":{"tf":1.4142135623730951},"113":{"tf":1.0},"114":{"tf":1.7320508075688772},"116":{"tf":1.0},"118":{"tf":2.0},"119":{"tf":1.0},"121":{"tf":1.0},"123":{"tf":2.449489742783178},"124":{"tf":1.4142135623730951},"125":{"tf":2.0},"128":{"tf":1.0},"13":{"tf":1.0},"130":{"tf":1.0},"134":{"tf":1.4142135623730951},"136":{"tf":1.4142135623730951},"14":{"tf":2.8284271247461903},"142":{"tf":1.0},"144":{"tf":1.4142135623730951},"146":{"tf":4.0},"147":{"tf":1.0},"149":{"tf":1.4142135623730951},"150":{"tf":1.0},"151":{"tf":2.23606797749979},"19":{"tf":1.0},"21":{"tf":1.0},"33":{"tf":1.0},"34":{"tf":1.4142135623730951},"35":{"tf":1.0},"36":{"tf":1.0},"38":{"tf":1.0},"4":{"tf":1.0},"40":{"tf":1.7320508075688772},"41":{"tf":1.0},"44":{"tf":1.4142135623730951},"45":{"tf":2.0},"48":{"tf":1.4142135623730951},"49":{"tf":1.4142135623730951},"5":{"tf":1.4142135623730951},"50":{"tf":2.23606797749979},"57":{"tf":1.0},"59":{"tf":1.0},"60":{"tf":1.0},"63":{"tf":1.0},"68":{"tf":1.0},"7":{"tf":2.23606797749979},"71":{"tf":1.0},"74":{"tf":1.7320508075688772},"75":{"tf":1.0},"8":{"tf":1.7320508075688772},"87":{"tf":1.0},"89":{"tf":1.0},"9":{"tf":1.0},"91":{"tf":1.4142135623730951},"92":{"tf":1.0},"94":{"tf":1.0},"95":{"tf":1.4142135623730951},"97":{"tf":1.0}},"e":{"df":0,"docs":{},"r":{"df":1,"docs":{"50":{"tf":1.0}}}},"i":{"df":0,"docs":{},"z":{"df":1,"docs":{"35":{"tf":1.0}}}},"u":{"a":{"df":0,"docs":{},"l":{"df":9,"docs":{"123":{"tf":1.0},"133":{"tf":1.0},"14":{"tf":1.0},"151":{"tf":1.0},"5":{"tf":1.0},"53":{"tf":1.0},"70":{"tf":1.0},"74":{"tf":1.0},"75":{"tf":1.0}}}},"df":0,"docs":{}}},"t":{"df":0,"docs":{},"i":{"df":0,"docs":{},"l":{"df":1,"docs":{"99":{"tf":1.0}}}}}},"v":{"=":{"9":{"df":1,"docs":{"9":{"tf":1.0}}},"df":0,"docs":{}},"a":{"df":0,"docs":{},"l":{"df":0,"docs":{},"i":{"d":{"df":23,"docs":{"10":{"tf":1.0},"101":{"tf":1.0},"105":{"tf":1.0},"11":{"tf":1.4142135623730951},"125":{"tf":1.0},"13":{"tf":1.0},"14":{"tf":1.0},"146":{"tf":1.4142135623730951},"16":{"tf":1.7320508075688772},"21":{"tf":1.0},"33":{"tf":1.0},"34":{"tf":1.0},"48":{"tf":1.0},"49":{"tf":1.0},"50":{"tf":1.0},"52":{"tf":1.4142135623730951},"69":{"tf":1.4142135623730951},"70":{"tf":1.0},"77":{"tf":1.0},"80":{"tf":1.0},"84":{"tf":1.7320508075688772},"86":{"tf":1.0},"9":{"tf":1.4142135623730951}}},"df":0,"docs":{}},"u":{"a":{"b":{"df":0,"docs":{},"l":{"df":1,"docs":{"103":{"tf":1.0}}}},"df":0,"docs":{}},"df":58,"docs":{"101":{"tf":2.23606797749979},"102":{"tf":1.7320508075688772},"107":{"tf":1.7320508075688772},"108":{"tf":1.4142135623730951},"114":{"tf":1.4142135623730951},"118":{"tf":2.0},"119":{"tf":1.0},"128":{"tf":1.0},"13":{"tf":2.23606797749979},"130":{"tf":3.0},"131":{"tf":1.7320508075688772},"133":{"tf":3.3166247903554},"134":{"tf":1.7320508075688772},"135":{"tf":2.0},"136":{"tf":1.7320508075688772},"137":{"tf":1.0},"139":{"tf":1.0},"14":{"tf":3.3166247903554},"142":{"tf":2.449489742783178},"146":{"tf":5.196152422706632},"147":{"tf":2.23606797749979},"151":{"tf":2.449489742783178},"16":{"tf":1.4142135623730951},"19":{"tf":1.0},"20":{"tf":2.23606797749979},"21":{"tf":2.8284271247461903},"22":{"tf":1.0},"25":{"tf":1.4142135623730951},"3":{"tf":1.0},"32":{"tf":2.0},"33":{"tf":1.4142135623730951},"34":{"tf":1.7320508075688772},"35":{"tf":3.0},"36":{"tf":1.7320508075688772},"42":{"tf":1.0},"49":{"tf":1.4142135623730951},"5":{"tf":1.0},"52":{"tf":1.4142135623730951},"54":{"tf":1.0},"58":{"tf":1.0},"60":{"tf":1.0},"66":{"tf":1.0},"69":{"tf":1.0},"7":{"tf":2.23606797749979},"70":{"tf":1.4142135623730951},"72":{"tf":1.7320508075688772},"73":{"tf":1.0},"75":{"tf":2.0},"77":{"tf":1.4142135623730951},"79":{"tf":1.0},"80":{"tf":1.7320508075688772},"83":{"tf":1.0},"87":{"tf":1.0},"89":{"tf":1.0},"9":{"tf":2.23606797749979},"91":{"tf":2.23606797749979},"94":{"tf":2.0},"95":{"tf":2.8284271247461903}}}},"n":{"df":0,"docs":{},"i":{"df":0,"docs":{},"s":{"df":0,"docs":{},"h":{"df":5,"docs":{"123":{"tf":2.6457513110645907},"125":{"tf":1.7320508075688772},"20":{"tf":1.0},"55":{"tf":1.0},"84":{"tf":1.0}}}}}},"r":{"df":0,"docs":{},"i":{"a":{"b":{"df":0,"docs":{},"l":{"df":11,"docs":{"11":{"tf":1.0},"13":{"tf":1.4142135623730951},"14":{"tf":1.0},"151":{"tf":1.0},"36":{"tf":1.0},"44":{"tf":1.7320508075688772},"45":{"tf":1.7320508075688772},"5":{"tf":1.0},"70":{"tf":1.7320508075688772},"9":{"tf":1.4142135623730951},"95":{"tf":1.0}}}},"df":0,"docs":{},"n":{"df":0,"docs":{},"t":{"df":5,"docs":{"74":{"tf":1.7320508075688772},"77":{"tf":1.0},"80":{"tf":1.0},"96":{"tf":1.0},"97":{"tf":2.0}}}}},"df":0,"docs":{}}}},"df":13,"docs":{"11":{"tf":1.7320508075688772},"12":{"tf":1.0},"13":{"tf":1.4142135623730951},"14":{"tf":2.23606797749979},"141":{"tf":1.0},"142":{"tf":2.0},"146":{"tf":2.449489742783178},"147":{"tf":1.7320508075688772},"16":{"tf":1.4142135623730951},"34":{"tf":1.4142135623730951},"36":{"tf":1.7320508075688772},"8":{"tf":1.0},"9":{"tf":2.0}},"e":{"c":{"!":{"[":{"0":{"df":1,"docs":{"109":{"tf":1.0}}},"1":{"df":2,"docs":{"108":{"tf":1.0},"109":{"tf":1.0}}},"2":{"df":1,"docs":{"109":{"tf":1.0}}},"df":0,"docs":{},"t":{"df":0,"docs":{},"h":{"df":0,"docs":{},"i":{"df":0,"docs":{},"r":{"d":{"_":{"df":0,"docs":{},"r":{"df":0,"docs":{},"o":{"df":0,"docs":{},"w":{"[":{"0":{"df":2,"docs":{"107":{"tf":1.0},"114":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"x":{".":{"c":{"df":0,"docs":{},"l":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"df":1,"docs":{"35":{"tf":1.4142135623730951}}}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"<":{"df":0,"docs":{},"f":{"df":0,"docs":{},"i":{"df":0,"docs":{},"e":{"df":0,"docs":{},"l":{"d":{"df":0,"docs":{},"e":{"df":0,"docs":{},"l":{"df":0,"docs":{},"e":{"df":0,"docs":{},"m":{"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"df":0,"docs":{},"t":{"<":{"df":0,"docs":{},"f":{"df":1,"docs":{"35":{"tf":2.6457513110645907}}},"s":{"df":0,"docs":{},"e":{"df":0,"docs":{},"l":{"df":0,"docs":{},"f":{":":{":":{"df":0,"docs":{},"f":{"df":0,"docs":{},"i":{"df":0,"docs":{},"e":{"df":0,"docs":{},"l":{"d":{"df":2,"docs":{"107":{"tf":1.0},"114":{"tf":1.0}}},"df":0,"docs":{}}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}}}},"df":0,"docs":{}}}}}}}}},"df":0,"docs":{}}}}}},"df":3,"docs":{"108":{"tf":1.0},"109":{"tf":1.0},"116":{"tf":1.0}},"t":{"df":0,"docs":{},"o":{"df":0,"docs":{},"r":{"'":{"df":1,"docs":{"72":{"tf":1.0}}},"df":18,"docs":{"101":{"tf":1.0},"107":{"tf":1.0},"141":{"tf":1.0},"142":{"tf":1.7320508075688772},"147":{"tf":2.449489742783178},"71":{"tf":1.0},"72":{"tf":1.7320508075688772},"73":{"tf":3.1622776601683795},"74":{"tf":2.449489742783178},"75":{"tf":1.0},"76":{"tf":1.4142135623730951},"78":{"tf":1.4142135623730951},"79":{"tf":2.449489742783178},"80":{"tf":2.23606797749979},"82":{"tf":1.0},"90":{"tf":1.4142135623730951},"91":{"tf":1.0},"92":{"tf":2.449489742783178}}}}}},"df":0,"docs":{},"r":{"df":0,"docs":{},"i":{"df":10,"docs":{"118":{"tf":1.0},"123":{"tf":1.0},"14":{"tf":1.7320508075688772},"20":{"tf":1.0},"49":{"tf":1.0},"50":{"tf":1.0},"69":{"tf":1.0},"73":{"tf":1.0},"79":{"tf":1.0},"98":{"tf":1.0}},"f":{"df":3,"docs":{"30":{"tf":1.0},"4":{"tf":1.0},"7":{"tf":1.4142135623730951}},"i":{"c":{"a":{"df":0,"docs":{},"t":{"df":0,"docs":{},"i":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"df":0,"docs":{},"k":{"df":0,"docs":{},"e":{"df":0,"docs":{},"y":{"<":{"df":0,"docs":{},"g":{"1":{"df":0,"docs":{},"p":{"df":0,"docs":{},"o":{"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"df":0,"docs":{},"t":{"df":1,"docs":{"35":{"tf":1.0}}}}}}}},"df":0,"docs":{}}},"df":1,"docs":{"35":{"tf":1.0}}}}}}}}}},"df":0,"docs":{}},"df":59,"docs":{"102":{"tf":2.0},"104":{"tf":2.0},"109":{"tf":1.0},"110":{"tf":1.4142135623730951},"111":{"tf":1.4142135623730951},"112":{"tf":1.0},"113":{"tf":1.7320508075688772},"115":{"tf":1.4142135623730951},"117":{"tf":1.4142135623730951},"118":{"tf":2.23606797749979},"119":{"tf":1.4142135623730951},"127":{"tf":1.0},"13":{"tf":1.4142135623730951},"137":{"tf":1.0},"144":{"tf":1.0},"145":{"tf":1.0},"146":{"tf":2.449489742783178},"147":{"tf":1.4142135623730951},"151":{"tf":1.0},"20":{"tf":1.0},"21":{"tf":3.3166247903554},"32":{"tf":1.0},"33":{"tf":1.7320508075688772},"34":{"tf":1.0},"35":{"tf":2.23606797749979},"4":{"tf":1.0},"44":{"tf":2.0},"48":{"tf":1.0},"50":{"tf":1.0},"53":{"tf":1.0},"55":{"tf":1.0},"56":{"tf":1.0},"57":{"tf":1.7320508075688772},"58":{"tf":1.0},"59":{"tf":1.4142135623730951},"60":{"tf":1.4142135623730951},"61":{"tf":1.0},"62":{"tf":1.7320508075688772},"63":{"tf":1.4142135623730951},"69":{"tf":1.7320508075688772},"7":{"tf":2.0},"70":{"tf":1.4142135623730951},"71":{"tf":1.0},"72":{"tf":1.7320508075688772},"73":{"tf":1.4142135623730951},"74":{"tf":1.7320508075688772},"75":{"tf":2.449489742783178},"77":{"tf":2.23606797749979},"79":{"tf":1.4142135623730951},"8":{"tf":1.0},"80":{"tf":2.6457513110645907},"82":{"tf":1.4142135623730951},"83":{"tf":1.7320508075688772},"84":{"tf":1.4142135623730951},"85":{"tf":1.7320508075688772},"86":{"tf":1.0},"91":{"tf":1.7320508075688772},"94":{"tf":1.0},"95":{"tf":2.8284271247461903}},"e":{"df":0,"docs":{},"r":{"'":{"df":2,"docs":{"35":{"tf":1.0},"95":{"tf":1.0}}},":":{":":{"df":0,"docs":{},"n":{"df":0,"docs":{},"e":{"df":0,"docs":{},"w":{"(":{"df":0,"docs":{},"k":{"df":0,"docs":{},"z":{"df":0,"docs":{},"g":{"df":2,"docs":{"35":{"tf":1.4142135623730951},"44":{"tf":1.0}}}}}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"<":{"df":0,"docs":{},"f":{"df":1,"docs":{"35":{"tf":1.0}}}},"df":0,"docs":{}}}},"y":{"(":{"(":{"df":0,"docs":{},"υ":{"df":0,"docs":{},"s":{"2":{"df":0,"docs":{},"k":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"x":{")":{",":{"df":0,"docs":{},"p":{"df":0,"docs":{},"k":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"p":{"df":0,"docs":{},"k":{"df":1,"docs":{"102":{"tf":1.0}}}}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}},"π":{"df":0,"docs":{},"k":{"df":0,"docs":{},"υ":{"df":0,"docs":{},"s":{"2":{"df":0,"docs":{},"k":{"df":0,"docs":{},"​":{"df":0,"docs":{},"​":{")":{",":{"df":0,"docs":{},"p":{"df":0,"docs":{},"k":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"p":{"df":0,"docs":{},"k":{"df":1,"docs":{"102":{"tf":1.0}}}}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}}},"−":{"df":0,"docs":{},"υ":{"df":0,"docs":{},"s":{"2":{"df":0,"docs":{},"k":{"df":0,"docs":{},"​":{"df":0,"docs":{},"​":{")":{",":{"df":0,"docs":{},"p":{"df":0,"docs":{},"k":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"p":{"df":0,"docs":{},"k":{"df":1,"docs":{"95":{"tf":1.0}}}}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}}}}}}},"df":0,"docs":{}}}},"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"η":{"1":{"df":0,"docs":{},"υ":{"df":0,"docs":{},"s":{"df":0,"docs":{},"​":{"df":0,"docs":{},"​":{")":{",":{"df":0,"docs":{},"h":{"1":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"h":{"1":{"df":1,"docs":{"95":{"tf":1.0}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}}}},"2":{"df":0,"docs":{},"υ":{"df":0,"docs":{},"s":{"df":0,"docs":{},"​":{"df":0,"docs":{},"​":{")":{",":{"df":0,"docs":{},"h":{"2":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"h":{"2":{"df":1,"docs":{"95":{"tf":1.0}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}}}},"df":0,"docs":{}},"τ":{"df":0,"docs":{},"j":{"df":0,"docs":{},"υ":{"df":0,"docs":{},"s":{"df":0,"docs":{},"​":{"df":0,"docs":{},"​":{")":{",":{"df":0,"docs":{},"t":{"df":0,"docs":{},"j":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"t":{"df":0,"docs":{},"j":{"df":1,"docs":{"95":{"tf":1.0}}}}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}}}}}},"df":0,"docs":{}}}},"−":{"df":0,"docs":{},"υ":{"df":0,"docs":{},"s":{"2":{"df":0,"docs":{},"k":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"π":{"df":0,"docs":{},"k":{"df":0,"docs":{},"−":{"df":0,"docs":{},"υ":{"df":0,"docs":{},"s":{"2":{"df":0,"docs":{},"k":{"df":0,"docs":{},"​":{"df":0,"docs":{},"​":{")":{",":{"df":0,"docs":{},"p":{"df":0,"docs":{},"k":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"p":{"df":0,"docs":{},"k":{"df":1,"docs":{"95":{"tf":1.0}}}}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}}}}}}},"df":0,"docs":{}}}},"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"η":{"1":{"df":0,"docs":{},"υ":{"df":0,"docs":{},"s":{"df":0,"docs":{},"​":{"df":0,"docs":{},"​":{")":{",":{"df":0,"docs":{},"h":{"1":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"h":{"1":{"df":1,"docs":{"95":{"tf":1.0}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}}}},"2":{"df":0,"docs":{},"υ":{"df":0,"docs":{},"s":{"df":0,"docs":{},"​":{"df":0,"docs":{},"​":{")":{",":{"df":0,"docs":{},"h":{"2":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"h":{"2":{"df":1,"docs":{"95":{"tf":1.0}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}}}},"df":0,"docs":{}},"τ":{"df":0,"docs":{},"j":{"df":0,"docs":{},"υ":{"df":0,"docs":{},"s":{"df":0,"docs":{},"​":{"df":0,"docs":{},"​":{")":{",":{"df":0,"docs":{},"t":{"df":0,"docs":{},"j":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"t":{"df":0,"docs":{},"j":{"df":1,"docs":{"95":{"tf":1.0}}}}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}}}}}},"df":0,"docs":{}}}}}},"[":{"df":0,"docs":{},"f":{"]":{"1":{"df":1,"docs":{"19":{"tf":1.0}}},"df":0,"docs":{}},"b":{"a":{"df":0,"docs":{},"t":{"c":{"df":0,"docs":{},"h":{"df":0,"docs":{},"​":{"]":{"1":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"π":{"b":{"a":{"df":0,"docs":{},"t":{"c":{"df":0,"docs":{},"h":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"ζ":{",":{"df":0,"docs":{},"f":{"b":{"a":{"df":0,"docs":{},"t":{"c":{"df":0,"docs":{},"h":{"df":1,"docs":{"33":{"tf":1.0}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"z":{"]":{"1":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"π":{"df":0,"docs":{},"s":{"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"df":0,"docs":{},"g":{"df":0,"docs":{},"l":{"df":0,"docs":{},"e":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"ζ":{"df":0,"docs":{},"ω":{",":{"df":0,"docs":{},"z":{"df":1,"docs":{"33":{"tf":1.0}}}},"df":0,"docs":{}}}},"df":0,"docs":{}}}}}}}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"a":{"b":{"df":0,"docs":{},"i":{",":{"df":0,"docs":{},"y":{",":{"df":0,"docs":{},"r":{",":{"df":1,"docs":{"92":{"tf":1.4142135623730951}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{},"i":{",":{"df":0,"docs":{},"y":{",":{"df":0,"docs":{},"r":{",":{"df":1,"docs":{"92":{"tf":1.4142135623730951}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"df":0,"docs":{},"g":{"_":{"df":0,"docs":{},"k":{"df":0,"docs":{},"e":{"df":0,"docs":{},"y":{"df":1,"docs":{"35":{"tf":2.449489742783178}}}}}},"df":0,"docs":{}}}}}}},"s":{"df":0,"docs":{},"i":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"df":1,"docs":{"13":{"tf":1.0}}}}}}}},"i":{",":{"df":0,"docs":{},"j":{"df":0,"docs":{},"​":{"=":{"df":0,"docs":{},"v":{"df":0,"docs":{},"k":{",":{"df":0,"docs":{},"l":{"df":4,"docs":{"11":{"tf":1.0},"13":{"tf":1.0},"14":{"tf":1.0},"16":{"tf":1.0}}}},"df":0,"docs":{}}}},"df":0,"docs":{}}}},"a":{"df":1,"docs":{"3":{"tf":1.0}}},"df":0,"docs":{},"e":{"df":0,"docs":{},"w":{"df":1,"docs":{"95":{"tf":1.0}}}},"o":{"df":0,"docs":{},"l":{"a":{"df":0,"docs":{},"t":{"df":1,"docs":{"20":{"tf":1.0}}}},"df":0,"docs":{}}},"r":{"df":0,"docs":{},"t":{"df":0,"docs":{},"u":{"a":{"df":0,"docs":{},"l":{"df":11,"docs":{"128":{"tf":2.6457513110645907},"130":{"tf":1.0},"131":{"tf":1.4142135623730951},"133":{"tf":2.0},"134":{"tf":1.0},"136":{"tf":1.0},"139":{"tf":1.0},"148":{"tf":1.0},"149":{"tf":2.23606797749979},"150":{"tf":2.0},"50":{"tf":1.4142135623730951}}}},"df":0,"docs":{}}}},"s":{"df":0,"docs":{},"u":{"a":{"df":0,"docs":{},"l":{"df":3,"docs":{"128":{"tf":1.0},"133":{"tf":1.0},"137":{"tf":1.0}}}},"df":0,"docs":{}}}},"k":{"df":1,"docs":{"44":{"tf":1.7320508075688772}}},"m":{"'":{"df":2,"docs":{"144":{"tf":1.0},"146":{"tf":1.0}}},"df":7,"docs":{"127":{"tf":1.4142135623730951},"133":{"tf":1.0},"144":{"tf":1.7320508075688772},"146":{"tf":1.0},"149":{"tf":1.0},"151":{"tf":2.8284271247461903},"50":{"tf":1.7320508075688772}}},"u":{"df":0,"docs":{},"l":{"df":0,"docs":{},"n":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":1,"docs":{"42":{"tf":1.0}}}}}}}},"w":{"2":{"=":{"df":0,"docs":{},"g":{"df":1,"docs":{"125":{"tf":1.0}}}},"df":1,"docs":{"124":{"tf":1.0}},"k":{"df":1,"docs":{"124":{"tf":1.0}}}},"=":{"8":{"df":1,"docs":{"9":{"tf":1.0}}},"df":0,"docs":{}},"_":{"df":0,"docs":{},"z":{"df":0,"docs":{},"e":{"df":0,"docs":{},"t":{"a":{"_":{"1":{"df":1,"docs":{"35":{"tf":1.0}}},"df":0,"docs":{},"o":{"df":0,"docs":{},"m":{"df":0,"docs":{},"e":{"df":0,"docs":{},"g":{"a":{"_":{"1":{"df":1,"docs":{"35":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"a":{"df":0,"docs":{},"l":{"df":0,"docs":{},"k":{"df":0,"docs":{},"t":{"df":0,"docs":{},"h":{"df":0,"docs":{},"r":{"df":0,"docs":{},"o":{"df":0,"docs":{},"u":{"df":0,"docs":{},"g":{"df":0,"docs":{},"h":{"df":2,"docs":{"51":{"tf":1.0},"64":{"tf":1.0}}}}}}}}}}},"n":{"df":0,"docs":{},"t":{"df":10,"docs":{"103":{"tf":1.0},"123":{"tf":1.4142135623730951},"125":{"tf":1.0},"14":{"tf":1.4142135623730951},"150":{"tf":1.4142135623730951},"151":{"tf":1.0},"16":{"tf":1.0},"44":{"tf":1.0},"5":{"tf":1.0},"50":{"tf":1.0}}}},"y":{"df":22,"docs":{"10":{"tf":1.0},"101":{"tf":1.4142135623730951},"107":{"tf":1.0},"12":{"tf":1.0},"130":{"tf":1.0},"131":{"tf":1.0},"14":{"tf":1.4142135623730951},"146":{"tf":1.0},"149":{"tf":1.0},"151":{"tf":1.0},"31":{"tf":1.0},"50":{"tf":1.0},"52":{"tf":1.0},"54":{"tf":1.0},"66":{"tf":1.4142135623730951},"67":{"tf":1.0},"7":{"tf":1.0},"70":{"tf":1.0},"79":{"tf":1.0},"83":{"tf":1.0},"84":{"tf":1.0},"9":{"tf":1.0}}}},"df":3,"docs":{"11":{"tf":1.0},"124":{"tf":1.4142135623730951},"9":{"tf":1.0}},"e":{"'":{"d":{"df":1,"docs":{"125":{"tf":1.0}}},"df":0,"docs":{},"l":{"df":0,"docs":{},"l":{"df":9,"docs":{"10":{"tf":1.0},"19":{"tf":1.0},"43":{"tf":1.0},"51":{"tf":1.0},"53":{"tf":1.0},"64":{"tf":1.0},"7":{"tf":1.4142135623730951},"80":{"tf":1.0},"9":{"tf":1.7320508075688772}}}},"r":{"df":1,"docs":{"50":{"tf":1.0}}},"v":{"df":1,"docs":{"105":{"tf":1.0}}}},"a":{"df":0,"docs":{},"k":{"df":1,"docs":{"42":{"tf":1.4142135623730951}}}},"df":0,"docs":{},"l":{"df":0,"docs":{},"l":{"df":6,"docs":{"113":{"tf":1.0},"146":{"tf":1.4142135623730951},"34":{"tf":1.0},"36":{"tf":1.0},"4":{"tf":1.0},"98":{"tf":1.0}}}},"r":{"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"'":{"df":0,"docs":{},"t":{"df":2,"docs":{"151":{"tf":1.0},"3":{"tf":1.0}}}},"df":0,"docs":{}}}}},"h":{"a":{"df":0,"docs":{},"t":{"'":{"df":2,"docs":{"15":{"tf":1.0},"69":{"tf":1.0}}},"df":0,"docs":{}}},"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"df":0,"docs":{},"e":{"df":0,"docs":{},"v":{"df":2,"docs":{"118":{"tf":1.0},"123":{"tf":1.0}}}}},"t":{"df":0,"docs":{},"h":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":3,"docs":{"14":{"tf":1.7320508075688772},"84":{"tf":1.0},"92":{"tf":1.4142135623730951}}}}}}},"i":{"df":0,"docs":{},"t":{"df":0,"docs":{},"e":{"df":0,"docs":{},"p":{"a":{"df":0,"docs":{},"p":{"df":5,"docs":{"130":{"tf":1.0},"131":{"tf":1.0},"138":{"tf":1.0},"139":{"tf":1.0},"146":{"tf":1.0}}}},"df":0,"docs":{}}}}},"o":{"df":0,"docs":{},"l":{"df":0,"docs":{},"e":{"df":4,"docs":{"0":{"tf":1.0},"16":{"tf":1.0},"21":{"tf":1.0},"80":{"tf":1.0}}}},"s":{"df":0,"docs":{},"e":{"df":5,"docs":{"101":{"tf":1.0},"114":{"tf":1.0},"123":{"tf":1.0},"149":{"tf":1.0},"54":{"tf":1.0}}}}}},"i":{"df":0,"docs":{},"k":{"df":0,"docs":{},"i":{"df":0,"docs":{},"p":{"df":0,"docs":{},"e":{"d":{"df":0,"docs":{},"i":{"a":{"df":1,"docs":{"101":{"tf":1.0}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}},"n":{"df":0,"docs":{},"t":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":0,"docs":{},"f":{"df":0,"docs":{},"e":{"df":0,"docs":{},"l":{"df":1,"docs":{"103":{"tf":1.0}}}}}}}}},"r":{"df":0,"docs":{},"e":{"df":2,"docs":{"11":{"tf":1.0},"35":{"tf":1.7320508075688772}}}},"s":{"df":0,"docs":{},"h":{"df":2,"docs":{"20":{"tf":1.0},"80":{"tf":1.0}}}},"t":{"df":2,"docs":{"35":{"tf":2.0},"44":{"tf":1.4142135623730951}},"h":{"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"df":1,"docs":{"4":{"tf":1.0}}}},"o":{"df":0,"docs":{},"u":{"df":0,"docs":{},"t":{"df":5,"docs":{"103":{"tf":1.0},"125":{"tf":1.0},"146":{"tf":1.7320508075688772},"19":{"tf":1.0},"7":{"tf":1.0}}}}}},"n":{"df":0,"docs":{},"e":{"df":0,"docs":{},"s":{"df":0,"docs":{},"s":{":":{":":{"df":0,"docs":{},"n":{"df":0,"docs":{},"e":{"df":0,"docs":{},"w":{"(":{"a":{"df":0,"docs":{},"s":{"df":0,"docs":{},"s":{"df":0,"docs":{},"i":{"df":0,"docs":{},"g":{"df":0,"docs":{},"n":{"df":1,"docs":{"44":{"tf":1.0}}}}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"<":{"df":0,"docs":{},"f":{"df":1,"docs":{"35":{"tf":1.0}}}},"df":0,"docs":{}}}}}}},"o":{"df":0,"docs":{},"n":{"'":{"df":0,"docs":{},"t":{"df":9,"docs":{"109":{"tf":1.0},"118":{"tf":1.0},"146":{"tf":1.4142135623730951},"149":{"tf":1.0},"151":{"tf":1.0},"67":{"tf":1.0},"72":{"tf":1.0},"83":{"tf":1.0},"9":{"tf":1.0}}}},"d":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":1,"docs":{"16":{"tf":1.0}}}}},"df":0,"docs":{}},"r":{"d":{"df":2,"docs":{"133":{"tf":1.4142135623730951},"146":{"tf":1.0}}},"df":0,"docs":{},"k":{"df":21,"docs":{"0":{"tf":1.0},"111":{"tf":1.0},"119":{"tf":1.0},"125":{"tf":1.0},"14":{"tf":2.23606797749979},"146":{"tf":1.4142135623730951},"149":{"tf":1.0},"151":{"tf":1.4142135623730951},"16":{"tf":1.0},"19":{"tf":1.0},"20":{"tf":1.0},"21":{"tf":1.0},"4":{"tf":1.0},"46":{"tf":1.0},"50":{"tf":1.0},"52":{"tf":1.0},"56":{"tf":1.0},"69":{"tf":1.0},"73":{"tf":1.0},"75":{"tf":1.4142135623730951},"88":{"tf":1.4142135623730951}}},"t":{"df":0,"docs":{},"h":{"df":2,"docs":{"3":{"tf":1.0},"37":{"tf":1.0}}}}},"u":{"df":0,"docs":{},"l":{"d":{"df":0,"docs":{},"n":{"'":{"df":0,"docs":{},"t":{"df":1,"docs":{"151":{"tf":1.0}}}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"r":{"a":{"df":0,"docs":{},"p":{"df":4,"docs":{"124":{"tf":1.0},"146":{"tf":1.0},"16":{"tf":1.4142135623730951},"45":{"tf":1.0}}}},"df":0,"docs":{},"i":{"df":0,"docs":{},"t":{"df":0,"docs":{},"e":{"df":6,"docs":{"14":{"tf":1.0},"146":{"tf":1.0},"26":{"tf":1.0},"45":{"tf":1.0},"50":{"tf":1.7320508075688772},"92":{"tf":1.4142135623730951}}},"t":{"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"df":1,"docs":{"7":{"tf":1.4142135623730951}}}}}}},"o":{"df":0,"docs":{},"n":{"df":0,"docs":{},"g":{"df":2,"docs":{"124":{"tf":1.0},"42":{"tf":1.0}}}}}}},"x":{",":{"0":{"df":1,"docs":{"7":{"tf":1.0}}},"df":0,"docs":{},"i":{"df":1,"docs":{"34":{"tf":1.0}}}},"0":{"df":1,"docs":{"114":{"tf":1.0}}},"2":{"df":0,"docs":{},"n":{"df":0,"docs":{},"−":{"1":{"df":1,"docs":{"123":{"tf":1.0}},"q":{"df":0,"docs":{},"k":{"df":0,"docs":{},"t":{"df":2,"docs":{"84":{"tf":1.4142135623730951},"85":{"tf":1.0}}}}}},"df":0,"docs":{}}}},":":{"=":{"df":0,"docs":{},"g":{"+":{"df":0,"docs":{},"ζ":{"df":0,"docs":{},"k":{"df":0,"docs":{},"​":{"df":0,"docs":{},"h":{"df":1,"docs":{"102":{"tf":1.0}}}}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"=":{"3":{"df":2,"docs":{"7":{"tf":1.4142135623730951},"9":{"tf":1.0}}},"df":0,"docs":{},"x":{"0":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"x":{"1":{"df":0,"docs":{},"​":{"df":0,"docs":{},"∗":{"2":{"1":{"6":{"+":{"df":0,"docs":{},"x":{"2":{"df":0,"docs":{},"​":{"df":0,"docs":{},"∗":{"2":{"3":{"2":{"+":{"df":0,"docs":{},"x":{"3":{"df":0,"docs":{},"​":{"df":0,"docs":{},"∗":{"2":{"4":{"8":{"+":{".":{".":{".":{"+":{"df":0,"docs":{},"x":{"7":{"df":0,"docs":{},"​":{"df":0,"docs":{},"∗":{"2":{"1":{"1":{"2":{"df":1,"docs":{"151":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"_":{"0":{"df":3,"docs":{"112":{"tf":1.0},"60":{"tf":1.4142135623730951},"62":{"tf":1.0}}},"df":0,"docs":{}},"df":18,"docs":{"102":{"tf":1.0},"106":{"tf":1.0},"11":{"tf":1.0},"124":{"tf":1.0},"14":{"tf":1.0},"142":{"tf":1.4142135623730951},"151":{"tf":1.7320508075688772},"28":{"tf":1.0},"34":{"tf":1.0},"35":{"tf":1.7320508075688772},"44":{"tf":2.23606797749979},"45":{"tf":1.4142135623730951},"5":{"tf":1.7320508075688772},"52":{"tf":1.0},"7":{"tf":2.23606797749979},"88":{"tf":1.4142135623730951},"9":{"tf":2.0},"94":{"tf":1.0}},"i":{"df":2,"docs":{"123":{"tf":1.0},"151":{"tf":1.4142135623730951}}},"n":{"=":{"1":{"df":1,"docs":{"124":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{},"−":{"1":{"df":2,"docs":{"124":{"tf":1.0},"14":{"tf":1.0}}},"df":0,"docs":{}}},"x":{"df":0,"docs":{},"x":{"df":0,"docs":{},"x":{"df":0,"docs":{},"x":{"df":0,"docs":{},"x":{"df":0,"docs":{},"x":{"df":0,"docs":{},"x":{"df":0,"docs":{},"x":{"df":0,"docs":{},"x":{"df":0,"docs":{},"x":{"df":0,"docs":{},"x":{"df":0,"docs":{},"x":{"df":0,"docs":{},"x":{"df":0,"docs":{},"x":{"df":0,"docs":{},"x":{"df":0,"docs":{},"|":{"df":0,"docs":{},"x":{"df":0,"docs":{},"|":{"df":0,"docs":{},"x":{"df":0,"docs":{},"x":{"df":0,"docs":{},"|":{"df":0,"docs":{},"x":{"df":0,"docs":{},"x":{"df":0,"docs":{},"x":{"df":0,"docs":{},"x":{"df":0,"docs":{},"|":{"df":0,"docs":{},"x":{"df":0,"docs":{},"x":{"df":0,"docs":{},"x":{"df":0,"docs":{},"x":{"df":0,"docs":{},"|":{"df":0,"docs":{},"x":{"df":0,"docs":{},"x":{"df":0,"docs":{},"x":{"df":0,"docs":{},"|":{"df":0,"docs":{},"x":{"df":0,"docs":{},"x":{"df":0,"docs":{},"x":{"df":2,"docs":{"142":{"tf":1.0},"146":{"tf":1.0}},"|":{"df":0,"docs":{},"x":{"df":0,"docs":{},"x":{"df":0,"docs":{},"x":{"df":0,"docs":{},"x":{"df":0,"docs":{},"|":{"df":0,"docs":{},"x":{"df":0,"docs":{},"x":{"df":0,"docs":{},"x":{"df":0,"docs":{},"x":{"df":0,"docs":{},"|":{"df":0,"docs":{},"x":{"df":0,"docs":{},"x":{"df":0,"docs":{},"x":{"df":0,"docs":{},"x":{"df":0,"docs":{},"|":{"df":0,"docs":{},"x":{"df":0,"docs":{},"x":{"df":0,"docs":{},"x":{"df":0,"docs":{},"|":{"df":0,"docs":{},"x":{"df":0,"docs":{},"x":{"df":0,"docs":{},"x":{"df":1,"docs":{"147":{"tf":1.0}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}},"−":{"a":{"df":1,"docs":{"100":{"tf":1.0}}},"df":0,"docs":{},"z":{"2":{"df":0,"docs":{},"h":{"1":{"df":0,"docs":{},"​":{"df":0,"docs":{},"−":{"df":0,"docs":{},"h":{"1":{"df":0,"docs":{},"​":{"(":{"df":0,"docs":{},"z":{"2":{")":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"γ":{"df":0,"docs":{},"′":{"df":0,"docs":{},"x":{"df":0,"docs":{},"−":{"df":0,"docs":{},"z":{"2":{"df":0,"docs":{},"h":{"2":{"df":0,"docs":{},"​":{"df":0,"docs":{},"−":{"df":0,"docs":{},"h":{"2":{"df":0,"docs":{},"​":{"(":{"df":0,"docs":{},"z":{"2":{")":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"j":{"df":0,"docs":{},"∑":{"df":0,"docs":{},"​":{"df":0,"docs":{},"γ":{"df":0,"docs":{},"j":{"df":0,"docs":{},"​":{"df":0,"docs":{},"x":{"df":0,"docs":{},"−":{"df":0,"docs":{},"z":{"df":0,"docs":{},"t":{"df":0,"docs":{},"j":{"df":0,"docs":{},"​":{"df":0,"docs":{},"−":{"df":0,"docs":{},"t":{"df":0,"docs":{},"j":{"df":0,"docs":{},"​":{"(":{"df":0,"docs":{},"z":{")":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"γ":{"df":0,"docs":{},"j":{"df":0,"docs":{},"′":{"df":0,"docs":{},"​":{"df":0,"docs":{},"x":{"df":0,"docs":{},"−":{"df":0,"docs":{},"g":{"df":0,"docs":{},"z":{"df":0,"docs":{},"t":{"df":0,"docs":{},"j":{"df":0,"docs":{},"​":{"df":0,"docs":{},"−":{"df":0,"docs":{},"t":{"df":0,"docs":{},"j":{"df":0,"docs":{},"​":{"(":{"df":0,"docs":{},"g":{"df":0,"docs":{},"z":{"df":1,"docs":{"94":{"tf":1.0}}}}},"df":0,"docs":{}}}}}}}}}}}}}}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}}}}}}}}}}}}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"y":{",":{"df":1,"docs":{"95":{"tf":1.0}}},"0":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"…":{",":{"df":0,"docs":{},"y":{"df":0,"docs":{},"m":{"df":1,"docs":{"73":{"tf":1.0}}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"1":{"df":0,"docs":{},"​":{"+":{"d":{"1":{"df":0,"docs":{},"−":{"1":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"…":{",":{"df":0,"docs":{},"y":{"df":0,"docs":{},"m":{"df":0,"docs":{},"​":{"+":{"d":{"df":0,"docs":{},"m":{"df":0,"docs":{},"−":{"1":{"df":1,"docs":{"74":{"tf":1.0}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"=":{"(":{"1":{",":{"1":{",":{"df":0,"docs":{},"…":{",":{"1":{"df":1,"docs":{"73":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{},"p":{"(":{"d":{"1":{"df":0,"docs":{},"​":{")":{",":{"df":0,"docs":{},"…":{",":{"df":0,"docs":{},"p":{"(":{"d":{"df":0,"docs":{},"m":{"df":1,"docs":{"73":{"tf":1.4142135623730951}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"y":{"0":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"…":{",":{"df":0,"docs":{},"y":{"df":0,"docs":{},"m":{"df":1,"docs":{"72":{"tf":1.0}}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"1":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"…":{",":{"df":0,"docs":{},"y":{"df":0,"docs":{},"m":{"df":3,"docs":{"73":{"tf":1.4142135623730951},"74":{"tf":1.0},"79":{"tf":1.0}}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{},"p":{"(":{"df":0,"docs":{},"z":{"df":1,"docs":{"75":{"tf":1.0}}}},"df":0,"docs":{}},"x":{"df":0,"docs":{},"e":{"+":{"5":{"df":1,"docs":{"34":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":15,"docs":{"19":{"tf":1.4142135623730951},"35":{"tf":2.23606797749979},"44":{"tf":2.23606797749979},"45":{"tf":1.4142135623730951},"72":{"tf":2.0},"73":{"tf":2.0},"74":{"tf":1.7320508075688772},"75":{"tf":1.4142135623730951},"77":{"tf":1.4142135623730951},"78":{"tf":1.0},"79":{"tf":1.4142135623730951},"88":{"tf":1.4142135623730951},"92":{"tf":1.7320508075688772},"94":{"tf":1.4142135623730951},"95":{"tf":1.4142135623730951}},"i":{"df":2,"docs":{"72":{"tf":1.0},"80":{"tf":1.0}},"​":{",":{"df":1,"docs":{"92":{"tf":1.0}}},"=":{"df":0,"docs":{},"p":{"df":0,"docs":{},"i":{"df":0,"docs":{},"​":{"(":{"df":0,"docs":{},"z":{"df":0,"docs":{},"i":{"df":1,"docs":{"80":{"tf":1.0}}}}},"df":0,"docs":{}}}}},"df":0,"docs":{},"−":{"df":0,"docs":{},"y":{")":{"/":{"(":{"d":{"df":0,"docs":{},"i":{"df":0,"docs":{},"​":{"df":0,"docs":{},"−":{"df":0,"docs":{},"z":{"df":1,"docs":{"79":{"tf":1.0}}}}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"j":{"(":{"1":{")":{"df":0,"docs":{},"​":{"df":0,"docs":{},"∣":{"df":0,"docs":{},"∣":{"df":0,"docs":{},"…":{"df":0,"docs":{},"∣":{"df":0,"docs":{},"∣":{"df":0,"docs":{},"y":{"df":0,"docs":{},"j":{"(":{"df":0,"docs":{},"k":{"df":1,"docs":{"92":{"tf":1.0}}}},"df":0,"docs":{}}}}}}}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"o":{"df":0,"docs":{},"u":{"'":{"df":0,"docs":{},"l":{"df":0,"docs":{},"l":{"df":2,"docs":{"114":{"tf":1.0},"16":{"tf":1.0}}}},"r":{"df":1,"docs":{"111":{"tf":1.0}}}},"df":0,"docs":{},"r":{"df":0,"docs":{},"s":{"df":0,"docs":{},"e":{"df":0,"docs":{},"l":{"df":0,"docs":{},"f":{"df":2,"docs":{"118":{"tf":1.0},"42":{"tf":1.0}}}}}}}}}},"z":{"(":{"d":{")":{"df":0,"docs":{},"f":{"(":{"d":{")":{"=":{"df":0,"docs":{},"g":{"(":{"d":{")":{"df":0,"docs":{},"z":{"(":{"df":0,"docs":{},"η":{"d":{"df":1,"docs":{"14":{"tf":1.0}}},"df":0,"docs":{}},"ω":{"d":{"df":1,"docs":{"14":{"tf":1.0}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{},"h":{")":{"df":0,"docs":{},"f":{"(":{"df":0,"docs":{},"h":{")":{"=":{"df":0,"docs":{},"g":{"(":{"df":0,"docs":{},"h":{")":{"df":0,"docs":{},"z":{"(":{"df":0,"docs":{},"ω":{"df":0,"docs":{},"h":{"df":1,"docs":{"14":{"tf":1.0}}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"x":{")":{"=":{"df":0,"docs":{},"∏":{"(":{"df":0,"docs":{},"x":{"df":0,"docs":{},"−":{"df":0,"docs":{},"e":{"df":0,"docs":{},"i":{"df":0,"docs":{},"​":{")":{"df":0,"docs":{},"x":{"2":{"df":0,"docs":{},"n":{"df":0,"docs":{},"−":{"1":{"df":1,"docs":{"123":{"tf":1.0}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"x":{"df":0,"docs":{},"i":{"df":1,"docs":{"123":{"tf":1.0}}}}}}},"df":0,"docs":{}}},"df":0,"docs":{},"f":{"(":{"df":0,"docs":{},"x":{")":{"=":{"df":0,"docs":{},"g":{"(":{"df":0,"docs":{},"x":{")":{"df":0,"docs":{},"z":{"(":{"df":0,"docs":{},"ω":{"df":0,"docs":{},"x":{"df":1,"docs":{"14":{"tf":1.7320508075688772}}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":2,"docs":{"35":{"tf":1.0},"5":{"tf":1.0}}},"η":{"0":{")":{"=":{"1":{"df":1,"docs":{"14":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"ω":{"0":{")":{"=":{"1":{"df":1,"docs":{"14":{"tf":2.0}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{},"k":{"df":0,"docs":{},"−":{"1":{")":{"=":{"df":0,"docs":{},"∏":{"df":0,"docs":{},"i":{"=":{"0":{"df":0,"docs":{},"k":{"df":0,"docs":{},"−":{"2":{"df":0,"docs":{},"​":{"(":{"a":{"df":0,"docs":{},"i":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"γ":{")":{"/":{"df":0,"docs":{},"∏":{"df":0,"docs":{},"i":{"=":{"0":{"df":0,"docs":{},"k":{"df":0,"docs":{},"−":{"2":{"df":0,"docs":{},"​":{"(":{"b":{"df":0,"docs":{},"i":{"df":1,"docs":{"14":{"tf":1.0}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{},"g":{"(":{"df":0,"docs":{},"ω":{"df":0,"docs":{},"k":{"df":0,"docs":{},"−":{"1":{")":{"df":0,"docs":{},"f":{"(":{"df":0,"docs":{},"ω":{"df":0,"docs":{},"k":{"df":0,"docs":{},"−":{"1":{")":{"df":0,"docs":{},"​":{"=":{"df":0,"docs":{},"z":{"(":{"df":0,"docs":{},"ω":{"df":0,"docs":{},"k":{")":{"=":{"df":0,"docs":{},"z":{"(":{"df":0,"docs":{},"w":{"0":{")":{"=":{"1":{"df":1,"docs":{"14":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"0":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"…":{",":{"df":0,"docs":{},"z":{"df":0,"docs":{},"n":{"df":0,"docs":{},"−":{"1":{"df":1,"docs":{"25":{"tf":1.0}}},"df":0,"docs":{}}}}},"df":0,"docs":{}}},"=":{"1":{"df":1,"docs":{"25":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}}},"1":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"…":{",":{"df":0,"docs":{},"z":{"df":0,"docs":{},"l":{"df":1,"docs":{"80":{"tf":1.0}}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"=":{"(":{"b":{"7":{"df":0,"docs":{},"​":{"df":0,"docs":{},"x":{"2":{"+":{"b":{"8":{"df":0,"docs":{},"​":{"df":0,"docs":{},"x":{"+":{"b":{"9":{"df":0,"docs":{},"​":{")":{"df":0,"docs":{},"z":{"df":0,"docs":{},"h":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"z":{"df":1,"docs":{"25":{"tf":1.0}}}},"df":0,"docs":{}}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"]":{"1":{"df":3,"docs":{"25":{"tf":1.0},"32":{"tf":1.0},"33":{"tf":1.0}}},"df":0,"docs":{}},"^":{"2":{"df":1,"docs":{"116":{"tf":1.0}}},"df":0,"docs":{}},"_":{"1":{"df":1,"docs":{"35":{"tf":1.0}}},"df":0,"docs":{},"z":{"df":0,"docs":{},"e":{"df":0,"docs":{},"t":{"a":{"_":{"df":0,"docs":{},"o":{"df":0,"docs":{},"m":{"df":0,"docs":{},"e":{"df":0,"docs":{},"g":{"a":{"df":1,"docs":{"35":{"tf":1.0}}},"df":0,"docs":{}}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"a":{"df":0,"docs":{},"v":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":0,"docs":{},"u":{"c":{"df":0,"docs":{},"h":{"a":{"df":3,"docs":{"19":{"tf":1.0},"35":{"tf":1.0},"38":{"tf":1.0}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}}},"df":22,"docs":{"112":{"tf":1.0},"14":{"tf":3.1622776601683795},"16":{"tf":1.0},"25":{"tf":1.0},"28":{"tf":1.0},"33":{"tf":1.4142135623730951},"35":{"tf":1.4142135623730951},"44":{"tf":1.4142135623730951},"45":{"tf":1.4142135623730951},"5":{"tf":1.0},"58":{"tf":1.4142135623730951},"59":{"tf":1.0},"60":{"tf":1.4142135623730951},"62":{"tf":1.0},"69":{"tf":1.4142135623730951},"75":{"tf":1.4142135623730951},"77":{"tf":1.0},"79":{"tf":1.7320508075688772},"85":{"tf":1.7320508075688772},"86":{"tf":1.0},"94":{"tf":1.0},"95":{"tf":1.4142135623730951}},"e":{"df":0,"docs":{},"r":{"df":0,"docs":{},"o":{"df":14,"docs":{"10":{"tf":1.0},"107":{"tf":1.0},"121":{"tf":1.0},"125":{"tf":1.0},"13":{"tf":1.7320508075688772},"142":{"tf":1.4142135623730951},"146":{"tf":1.4142135623730951},"20":{"tf":1.0},"36":{"tf":1.0},"4":{"tf":1.0},"84":{"tf":1.0},"88":{"tf":1.0},"94":{"tf":1.0},"95":{"tf":1.0}},"f":{"df":0,"docs":{},"i":{"df":5,"docs":{"114":{"tf":1.7320508075688772},"123":{"tf":1.4142135623730951},"125":{"tf":2.0},"54":{"tf":1.0},"91":{"tf":1.4142135623730951}}}}}}},"f":{"df":0,"docs":{},"−":{"df":0,"docs":{},"g":{"df":0,"docs":{},"z":{"df":0,"docs":{},"′":{"=":{"df":0,"docs":{},"z":{"df":0,"docs":{},"h":{"df":0,"docs":{},"​":{"df":0,"docs":{},"t":{"2":{"df":1,"docs":{"16":{"tf":1.0}}},"df":0,"docs":{}}}}}},"df":0,"docs":{}}}}}},"h":{"df":2,"docs":{"14":{"tf":1.0},"20":{"tf":1.0}},"​":{"(":{"df":0,"docs":{},"ω":{"df":0,"docs":{},"i":{")":{"=":{"0":{"df":1,"docs":{"20":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}},":":{"=":{"df":0,"docs":{},"x":{"df":0,"docs":{},"n":{"df":0,"docs":{},"−":{"1":{"df":1,"docs":{"23":{"tf":1.0}}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"=":{"df":0,"docs":{},"x":{"df":0,"docs":{},"n":{"df":0,"docs":{},"−":{"1":{"df":2,"docs":{"16":{"tf":1.0},"20":{"tf":1.0}}},"df":0,"docs":{}}}}},"df":0,"docs":{},"t":{"=":{"df":0,"docs":{},"​":{"a":{"df":0,"docs":{},"q":{"df":0,"docs":{},"l":{"df":0,"docs":{},"​":{"+":{"b":{"df":0,"docs":{},"q":{"df":0,"docs":{},"r":{"df":0,"docs":{},"​":{"+":{"a":{"b":{"df":0,"docs":{},"q":{"df":0,"docs":{},"m":{"df":0,"docs":{},"​":{"+":{"c":{"df":0,"docs":{},"q":{"df":0,"docs":{},"o":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"q":{"c":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"p":{"df":0,"docs":{},"i":{"+":{"df":0,"docs":{},"α":{"(":{"df":0,"docs":{},"g":{"df":0,"docs":{},"z":{"df":0,"docs":{},"′":{"df":0,"docs":{},"−":{"df":0,"docs":{},"f":{"df":0,"docs":{},"z":{")":{"+":{"df":0,"docs":{},"α":{"2":{"(":{"df":0,"docs":{},"z":{"df":0,"docs":{},"−":{"1":{")":{"df":0,"docs":{},"l":{"1":{"df":1,"docs":{"16":{"tf":1.0}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}}}}}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"i":{"df":0,"docs":{},"p":{"df":0,"docs":{},"p":{"df":0,"docs":{},"e":{"df":0,"docs":{},"l":{"df":1,"docs":{"14":{"tf":1.4142135623730951}}}}}}},"j":{"b":{"df":1,"docs":{"91":{"tf":1.0}}},"df":0,"docs":{},"t":{"df":1,"docs":{"91":{"tf":1.0}}}},"k":{"+":{"1":{"df":0,"docs":{},"​":{"=":{"df":0,"docs":{},"z":{"df":0,"docs":{},"k":{"df":0,"docs":{},"​":{"(":{"a":{"df":0,"docs":{},"k":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"β":{"df":0,"docs":{},"s":{"df":0,"docs":{},"σ":{"1":{"df":0,"docs":{},"​":{"(":{"df":0,"docs":{},"ω":{"df":0,"docs":{},"k":{")":{"+":{"df":0,"docs":{},"γ":{")":{"(":{"b":{"df":0,"docs":{},"k":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"β":{"df":0,"docs":{},"s":{"df":0,"docs":{},"σ":{"2":{"df":0,"docs":{},"​":{"(":{"df":0,"docs":{},"ω":{"df":0,"docs":{},"k":{")":{"+":{"df":0,"docs":{},"γ":{")":{"(":{"c":{"df":0,"docs":{},"k":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"β":{"df":0,"docs":{},"s":{"df":0,"docs":{},"σ":{"3":{"df":0,"docs":{},"​":{"(":{"df":0,"docs":{},"ω":{"df":0,"docs":{},"k":{")":{"+":{"df":0,"docs":{},"γ":{")":{"(":{"a":{"df":0,"docs":{},"k":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"β":{"df":0,"docs":{},"ω":{"df":0,"docs":{},"k":{"+":{"df":0,"docs":{},"γ":{")":{"(":{"b":{"df":0,"docs":{},"k":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"β":{"df":0,"docs":{},"ω":{"df":0,"docs":{},"k":{"df":0,"docs":{},"k":{"1":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"γ":{")":{"(":{"c":{"df":0,"docs":{},"k":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"β":{"df":0,"docs":{},"ω":{"df":0,"docs":{},"k":{"df":0,"docs":{},"k":{"2":{"df":1,"docs":{"25":{"tf":1.0}}},"df":0,"docs":{}}}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"d":{"df":0,"docs":{},"o":{"c":{"df":1,"docs":{"42":{"tf":1.0}}},"df":0,"docs":{}}},"df":1,"docs":{"4":{"tf":1.0}}},"′":{"(":{"df":0,"docs":{},"x":{")":{"=":{"df":0,"docs":{},"z":{"(":{"df":0,"docs":{},"x":{"df":1,"docs":{"16":{"tf":1.0}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},":":{"=":{"df":0,"docs":{},"z":{"(":{"df":0,"docs":{},"ω":{"df":0,"docs":{},"x":{"df":1,"docs":{"5":{"tf":1.0}}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{},"∈":{"df":0,"docs":{},"f":{"df":1,"docs":{"147":{"tf":1.0}}}}},"∈":{"df":0,"docs":{},"f":{"df":2,"docs":{"147":{"tf":1.0},"85":{"tf":1.0}},"∖":{"d":{"df":0,"docs":{},"l":{"d":{"df":0,"docs":{},"e":{"df":1,"docs":{"94":{"tf":1.0}}}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"−":{"1":{")":{"df":0,"docs":{},"l":{"1":{"df":0,"docs":{},"​":{"=":{"df":0,"docs":{},"z":{"df":0,"docs":{},"h":{"df":0,"docs":{},"​":{"df":0,"docs":{},"t":{"3":{"df":1,"docs":{"16":{"tf":1.0}}},"df":0,"docs":{}}}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"breadcrumbs":{"root":{"0":{")":{"=":{"0":{"df":1,"docs":{"101":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}},",":{"0":{")":{")":{"=":{"(":{"2":{",":{"1":{"df":1,"docs":{"14":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},",":{"df":0,"docs":{},"t":{"0":{",":{"0":{"df":0,"docs":{},"​":{")":{",":{"(":{"(":{"0":{",":{"1":{")":{",":{"df":0,"docs":{},"t":{"0":{",":{"1":{"df":0,"docs":{},"​":{")":{",":{"(":{"(":{"0":{",":{"2":{")":{",":{"df":0,"docs":{},"t":{"0":{",":{"2":{"df":0,"docs":{},"​":{")":{",":{"(":{"(":{"0":{",":{"3":{")":{",":{"df":0,"docs":{},"t":{"0":{",":{"3":{"df":0,"docs":{},"​":{")":{",":{"(":{"(":{"2":{",":{"1":{")":{",":{"df":0,"docs":{},"t":{"2":{",":{"1":{"df":0,"docs":{},"​":{")":{",":{"(":{"(":{"3":{",":{"1":{")":{",":{"df":0,"docs":{},"t":{"3":{",":{"1":{"df":0,"docs":{},"​":{")":{",":{"(":{"(":{"2":{",":{"2":{")":{",":{"df":0,"docs":{},"t":{"2":{",":{"2":{"df":1,"docs":{"14":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":2,"docs":{"133":{"tf":1.0},"135":{"tf":1.0}}},"1":{")":{")":{"=":{"(":{"0":{",":{"3":{"df":1,"docs":{"14":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"4":{"df":1,"docs":{"131":{"tf":1.0}}},"df":0,"docs":{}},"2":{")":{")":{"=":{"(":{"0":{",":{"2":{"df":1,"docs":{"14":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"3":{")":{")":{"=":{"(":{"0":{",":{"1":{"df":1,"docs":{"14":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{},"ω":{",":{"df":0,"docs":{},"ω":{"2":{",":{"df":0,"docs":{},"…":{",":{"df":0,"docs":{},"ω":{"1":{"5":{"df":1,"docs":{"114":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},".":{".":{"3":{"2":{"df":1,"docs":{"45":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}},"2":{"df":1,"docs":{"3":{"tf":1.0}}},"4":{"df":1,"docs":{"3":{"tf":1.0}}},"8":{"df":1,"docs":{"3":{"tf":1.0}}},"df":0,"docs":{}},"0":{"1":{"df":1,"docs":{"101":{"tf":1.0}}},"df":0,"docs":{}},"<":{"df":0,"docs":{},"x":{"df":0,"docs":{},"i":{"df":0,"docs":{},"​":{"<":{"2":{"1":{"6":{"df":1,"docs":{"151":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":29,"docs":{"10":{"tf":4.358898943540674},"106":{"tf":1.0},"109":{"tf":1.0},"11":{"tf":2.23606797749979},"12":{"tf":1.4142135623730951},"127":{"tf":1.4142135623730951},"13":{"tf":5.0},"130":{"tf":1.7320508075688772},"131":{"tf":1.0},"132":{"tf":1.0},"133":{"tf":1.4142135623730951},"134":{"tf":1.7320508075688772},"135":{"tf":2.8284271247461903},"136":{"tf":1.0},"139":{"tf":1.0},"14":{"tf":2.0},"146":{"tf":2.23606797749979},"149":{"tf":1.7320508075688772},"150":{"tf":1.0},"151":{"tf":1.4142135623730951},"36":{"tf":1.0},"45":{"tf":1.0},"52":{"tf":1.4142135623730951},"62":{"tf":1.0},"68":{"tf":1.4142135623730951},"70":{"tf":1.0},"73":{"tf":1.0},"9":{"tf":1.0},"94":{"tf":1.0}},"s":{"df":2,"docs":{"131":{"tf":1.0},"139":{"tf":1.0}}},"x":{"0":{"1":{"2":{"3":{"4":{"5":{"6":{"7":{"8":{"9":{"a":{"b":{"c":{"d":{"df":1,"docs":{"88":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"7":{"3":{"df":0,"docs":{},"e":{"d":{"a":{"7":{"5":{"3":{"2":{"9":{"9":{"d":{"7":{"d":{"4":{"8":{"3":{"3":{"3":{"9":{"d":{"8":{"0":{"8":{"0":{"9":{"a":{"1":{"d":{"8":{"0":{"5":{"5":{"3":{"b":{"d":{"a":{"4":{"0":{"2":{"df":0,"docs":{},"f":{"df":0,"docs":{},"f":{"df":0,"docs":{},"f":{"df":0,"docs":{},"e":{"5":{"b":{"df":0,"docs":{},"f":{"df":0,"docs":{},"e":{"df":0,"docs":{},"f":{"df":0,"docs":{},"f":{"df":0,"docs":{},"f":{"df":0,"docs":{},"f":{"df":0,"docs":{},"f":{"df":0,"docs":{},"f":{"df":0,"docs":{},"f":{"df":0,"docs":{},"f":{"0":{"0":{"0":{"0":{"0":{"0":{"0":{"1":{"df":1,"docs":{"38":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}}}}}}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"υ":{"df":0,"docs":{},"s":{"df":0,"docs":{},"​":{"df":0,"docs":{},"​":{"df":0,"docs":{},"π":{"0":{"df":0,"docs":{},"−":{"df":0,"docs":{},"υ":{"df":0,"docs":{},"s":{"df":0,"docs":{},"​":{"df":0,"docs":{},"​":{"df":0,"docs":{},"​":{":":{"=":{"df":0,"docs":{},"γ":{"df":0,"docs":{},"υ":{"df":0,"docs":{},"s":{"df":0,"docs":{},"​":{"df":0,"docs":{},"−":{"df":0,"docs":{},"z":{"2":{"df":0,"docs":{},"η":{"1":{"df":0,"docs":{},"υ":{"df":0,"docs":{},"s":{"df":0,"docs":{},"​":{"df":0,"docs":{},"​":{"df":0,"docs":{},"−":{"df":0,"docs":{},"η":{"1":{"df":0,"docs":{},"z":{"2":{"df":0,"docs":{},"​":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"γ":{"df":0,"docs":{},"′":{"df":0,"docs":{},"υ":{"df":0,"docs":{},"s":{"df":0,"docs":{},"​":{"df":0,"docs":{},"−":{"df":0,"docs":{},"z":{"2":{"df":0,"docs":{},"η":{"2":{"df":0,"docs":{},"υ":{"df":0,"docs":{},"s":{"df":0,"docs":{},"​":{"df":0,"docs":{},"​":{"df":0,"docs":{},"−":{"df":0,"docs":{},"η":{"2":{"df":0,"docs":{},"z":{"2":{"df":0,"docs":{},"​":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"j":{"df":0,"docs":{},"∑":{"df":0,"docs":{},"​":{"df":0,"docs":{},"γ":{"df":0,"docs":{},"j":{"df":0,"docs":{},"​":{"df":0,"docs":{},"υ":{"df":0,"docs":{},"s":{"df":0,"docs":{},"​":{"df":0,"docs":{},"−":{"df":0,"docs":{},"z":{"df":0,"docs":{},"τ":{"df":0,"docs":{},"j":{"df":0,"docs":{},"υ":{"df":0,"docs":{},"s":{"df":0,"docs":{},"​":{"df":0,"docs":{},"​":{"df":0,"docs":{},"−":{"df":0,"docs":{},"τ":{"df":0,"docs":{},"j":{"df":0,"docs":{},"z":{"df":0,"docs":{},"​":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"γ":{"df":0,"docs":{},"j":{"df":0,"docs":{},"′":{"df":0,"docs":{},"​":{"df":0,"docs":{},"υ":{"df":0,"docs":{},"s":{"df":0,"docs":{},"​":{"df":0,"docs":{},"−":{"df":0,"docs":{},"g":{"df":0,"docs":{},"z":{"df":0,"docs":{},"τ":{"df":0,"docs":{},"j":{"df":0,"docs":{},"υ":{"df":0,"docs":{},"s":{"df":0,"docs":{},"​":{"df":0,"docs":{},"​":{"df":0,"docs":{},"−":{"df":0,"docs":{},"τ":{"df":0,"docs":{},"j":{"df":0,"docs":{},"g":{"df":0,"docs":{},"z":{"df":0,"docs":{},"​":{"df":0,"docs":{},"​":{",":{":":{"=":{"df":0,"docs":{},"γ":{"df":0,"docs":{},"−":{"df":0,"docs":{},"υ":{"df":0,"docs":{},"s":{"df":0,"docs":{},"​":{"df":0,"docs":{},"−":{"df":0,"docs":{},"z":{"2":{"df":0,"docs":{},"η":{"1":{"df":0,"docs":{},"−":{"df":0,"docs":{},"υ":{"df":0,"docs":{},"s":{"df":0,"docs":{},"​":{"df":0,"docs":{},"​":{"df":0,"docs":{},"−":{"df":0,"docs":{},"η":{"1":{"df":0,"docs":{},"z":{"2":{"df":0,"docs":{},"​":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"γ":{"df":0,"docs":{},"′":{"df":0,"docs":{},"−":{"df":0,"docs":{},"υ":{"df":0,"docs":{},"s":{"df":0,"docs":{},"​":{"df":0,"docs":{},"−":{"df":0,"docs":{},"z":{"2":{"df":0,"docs":{},"η":{"2":{"df":0,"docs":{},"−":{"df":0,"docs":{},"υ":{"df":0,"docs":{},"s":{"df":0,"docs":{},"​":{"df":0,"docs":{},"​":{"df":0,"docs":{},"−":{"df":0,"docs":{},"η":{"2":{"df":0,"docs":{},"z":{"2":{"df":0,"docs":{},"​":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"j":{"df":0,"docs":{},"∑":{"df":0,"docs":{},"​":{"df":0,"docs":{},"γ":{"df":0,"docs":{},"j":{"df":0,"docs":{},"​":{"df":0,"docs":{},"−":{"df":0,"docs":{},"υ":{"df":0,"docs":{},"s":{"df":0,"docs":{},"​":{"df":0,"docs":{},"−":{"df":0,"docs":{},"z":{"df":0,"docs":{},"τ":{"df":0,"docs":{},"j":{"df":0,"docs":{},"−":{"df":0,"docs":{},"υ":{"df":0,"docs":{},"s":{"df":0,"docs":{},"​":{"df":0,"docs":{},"​":{"df":0,"docs":{},"−":{"df":0,"docs":{},"τ":{"df":0,"docs":{},"j":{"df":0,"docs":{},"z":{"df":0,"docs":{},"​":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"γ":{"df":0,"docs":{},"j":{"df":0,"docs":{},"′":{"df":0,"docs":{},"​":{"df":0,"docs":{},"−":{"df":0,"docs":{},"υ":{"df":0,"docs":{},"s":{"df":0,"docs":{},"​":{"df":0,"docs":{},"−":{"df":0,"docs":{},"g":{"df":0,"docs":{},"z":{"df":0,"docs":{},"τ":{"df":0,"docs":{},"j":{"df":0,"docs":{},"−":{"df":0,"docs":{},"υ":{"df":0,"docs":{},"s":{"df":0,"docs":{},"​":{"df":0,"docs":{},"​":{"df":0,"docs":{},"−":{"df":0,"docs":{},"τ":{"df":0,"docs":{},"j":{"df":0,"docs":{},"g":{"df":0,"docs":{},"z":{"df":1,"docs":{"95":{"tf":1.0}}}}}}}}}}}}}}}}}}}}}}}}}},"df":0,"docs":{}}}}}}}}}}}}}}}}}}}}}}}}}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}}}}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}}}}}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}}}}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}}}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}}}}}}}}}}}}}}}}}}}}}}},"df":0,"docs":{}}}}}}}}}}}}}}}}}}}}}}}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}}}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}}}}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}}}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}}}}}},"df":0,"docs":{}}}}}},"≤":{"df":0,"docs":{},"i":{"<":{"df":0,"docs":{},"n":{"df":1,"docs":{"14":{"tf":1.0}}}},"df":0,"docs":{}},"j":{"<":{"3":{"df":1,"docs":{"14":{"tf":1.0}}},"df":0,"docs":{},"m":{"df":1,"docs":{"95":{"tf":1.4142135623730951}}}},"df":0,"docs":{},"≤":{"df":0,"docs":{},"j":{"df":1,"docs":{"142":{"tf":1.0}}}}},"k":{"<":{"df":0,"docs":{},"n":{"df":1,"docs":{"25":{"tf":1.0}}}},"df":0,"docs":{}}}},"1":{")":{"=":{"8":{"df":1,"docs":{"101":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}},",":{"b":{"0":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"γ":{"a":{"0":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"γ":{"df":0,"docs":{},"​":{",":{"(":{"b":{"0":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"γ":{")":{"(":{"b":{"1":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"γ":{")":{"(":{"a":{"0":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"γ":{")":{"(":{"a":{"1":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"γ":{")":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"…":{",":{"df":0,"docs":{},"i":{"=":{"0":{"df":0,"docs":{},"∏":{"df":0,"docs":{},"k":{"df":0,"docs":{},"−":{"2":{"df":0,"docs":{},"​":{"b":{"df":0,"docs":{},"i":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"γ":{"a":{"df":0,"docs":{},"i":{"df":1,"docs":{"14":{"tf":1.0}}}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{},"ω":{",":{"df":0,"docs":{},"ω":{"2":{",":{"df":0,"docs":{},"…":{",":{"df":0,"docs":{},"ω":{"df":0,"docs":{},"n":{"df":0,"docs":{},"−":{"1":{"df":1,"docs":{"20":{"tf":1.0}}},"df":0,"docs":{}}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"…":{",":{"df":0,"docs":{},"ω":{"df":0,"docs":{},"k":{"df":0,"docs":{},"−":{"1":{"df":1,"docs":{"14":{"tf":1.0}}},"2":{"df":1,"docs":{"14":{"tf":1.0}}},"df":0,"docs":{}}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},".":{"1":{"df":1,"docs":{"94":{"tf":1.0}}},"2":{"df":1,"docs":{"94":{"tf":1.0}}},"7":{"df":1,"docs":{"3":{"tf":1.0}}},"df":0,"docs":{}},"0":{"0":{"df":1,"docs":{"101":{"tf":1.0}}},"df":3,"docs":{"130":{"tf":1.0},"136":{"tf":1.0},"146":{"tf":1.0}}},"1":{"df":2,"docs":{"130":{"tf":1.0},"146":{"tf":1.0}}},"2":{"df":6,"docs":{"130":{"tf":1.0},"133":{"tf":1.0},"136":{"tf":1.0},"146":{"tf":1.0},"147":{"tf":1.0},"38":{"tf":1.0}}},"3":{"df":4,"docs":{"130":{"tf":1.0},"133":{"tf":1.0},"146":{"tf":1.0},"49":{"tf":1.0}}},"4":{"df":3,"docs":{"130":{"tf":1.0},"134":{"tf":1.4142135623730951},"146":{"tf":1.0}}},"5":{"df":7,"docs":{"127":{"tf":1.0},"130":{"tf":1.0},"131":{"tf":2.0},"134":{"tf":1.4142135623730951},"146":{"tf":1.0},"149":{"tf":1.0},"68":{"tf":1.4142135623730951}}},"6":{"df":11,"docs":{"114":{"tf":1.0},"127":{"tf":1.4142135623730951},"130":{"tf":1.7320508075688772},"131":{"tf":1.0},"133":{"tf":3.0},"136":{"tf":2.449489742783178},"142":{"tf":1.0},"146":{"tf":1.4142135623730951},"147":{"tf":1.0},"151":{"tf":1.0},"68":{"tf":2.0}},"x":{"df":0,"docs":{},"n":{"df":1,"docs":{"127":{"tf":1.0}}}}},"9":{"df":1,"docs":{"11":{"tf":1.0}}},"b":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"…":{",":{"df":0,"docs":{},"α":{"df":0,"docs":{},"m":{"b":{"df":1,"docs":{"95":{"tf":1.0}}},"df":0,"docs":{}}},"β":{"df":0,"docs":{},"m":{"b":{"df":2,"docs":{"94":{"tf":1.0},"95":{"tf":1.0}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":33,"docs":{"10":{"tf":4.0},"101":{"tf":1.0},"106":{"tf":1.7320508075688772},"108":{"tf":2.0},"109":{"tf":2.0},"11":{"tf":1.7320508075688772},"12":{"tf":2.23606797749979},"127":{"tf":1.0},"13":{"tf":4.58257569495584},"130":{"tf":1.4142135623730951},"131":{"tf":1.7320508075688772},"133":{"tf":1.4142135623730951},"134":{"tf":1.4142135623730951},"135":{"tf":1.7320508075688772},"139":{"tf":1.0},"14":{"tf":1.7320508075688772},"142":{"tf":1.0},"146":{"tf":2.23606797749979},"147":{"tf":1.0},"150":{"tf":1.0},"24":{"tf":1.4142135623730951},"3":{"tf":1.0},"35":{"tf":1.0},"49":{"tf":2.449489742783178},"52":{"tf":2.0},"54":{"tf":1.4142135623730951},"68":{"tf":1.4142135623730951},"7":{"tf":1.0},"83":{"tf":2.0},"84":{"tf":1.0},"9":{"tf":2.0},"94":{"tf":1.0},"95":{"tf":1.0}},"s":{"df":1,"docs":{"131":{"tf":1.0}}},"t":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"…":{",":{"df":0,"docs":{},"α":{"df":0,"docs":{},"n":{"df":0,"docs":{},"t":{"df":0,"docs":{},"​":{"df":0,"docs":{},"t":{"df":1,"docs":{"95":{"tf":1.0}}}}}}},"β":{"df":0,"docs":{},"n":{"df":0,"docs":{},"t":{"df":0,"docs":{},"​":{"df":0,"docs":{},"t":{"df":2,"docs":{"94":{"tf":1.0},"95":{"tf":1.0}}}}}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"z":{"2":{"df":1,"docs":{"95":{"tf":1.0}}},"df":0,"docs":{}},"​":{",":{"df":0,"docs":{},"…":{",":{"df":0,"docs":{},"γ":{"df":0,"docs":{},"m":{"df":1,"docs":{"94":{"tf":1.0}},"​":{",":{"df":0,"docs":{},"γ":{"1":{"df":0,"docs":{},"′":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"…":{",":{"df":0,"docs":{},"γ":{"df":0,"docs":{},"m":{"df":1,"docs":{"95":{"tf":1.0}}}}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"′":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"…":{",":{"df":0,"docs":{},"γ":{"df":0,"docs":{},"m":{"df":1,"docs":{"94":{"tf":1.0}}}}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"2":{")":{"=":{"4":{"df":1,"docs":{"101":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}},",":{"1":{")":{")":{"=":{"(":{"0":{",":{"0":{"df":1,"docs":{"14":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},",":{"df":0,"docs":{},"t":{"0":{",":{"0":{"df":0,"docs":{},"​":{")":{",":{"(":{"(":{"0":{",":{"3":{")":{",":{"df":0,"docs":{},"t":{"0":{",":{"1":{"df":0,"docs":{},"​":{")":{",":{"(":{"(":{"0":{",":{"2":{")":{",":{"df":0,"docs":{},"t":{"0":{",":{"2":{"df":0,"docs":{},"​":{")":{",":{"(":{"(":{"0":{",":{"1":{")":{",":{"df":0,"docs":{},"t":{"0":{",":{"3":{"df":0,"docs":{},"​":{")":{",":{"(":{"(":{"0":{",":{"0":{")":{",":{"df":0,"docs":{},"t":{"2":{",":{"1":{"df":0,"docs":{},"​":{")":{",":{"(":{"(":{"2":{",":{"2":{")":{",":{"df":0,"docs":{},"t":{"3":{",":{"1":{"df":0,"docs":{},"​":{")":{",":{"(":{"(":{"3":{",":{"1":{")":{",":{"df":0,"docs":{},"t":{"2":{",":{"2":{"df":1,"docs":{"14":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"2":{")":{")":{"=":{"(":{"3":{",":{"1":{"df":1,"docs":{"14":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},".":{"2":{"df":1,"docs":{"3":{"tf":1.0}}},"df":0,"docs":{}},"0":{"0":{".":{"8":{"df":1,"docs":{"3":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":1,"docs":{"11":{"tf":1.0}}},"1":{"2":{"8":{"df":1,"docs":{"151":{"tf":1.4142135623730951}}},"df":0,"docs":{}},"6":{"df":2,"docs":{"146":{"tf":1.0},"151":{"tf":1.0}}},"df":1,"docs":{"49":{"tf":1.0}}},"2":{"6":{"4":{"df":1,"docs":{"121":{"tf":1.0}}},"df":0,"docs":{}},"df":1,"docs":{"134":{"tf":1.0}}},"3":{"2":{"df":2,"docs":{"36":{"tf":1.0},"38":{"tf":1.0}}},"df":1,"docs":{"134":{"tf":1.0}}},"5":{"5":{"df":1,"docs":{"41":{"tf":1.0}}},"6":{"df":1,"docs":{"41":{"tf":1.4142135623730951}}},"df":0,"docs":{}},"8":{"7":{"4":{"5":{"df":1,"docs":{"3":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"=":{"df":0,"docs":{},"g":{"df":2,"docs":{"114":{"tf":1.4142135623730951},"125":{"tf":1.0}}}},"^":{"1":{"6":{"df":1,"docs":{"146":{"tf":1.0}}},"df":0,"docs":{}},"2":{"1":{"df":1,"docs":{"3":{"tf":1.0}}},"2":{"df":1,"docs":{"3":{"tf":1.0}}},"3":{"df":1,"docs":{"3":{"tf":1.0}}},"4":{"df":1,"docs":{"3":{"tf":1.0}}},"df":0,"docs":{}},"4":{"df":1,"docs":{"3":{"tf":1.0}}},"5":{"df":1,"docs":{"3":{"tf":1.0}}},"6":{"df":1,"docs":{"3":{"tf":1.0}}},"7":{"df":1,"docs":{"3":{"tf":1.0}}},"df":0,"docs":{},"n":{"df":3,"docs":{"51":{"tf":1.0},"62":{"tf":1.4142135623730951},"68":{"tf":1.7320508075688772}}},"{":{"df":0,"docs":{},"n":{"df":1,"docs":{"68":{"tf":1.0}}}}},"df":29,"docs":{"10":{"tf":1.0},"107":{"tf":1.0},"108":{"tf":1.7320508075688772},"109":{"tf":1.7320508075688772},"11":{"tf":2.0},"12":{"tf":1.4142135623730951},"121":{"tf":1.0},"125":{"tf":1.0},"127":{"tf":1.0},"13":{"tf":1.4142135623730951},"130":{"tf":1.4142135623730951},"132":{"tf":1.4142135623730951},"133":{"tf":2.0},"135":{"tf":1.4142135623730951},"136":{"tf":1.0},"139":{"tf":1.4142135623730951},"14":{"tf":1.7320508075688772},"142":{"tf":1.0},"146":{"tf":1.4142135623730951},"147":{"tf":1.0},"149":{"tf":1.0},"25":{"tf":1.4142135623730951},"35":{"tf":1.0},"49":{"tf":1.0},"84":{"tf":1.7320508075688772},"9":{"tf":1.4142135623730951},"91":{"tf":1.0},"94":{"tf":1.0},"95":{"tf":1.0}},"k":{"df":0,"docs":{},"−":{"1":{"df":1,"docs":{"101":{"tf":1.0}}},"df":0,"docs":{}}},"l":{"df":1,"docs":{"91":{"tf":1.0}}},"m":{",":{"df":0,"docs":{},"γ":{"df":0,"docs":{},"′":{":":{"=":{"df":0,"docs":{},"α":{"2":{"df":0,"docs":{},"m":{"+":{"1":{"df":1,"docs":{"97":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":1,"docs":{"70":{"tf":1.0}}},"n":{"+":{"1":{"df":2,"docs":{"124":{"tf":1.0},"125":{"tf":1.0}}},"2":{"df":1,"docs":{"147":{"tf":1.7320508075688772}},"×":{"2":{"df":1,"docs":{"147":{"tf":1.0}}},"df":0,"docs":{}},"−":{"df":0,"docs":{},"l":{"df":0,"docs":{},"p":{"df":0,"docs":{},"u":{"b":{"df":1,"docs":{"147":{"tf":1.0}}},"df":0,"docs":{}}}}}},"df":0,"docs":{},"k":{"df":1,"docs":{"124":{"tf":1.0}}},"l":{"df":2,"docs":{"82":{"tf":1.0},"91":{"tf":1.0}}}},"df":6,"docs":{"112":{"tf":1.0},"123":{"tf":1.4142135623730951},"124":{"tf":1.4142135623730951},"125":{"tf":1.4142135623730951},"82":{"tf":1.0},"91":{"tf":1.4142135623730951}},"×":{"1":{"2":{"df":1,"docs":{"147":{"tf":1.0}}},"8":{"df":1,"docs":{"147":{"tf":1.0}}},"df":0,"docs":{}},"3":{"df":1,"docs":{"147":{"tf":1.0}}},"4":{"df":1,"docs":{"147":{"tf":1.0}}},"6":{"df":1,"docs":{"147":{"tf":1.0}}},"8":{"df":1,"docs":{"147":{"tf":1.0}}},"df":0,"docs":{}}},"z":{"2":{"df":1,"docs":{"95":{"tf":1.0}}},"df":0,"docs":{}},"×":{"0":{"+":{"3":{"df":0,"docs":{},"×":{"0":{"+":{"2":{"df":0,"docs":{},"×":{"3":{"df":0,"docs":{},"×":{"1":{"+":{"6":{"df":0,"docs":{},"×":{"(":{"df":0,"docs":{},"−":{"1":{")":{"+":{"0":{"=":{"0":{"df":1,"docs":{"10":{"tf":1.0}}},"df":0,"docs":{}},"df":1,"docs":{"10":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"1":{"+":{"2":{"df":0,"docs":{},"×":{"3":{"df":0,"docs":{},"×":{"1":{"+":{"8":{"df":0,"docs":{},"×":{"(":{"df":0,"docs":{},"−":{"1":{")":{"+":{"(":{"df":0,"docs":{},"−":{"1":{")":{"=":{"0":{"df":1,"docs":{"12":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"3":{",":{"1":{")":{")":{"=":{"(":{"2":{",":{"2":{"df":1,"docs":{"14":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"8":{"df":1,"docs":{"7":{"tf":1.0}}},"df":0,"docs":{}},"0":{"0":{"0":{"0":{"df":1,"docs":{"3":{"tf":2.6457513110645907}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":3,"docs":{"3":{"tf":1.0},"50":{"tf":1.0},"65":{"tf":1.0}}},"1":{"df":1,"docs":{"127":{"tf":1.0}}},"2":{"4":{"5":{"4":{"4":{"4":{"df":1,"docs":{"135":{"tf":1.4142135623730951}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"5":{"2":{"6":{"4":{"3":{"df":1,"docs":{"135":{"tf":1.4142135623730951}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"8":{"1":{"df":1,"docs":{"38":{"tf":1.0}}},"df":0,"docs":{}},"df":23,"docs":{"10":{"tf":1.4142135623730951},"102":{"tf":1.0},"108":{"tf":1.4142135623730951},"109":{"tf":1.0},"11":{"tf":2.0},"12":{"tf":1.0},"13":{"tf":2.8284271247461903},"130":{"tf":1.4142135623730951},"133":{"tf":1.7320508075688772},"135":{"tf":1.7320508075688772},"14":{"tf":2.23606797749979},"142":{"tf":1.4142135623730951},"146":{"tf":1.7320508075688772},"147":{"tf":2.0},"149":{"tf":1.4142135623730951},"150":{"tf":1.0},"26":{"tf":1.4142135623730951},"35":{"tf":1.0},"49":{"tf":1.0},"85":{"tf":1.7320508075688772},"9":{"tf":1.7320508075688772},"94":{"tf":1.0},"95":{"tf":1.0}},"i":{"+":{"df":0,"docs":{},"j":{",":{"df":0,"docs":{},"v":{"df":1,"docs":{"14":{"tf":1.0}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"k":{"+":{"df":0,"docs":{},"l":{":":{"(":{"df":0,"docs":{},"i":{",":{"0":{")":{"df":0,"docs":{},"∈":{"df":0,"docs":{},"i":{",":{"df":0,"docs":{},"σ":{"(":{"(":{"df":0,"docs":{},"i":{",":{"0":{")":{")":{"=":{"(":{"df":0,"docs":{},"k":{",":{"df":0,"docs":{},"l":{"df":1,"docs":{"14":{"tf":1.0}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"1":{")":{"df":0,"docs":{},"∈":{"df":0,"docs":{},"i":{",":{"df":0,"docs":{},"σ":{"(":{"(":{"df":0,"docs":{},"i":{",":{"1":{")":{")":{"=":{"(":{"df":0,"docs":{},"k":{",":{"df":0,"docs":{},"l":{"df":1,"docs":{"14":{"tf":1.0}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"2":{")":{"df":0,"docs":{},"∈":{"df":0,"docs":{},"i":{",":{"df":0,"docs":{},"σ":{"(":{"(":{"df":0,"docs":{},"i":{",":{"2":{")":{")":{"=":{"(":{"df":0,"docs":{},"k":{",":{"df":0,"docs":{},"l":{"df":1,"docs":{"14":{"tf":1.0}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"n":{"df":1,"docs":{"14":{"tf":1.4142135623730951}}},"⋅":{"2":{"df":0,"docs":{},"n":{"df":1,"docs":{"147":{"tf":1.4142135623730951}}}},"df":0,"docs":{}}},"4":{".":{"1":{".":{"df":0,"docs":{},"k":{"df":1,"docs":{"94":{"tf":1.0}}}},"df":0,"docs":{}},"2":{"df":1,"docs":{"94":{"tf":1.0}}},"3":{"df":1,"docs":{"94":{"tf":1.0}}},"4":{"df":2,"docs":{"130":{"tf":1.0},"146":{"tf":1.0}}},"5":{"df":1,"docs":{"146":{"tf":1.0}}},"df":0,"docs":{}},"2":{".":{"6":{"df":1,"docs":{"3":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}},"3":{"3":{"4":{"5":{"2":{"4":{"df":1,"docs":{"135":{"tf":1.7320508075688772}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":17,"docs":{"104":{"tf":1.0},"108":{"tf":2.0},"11":{"tf":1.4142135623730951},"130":{"tf":1.4142135623730951},"133":{"tf":1.0},"134":{"tf":1.0},"135":{"tf":1.4142135623730951},"136":{"tf":1.0},"142":{"tf":1.4142135623730951},"146":{"tf":1.7320508075688772},"147":{"tf":2.23606797749979},"149":{"tf":1.0},"27":{"tf":1.4142135623730951},"35":{"tf":1.0},"86":{"tf":1.4142135623730951},"94":{"tf":1.0},"95":{"tf":1.0}}},"5":{"df":8,"docs":{"108":{"tf":1.4142135623730951},"130":{"tf":1.7320508075688772},"133":{"tf":1.0},"136":{"tf":1.4142135623730951},"146":{"tf":1.0},"28":{"tf":1.4142135623730951},"35":{"tf":1.4142135623730951},"49":{"tf":1.0}}},"6":{"0":{"df":1,"docs":{"14":{"tf":1.0}}},"3":{"df":1,"docs":{"146":{"tf":1.4142135623730951}}},"df":10,"docs":{"10":{"tf":1.4142135623730951},"108":{"tf":1.4142135623730951},"11":{"tf":1.0},"127":{"tf":1.0},"130":{"tf":1.0},"134":{"tf":1.4142135623730951},"138":{"tf":1.4142135623730951},"139":{"tf":1.0},"146":{"tf":1.0},"9":{"tf":1.4142135623730951}},"×":{"1":{"+":{"3":{"df":0,"docs":{},"×":{"1":{"+":{"2":{"df":0,"docs":{},"×":{"3":{"df":0,"docs":{},"×":{"0":{"+":{"9":{"df":0,"docs":{},"×":{"(":{"df":0,"docs":{},"−":{"1":{")":{"+":{"0":{"df":1,"docs":{"10":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"6":{"df":0,"docs":{},"×":{"3":{"df":0,"docs":{},"×":{"0":{"+":{"9":{"df":0,"docs":{},"×":{"(":{"df":0,"docs":{},"−":{"1":{")":{"+":{"0":{"=":{"0":{"df":1,"docs":{"10":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"7":{"df":5,"docs":{"130":{"tf":1.0},"134":{"tf":1.4142135623730951},"139":{"tf":1.4142135623730951},"146":{"tf":1.0},"52":{"tf":1.0}}},"8":{"df":18,"docs":{"10":{"tf":1.0},"104":{"tf":1.0},"114":{"tf":1.0},"12":{"tf":1.0},"124":{"tf":1.0},"127":{"tf":1.4142135623730951},"13":{"tf":2.8284271247461903},"130":{"tf":1.4142135623730951},"133":{"tf":2.0},"136":{"tf":1.0},"14":{"tf":2.0},"146":{"tf":1.0},"151":{"tf":1.0},"49":{"tf":1.0},"52":{"tf":1.7320508075688772},"68":{"tf":1.4142135623730951},"7":{"tf":1.4142135623730951},"9":{"tf":1.0}}},"9":{".":{"4":{"df":2,"docs":{"131":{"tf":1.0},"138":{"tf":1.0}}},"6":{"df":1,"docs":{"3":{"tf":1.0}}},"7":{"df":1,"docs":{"139":{"tf":1.0}}},"8":{"df":2,"docs":{"139":{"tf":1.0},"146":{"tf":1.0}}},"9":{"df":2,"docs":{"138":{"tf":1.0},"146":{"tf":1.0}}},"df":0,"docs":{}},"df":4,"docs":{"10":{"tf":1.4142135623730951},"133":{"tf":1.0},"146":{"tf":1.0},"9":{"tf":1.4142135623730951}},"×":{"1":{"+":{"0":{"df":0,"docs":{},"×":{"0":{"+":{"9":{"df":0,"docs":{},"×":{"0":{"df":0,"docs":{},"×":{"0":{"+":{"8":{"df":0,"docs":{},"×":{"(":{"df":0,"docs":{},"−":{"1":{")":{"+":{"(":{"df":0,"docs":{},"−":{"1":{")":{"=":{"0":{"df":1,"docs":{"10":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"c":{"df":1,"docs":{"10":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"_":{"1":{"df":1,"docs":{"35":{"tf":1.0}}},"df":0,"docs":{},"r":{"a":{"df":0,"docs":{},"p":{"_":{"c":{"df":0,"docs":{},"h":{"a":{"df":0,"docs":{},"l":{"df":0,"docs":{},"l":{"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"df":0,"docs":{},"g":{"df":2,"docs":{"106":{"tf":1.0},"107":{"tf":1.0}}}}}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"a":{"(":{"df":0,"docs":{},"x":{")":{"df":0,"docs":{},"q":{"df":0,"docs":{},"l":{"df":0,"docs":{},"​":{"(":{"df":0,"docs":{},"x":{")":{"+":{"b":{"(":{"df":0,"docs":{},"x":{")":{"df":0,"docs":{},"q":{"df":0,"docs":{},"r":{"df":0,"docs":{},"​":{"(":{"df":0,"docs":{},"x":{")":{"+":{"a":{"(":{"df":0,"docs":{},"x":{")":{"b":{"(":{"df":0,"docs":{},"x":{")":{"df":0,"docs":{},"q":{"df":0,"docs":{},"m":{"df":0,"docs":{},"​":{"(":{"df":0,"docs":{},"x":{")":{"+":{"c":{"(":{"df":0,"docs":{},"x":{")":{"df":0,"docs":{},"q":{"df":0,"docs":{},"o":{"df":0,"docs":{},"​":{"(":{"df":0,"docs":{},"x":{")":{"+":{"df":0,"docs":{},"q":{"c":{"df":0,"docs":{},"​":{"(":{"df":0,"docs":{},"x":{")":{"+":{"df":0,"docs":{},"p":{"df":0,"docs":{},"i":{"(":{"df":0,"docs":{},"x":{")":{"=":{"0":{"df":1,"docs":{"14":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}}},"df":1,"docs":{"35":{"tf":1.0}}},"ζ":{")":{",":{"b":{"df":1,"docs":{"21":{"tf":1.0}}},"df":0,"docs":{}},"[":{"df":0,"docs":{},"q":{"df":0,"docs":{},"l":{"df":0,"docs":{},"​":{"]":{"1":{"df":0,"docs":{},"​":{"+":{"b":{"(":{"df":0,"docs":{},"ζ":{")":{"[":{"df":0,"docs":{},"q":{"df":0,"docs":{},"r":{"df":0,"docs":{},"​":{"]":{"1":{"df":1,"docs":{"21":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}},"ω":{"df":0,"docs":{},"i":{")":{"=":{"a":{"df":0,"docs":{},"i":{"df":1,"docs":{"14":{"tf":1.0}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}},",":{"b":{",":{"c":{",":{"df":0,"docs":{},"p":{"df":0,"docs":{},"i":{"df":1,"docs":{"16":{"tf":1.0}}}},"q":{"df":0,"docs":{},"l":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"q":{"df":0,"docs":{},"r":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"q":{"df":0,"docs":{},"m":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"q":{"df":0,"docs":{},"o":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"q":{"c":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"p":{"df":0,"docs":{},"i":{"df":1,"docs":{"14":{"tf":1.4142135623730951}}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}},"df":0,"docs":{}}}}},"df":0,"docs":{}}}}},"df":0,"docs":{}}}}},"df":5,"docs":{"11":{"tf":1.0},"13":{"tf":1.0},"14":{"tf":1.0},"16":{"tf":1.4142135623730951},"35":{"tf":1.0}}},"df":0,"docs":{}},"df":1,"docs":{"21":{"tf":1.0}},"∈":{"df":0,"docs":{},"f":{"df":1,"docs":{"92":{"tf":1.0}}}}},"df":0,"docs":{}},"0":{"df":4,"docs":{"104":{"tf":1.0},"106":{"tf":1.4142135623730951},"14":{"tf":1.0},"149":{"tf":2.8284271247461903}},"​":{"+":{"df":0,"docs":{},"x":{")":{"(":{"a":{"1":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"x":{")":{"=":{"(":{"b":{"0":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"x":{")":{"(":{"b":{"1":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"x":{"df":1,"docs":{"14":{"tf":1.4142135623730951}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"β":{"a":{"1":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"γ":{")":{"(":{"a":{"2":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"β":{"a":{"3":{"df":1,"docs":{"14":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"γ":{")":{"(":{"a":{"1":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"γ":{")":{"=":{"(":{"b":{"0":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"γ":{")":{"(":{"b":{"1":{"df":1,"docs":{"14":{"tf":1.4142135623730951}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},",":{"df":0,"docs":{},"…":{",":{"a":{"df":0,"docs":{},"k":{"df":0,"docs":{},"−":{"1":{"df":1,"docs":{"14":{"tf":1.4142135623730951}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},",":{"a":{"1":{"df":1,"docs":{"14":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}},"=":{"df":0,"docs":{},"η":{"3":{"df":0,"docs":{},"i":{"+":{"df":0,"docs":{},"j":{"df":1,"docs":{"14":{"tf":1.0}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"a":{"1":{"df":1,"docs":{"14":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}}},"1":{"df":4,"docs":{"104":{"tf":1.0},"106":{"tf":1.7320508075688772},"14":{"tf":1.0},"149":{"tf":2.8284271247461903}},"​":{",":{"df":0,"docs":{},"…":{",":{"a":{"df":0,"docs":{},"k":{"df":1,"docs":{"92":{"tf":1.0}}},"l":{"df":2,"docs":{"94":{"tf":1.4142135623730951},"95":{"tf":1.0}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"=":{"df":0,"docs":{},"v":{"df":1,"docs":{"14":{"tf":1.0}}}},"df":0,"docs":{}}},"2":{"df":1,"docs":{"149":{"tf":2.8284271247461903}}},"3":{"df":1,"docs":{"149":{"tf":2.8284271247461903}}},":":{"=":{"(":{"b":{"1":{"df":0,"docs":{},"​":{"df":0,"docs":{},"x":{"+":{"b":{"2":{"df":0,"docs":{},"​":{")":{"df":0,"docs":{},"z":{"df":0,"docs":{},"h":{"df":0,"docs":{},"​":{"+":{"a":{"df":1,"docs":{"24":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"=":{"(":{"df":0,"docs":{},"f":{"(":{"a":{")":{",":{"df":0,"docs":{},"f":{"(":{"a":{"b":{")":{",":{"df":0,"docs":{},"f":{"(":{"a":{"b":{"2":{")":{",":{"df":0,"docs":{},"…":{",":{"df":0,"docs":{},"f":{"(":{"a":{"b":{"df":0,"docs":{},"l":{"df":1,"docs":{"92":{"tf":1.0}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"y":{"0":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"…":{",":{"df":0,"docs":{},"y":{"df":0,"docs":{},"l":{"df":1,"docs":{"92":{"tf":1.0}}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{},"{":{"(":{"(":{"df":0,"docs":{},"i":{",":{"df":0,"docs":{},"j":{")":{",":{"df":0,"docs":{},"t":{"df":0,"docs":{},"i":{",":{"df":0,"docs":{},"j":{"df":0,"docs":{},"​":{")":{":":{"(":{"df":0,"docs":{},"i":{",":{"df":0,"docs":{},"j":{")":{"df":0,"docs":{},"∈":{"df":0,"docs":{},"i":{"df":1,"docs":{"14":{"tf":2.0}}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"a":{"0":{"df":0,"docs":{},"​":{",":{"a":{"1":{"df":0,"docs":{},"​":{")":{",":{"(":{"a":{"2":{"df":0,"docs":{},"​":{",":{"a":{"3":{"df":1,"docs":{"14":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{},"η":{"3":{"df":0,"docs":{},"i":{"+":{"df":0,"docs":{},"j":{",":{"df":0,"docs":{},"t":{"df":0,"docs":{},"i":{",":{"df":0,"docs":{},"j":{"df":0,"docs":{},"​":{")":{":":{"(":{"df":0,"docs":{},"i":{",":{"df":0,"docs":{},"j":{")":{"df":0,"docs":{},"∈":{"df":0,"docs":{},"i":{"df":1,"docs":{"14":{"tf":1.0}}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"4":{",":{"1":{"5":{"df":1,"docs":{"14":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"a":{"0":{"df":0,"docs":{},"​":{",":{"a":{"1":{"df":1,"docs":{"14":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{},"…":{",":{"a":{"df":0,"docs":{},"k":{"df":0,"docs":{},"−":{"1":{"df":1,"docs":{"14":{"tf":1.4142135623730951}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{},"ˉ":{"0":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"…":{",":{"a":{"df":0,"docs":{},"ˉ":{"df":0,"docs":{},"k":{"df":0,"docs":{},"−":{"1":{"df":1,"docs":{"14":{"tf":1.0}}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"]":{"1":{"df":0,"docs":{},"​":{",":{"[":{"b":{"]":{"1":{"df":0,"docs":{},"​":{",":{"[":{"c":{"]":{"1":{"df":2,"docs":{"24":{"tf":1.0},"32":{"tf":1.0}},"​":{",":{"[":{"df":0,"docs":{},"z":{"]":{"1":{"df":0,"docs":{},"​":{",":{"[":{"df":0,"docs":{},"t":{"df":0,"docs":{},"l":{"df":0,"docs":{},"o":{"df":0,"docs":{},"​":{"]":{"1":{"df":0,"docs":{},"​":{",":{"[":{"df":0,"docs":{},"t":{"df":0,"docs":{},"m":{"df":0,"docs":{},"i":{"d":{"df":0,"docs":{},"​":{"]":{"1":{"df":0,"docs":{},"​":{",":{"[":{"df":0,"docs":{},"t":{"df":0,"docs":{},"h":{"df":0,"docs":{},"i":{"df":0,"docs":{},"​":{"]":{"1":{"df":0,"docs":{},"​":{",":{"a":{"df":0,"docs":{},"ˉ":{",":{"b":{"df":0,"docs":{},"ˉ":{",":{"c":{"df":0,"docs":{},"ˉ":{",":{"df":0,"docs":{},"s":{"df":0,"docs":{},"σ":{"1":{"df":0,"docs":{},"​":{"df":0,"docs":{},"ˉ":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"s":{"df":0,"docs":{},"σ":{"2":{"df":0,"docs":{},"​":{"df":0,"docs":{},"ˉ":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"z":{"df":0,"docs":{},"ω":{"df":0,"docs":{},"​":{"df":0,"docs":{},"ˉ":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"π":{"b":{"a":{"df":0,"docs":{},"t":{"c":{"df":0,"docs":{},"h":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"π":{"df":0,"docs":{},"s":{"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"df":0,"docs":{},"g":{"df":0,"docs":{},"l":{"df":0,"docs":{},"e":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"p":{"df":0,"docs":{},"ˉ":{"df":0,"docs":{},"​":{"df":0,"docs":{},"n":{"c":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"t":{"df":1,"docs":{"29":{"tf":1.0}}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}}},"df":0,"docs":{}}}}}}}}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}}}}},"df":0,"docs":{}}}}},"df":0,"docs":{}}}},"df":0,"docs":{}}}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{},"q":{"df":0,"docs":{},"l":{"df":0,"docs":{},"​":{"]":{"1":{"df":1,"docs":{"21":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"_":{"0":{"df":2,"docs":{"49":{"tf":1.0},"51":{"tf":1.0}}},"1":{"df":3,"docs":{"35":{"tf":1.0},"49":{"tf":1.0},"51":{"tf":1.0}}},"2":{"df":1,"docs":{"51":{"tf":1.0}}},"3":{"df":1,"docs":{"51":{"tf":1.0}}},"4":{"df":1,"docs":{"51":{"tf":1.0}}},"5":{"df":2,"docs":{"51":{"tf":1.0},"52":{"tf":1.4142135623730951}}},"6":{"df":1,"docs":{"51":{"tf":1.0}}},"7":{"df":1,"docs":{"51":{"tf":1.0}}},"df":0,"docs":{},"i":{"df":2,"docs":{"49":{"tf":1.0},"51":{"tf":1.0}}},"n":{"df":1,"docs":{"49":{"tf":1.4142135623730951}}},"z":{"df":0,"docs":{},"e":{"df":0,"docs":{},"t":{"a":{"df":1,"docs":{"35":{"tf":1.0}}},"df":0,"docs":{}}}},"{":{"df":0,"docs":{},"n":{"+":{"2":{"df":1,"docs":{"49":{"tf":1.0}}},"df":0,"docs":{}},"df":1,"docs":{"49":{"tf":1.0}}}}},"b":{"c":{"df":1,"docs":{"13":{"tf":1.0}}},"df":0,"docs":{},"l":{"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"d":{"df":1,"docs":{"20":{"tf":1.0}},"e":{"d":{"df":0,"docs":{},"​":{"(":{"df":0,"docs":{},"ω":{"df":0,"docs":{},"i":{")":{"=":{"a":{"(":{"df":0,"docs":{},"ω":{"df":0,"docs":{},"i":{"df":1,"docs":{"20":{"tf":1.0}}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":1,"docs":{"20":{"tf":1.0}}}}},":":{"=":{"(":{"b":{"0":{"df":0,"docs":{},"​":{"+":{"b":{"1":{"df":0,"docs":{},"​":{"df":0,"docs":{},"x":{"+":{"df":0,"docs":{},"⋯":{"+":{"b":{"df":0,"docs":{},"k":{"df":0,"docs":{},"​":{"df":0,"docs":{},"x":{"df":0,"docs":{},"k":{")":{"df":0,"docs":{},"z":{"df":0,"docs":{},"h":{"df":0,"docs":{},"​":{"+":{"a":{"df":1,"docs":{"20":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}}}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"o":{"df":0,"docs":{},"v":{"df":22,"docs":{"110":{"tf":1.0},"112":{"tf":1.0},"113":{"tf":1.0},"114":{"tf":2.0},"117":{"tf":1.0},"118":{"tf":1.0},"123":{"tf":1.0},"14":{"tf":1.0},"16":{"tf":1.0},"50":{"tf":1.0},"51":{"tf":1.0},"53":{"tf":1.0},"55":{"tf":1.0},"56":{"tf":1.0},"58":{"tf":1.4142135623730951},"60":{"tf":1.0},"62":{"tf":1.0},"64":{"tf":1.0},"66":{"tf":1.0},"74":{"tf":1.0},"86":{"tf":1.0},"95":{"tf":1.0}}}},"s":{"df":0,"docs":{},"t":{"df":0,"docs":{},"r":{"a":{"c":{"df":0,"docs":{},"t":{"df":2,"docs":{"48":{"tf":1.0},"49":{"tf":1.0}}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"u":{"df":0,"docs":{},"s":{"df":2,"docs":{"90":{"tf":1.0},"92":{"tf":1.0}}}}},"c":{"c":{"df":0,"docs":{},"e":{"df":0,"docs":{},"p":{"df":0,"docs":{},"t":{"df":10,"docs":{"102":{"tf":1.0},"19":{"tf":1.0},"33":{"tf":1.7320508075688772},"7":{"tf":1.0},"73":{"tf":1.0},"74":{"tf":1.0},"77":{"tf":1.4142135623730951},"80":{"tf":1.4142135623730951},"92":{"tf":1.0},"95":{"tf":1.4142135623730951}}}},"s":{"df":0,"docs":{},"s":{"df":5,"docs":{"107":{"tf":1.0},"133":{"tf":1.7320508075688772},"134":{"tf":1.4142135623730951},"135":{"tf":2.0},"146":{"tf":2.0}}}}},"o":{"df":0,"docs":{},"r":{"d":{"df":4,"docs":{"50":{"tf":1.0},"58":{"tf":1.0},"60":{"tf":1.0},"85":{"tf":1.0}}},"df":0,"docs":{}},"u":{"df":0,"docs":{},"n":{"df":0,"docs":{},"t":{"df":1,"docs":{"149":{"tf":1.0}}}}}}},"df":0,"docs":{},"h":{"df":0,"docs":{},"i":{"df":0,"docs":{},"e":{"df":0,"docs":{},"v":{"df":4,"docs":{"133":{"tf":1.0},"14":{"tf":1.0},"44":{"tf":1.0},"60":{"tf":1.0}}}}}},"t":{"df":1,"docs":{"14":{"tf":1.0}},"u":{"a":{"df":0,"docs":{},"l":{"df":22,"docs":{"102":{"tf":1.0},"107":{"tf":1.0},"108":{"tf":1.0},"113":{"tf":1.4142135623730951},"116":{"tf":1.0},"118":{"tf":1.0},"12":{"tf":1.0},"125":{"tf":2.0},"131":{"tf":1.0},"133":{"tf":2.23606797749979},"135":{"tf":1.0},"147":{"tf":1.0},"151":{"tf":1.0},"20":{"tf":1.0},"21":{"tf":1.4142135623730951},"32":{"tf":1.0},"34":{"tf":1.0},"46":{"tf":1.0},"50":{"tf":1.4142135623730951},"63":{"tf":1.4142135623730951},"67":{"tf":1.0},"9":{"tf":1.0}}}},"df":0,"docs":{}}}},"d":{"d":{"df":19,"docs":{"10":{"tf":1.4142135623730951},"102":{"tf":1.0},"114":{"tf":1.0},"13":{"tf":1.0},"145":{"tf":1.0},"146":{"tf":1.4142135623730951},"149":{"tf":1.0},"151":{"tf":1.7320508075688772},"24":{"tf":1.4142135623730951},"25":{"tf":1.0},"26":{"tf":1.0},"27":{"tf":1.0},"32":{"tf":2.0},"40":{"tf":1.0},"44":{"tf":1.0},"57":{"tf":1.0},"83":{"tf":1.0},"94":{"tf":2.8284271247461903},"95":{"tf":2.8284271247461903}},"i":{"df":0,"docs":{},"t":{"df":12,"docs":{"10":{"tf":1.4142135623730951},"128":{"tf":1.0},"13":{"tf":1.0},"130":{"tf":1.0},"133":{"tf":1.4142135623730951},"142":{"tf":1.4142135623730951},"146":{"tf":1.0},"147":{"tf":1.0},"19":{"tf":1.4142135623730951},"57":{"tf":1.0},"87":{"tf":1.0},"9":{"tf":1.0}},"i":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"df":4,"docs":{"146":{"tf":1.0},"150":{"tf":1.0},"151":{"tf":1.0},"68":{"tf":1.0}}}}}}},"r":{"df":2,"docs":{"133":{"tf":2.23606797749979},"135":{"tf":1.0}},"e":{"df":0,"docs":{},"s":{"df":0,"docs":{},"s":{"df":8,"docs":{"133":{"tf":2.8284271247461903},"134":{"tf":2.449489742783178},"135":{"tf":1.4142135623730951},"142":{"tf":1.4142135623730951},"144":{"tf":1.0},"146":{"tf":2.8284271247461903},"147":{"tf":1.7320508075688772},"151":{"tf":1.4142135623730951}}}}}}},"df":14,"docs":{"117":{"tf":1.0},"133":{"tf":1.0},"146":{"tf":2.0},"147":{"tf":1.4142135623730951},"20":{"tf":1.0},"31":{"tf":1.0},"32":{"tf":1.0},"34":{"tf":1.0},"42":{"tf":1.0},"70":{"tf":1.0},"83":{"tf":1.0},"84":{"tf":1.0},"88":{"tf":1.0},"89":{"tf":1.0}},"j":{"df":0,"docs":{},"u":{"df":0,"docs":{},"s":{"df":0,"docs":{},"t":{"df":1,"docs":{"71":{"tf":1.0}}}}}},"v":{"a":{"df":0,"docs":{},"n":{"c":{"df":3,"docs":{"52":{"tf":1.0},"79":{"tf":1.0},"83":{"tf":1.0}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{},"g":{"a":{"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"df":4,"docs":{"111":{"tf":1.0},"146":{"tf":1.0},"66":{"tf":1.0},"67":{"tf":1.0}},"s":{"df":0,"docs":{},"t":{"df":2,"docs":{"108":{"tf":1.0},"146":{"tf":1.0}}}}}}},"df":0,"docs":{},"g":{"df":0,"docs":{},"r":{"df":0,"docs":{},"e":{"df":0,"docs":{},"g":{"df":1,"docs":{"145":{"tf":1.0}}}}}},"r":{"df":0,"docs":{},"e":{"df":2,"docs":{"104":{"tf":1.0},"84":{"tf":1.0}}}}},"i":{",":{"0":{"df":0,"docs":{},"​":{"+":{"a":{"df":0,"docs":{},"i":{",":{"1":{"df":0,"docs":{},"​":{"df":0,"docs":{},"β":{"+":{"df":0,"docs":{},"γ":{",":{"df":0,"docs":{},"…":{",":{"a":{"df":0,"docs":{},"k":{"df":0,"docs":{},"−":{"1":{",":{"0":{"df":0,"docs":{},"​":{"+":{"a":{"df":0,"docs":{},"k":{"df":0,"docs":{},"−":{"1":{",":{"1":{"df":1,"docs":{"14":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{},"m":{"df":2,"docs":{"50":{"tf":1.0},"84":{"tf":1.4142135623730951}}},"r":{":":{":":{"df":0,"docs":{},"f":{"df":0,"docs":{},"r":{"a":{"df":0,"docs":{},"m":{"df":0,"docs":{},"e":{":":{":":{"df":0,"docs":{},"f":{"df":0,"docs":{},"r":{"a":{"df":0,"docs":{},"m":{"df":0,"docs":{},"e":{"<":{"df":0,"docs":{},"s":{"df":0,"docs":{},"e":{"df":0,"docs":{},"l":{"df":0,"docs":{},"f":{":":{":":{"df":0,"docs":{},"f":{"df":0,"docs":{},"i":{"df":0,"docs":{},"e":{"df":0,"docs":{},"l":{"d":{"df":2,"docs":{"107":{"tf":1.0},"114":{"tf":1.0}}},"df":0,"docs":{}}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}}}},"df":0,"docs":{}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"c":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"df":0,"docs":{},"t":{"df":0,"docs":{},"e":{"df":0,"docs":{},"x":{"df":0,"docs":{},"t":{"df":1,"docs":{"109":{"tf":1.4142135623730951}}}}}}}}},"df":14,"docs":{"105":{"tf":2.23606797749979},"106":{"tf":1.0},"108":{"tf":1.0},"109":{"tf":2.23606797749979},"110":{"tf":1.0},"113":{"tf":1.0},"114":{"tf":1.4142135623730951},"48":{"tf":1.0},"50":{"tf":2.449489742783178},"65":{"tf":1.0},"66":{"tf":1.0},"70":{"tf":1.0},"83":{"tf":1.0},"91":{"tf":1.0}}},"​":{"(":{"df":0,"docs":{},"q":{"df":0,"docs":{},"l":{"df":0,"docs":{},"​":{")":{"df":0,"docs":{},"i":{"df":0,"docs":{},"​":{"+":{"b":{"df":0,"docs":{},"i":{"df":0,"docs":{},"​":{"(":{"df":0,"docs":{},"q":{"df":0,"docs":{},"r":{"df":0,"docs":{},"​":{")":{"df":0,"docs":{},"i":{"df":0,"docs":{},"​":{"+":{"a":{"df":0,"docs":{},"i":{"df":0,"docs":{},"​":{"b":{"df":0,"docs":{},"i":{"df":0,"docs":{},"​":{"df":0,"docs":{},"q":{"df":0,"docs":{},"m":{"df":0,"docs":{},"​":{"+":{"c":{"df":0,"docs":{},"i":{"df":0,"docs":{},"​":{"(":{"df":0,"docs":{},"q":{"df":0,"docs":{},"o":{"df":0,"docs":{},"​":{")":{"df":0,"docs":{},"i":{"df":0,"docs":{},"​":{"+":{"(":{"df":0,"docs":{},"q":{"c":{"df":0,"docs":{},"​":{")":{"df":0,"docs":{},"i":{"df":0,"docs":{},"​":{"+":{"(":{"df":0,"docs":{},"p":{"df":0,"docs":{},"i":{")":{"df":0,"docs":{},"i":{"df":0,"docs":{},"​":{"=":{"0":{"df":3,"docs":{"13":{"tf":1.0},"14":{"tf":1.0},"16":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"=":{"0":{"df":2,"docs":{"10":{"tf":1.0},"11":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}}}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}}}},",":{"b":{"df":0,"docs":{},"i":{"df":0,"docs":{},"​":{"df":0,"docs":{},"∈":{"df":0,"docs":{},"f":{"df":1,"docs":{"14":{"tf":1.0}}}}}}},"df":0,"docs":{}},"=":{"(":{"df":0,"docs":{},"y":{"0":{"(":{"df":0,"docs":{},"i":{")":{",":{"df":0,"docs":{},"…":{",":{"df":0,"docs":{},"y":{"df":0,"docs":{},"l":{"(":{"df":0,"docs":{},"i":{"df":1,"docs":{"92":{"tf":1.0}}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"l":{"df":0,"docs":{},"g":{"df":0,"docs":{},"e":{"b":{"df":0,"docs":{},"r":{"a":{"df":1,"docs":{"48":{"tf":1.0}}},"df":0,"docs":{}}},"df":0,"docs":{}},"o":{"df":0,"docs":{},"r":{"df":0,"docs":{},"i":{"df":0,"docs":{},"t":{"df":0,"docs":{},"h":{"df":0,"docs":{},"m":{"df":9,"docs":{"121":{"tf":1.0},"19":{"tf":1.4142135623730951},"23":{"tf":1.7320508075688772},"3":{"tf":1.0},"30":{"tf":1.4142135623730951},"34":{"tf":1.7320508075688772},"45":{"tf":1.0},"7":{"tf":1.7320508075688772},"98":{"tf":1.0}}}}}}}}},"l":{"df":0,"docs":{},"o":{"c":{"df":1,"docs":{"131":{"tf":1.4142135623730951}}},"df":0,"docs":{},"w":{"df":6,"docs":{"124":{"tf":1.0},"131":{"tf":1.0},"14":{"tf":1.0},"19":{"tf":1.0},"64":{"tf":1.0},"75":{"tf":1.0}}}}},"o":{"df":0,"docs":{},"n":{"df":0,"docs":{},"g":{"df":10,"docs":{"103":{"tf":1.0},"110":{"tf":1.0},"113":{"tf":1.4142135623730951},"114":{"tf":1.0},"127":{"tf":1.0},"21":{"tf":2.0},"5":{"tf":1.0},"7":{"tf":1.0},"80":{"tf":1.0},"91":{"tf":1.0}}}}},"p":{"df":0,"docs":{},"h":{"a":{"_":{"a":{"df":0,"docs":{},"n":{"d":{"_":{"b":{"df":0,"docs":{},"e":{"df":0,"docs":{},"t":{"a":{"_":{"b":{"df":0,"docs":{},"o":{"df":0,"docs":{},"u":{"df":0,"docs":{},"n":{"d":{"a":{"df":0,"docs":{},"r":{"df":0,"docs":{},"y":{"_":{"c":{"df":0,"docs":{},"o":{"df":0,"docs":{},"e":{"df":0,"docs":{},"f":{"df":0,"docs":{},"f":{"df":0,"docs":{},"i":{"c":{"df":0,"docs":{},"i":{"df":1,"docs":{"114":{"tf":1.0}}}},"df":0,"docs":{}}}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{},"t":{"df":0,"docs":{},"r":{"a":{"df":0,"docs":{},"n":{"df":0,"docs":{},"s":{"df":0,"docs":{},"i":{"df":0,"docs":{},"t":{"df":0,"docs":{},"i":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"_":{"c":{"df":0,"docs":{},"o":{"df":0,"docs":{},"e":{"df":0,"docs":{},"f":{"df":0,"docs":{},"f":{"df":0,"docs":{},"i":{"c":{"df":0,"docs":{},"i":{"df":1,"docs":{"114":{"tf":1.0}}}},"df":0,"docs":{}}}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}}}}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":3,"docs":{"114":{"tf":1.0},"117":{"tf":1.0},"118":{"tf":1.0}}},"df":0,"docs":{}}},"r":{"df":0,"docs":{},"e":{"a":{"d":{"df":0,"docs":{},"i":{"df":9,"docs":{"10":{"tf":1.0},"14":{"tf":1.4142135623730951},"146":{"tf":1.4142135623730951},"21":{"tf":1.0},"23":{"tf":1.0},"32":{"tf":1.0},"50":{"tf":1.0},"84":{"tf":1.4142135623730951},"91":{"tf":1.0}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"t":{"df":0,"docs":{},"h":{"df":0,"docs":{},"o":{"df":0,"docs":{},"u":{"df":0,"docs":{},"g":{"df":0,"docs":{},"h":{"df":1,"docs":{"130":{"tf":1.0}}}}}}},"o":{"df":0,"docs":{},"g":{"df":0,"docs":{},"e":{"df":0,"docs":{},"t":{"df":0,"docs":{},"h":{"df":2,"docs":{"10":{"tf":1.0},"14":{"tf":1.4142135623730951}}}}}}}},"w":{"a":{"df":0,"docs":{},"y":{"df":9,"docs":{"107":{"tf":1.0},"12":{"tf":1.0},"124":{"tf":1.7320508075688772},"131":{"tf":1.0},"133":{"tf":1.0},"134":{"tf":1.0},"56":{"tf":1.0},"73":{"tf":1.0},"82":{"tf":1.0}}}},"df":0,"docs":{}}},"m":{"b":{"df":0,"docs":{},"i":{"df":0,"docs":{},"g":{"df":0,"docs":{},"u":{"df":1,"docs":{"144":{"tf":1.0}}}}}},"df":0,"docs":{},"o":{"df":0,"docs":{},"u":{"df":0,"docs":{},"n":{"df":0,"docs":{},"t":{"df":3,"docs":{"110":{"tf":1.0},"3":{"tf":1.0},"80":{"tf":1.0}}}}}}},"n":{"a":{"df":0,"docs":{},"l":{"df":0,"docs":{},"y":{"df":0,"docs":{},"z":{"df":2,"docs":{"134":{"tf":1.0},"77":{"tf":1.0}}}}}},"df":0,"docs":{},"o":{"df":0,"docs":{},"t":{"df":0,"docs":{},"h":{"df":7,"docs":{"146":{"tf":1.0},"149":{"tf":1.4142135623730951},"5":{"tf":1.0},"53":{"tf":1.0},"70":{"tf":1.0},"73":{"tf":1.0},"74":{"tf":1.0}}}}},"s":{"df":0,"docs":{},"w":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":1,"docs":{"114":{"tf":1.0}}}}}},"y":{"df":0,"docs":{},"m":{"df":0,"docs":{},"o":{"df":0,"docs":{},"r":{"df":1,"docs":{"116":{"tf":1.0}}}}},"o":{"df":0,"docs":{},"n":{"df":1,"docs":{"75":{"tf":1.0}}}},"t":{"df":0,"docs":{},"h":{"df":1,"docs":{"50":{"tf":1.0}}}},"w":{"a":{"df":0,"docs":{},"y":{"df":2,"docs":{"14":{"tf":1.0},"16":{"tf":1.0}}}},"df":0,"docs":{}}}},"p":{"_":{"df":0,"docs":{},"u":{"df":0,"docs":{},"p":{"d":{"df":1,"docs":{"146":{"tf":1.0}}},"df":0,"docs":{}}}},"a":{"df":0,"docs":{},"r":{"df":0,"docs":{},"t":{"df":1,"docs":{"35":{"tf":1.0}}}}},"df":6,"docs":{"136":{"tf":1.4142135623730951},"141":{"tf":1.0},"142":{"tf":1.0},"144":{"tf":1.0},"146":{"tf":1.7320508075688772},"147":{"tf":1.0}},"i":{"df":12,"docs":{"104":{"tf":1.7320508075688772},"105":{"tf":1.0},"106":{"tf":1.0},"107":{"tf":1.0},"108":{"tf":1.0},"109":{"tf":1.0},"110":{"tf":1.0},"121":{"tf":1.0},"34":{"tf":1.4142135623730951},"43":{"tf":1.7320508075688772},"44":{"tf":1.0},"45":{"tf":1.0}}},"p":{"df":0,"docs":{},"e":{"a":{"df":0,"docs":{},"r":{"df":6,"docs":{"119":{"tf":1.0},"130":{"tf":1.0},"131":{"tf":1.0},"134":{"tf":1.7320508075688772},"146":{"tf":1.0},"95":{"tf":1.0}}}},"df":0,"docs":{},"n":{"d":{"df":3,"docs":{"118":{"tf":1.7320508075688772},"142":{"tf":1.4142135623730951},"40":{"tf":2.449489742783178}}},"df":0,"docs":{}}},"l":{"df":0,"docs":{},"i":{"c":{"df":1,"docs":{"42":{"tf":1.0}}},"df":12,"docs":{"109":{"tf":1.4142135623730951},"125":{"tf":1.0},"14":{"tf":1.4142135623730951},"146":{"tf":1.0},"149":{"tf":1.4142135623730951},"150":{"tf":1.4142135623730951},"3":{"tf":1.0},"57":{"tf":1.0},"59":{"tf":1.0},"68":{"tf":1.0},"94":{"tf":1.7320508075688772},"97":{"tf":1.0}}}},"r":{"df":0,"docs":{},"o":{"a":{"c":{"df":0,"docs":{},"h":{"df":3,"docs":{"101":{"tf":1.0},"14":{"tf":1.0},"146":{"tf":1.0}}}},"df":0,"docs":{}},"df":0,"docs":{},"p":{"df":0,"docs":{},"r":{"df":0,"docs":{},"i":{"df":3,"docs":{"114":{"tf":1.4142135623730951},"13":{"tf":1.0},"66":{"tf":1.0}}}}}}}}},"q":{"df":0,"docs":{},"l":{"df":0,"docs":{},"​":{"+":{"b":{"df":0,"docs":{},"q":{"df":0,"docs":{},"r":{"df":1,"docs":{"21":{"tf":1.0}},"​":{"+":{"a":{"b":{"df":0,"docs":{},"q":{"df":0,"docs":{},"m":{"df":0,"docs":{},"​":{"+":{"c":{"df":0,"docs":{},"q":{"df":0,"docs":{},"o":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"q":{"c":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"p":{"df":0,"docs":{},"i":{"=":{"df":0,"docs":{},"z":{"df":0,"docs":{},"h":{"df":0,"docs":{},"​":{"df":0,"docs":{},"t":{"1":{"df":1,"docs":{"16":{"tf":1.0}}},"df":1,"docs":{"14":{"tf":1.0}}}}}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"r":{"b":{"df":0,"docs":{},"i":{"df":0,"docs":{},"t":{"df":0,"docs":{},"r":{"a":{"df":0,"docs":{},"r":{"df":0,"docs":{},"i":{"df":1,"docs":{"142":{"tf":1.0}}}}},"df":0,"docs":{}}}}},"df":1,"docs":{"28":{"tf":1.0}},"g":{"df":0,"docs":{},"u":{"df":0,"docs":{},"m":{"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"df":0,"docs":{},"t":{"df":4,"docs":{"107":{"tf":1.0},"114":{"tf":1.0},"146":{"tf":1.0},"147":{"tf":1.4142135623730951}}}}}}}},"i":{"df":0,"docs":{},"t":{"df":0,"docs":{},"h":{"df":0,"docs":{},"m":{"df":0,"docs":{},"e":{"df":0,"docs":{},"t":{"df":9,"docs":{"14":{"tf":1.0},"4":{"tf":1.7320508075688772},"50":{"tf":1.0},"69":{"tf":1.4142135623730951},"7":{"tf":1.0},"70":{"tf":1.4142135623730951},"8":{"tf":1.4142135623730951},"83":{"tf":1.4142135623730951},"94":{"tf":1.0}}}}}}}},"o":{"df":0,"docs":{},"u":{"df":0,"docs":{},"n":{"d":{"df":5,"docs":{"104":{"tf":1.0},"124":{"tf":1.0},"50":{"tf":1.0},"65":{"tf":1.0},"68":{"tf":1.0}}},"df":0,"docs":{}}}},"r":{"a":{"df":0,"docs":{},"n":{"df":0,"docs":{},"g":{"df":1,"docs":{"127":{"tf":1.0}}}}},"df":0,"docs":{},"i":{"df":0,"docs":{},"v":{"df":1,"docs":{"15":{"tf":1.0}}}}},"t":{"df":0,"docs":{},"i":{"c":{"df":0,"docs":{},"l":{"df":1,"docs":{"101":{"tf":1.0}}}},"df":0,"docs":{}}}},"s":{"c":{"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"d":{"df":4,"docs":{"130":{"tf":1.0},"132":{"tf":1.0},"134":{"tf":1.0},"135":{"tf":1.0}}},"df":0,"docs":{}}}},"df":0,"docs":{},"i":{"d":{"df":2,"docs":{"13":{"tf":1.0},"7":{"tf":1.0}}},"df":0,"docs":{}},"k":{"df":4,"docs":{"58":{"tf":1.4142135623730951},"60":{"tf":1.0},"72":{"tf":1.0},"80":{"tf":1.0}}},"p":{"df":0,"docs":{},"e":{"c":{"df":0,"docs":{},"t":{"df":1,"docs":{"70":{"tf":1.0}}}},"df":0,"docs":{}}},"s":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":0,"docs":{},"t":{"!":{"(":{"df":0,"docs":{},"v":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":0,"docs":{},"i":{"df":0,"docs":{},"f":{"df":0,"docs":{},"i":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{".":{"df":0,"docs":{},"v":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":0,"docs":{},"i":{"df":0,"docs":{},"f":{"df":0,"docs":{},"i":{"df":1,"docs":{"35":{"tf":1.4142135623730951}}},"y":{"(":{"&":{"df":0,"docs":{},"p":{"df":0,"docs":{},"r":{"df":0,"docs":{},"o":{"df":0,"docs":{},"o":{"df":0,"docs":{},"f":{"df":1,"docs":{"44":{"tf":1.0}}}}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}}}}}},"df":0,"docs":{}}}},"y":{"(":{"&":{"df":0,"docs":{},"p":{"df":0,"docs":{},"r":{"df":0,"docs":{},"o":{"df":0,"docs":{},"o":{"df":0,"docs":{},"f":{"df":1,"docs":{"110":{"tf":1.0}}}}}}}},"df":0,"docs":{}},":":{":":{"<":{"df":0,"docs":{},"f":{"df":1,"docs":{"104":{"tf":1.0}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}}}}}},"df":0,"docs":{}},"(":{"df":0,"docs":{},"x":{"*":{"df":0,"docs":{},"e":{"+":{"5":{"=":{"=":{"df":0,"docs":{},"i":{"df":1,"docs":{"34":{"tf":1.0}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":1,"docs":{"35":{"tf":1.0}}}},"df":4,"docs":{"13":{"tf":1.0},"142":{"tf":1.0},"34":{"tf":1.0},"44":{"tf":1.4142135623730951}}}}},"i":{"df":0,"docs":{},"g":{"df":0,"docs":{},"n":{"df":3,"docs":{"101":{"tf":1.4142135623730951},"151":{"tf":1.0},"44":{"tf":1.0}}}}},"o":{"c":{"df":0,"docs":{},"i":{"df":4,"docs":{"130":{"tf":1.0},"149":{"tf":1.4142135623730951},"151":{"tf":1.4142135623730951},"79":{"tf":1.0}}}},"df":0,"docs":{}},"u":{"df":0,"docs":{},"m":{"df":13,"docs":{"118":{"tf":1.0},"149":{"tf":1.4142135623730951},"150":{"tf":1.0},"151":{"tf":1.0},"21":{"tf":1.0},"23":{"tf":1.0},"51":{"tf":1.0},"70":{"tf":1.0},"77":{"tf":1.0},"84":{"tf":1.0},"89":{"tf":1.0},"91":{"tf":2.23606797749979},"95":{"tf":1.4142135623730951}}}}},"t":{"df":0,"docs":{},"u":{"df":0,"docs":{},"t":{"df":1,"docs":{"13":{"tf":1.0}}}}}},"t":{"df":1,"docs":{"38":{"tf":1.0}},"t":{"a":{"c":{"df":0,"docs":{},"k":{"df":1,"docs":{"42":{"tf":1.0}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"u":{"df":0,"docs":{},"g":{"df":0,"docs":{},"m":{"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"df":0,"docs":{},"t":{"df":3,"docs":{"142":{"tf":1.0},"146":{"tf":1.0},"149":{"tf":1.0}}}}}}},"t":{"df":0,"docs":{},"h":{"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"df":0,"docs":{},"t":{"df":6,"docs":{"101":{"tf":1.7320508075688772},"102":{"tf":1.0},"72":{"tf":1.7320508075688772},"80":{"tf":1.4142135623730951},"92":{"tf":1.4142135623730951},"95":{"tf":1.7320508075688772}}}}}}}},"v":{"a":{"df":0,"docs":{},"i":{"df":0,"docs":{},"l":{"df":2,"docs":{"130":{"tf":1.0},"144":{"tf":1.0}}}}},"df":0,"docs":{},"o":{"df":0,"docs":{},"i":{"d":{"df":2,"docs":{"146":{"tf":1.0},"95":{"tf":1.0}}},"df":0,"docs":{}}}},"ˉ":{",":{"b":{"df":0,"docs":{},"ˉ":{",":{"c":{"df":0,"docs":{},"ˉ":{",":{"df":0,"docs":{},"s":{"df":0,"docs":{},"ˉ":{"df":0,"docs":{},"σ":{"1":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"s":{"df":0,"docs":{},"ˉ":{"df":0,"docs":{},"σ":{"2":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"z":{"df":1,"docs":{"32":{"tf":1.0}}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"=":{"a":{"(":{"df":0,"docs":{},"ζ":{")":{",":{"b":{"df":0,"docs":{},"ˉ":{"=":{"b":{"(":{"df":0,"docs":{},"ζ":{")":{",":{"c":{"df":0,"docs":{},"ˉ":{"=":{"c":{"(":{"df":0,"docs":{},"ζ":{")":{",":{"df":0,"docs":{},"s":{"df":0,"docs":{},"ˉ":{"df":0,"docs":{},"σ":{"1":{"df":0,"docs":{},"​":{"=":{"df":0,"docs":{},"s":{"df":0,"docs":{},"σ":{"1":{"df":0,"docs":{},"​":{"(":{"df":0,"docs":{},"ζ":{")":{",":{"df":0,"docs":{},"s":{"df":0,"docs":{},"ˉ":{"df":0,"docs":{},"σ":{"2":{"df":0,"docs":{},"​":{"=":{"df":0,"docs":{},"s":{"df":0,"docs":{},"σ":{"2":{"df":0,"docs":{},"​":{"(":{"df":0,"docs":{},"ζ":{")":{",":{"df":0,"docs":{},"z":{"df":0,"docs":{},"ˉ":{"df":0,"docs":{},"ω":{"df":0,"docs":{},"​":{"=":{"df":0,"docs":{},"z":{"df":1,"docs":{"27":{"tf":1.0}}}},"df":0,"docs":{}}}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{},"i":{"df":0,"docs":{},"​":{"=":{"(":{"a":{"df":0,"docs":{},"i":{",":{"0":{"df":0,"docs":{},"​":{",":{"a":{"df":0,"docs":{},"i":{",":{"1":{"df":1,"docs":{"14":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}},"′":{",":{"b":{"df":0,"docs":{},"′":{",":{"c":{"df":1,"docs":{"24":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"=":{"df":0,"docs":{},"{":{"a":{"0":{"df":0,"docs":{},"​":{"+":{"a":{"1":{"df":0,"docs":{},"​":{"df":0,"docs":{},"y":{"+":{"df":0,"docs":{},"x":{",":{"a":{"2":{"df":0,"docs":{},"​":{"+":{"a":{"3":{"df":0,"docs":{},"​":{"df":0,"docs":{},"y":{"+":{"df":0,"docs":{},"x":{"df":1,"docs":{"14":{"tf":1.0}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{},"x":{",":{"a":{"1":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"x":{"df":1,"docs":{"14":{"tf":1.0}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"b":{"(":{"df":0,"docs":{},"x":{")":{"=":{"(":{"df":0,"docs":{},"x":{"df":0,"docs":{},"−":{"1":{")":{"(":{"df":0,"docs":{},"x":{"df":0,"docs":{},"−":{"df":0,"docs":{},"g":{")":{"df":0,"docs":{},"t":{"(":{"df":0,"docs":{},"x":{")":{"df":0,"docs":{},"−":{"df":0,"docs":{},"p":{"(":{"df":0,"docs":{},"x":{"df":1,"docs":{"54":{"tf":1.0}}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":3,"docs":{"35":{"tf":1.0},"54":{"tf":1.0},"56":{"tf":1.0}}},"z":{"df":2,"docs":{"113":{"tf":1.0},"63":{"tf":1.0}}}},"0":{"df":1,"docs":{"149":{"tf":2.8284271247461903}},"​":{"+":{"df":0,"docs":{},"β":{"b":{"1":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"γ":{")":{"(":{"b":{"2":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"β":{"b":{"3":{"df":1,"docs":{"14":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"γ":{",":{"df":0,"docs":{},"…":{",":{"b":{"df":0,"docs":{},"k":{"df":0,"docs":{},"−":{"1":{"df":1,"docs":{"14":{"tf":1.4142135623730951}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},",":{"df":0,"docs":{},"…":{",":{"b":{"df":0,"docs":{},"k":{"df":1,"docs":{"20":{"tf":1.0}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"b":{"1":{"df":1,"docs":{"14":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}}},"1":{"0":{"df":0,"docs":{},"​":{",":{"b":{"1":{"1":{"df":1,"docs":{"26":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":1,"docs":{"149":{"tf":2.8284271247461903}},"​":{",":{"b":{"2":{"df":0,"docs":{},"​":{",":{"b":{"3":{"df":0,"docs":{},"​":{",":{"b":{"4":{"df":0,"docs":{},"​":{",":{"b":{"5":{"df":0,"docs":{},"​":{",":{"b":{"6":{"df":1,"docs":{"24":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"2":{"df":1,"docs":{"149":{"tf":2.8284271247461903}}},"3":{"df":1,"docs":{"149":{"tf":2.8284271247461903}}},"7":{"df":0,"docs":{},"​":{",":{"b":{"8":{"df":0,"docs":{},"​":{",":{"b":{"9":{"df":1,"docs":{"25":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},":":{"=":{"(":{"b":{"3":{"df":0,"docs":{},"​":{"df":0,"docs":{},"x":{"+":{"b":{"4":{"df":0,"docs":{},"​":{")":{"df":0,"docs":{},"z":{"df":0,"docs":{},"h":{"df":0,"docs":{},"​":{"+":{"b":{"df":1,"docs":{"24":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"=":{"2":{"df":0,"docs":{},"l":{"df":1,"docs":{"91":{"tf":1.0}}}},"df":0,"docs":{},"m":{"/":{"df":0,"docs":{},"n":{"=":{"2":{"df":0,"docs":{},"m":{"df":0,"docs":{},"−":{"df":0,"docs":{},"n":{"df":1,"docs":{"73":{"tf":1.0}}}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"{":{"(":{"b":{"0":{"df":0,"docs":{},"​":{",":{"b":{"1":{"df":0,"docs":{},"​":{")":{",":{"(":{"b":{"2":{"df":0,"docs":{},"​":{",":{"b":{"3":{"df":1,"docs":{"14":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{},"η":{"3":{"df":0,"docs":{},"k":{"+":{"df":0,"docs":{},"l":{",":{"df":0,"docs":{},"t":{"df":0,"docs":{},"i":{",":{"df":0,"docs":{},"j":{"df":0,"docs":{},"​":{")":{":":{"(":{"df":0,"docs":{},"i":{",":{"df":0,"docs":{},"j":{")":{"df":0,"docs":{},"∈":{"df":0,"docs":{},"i":{",":{"df":0,"docs":{},"σ":{"(":{"(":{"df":0,"docs":{},"i":{",":{"df":0,"docs":{},"j":{")":{")":{"=":{"(":{"df":0,"docs":{},"k":{",":{"df":0,"docs":{},"l":{"df":1,"docs":{"14":{"tf":1.0}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"σ":{"(":{"(":{"df":0,"docs":{},"i":{",":{"df":0,"docs":{},"j":{")":{")":{",":{"df":0,"docs":{},"t":{"df":0,"docs":{},"i":{",":{"df":0,"docs":{},"j":{"df":0,"docs":{},"​":{")":{":":{"(":{"df":0,"docs":{},"i":{",":{"df":0,"docs":{},"j":{")":{"df":0,"docs":{},"∈":{"df":0,"docs":{},"i":{"df":1,"docs":{"14":{"tf":2.0}}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"6":{",":{"1":{"0":{"df":1,"docs":{"14":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"b":{"0":{"df":0,"docs":{},"​":{",":{"b":{"1":{"df":1,"docs":{"14":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{},"…":{",":{"b":{"df":0,"docs":{},"k":{"df":0,"docs":{},"−":{"1":{"df":1,"docs":{"14":{"tf":1.4142135623730951}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{},"ˉ":{"0":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"…":{",":{"b":{"df":0,"docs":{},"ˉ":{"df":0,"docs":{},"k":{"df":0,"docs":{},"−":{"1":{"df":1,"docs":{"14":{"tf":1.0}}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"_":{"1":{"(":{"df":0,"docs":{},"x":{"df":1,"docs":{"65":{"tf":1.0}}}},"df":1,"docs":{"35":{"tf":1.0}}},"df":0,"docs":{},"i":{"df":1,"docs":{"65":{"tf":1.0}}},"k":{"(":{"df":0,"docs":{},"x":{"df":1,"docs":{"65":{"tf":1.0}}}},"df":0,"docs":{}},"z":{"df":0,"docs":{},"e":{"df":0,"docs":{},"t":{"a":{"df":1,"docs":{"35":{"tf":1.0}}},"df":0,"docs":{}}}}},"a":{"c":{"df":0,"docs":{},"k":{"df":2,"docs":{"14":{"tf":1.4142135623730951},"77":{"tf":1.0}},"e":{"df":0,"docs":{},"n":{"d":{"df":1,"docs":{"34":{"tf":1.0}}},"df":0,"docs":{}}}}},"df":0,"docs":{},"l":{"a":{"df":0,"docs":{},"n":{"c":{"df":1,"docs":{"146":{"tf":1.0}}},"df":0,"docs":{}}},"df":0,"docs":{}},"s":{"df":0,"docs":{},"e":{"df":1,"docs":{"45":{"tf":1.4142135623730951}}},"i":{"c":{"df":2,"docs":{"128":{"tf":1.0},"9":{"tf":1.0}}},"df":3,"docs":{"32":{"tf":1.0},"35":{"tf":1.0},"5":{"tf":1.0}}}},"t":{"c":{"df":0,"docs":{},"h":{"df":7,"docs":{"28":{"tf":1.0},"35":{"tf":1.0},"80":{"tf":2.8284271247461903},"86":{"tf":1.7320508075688772},"92":{"tf":1.4142135623730951},"94":{"tf":2.0},"98":{"tf":1.7320508075688772}}}},"df":0,"docs":{}}},"df":20,"docs":{"10":{"tf":1.7320508075688772},"11":{"tf":1.4142135623730951},"112":{"tf":1.4142135623730951},"12":{"tf":1.0},"13":{"tf":1.4142135623730951},"14":{"tf":4.795831523312719},"142":{"tf":1.4142135623730951},"146":{"tf":1.7320508075688772},"147":{"tf":2.449489742783178},"149":{"tf":1.0},"150":{"tf":1.4142135623730951},"16":{"tf":1.7320508075688772},"21":{"tf":1.4142135623730951},"35":{"tf":1.4142135623730951},"36":{"tf":1.0},"54":{"tf":1.7320508075688772},"55":{"tf":1.0},"62":{"tf":1.4142135623730951},"68":{"tf":1.7320508075688772},"9":{"tf":1.0}},"e":{"c":{"df":0,"docs":{},"o":{"df":0,"docs":{},"m":{"df":2,"docs":{"114":{"tf":1.0},"131":{"tf":1.0}}}}},"df":11,"docs":{"109":{"tf":1.0},"12":{"tf":1.0},"124":{"tf":1.0},"14":{"tf":1.4142135623730951},"149":{"tf":1.0},"19":{"tf":1.0},"5":{"tf":1.0},"52":{"tf":1.0},"68":{"tf":1.0},"69":{"tf":1.0},"91":{"tf":1.0}},"f":{"df":0,"docs":{},"o":{"df":0,"docs":{},"r":{"df":9,"docs":{"107":{"tf":1.0},"14":{"tf":1.4142135623730951},"146":{"tf":1.0},"21":{"tf":1.0},"44":{"tf":1.0},"69":{"tf":1.0},"74":{"tf":1.0},"80":{"tf":1.0},"83":{"tf":1.0}},"e":{"df":0,"docs":{},"h":{"a":{"df":0,"docs":{},"n":{"d":{"df":3,"docs":{"104":{"tf":1.4142135623730951},"21":{"tf":1.0},"56":{"tf":1.0}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}}},"g":{"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"df":4,"docs":{"147":{"tf":1.0},"15":{"tf":1.0},"71":{"tf":1.0},"87":{"tf":1.0}}}}},"h":{"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"d":{"df":2,"docs":{"151":{"tf":1.0},"4":{"tf":1.0}}},"df":0,"docs":{}}}},"l":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"df":0,"docs":{},"g":{"df":3,"docs":{"113":{"tf":1.0},"60":{"tf":1.4142135623730951},"63":{"tf":1.0}}}},"w":{"df":4,"docs":{"104":{"tf":1.0},"16":{"tf":1.0},"51":{"tf":1.0},"61":{"tf":1.0}}}}},"n":{"c":{"df":0,"docs":{},"h":{"df":0,"docs":{},"m":{"a":{"df":0,"docs":{},"r":{"df":0,"docs":{},"k":{"df":2,"docs":{"2":{"tf":1.7320508075688772},"3":{"tf":1.7320508075688772}}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"t":{"a":{"_":{"1":{"df":1,"docs":{"56":{"tf":1.0}}},"2":{"df":1,"docs":{"56":{"tf":1.0}}},"df":0,"docs":{}},"df":4,"docs":{"114":{"tf":1.0},"117":{"tf":1.0},"118":{"tf":1.0},"56":{"tf":1.0}}},"df":0,"docs":{},"t":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":7,"docs":{"10":{"tf":1.0},"101":{"tf":1.0},"133":{"tf":1.0},"14":{"tf":1.0},"151":{"tf":1.0},"21":{"tf":1.0},"7":{"tf":1.4142135623730951}}}}},"w":{"df":0,"docs":{},"e":{"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"df":10,"docs":{"107":{"tf":1.0},"118":{"tf":1.4142135623730951},"138":{"tf":1.0},"142":{"tf":1.0},"146":{"tf":1.0},"150":{"tf":1.0},"151":{"tf":2.0},"66":{"tf":1.0},"75":{"tf":1.0},"9":{"tf":1.0}}}}}}}},"i":{",":{"0":{"df":0,"docs":{},"​":{"+":{"b":{"df":0,"docs":{},"i":{",":{"1":{"df":0,"docs":{},"​":{"df":0,"docs":{},"β":{"+":{"df":0,"docs":{},"γ":{",":{"df":0,"docs":{},"…":{",":{"b":{"df":0,"docs":{},"k":{"df":0,"docs":{},"−":{"1":{",":{"0":{"df":0,"docs":{},"​":{"+":{"b":{"df":0,"docs":{},"k":{"df":0,"docs":{},"−":{"1":{",":{"1":{"df":1,"docs":{"14":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"a":{"df":0,"docs":{},"s":{"df":1,"docs":{"146":{"tf":1.7320508075688772}}}},"df":0,"docs":{},"g":{"df":2,"docs":{"114":{"tf":1.0},"41":{"tf":1.0}}},"n":{"a":{"df":0,"docs":{},"r":{"df":0,"docs":{},"i":{"df":3,"docs":{"101":{"tf":1.7320508075688772},"150":{"tf":1.0},"91":{"tf":1.0}}}}},"df":0,"docs":{}},"t":{"df":9,"docs":{"101":{"tf":2.23606797749979},"131":{"tf":2.0},"146":{"tf":2.0},"151":{"tf":1.0},"41":{"tf":1.7320508075688772},"49":{"tf":1.0},"56":{"tf":1.0},"88":{"tf":1.4142135623730951},"9":{"tf":1.0}}}},"j":{"df":0,"docs":{},"​":{":":{"=":{"df":0,"docs":{},"z":{"df":0,"docs":{},"j":{"b":{"df":0,"docs":{},"​":{"(":{"df":0,"docs":{},"z":{")":{"df":0,"docs":{},"τ":{"df":0,"docs":{},"j":{"df":0,"docs":{},"z":{"df":0,"docs":{},"​":{"df":0,"docs":{},"−":{"df":0,"docs":{},"p":{"df":0,"docs":{},"j":{"b":{"df":0,"docs":{},"​":{"(":{"df":0,"docs":{},"z":{"df":1,"docs":{"95":{"tf":1.0}}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}}}}}},"df":0,"docs":{}}},"df":0,"docs":{},"t":{"df":0,"docs":{},"j":{"df":0,"docs":{},"​":{"df":0,"docs":{},"−":{"df":0,"docs":{},"p":{"df":0,"docs":{},"j":{"b":{"df":1,"docs":{"94":{"tf":1.0}}},"df":0,"docs":{}}}}}}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"l":{"a":{"c":{"df":0,"docs":{},"k":{"df":2,"docs":{"57":{"tf":1.0},"68":{"tf":1.0}}}},"df":0,"docs":{}},"df":1,"docs":{"38":{"tf":1.0}},"i":{"df":0,"docs":{},"n":{"d":{"df":2,"docs":{"20":{"tf":2.0},"35":{"tf":1.0}}},"df":0,"docs":{}}},"o":{"c":{"df":0,"docs":{},"k":{"df":1,"docs":{"74":{"tf":1.0}}}},"df":0,"docs":{},"w":{"df":0,"docs":{},"u":{"df":0,"docs":{},"p":{"_":{"df":0,"docs":{},"f":{"a":{"c":{"df":0,"docs":{},"t":{"df":0,"docs":{},"o":{"df":0,"docs":{},"r":{"df":2,"docs":{"109":{"tf":1.4142135623730951},"114":{"tf":1.0}}}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":4,"docs":{"125":{"tf":1.0},"68":{"tf":1.7320508075688772},"73":{"tf":1.0},"91":{"tf":1.4142135623730951}}}}}},"s":{"1":{"2":{"3":{"8":{"1":{"df":1,"docs":{"44":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"o":{"df":0,"docs":{},"i":{"df":0,"docs":{},"l":{"df":3,"docs":{"14":{"tf":1.0},"16":{"tf":1.0},"21":{"tf":1.0}}}},"l":{"d":{"df":1,"docs":{"95":{"tf":1.0}}},"df":0,"docs":{}},"o":{"df":0,"docs":{},"l":{"df":1,"docs":{"35":{"tf":1.0}}}},"t":{"df":0,"docs":{},"h":{"df":18,"docs":{"101":{"tf":1.7320508075688772},"104":{"tf":1.0},"105":{"tf":1.0},"114":{"tf":1.0},"118":{"tf":1.4142135623730951},"123":{"tf":1.0},"14":{"tf":1.0},"16":{"tf":1.0},"34":{"tf":1.0},"53":{"tf":1.0},"58":{"tf":1.0},"59":{"tf":1.0},"60":{"tf":1.0},"61":{"tf":1.0},"7":{"tf":1.0},"74":{"tf":1.0},"75":{"tf":1.0},"91":{"tf":1.0}}},"t":{"df":0,"docs":{},"o":{"df":0,"docs":{},"m":{"df":1,"docs":{"142":{"tf":1.4142135623730951}}}}}},"u":{"df":0,"docs":{},"n":{"d":{"a":{"df":0,"docs":{},"r":{"df":0,"docs":{},"i":{"df":18,"docs":{"105":{"tf":1.4142135623730951},"106":{"tf":2.0},"112":{"tf":1.0},"113":{"tf":1.0},"114":{"tf":1.4142135623730951},"123":{"tf":1.4142135623730951},"125":{"tf":1.0},"49":{"tf":1.0},"53":{"tf":1.0},"54":{"tf":3.0},"56":{"tf":1.0},"58":{"tf":1.0},"62":{"tf":1.0},"65":{"tf":1.4142135623730951},"70":{"tf":1.0},"84":{"tf":1.7320508075688772},"85":{"tf":1.0},"91":{"tf":1.4142135623730951}}},"y":{"_":{"c":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"df":0,"docs":{},"s":{"df":0,"docs":{},"t":{"df":0,"docs":{},"r":{"a":{"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"df":0,"docs":{},"t":{"df":2,"docs":{"105":{"tf":1.0},"106":{"tf":1.0}}}}}},"df":0,"docs":{}}}}}}},"df":0,"docs":{}},"c":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"df":0,"docs":{},"s":{"df":0,"docs":{},"t":{"df":0,"docs":{},"r":{"a":{"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"df":0,"docs":{},"t":{":":{":":{"df":0,"docs":{},"n":{"df":0,"docs":{},"e":{"df":0,"docs":{},"w":{"_":{"df":0,"docs":{},"s":{"df":0,"docs":{},"i":{"df":0,"docs":{},"m":{"df":0,"docs":{},"p":{"df":0,"docs":{},"l":{"df":0,"docs":{},"e":{"(":{"0":{"df":1,"docs":{"106":{"tf":1.0}}},"1":{"df":1,"docs":{"106":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}}}}}}}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":1,"docs":{"106":{"tf":1.4142135623730951}},"s":{":":{":":{"df":0,"docs":{},"f":{"df":0,"docs":{},"r":{"df":0,"docs":{},"o":{"df":0,"docs":{},"m":{"_":{"c":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"df":0,"docs":{},"s":{"df":0,"docs":{},"t":{"df":0,"docs":{},"r":{"a":{"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"df":0,"docs":{},"t":{"df":0,"docs":{},"s":{"(":{"df":0,"docs":{},"v":{"df":0,"docs":{},"e":{"c":{"!":{"[":{"a":{"0":{"df":1,"docs":{"106":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}}}}},"df":0,"docs":{}}}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}}}},"df":0,"docs":{}},"<":{"df":0,"docs":{},"s":{"df":0,"docs":{},"e":{"df":0,"docs":{},"l":{"df":0,"docs":{},"f":{":":{":":{"df":0,"docs":{},"f":{"df":0,"docs":{},"i":{"df":0,"docs":{},"e":{"df":0,"docs":{},"l":{"d":{"df":1,"docs":{"106":{"tf":1.0}}},"df":0,"docs":{}}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}}}},"df":0,"docs":{}}}}}},"df":0,"docs":{}}}}}}},"df":0,"docs":{}}}},"df":2,"docs":{"73":{"tf":1.0},"75":{"tf":1.0}}},"df":0,"docs":{}}},"x":{"df":2,"docs":{"57":{"tf":1.0},"68":{"tf":1.0}}}},"r":{"a":{"df":0,"docs":{},"k":{"df":0,"docs":{},"e":{"df":1,"docs":{"35":{"tf":1.0}}}}},"df":0,"docs":{},"e":{"a":{"df":0,"docs":{},"k":{"df":2,"docs":{"130":{"tf":1.0},"146":{"tf":1.4142135623730951}}}},"df":0,"docs":{}},"o":{"a":{"d":{"df":0,"docs":{},"l":{"df":0,"docs":{},"i":{"df":1,"docs":{"128":{"tf":1.0}}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"u":{"df":0,"docs":{},"i":{"df":0,"docs":{},"l":{"d":{"_":{"c":{"a":{"df":0,"docs":{},"i":{"df":0,"docs":{},"r":{"df":0,"docs":{},"o":{"_":{"df":0,"docs":{},"e":{"df":0,"docs":{},"x":{"df":0,"docs":{},"e":{"c":{"df":0,"docs":{},"u":{"df":0,"docs":{},"t":{"df":0,"docs":{},"i":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"_":{"df":0,"docs":{},"t":{"df":0,"docs":{},"r":{"a":{"c":{"df":1,"docs":{"146":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}}}}}},"df":0,"docs":{}}}}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":20,"docs":{"100":{"tf":1.0},"101":{"tf":1.0},"110":{"tf":1.0},"114":{"tf":1.4142135623730951},"144":{"tf":1.0},"145":{"tf":1.0},"146":{"tf":1.0},"43":{"tf":1.0},"44":{"tf":1.0},"45":{"tf":1.7320508075688772},"50":{"tf":1.0},"56":{"tf":1.0},"71":{"tf":1.0},"72":{"tf":1.0},"74":{"tf":1.0},"84":{"tf":1.0},"9":{"tf":1.0},"92":{"tf":1.0},"94":{"tf":1.4142135623730951},"97":{"tf":1.4142135623730951}}},"df":0,"docs":{},"t":{"df":9,"docs":{"114":{"tf":1.4142135623730951},"127":{"tf":1.4142135623730951},"137":{"tf":1.0},"138":{"tf":1.0},"139":{"tf":1.0},"145":{"tf":1.0},"146":{"tf":1.4142135623730951},"151":{"tf":3.7416573867739413},"80":{"tf":1.4142135623730951}},"i":{"df":0,"docs":{},"n":{"df":2,"docs":{"144":{"tf":1.0},"151":{"tf":2.8284271247461903}}}}}}},"n":{"c":{"df":0,"docs":{},"h":{"df":2,"docs":{"20":{"tf":1.0},"95":{"tf":1.0}}}},"df":0,"docs":{}}},"y":{"df":0,"docs":{},"t":{"df":0,"docs":{},"e":{"c":{"df":0,"docs":{},"o":{"d":{"df":2,"docs":{"146":{"tf":1.4142135623730951},"50":{"tf":1.4142135623730951}}},"df":0,"docs":{}}},"df":1,"docs":{"40":{"tf":1.0}}}}},"ˉ":{"df":0,"docs":{},"i":{"df":1,"docs":{"14":{"tf":1.0}}}},"′":{"=":{"df":0,"docs":{},"{":{"b":{"0":{"df":0,"docs":{},"​":{"+":{"b":{"1":{"df":0,"docs":{},"​":{"df":0,"docs":{},"y":{"+":{"df":0,"docs":{},"x":{",":{"b":{"2":{"df":0,"docs":{},"​":{"+":{"b":{"3":{"df":0,"docs":{},"​":{"df":0,"docs":{},"y":{"+":{"df":0,"docs":{},"x":{"df":1,"docs":{"14":{"tf":1.0}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{},"x":{",":{"b":{"1":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"x":{"df":1,"docs":{"14":{"tf":1.0}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"c":{"(":{"df":0,"docs":{},"x":{")":{"=":{"df":0,"docs":{},"∏":{"df":0,"docs":{},"i":{"=":{"0":{"5":{"df":0,"docs":{},"​":{"(":{"df":0,"docs":{},"x":{"df":0,"docs":{},"−":{"df":0,"docs":{},"g":{"df":0,"docs":{},"i":{")":{"df":0,"docs":{},"t":{"(":{"df":0,"docs":{},"x":{"df":0,"docs":{},"g":{"2":{")":{"df":0,"docs":{},"−":{"df":0,"docs":{},"t":{"(":{"df":0,"docs":{},"x":{"df":0,"docs":{},"g":{")":{"df":0,"docs":{},"−":{"df":0,"docs":{},"t":{"(":{"df":0,"docs":{},"x":{"df":2,"docs":{"114":{"tf":1.0},"55":{"tf":1.0}}}},"df":0,"docs":{}}}},"df":0,"docs":{}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}},"0":{"df":1,"docs":{"114":{"tf":1.0}}},"df":4,"docs":{"114":{"tf":2.8284271247461903},"35":{"tf":1.0},"55":{"tf":1.4142135623730951},"56":{"tf":1.0}}},"z":{"df":2,"docs":{"113":{"tf":1.0},"63":{"tf":1.0}}}},"0":{"df":1,"docs":{"149":{"tf":3.1622776601683795}},"​":{"(":{"df":0,"docs":{},"w":{"df":0,"docs":{},"i":{"df":0,"docs":{},"∗":{"4":{",":{"df":0,"docs":{},"w":{"df":0,"docs":{},"i":{"df":0,"docs":{},"∗":{"4":{"+":{"4":{"df":1,"docs":{"149":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}}},"1":{"df":1,"docs":{"149":{"tf":3.1622776601683795}},"​":{"(":{"df":0,"docs":{},"w":{"df":0,"docs":{},"i":{"df":0,"docs":{},"∗":{"4":{"+":{"1":{",":{"df":0,"docs":{},"w":{"df":0,"docs":{},"i":{"df":0,"docs":{},"∗":{"4":{"+":{"5":{"df":1,"docs":{"149":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}},"x":{")":{"=":{"df":0,"docs":{},"t":{"1":{"df":0,"docs":{},"​":{"(":{"df":0,"docs":{},"g":{"df":0,"docs":{},"x":{")":{"df":0,"docs":{},"−":{"df":0,"docs":{},"t":{"2":{"df":0,"docs":{},"​":{"(":{"df":0,"docs":{},"x":{"df":1,"docs":{"66":{"tf":1.0}}}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"2":{"df":1,"docs":{"149":{"tf":3.1622776601683795}},"​":{"(":{"df":0,"docs":{},"w":{"df":0,"docs":{},"i":{"df":0,"docs":{},"∗":{"4":{"+":{"2":{",":{"df":0,"docs":{},"w":{"df":0,"docs":{},"i":{"df":0,"docs":{},"∗":{"4":{"+":{"6":{"df":1,"docs":{"149":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}}},"3":{"df":1,"docs":{"149":{"tf":2.8284271247461903}}},":":{"=":{"(":{"b":{"5":{"df":0,"docs":{},"​":{"df":0,"docs":{},"x":{"+":{"b":{"6":{"df":0,"docs":{},"​":{")":{"df":0,"docs":{},"z":{"df":0,"docs":{},"h":{"df":0,"docs":{},"​":{"+":{"c":{"df":1,"docs":{"24":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"=":{"df":0,"docs":{},"−":{"1":{"df":1,"docs":{"10":{"tf":1.0}}},"df":0,"docs":{}}},"_":{"1":{"(":{"df":0,"docs":{},"x":{"df":1,"docs":{"66":{"tf":1.4142135623730951}}}},"df":1,"docs":{"35":{"tf":1.0}}},"df":0,"docs":{},"i":{"(":{"df":0,"docs":{},"x":{"df":1,"docs":{"66":{"tf":1.0}}}},"df":0,"docs":{}},"n":{"(":{"df":0,"docs":{},"x":{"df":1,"docs":{"66":{"tf":1.0}}}},"df":0,"docs":{}},"z":{"df":0,"docs":{},"e":{"df":0,"docs":{},"t":{"a":{"df":1,"docs":{"35":{"tf":1.0}}},"df":0,"docs":{}}}}},"a":{"df":0,"docs":{},"i":{"df":0,"docs":{},"r":{"df":0,"docs":{},"o":{"'":{"df":1,"docs":{"50":{"tf":1.7320508075688772}}},"df":21,"docs":{"104":{"tf":1.0},"127":{"tf":1.4142135623730951},"130":{"tf":1.4142135623730951},"131":{"tf":2.0},"133":{"tf":1.7320508075688772},"138":{"tf":1.0},"140":{"tf":1.7320508075688772},"141":{"tf":1.4142135623730951},"142":{"tf":1.0},"143":{"tf":1.7320508075688772},"144":{"tf":2.23606797749979},"145":{"tf":1.0},"146":{"tf":2.8284271247461903},"147":{"tf":1.0},"148":{"tf":1.0},"149":{"tf":1.4142135623730951},"150":{"tf":1.0},"151":{"tf":1.4142135623730951},"152":{"tf":1.0},"50":{"tf":3.0},"65":{"tf":1.0}}}}},"l":{"c":{"df":0,"docs":{},"u":{"df":0,"docs":{},"l":{"df":3,"docs":{"146":{"tf":1.0},"151":{"tf":1.0},"49":{"tf":1.0}}}}},"df":0,"docs":{},"l":{"df":40,"docs":{"10":{"tf":1.0},"11":{"tf":1.0},"110":{"tf":1.4142135623730951},"114":{"tf":2.0},"117":{"tf":1.4142135623730951},"118":{"tf":2.23606797749979},"124":{"tf":1.4142135623730951},"125":{"tf":1.0},"13":{"tf":1.0},"14":{"tf":1.0},"144":{"tf":1.0},"146":{"tf":1.7320508075688772},"149":{"tf":1.4142135623730951},"15":{"tf":1.0},"16":{"tf":1.4142135623730951},"19":{"tf":1.4142135623730951},"21":{"tf":1.4142135623730951},"35":{"tf":1.0},"4":{"tf":1.0},"40":{"tf":2.449489742783178},"42":{"tf":1.0},"44":{"tf":1.0},"48":{"tf":1.0},"49":{"tf":1.0},"52":{"tf":1.0},"53":{"tf":1.0},"54":{"tf":1.4142135623730951},"55":{"tf":1.0},"59":{"tf":1.0},"68":{"tf":1.4142135623730951},"69":{"tf":1.4142135623730951},"70":{"tf":1.7320508075688772},"71":{"tf":1.0},"73":{"tf":1.4142135623730951},"75":{"tf":1.0},"8":{"tf":1.0},"80":{"tf":1.0},"84":{"tf":1.0},"9":{"tf":1.0},"91":{"tf":1.0}}}},"m":{"df":0,"docs":{},"e":{"df":3,"docs":{"146":{"tf":1.0},"16":{"tf":1.0},"7":{"tf":1.0}}}},"n":{"'":{"df":0,"docs":{},"t":{"df":8,"docs":{"114":{"tf":1.0},"118":{"tf":1.7320508075688772},"12":{"tf":1.0},"125":{"tf":1.0},"130":{"tf":1.0},"146":{"tf":1.0},"56":{"tf":1.0},"95":{"tf":1.0}}}},"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"df":1,"docs":{"35":{"tf":1.0}}}}},"p":{"df":0,"docs":{},"i":{"df":0,"docs":{},"t":{"df":1,"docs":{"95":{"tf":1.0}}}},"t":{"df":0,"docs":{},"u":{"df":0,"docs":{},"r":{"df":1,"docs":{"16":{"tf":1.0}}}}}},"r":{"df":0,"docs":{},"e":{"df":4,"docs":{"124":{"tf":1.4142135623730951},"125":{"tf":1.0},"49":{"tf":1.0},"57":{"tf":1.0}}},"r":{"df":0,"docs":{},"i":{"df":1,"docs":{"11":{"tf":1.0}}}}},"s":{"df":0,"docs":{},"e":{"df":26,"docs":{"10":{"tf":1.4142135623730951},"101":{"tf":1.0},"103":{"tf":1.0},"106":{"tf":1.4142135623730951},"107":{"tf":1.4142135623730951},"109":{"tf":1.0},"11":{"tf":1.0},"112":{"tf":1.0},"118":{"tf":1.0},"12":{"tf":1.0},"125":{"tf":1.0},"13":{"tf":1.0},"14":{"tf":2.23606797749979},"146":{"tf":1.7320508075688772},"149":{"tf":2.0},"151":{"tf":1.0},"3":{"tf":1.0},"34":{"tf":1.0},"51":{"tf":1.0},"68":{"tf":1.0},"7":{"tf":1.4142135623730951},"80":{"tf":1.7320508075688772},"84":{"tf":1.0},"9":{"tf":1.0},"92":{"tf":1.0},"97":{"tf":1.0}}}},"t":{"c":{"df":0,"docs":{},"h":{"df":1,"docs":{"57":{"tf":1.0}}}},"df":0,"docs":{}}},"df":19,"docs":{"10":{"tf":2.6457513110645907},"11":{"tf":1.0},"112":{"tf":1.4142135623730951},"12":{"tf":1.0},"13":{"tf":1.0},"14":{"tf":1.4142135623730951},"142":{"tf":1.4142135623730951},"146":{"tf":1.7320508075688772},"147":{"tf":1.4142135623730951},"149":{"tf":1.4142135623730951},"16":{"tf":1.7320508075688772},"35":{"tf":1.4142135623730951},"36":{"tf":1.0},"55":{"tf":1.0},"62":{"tf":1.4142135623730951},"9":{"tf":1.0},"91":{"tf":1.0},"94":{"tf":1.0},"95":{"tf":1.0}},"e":{"df":0,"docs":{},"i":{"df":0,"docs":{},"l":{"df":1,"docs":{"142":{"tf":1.0}}}},"l":{"df":0,"docs":{},"l":{"df":7,"docs":{"127":{"tf":1.7320508075688772},"130":{"tf":2.8284271247461903},"133":{"tf":2.449489742783178},"134":{"tf":2.6457513110645907},"146":{"tf":1.7320508075688772},"149":{"tf":1.0},"151":{"tf":2.449489742783178}}}},"r":{"df":0,"docs":{},"t":{"a":{"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"df":6,"docs":{"130":{"tf":1.0},"19":{"tf":1.7320508075688772},"20":{"tf":1.0},"48":{"tf":1.4142135623730951},"49":{"tf":1.0},"52":{"tf":1.0}}}}},"df":0,"docs":{}}}},"h":{"a":{"df":0,"docs":{},"l":{"df":0,"docs":{},"l":{"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"df":0,"docs":{},"g":{"df":14,"docs":{"118":{"tf":2.0},"147":{"tf":1.4142135623730951},"32":{"tf":2.449489742783178},"40":{"tf":2.0},"41":{"tf":1.0},"69":{"tf":1.0},"80":{"tf":1.0},"82":{"tf":1.0},"83":{"tf":2.0},"84":{"tf":1.0},"89":{"tf":1.0},"91":{"tf":1.0},"95":{"tf":1.0},"97":{"tf":2.449489742783178}}}}}}},"n":{"df":0,"docs":{},"g":{"df":5,"docs":{"146":{"tf":1.7320508075688772},"7":{"tf":1.0},"71":{"tf":1.0},"83":{"tf":1.0},"9":{"tf":1.0}}}},"p":{"df":0,"docs":{},"t":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":2,"docs":{"103":{"tf":1.0},"149":{"tf":1.0}}}}}},"r":{"df":0,"docs":{},"g":{"df":1,"docs":{"131":{"tf":1.0}}}}},"df":0,"docs":{},"e":{"a":{"df":0,"docs":{},"p":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":1,"docs":{"125":{"tf":1.0}}}},"l":{"df":0,"docs":{},"i":{"df":1,"docs":{"21":{"tf":1.0}}}}},"t":{"df":4,"docs":{"118":{"tf":1.0},"21":{"tf":1.0},"58":{"tf":1.0},"79":{"tf":1.4142135623730951}}}},"c":{"df":0,"docs":{},"k":{"df":34,"docs":{"101":{"tf":1.0},"102":{"tf":1.4142135623730951},"103":{"tf":1.0},"11":{"tf":1.0},"113":{"tf":2.0},"115":{"tf":1.0},"117":{"tf":1.4142135623730951},"12":{"tf":1.0},"13":{"tf":1.0},"130":{"tf":1.7320508075688772},"134":{"tf":1.0},"138":{"tf":1.4142135623730951},"14":{"tf":2.6457513110645907},"146":{"tf":3.3166247903554},"147":{"tf":1.4142135623730951},"151":{"tf":2.449489742783178},"16":{"tf":1.7320508075688772},"21":{"tf":2.449489742783178},"33":{"tf":2.8284271247461903},"42":{"tf":1.0},"45":{"tf":1.0},"50":{"tf":1.0},"58":{"tf":1.7320508075688772},"59":{"tf":1.0},"60":{"tf":2.0},"63":{"tf":2.0},"66":{"tf":1.0},"69":{"tf":1.4142135623730951},"70":{"tf":1.4142135623730951},"72":{"tf":1.0},"84":{"tf":1.7320508075688772},"85":{"tf":1.4142135623730951},"92":{"tf":1.4142135623730951},"95":{"tf":2.23606797749979}}}},"df":0,"docs":{}},"o":{"df":0,"docs":{},"i":{"c":{"df":2,"docs":{"68":{"tf":1.0},"72":{"tf":1.0}}},"df":0,"docs":{}},"o":{"df":0,"docs":{},"s":{"df":8,"docs":{"114":{"tf":1.0},"124":{"tf":1.4142135623730951},"16":{"tf":1.4142135623730951},"68":{"tf":1.0},"69":{"tf":1.0},"80":{"tf":1.0},"85":{"tf":1.0},"9":{"tf":1.0}}}},"s":{"df":0,"docs":{},"e":{"df":2,"docs":{"107":{"tf":1.0},"68":{"tf":1.0}},"n":{"df":7,"docs":{"19":{"tf":1.0},"68":{"tf":1.0},"7":{"tf":1.0},"77":{"tf":1.0},"79":{"tf":1.4142135623730951},"85":{"tf":1.0},"91":{"tf":1.0}}}}}}},"i":{"df":1,"docs":{"150":{"tf":1.4142135623730951}},"r":{"c":{"df":0,"docs":{},"u":{"df":0,"docs":{},"i":{"df":0,"docs":{},"t":{"df":15,"docs":{"10":{"tf":1.0},"11":{"tf":1.0},"13":{"tf":1.0},"14":{"tf":1.0},"16":{"tf":1.4142135623730951},"22":{"tf":1.0},"34":{"tf":1.7320508075688772},"35":{"tf":1.4142135623730951},"36":{"tf":1.0},"4":{"tf":1.7320508075688772},"43":{"tf":1.7320508075688772},"44":{"tf":1.0},"45":{"tf":1.4142135623730951},"8":{"tf":1.0},"9":{"tf":1.7320508075688772}}}}}},"df":0,"docs":{}},"​":{"(":{"df":0,"docs":{},"z":{"df":2,"docs":{"115":{"tf":1.0},"117":{"tf":1.0}}}},"df":0,"docs":{}}},"k":{"df":0,"docs":{},"​":{"(":{"df":0,"docs":{},"w":{"df":0,"docs":{},"i":{",":{"df":0,"docs":{},"w":{"df":0,"docs":{},"i":{"+":{"1":{"df":1,"docs":{"149":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}}},":":{"=":{"df":0,"docs":{},"z":{"df":0,"docs":{},"k":{"df":0,"docs":{},"t":{"df":0,"docs":{},"​":{"(":{"df":0,"docs":{},"z":{")":{"df":0,"docs":{},"p":{"df":0,"docs":{},"k":{"df":0,"docs":{},"t":{"df":0,"docs":{},"​":{"(":{"df":0,"docs":{},"τ":{"1":{"df":0,"docs":{},"z":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"…":{",":{"df":0,"docs":{},"τ":{"df":0,"docs":{},"m":{"df":0,"docs":{},"z":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"τ":{"1":{"df":0,"docs":{},"g":{"df":0,"docs":{},"z":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"…":{",":{"df":0,"docs":{},"τ":{"df":0,"docs":{},"m":{"df":0,"docs":{},"g":{"df":0,"docs":{},"z":{"df":1,"docs":{"95":{"tf":1.0}}}}}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}}},"df":0,"docs":{}}},"df":0,"docs":{},"p":{"df":0,"docs":{},"k":{"df":0,"docs":{},"t":{"df":0,"docs":{},"​":{"(":{"df":0,"docs":{},"t":{"1":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"…":{",":{"df":0,"docs":{},"t":{"df":0,"docs":{},"m":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"t":{"1":{"df":0,"docs":{},"​":{"(":{"df":0,"docs":{},"g":{"df":0,"docs":{},"x":{")":{",":{"df":0,"docs":{},"…":{",":{"df":0,"docs":{},"t":{"df":0,"docs":{},"m":{"df":0,"docs":{},"​":{"(":{"df":0,"docs":{},"g":{"df":0,"docs":{},"x":{"df":1,"docs":{"94":{"tf":1.0}}}}},"df":0,"docs":{}}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}}}}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"l":{"a":{"df":0,"docs":{},"i":{"df":0,"docs":{},"m":{"df":10,"docs":{"10":{"tf":2.23606797749979},"11":{"tf":1.7320508075688772},"113":{"tf":1.4142135623730951},"13":{"tf":1.7320508075688772},"14":{"tf":1.4142135623730951},"21":{"tf":1.4142135623730951},"32":{"tf":1.7320508075688772},"34":{"tf":1.0},"63":{"tf":1.4142135623730951},"95":{"tf":2.6457513110645907}}}},"r":{"df":0,"docs":{},"i":{"df":0,"docs":{},"t":{"df":0,"docs":{},"i":{"df":1,"docs":{"7":{"tf":1.0}}}}}}},"df":0,"docs":{},"e":{"a":{"df":0,"docs":{},"r":{"df":1,"docs":{"113":{"tf":1.0}},"l":{"df":0,"docs":{},"i":{"df":1,"docs":{"11":{"tf":1.0}}}}}},"df":0,"docs":{}},"i":{"df":1,"docs":{"152":{"tf":1.7320508075688772}}},"o":{"df":0,"docs":{},"s":{"df":0,"docs":{},"e":{"df":2,"docs":{"73":{"tf":1.0},"80":{"tf":1.0}}}}}},"o":{"d":{"df":0,"docs":{},"e":{"df":16,"docs":{"103":{"tf":1.0},"104":{"tf":1.0},"105":{"tf":1.0},"111":{"tf":1.0},"113":{"tf":1.0},"114":{"tf":2.0},"116":{"tf":1.4142135623730951},"117":{"tf":1.0},"124":{"tf":1.0},"127":{"tf":1.0},"142":{"tf":1.0},"147":{"tf":1.0},"44":{"tf":1.0},"46":{"tf":1.4142135623730951},"50":{"tf":1.4142135623730951},"67":{"tf":1.0}}}},"df":0,"docs":{},"e":{"df":0,"docs":{},"f":{"df":0,"docs":{},"f":{"df":0,"docs":{},"i":{"c":{"df":0,"docs":{},"i":{"df":5,"docs":{"121":{"tf":1.0},"14":{"tf":1.4142135623730951},"19":{"tf":1.0},"3":{"tf":1.7320508075688772},"5":{"tf":1.0}}}},"df":0,"docs":{}}}}},"i":{"df":0,"docs":{},"n":{"c":{"df":0,"docs":{},"i":{"d":{"df":2,"docs":{"14":{"tf":1.7320508075688772},"85":{"tf":1.0}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"l":{"df":0,"docs":{},"l":{"df":0,"docs":{},"e":{"c":{"df":0,"docs":{},"t":{"df":1,"docs":{"45":{"tf":1.0}}}},"df":0,"docs":{}},"o":{"df":0,"docs":{},"q":{"df":0,"docs":{},"u":{"df":0,"docs":{},"i":{"df":1,"docs":{"146":{"tf":1.0}}}}}}},"u":{"df":0,"docs":{},"m":{"df":0,"docs":{},"n":{"df":47,"docs":{"10":{"tf":1.4142135623730951},"106":{"tf":2.0},"108":{"tf":1.0},"109":{"tf":1.7320508075688772},"11":{"tf":1.4142135623730951},"112":{"tf":1.0},"12":{"tf":1.0},"127":{"tf":1.4142135623730951},"128":{"tf":3.872983346207417},"129":{"tf":1.4142135623730951},"13":{"tf":2.23606797749979},"130":{"tf":2.8284271247461903},"131":{"tf":2.23606797749979},"132":{"tf":2.0},"133":{"tf":2.8284271247461903},"134":{"tf":1.0},"135":{"tf":2.0},"136":{"tf":2.0},"137":{"tf":2.0},"138":{"tf":1.7320508075688772},"139":{"tf":2.8284271247461903},"14":{"tf":2.0},"142":{"tf":2.0},"145":{"tf":1.0},"146":{"tf":4.358898943540674},"147":{"tf":3.872983346207417},"148":{"tf":1.7320508075688772},"149":{"tf":3.1622776601683795},"150":{"tf":3.0},"16":{"tf":1.4142135623730951},"20":{"tf":1.0},"24":{"tf":1.0},"35":{"tf":1.0},"36":{"tf":1.4142135623730951},"49":{"tf":1.0},"5":{"tf":1.0},"50":{"tf":1.0},"51":{"tf":1.0},"62":{"tf":1.0},"65":{"tf":2.23606797749979},"66":{"tf":1.7320508075688772},"70":{"tf":2.23606797749979},"83":{"tf":2.6457513110645907},"84":{"tf":1.0},"9":{"tf":1.4142135623730951},"91":{"tf":1.7320508075688772},"94":{"tf":1.4142135623730951}}}}}},"m":{"b":{"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"df":4,"docs":{"12":{"tf":1.0},"71":{"tf":1.0},"84":{"tf":1.0},"85":{"tf":1.4142135623730951}}}}},"df":0,"docs":{},"e":{"df":6,"docs":{"107":{"tf":1.0},"14":{"tf":1.4142135623730951},"16":{"tf":1.4142135623730951},"35":{"tf":1.0},"45":{"tf":1.4142135623730951},"84":{"tf":1.0}}},"m":{"df":0,"docs":{},"i":{"df":0,"docs":{},"t":{"(":{"a":{"df":1,"docs":{"92":{"tf":1.0}}},"df":0,"docs":{},"f":{"df":1,"docs":{"19":{"tf":1.0}}}},"df":40,"docs":{"101":{"tf":1.0},"112":{"tf":1.0},"113":{"tf":1.0},"116":{"tf":1.0},"119":{"tf":1.0},"19":{"tf":2.6457513110645907},"21":{"tf":1.7320508075688772},"22":{"tf":1.0},"32":{"tf":2.0},"33":{"tf":1.0},"35":{"tf":3.4641016151377544},"38":{"tf":2.0},"42":{"tf":1.0},"44":{"tf":1.0},"53":{"tf":1.0},"57":{"tf":2.0},"58":{"tf":1.0},"59":{"tf":1.0},"60":{"tf":1.0},"62":{"tf":1.0},"63":{"tf":1.0},"67":{"tf":1.4142135623730951},"69":{"tf":1.4142135623730951},"71":{"tf":2.6457513110645907},"72":{"tf":2.0},"73":{"tf":2.0},"74":{"tf":2.23606797749979},"75":{"tf":3.0},"76":{"tf":2.0},"77":{"tf":1.0},"79":{"tf":1.4142135623730951},"80":{"tf":2.6457513110645907},"81":{"tf":1.0},"82":{"tf":1.4142135623730951},"83":{"tf":2.23606797749979},"84":{"tf":1.7320508075688772},"86":{"tf":1.0},"92":{"tf":2.0},"94":{"tf":2.8284271247461903},"95":{"tf":1.4142135623730951}},"m":{"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"df":0,"docs":{},"t":{"_":{"df":0,"docs":{},"s":{"c":{"df":0,"docs":{},"h":{"df":0,"docs":{},"e":{"df":0,"docs":{},"m":{"df":1,"docs":{"35":{"tf":1.4142135623730951}}}}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}}}},"o":{"df":0,"docs":{},"n":{"_":{"df":0,"docs":{},"p":{"df":0,"docs":{},"r":{"df":0,"docs":{},"e":{"df":0,"docs":{},"p":{"df":0,"docs":{},"r":{"df":0,"docs":{},"o":{"c":{"df":0,"docs":{},"e":{"df":0,"docs":{},"s":{"df":0,"docs":{},"s":{"df":0,"docs":{},"e":{"d":{"_":{"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"df":0,"docs":{},"p":{"df":0,"docs":{},"u":{"df":0,"docs":{},"t":{"df":1,"docs":{"35":{"tf":2.23606797749979}}}}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}}}},"df":0,"docs":{}}}}}}}},"df":8,"docs":{"15":{"tf":1.7320508075688772},"22":{"tf":1.0},"23":{"tf":1.0},"35":{"tf":1.7320508075688772},"42":{"tf":1.0},"44":{"tf":1.7320508075688772},"45":{"tf":1.4142135623730951},"83":{"tf":1.0}},"p":{"df":0,"docs":{},"r":{"df":0,"docs":{},"e":{"df":0,"docs":{},"p":{"df":0,"docs":{},"r":{"df":0,"docs":{},"o":{"c":{"df":0,"docs":{},"e":{"df":0,"docs":{},"s":{"df":0,"docs":{},"s":{"df":0,"docs":{},"e":{"d":{"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"df":0,"docs":{},"p":{"df":0,"docs":{},"u":{"df":0,"docs":{},"t":{":":{":":{"df":0,"docs":{},"f":{"df":0,"docs":{},"r":{"df":0,"docs":{},"o":{"df":0,"docs":{},"m":{"_":{"c":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"df":0,"docs":{},"s":{"df":0,"docs":{},"t":{"df":0,"docs":{},"r":{"a":{"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"df":0,"docs":{},"t":{"_":{"df":0,"docs":{},"s":{"df":0,"docs":{},"y":{"df":0,"docs":{},"s":{"df":0,"docs":{},"t":{"df":0,"docs":{},"e":{"df":0,"docs":{},"m":{"(":{"&":{"df":0,"docs":{},"s":{"df":0,"docs":{},"y":{"df":0,"docs":{},"s":{"df":0,"docs":{},"t":{"df":0,"docs":{},"e":{"df":0,"docs":{},"m":{"df":1,"docs":{"44":{"tf":1.0}}}}}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}}}}}},"df":0,"docs":{}}}}},"df":0,"docs":{}}}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}}}},"df":0,"docs":{}},"<":{"df":0,"docs":{},"f":{"df":1,"docs":{"35":{"tf":1.0}}}},"df":0,"docs":{}}}}}}},"df":0,"docs":{}}}}}},"df":0,"docs":{}}}}}}}}},"u":{"df":0,"docs":{},"n":{"df":3,"docs":{"151":{"tf":1.0},"4":{"tf":1.0},"81":{"tf":1.0}}}}},"p":{"a":{"c":{"df":0,"docs":{},"t":{"df":1,"docs":{"14":{"tf":1.4142135623730951}}}},"df":0,"docs":{},"r":{"df":1,"docs":{"14":{"tf":1.0}},"i":{"df":0,"docs":{},"s":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"df":1,"docs":{"3":{"tf":1.4142135623730951}}}}}}},"t":{"df":1,"docs":{"92":{"tf":1.0}}}},"df":0,"docs":{},"i":{"df":0,"docs":{},"l":{"df":1,"docs":{"50":{"tf":1.4142135623730951}}}},"l":{"df":0,"docs":{},"e":{"df":0,"docs":{},"t":{"df":4,"docs":{"107":{"tf":1.0},"59":{"tf":1.0},"78":{"tf":1.4142135623730951},"95":{"tf":1.0}}},"x":{"df":7,"docs":{"114":{"tf":1.0},"4":{"tf":1.0},"45":{"tf":1.7320508075688772},"50":{"tf":1.0},"64":{"tf":1.0},"66":{"tf":1.0},"81":{"tf":1.0}}}}},"o":{"df":0,"docs":{},"n":{"df":5,"docs":{"144":{"tf":1.0},"146":{"tf":1.4142135623730951},"6":{"tf":1.4142135623730951},"71":{"tf":1.0},"80":{"tf":1.0}}},"s":{"df":3,"docs":{"45":{"tf":1.0},"5":{"tf":1.0},"84":{"tf":1.0}},"i":{"df":0,"docs":{},"t":{"df":24,"docs":{"100":{"tf":1.0},"112":{"tf":1.4142135623730951},"113":{"tf":1.0},"114":{"tf":1.0},"119":{"tf":1.7320508075688772},"125":{"tf":1.4142135623730951},"149":{"tf":1.0},"53":{"tf":1.7320508075688772},"56":{"tf":1.0},"58":{"tf":1.0},"59":{"tf":2.0},"60":{"tf":1.0},"62":{"tf":1.4142135623730951},"65":{"tf":1.0},"66":{"tf":1.0},"67":{"tf":1.4142135623730951},"68":{"tf":1.0},"69":{"tf":1.4142135623730951},"80":{"tf":1.0},"84":{"tf":2.0},"9":{"tf":1.0},"94":{"tf":1.4142135623730951},"95":{"tf":1.7320508075688772},"97":{"tf":1.4142135623730951}},"i":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"_":{"df":0,"docs":{},"p":{"df":0,"docs":{},"o":{"df":0,"docs":{},"l":{"df":0,"docs":{},"i":{"df":2,"docs":{"114":{"tf":1.0},"116":{"tf":1.0}}},"y":{".":{"df":0,"docs":{},"e":{"df":0,"docs":{},"v":{"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"_":{"df":0,"docs":{},"o":{"d":{"d":{"_":{"d":{"df":0,"docs":{},"e":{"c":{"df":0,"docs":{},"o":{"df":0,"docs":{},"m":{"df":0,"docs":{},"p":{"df":0,"docs":{},"o":{"df":0,"docs":{},"s":{"df":0,"docs":{},"i":{"df":0,"docs":{},"t":{"df":1,"docs":{"116":{"tf":1.0}}}}}}}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}}}},"_":{"df":0,"docs":{},"e":{"df":0,"docs":{},"v":{"a":{"df":0,"docs":{},"l":{"df":0,"docs":{},"u":{"df":1,"docs":{"116":{"tf":1.0}}}}},"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{".":{"df":0,"docs":{},"e":{"df":0,"docs":{},"v":{"a":{"df":0,"docs":{},"l":{"df":0,"docs":{},"u":{"a":{"df":0,"docs":{},"t":{"df":0,"docs":{},"e":{"(":{"&":{"df":0,"docs":{},"z":{"_":{"df":0,"docs":{},"s":{"df":0,"docs":{},"q":{"df":0,"docs":{},"u":{"a":{"df":0,"docs":{},"r":{"df":1,"docs":{"116":{"tf":1.0}}}},"df":0,"docs":{}}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}}},"df":0,"docs":{}}}},"df":1,"docs":{"116":{"tf":1.0}}}}}},"o":{"d":{"d":{".":{"df":0,"docs":{},"e":{"df":0,"docs":{},"v":{"a":{"df":0,"docs":{},"l":{"df":0,"docs":{},"u":{"a":{"df":0,"docs":{},"t":{"df":0,"docs":{},"e":{"(":{"&":{"df":0,"docs":{},"z":{"_":{"df":0,"docs":{},"s":{"df":0,"docs":{},"q":{"df":0,"docs":{},"u":{"a":{"df":0,"docs":{},"r":{"df":1,"docs":{"116":{"tf":1.0}}}},"df":0,"docs":{}}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}}},"df":0,"docs":{}}}},"df":1,"docs":{"116":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}}}},"df":0,"docs":{}}}}}}}},"u":{"df":0,"docs":{},"t":{"a":{"df":0,"docs":{},"t":{"df":0,"docs":{},"i":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"df":1,"docs":{"99":{"tf":1.0}}}}}}},"df":36,"docs":{"102":{"tf":1.0},"104":{"tf":1.0},"105":{"tf":1.4142135623730951},"107":{"tf":1.0},"112":{"tf":1.7320508075688772},"114":{"tf":2.23606797749979},"123":{"tf":1.0},"124":{"tf":1.0},"14":{"tf":1.0},"146":{"tf":1.7320508075688772},"147":{"tf":1.4142135623730951},"151":{"tf":1.0},"16":{"tf":1.0},"21":{"tf":2.23606797749979},"22":{"tf":1.4142135623730951},"23":{"tf":1.0},"24":{"tf":1.4142135623730951},"25":{"tf":1.4142135623730951},"26":{"tf":1.4142135623730951},"27":{"tf":1.0},"28":{"tf":1.0},"32":{"tf":3.7416573867739413},"34":{"tf":1.0},"4":{"tf":1.4142135623730951},"48":{"tf":2.449489742783178},"49":{"tf":2.0},"50":{"tf":1.4142135623730951},"58":{"tf":1.0},"62":{"tf":1.7320508075688772},"8":{"tf":1.4142135623730951},"80":{"tf":2.0},"84":{"tf":1.0},"85":{"tf":1.0},"91":{"tf":1.7320508075688772},"94":{"tf":3.605551275463989},"95":{"tf":1.7320508075688772}},"e":{"_":{"df":0,"docs":{},"t":{"df":0,"docs":{},"r":{"a":{"df":0,"docs":{},"n":{"df":0,"docs":{},"s":{"df":0,"docs":{},"i":{"df":0,"docs":{},"t":{"df":5,"docs":{"105":{"tf":1.0},"107":{"tf":1.4142135623730951},"113":{"tf":1.0},"114":{"tf":1.7320508075688772},"117":{"tf":1.0}}}}}}},"df":0,"docs":{}}}},"df":0,"docs":{}}}}}},"n":{"c":{"a":{"df":0,"docs":{},"t":{"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"df":4,"docs":{"147":{"tf":1.7320508075688772},"80":{"tf":1.4142135623730951},"88":{"tf":1.0},"92":{"tf":1.0}}}}}},"df":0,"docs":{},"e":{"df":0,"docs":{},"p":{"df":0,"docs":{},"t":{"df":3,"docs":{"118":{"tf":1.0},"14":{"tf":1.0},"149":{"tf":1.0}}}}},"r":{"df":0,"docs":{},"e":{"df":0,"docs":{},"t":{"df":3,"docs":{"13":{"tf":1.0},"49":{"tf":1.0},"9":{"tf":1.0}}}}}},"d":{"df":0,"docs":{},"i":{"df":0,"docs":{},"t":{"df":3,"docs":{"14":{"tf":2.8284271247461903},"16":{"tf":1.0},"45":{"tf":1.0}}}}},"df":0,"docs":{},"f":{"df":0,"docs":{},"i":{"df":0,"docs":{},"g":{"df":0,"docs":{},"u":{"df":0,"docs":{},"r":{"df":7,"docs":{"109":{"tf":1.0},"127":{"tf":1.0},"131":{"tf":1.0},"146":{"tf":1.0},"149":{"tf":1.0},"75":{"tf":1.0},"80":{"tf":1.0}}}}}}},"s":{"df":0,"docs":{},"e":{"df":0,"docs":{},"q":{"df":0,"docs":{},"u":{"df":3,"docs":{"69":{"tf":1.0},"70":{"tf":1.0},"9":{"tf":1.0}}}}},"i":{"d":{"df":5,"docs":{"13":{"tf":1.0},"130":{"tf":1.0},"14":{"tf":1.4142135623730951},"7":{"tf":1.0},"77":{"tf":1.0}},"e":{"df":0,"docs":{},"r":{"df":2,"docs":{"120":{"tf":1.4142135623730951},"87":{"tf":1.0}}}}},"df":0,"docs":{},"s":{"df":0,"docs":{},"t":{"df":14,"docs":{"11":{"tf":1.0},"115":{"tf":1.0},"117":{"tf":1.0},"124":{"tf":1.0},"19":{"tf":1.0},"36":{"tf":1.0},"49":{"tf":1.4142135623730951},"51":{"tf":1.0},"58":{"tf":1.4142135623730951},"59":{"tf":1.0},"60":{"tf":1.7320508075688772},"66":{"tf":1.0},"70":{"tf":1.0},"71":{"tf":1.0}}}}},"t":{"a":{"df":0,"docs":{},"n":{"df":0,"docs":{},"t":{"df":6,"docs":{"10":{"tf":1.7320508075688772},"28":{"tf":1.4142135623730951},"32":{"tf":1.0},"73":{"tf":1.0},"9":{"tf":1.0},"94":{"tf":1.0}}}}},"df":0,"docs":{},"r":{"a":{"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"df":2,"docs":{"146":{"tf":1.0},"151":{"tf":1.4142135623730951}},"t":{"_":{"df":0,"docs":{},"e":{"df":0,"docs":{},"v":{"a":{"df":0,"docs":{},"l":{"df":0,"docs":{},"u":{"a":{"df":0,"docs":{},"t":{"df":0,"docs":{},"i":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"df":0,"docs":{},"s":{".":{"c":{"df":0,"docs":{},"o":{"df":0,"docs":{},"m":{"df":0,"docs":{},"p":{"df":0,"docs":{},"u":{"df":0,"docs":{},"t":{"df":0,"docs":{},"e":{"_":{"c":{"df":0,"docs":{},"o":{"df":0,"docs":{},"m":{"df":0,"docs":{},"p":{"df":0,"docs":{},"o":{"df":0,"docs":{},"s":{"df":0,"docs":{},"i":{"df":0,"docs":{},"t":{"df":0,"docs":{},"i":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"_":{"df":0,"docs":{},"p":{"df":0,"docs":{},"o":{"df":0,"docs":{},"l":{"df":0,"docs":{},"i":{"df":1,"docs":{"114":{"tf":1.0}}},"y":{"(":{"&":{"df":0,"docs":{},"l":{"d":{"df":0,"docs":{},"e":{"_":{"df":0,"docs":{},"r":{"df":0,"docs":{},"o":{"df":0,"docs":{},"o":{"df":0,"docs":{},"t":{"df":0,"docs":{},"s":{"_":{"df":0,"docs":{},"o":{"df":0,"docs":{},"f":{"_":{"df":0,"docs":{},"u":{"df":0,"docs":{},"n":{"df":0,"docs":{},"i":{"df":0,"docs":{},"t":{"df":0,"docs":{},"y":{"_":{"c":{"df":0,"docs":{},"o":{"df":0,"docs":{},"s":{"df":0,"docs":{},"e":{"df":0,"docs":{},"t":{"df":1,"docs":{"116":{"tf":1.0}}}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}}}}},"df":0,"docs":{}}}},"df":0,"docs":{}}}}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}}}},"df":0,"docs":{}}}}}}}}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}}}}},"df":1,"docs":{"114":{"tf":1.4142135623730951}}}}},"df":0,"docs":{}}}},"df":44,"docs":{"105":{"tf":1.4142135623730951},"106":{"tf":2.23606797749979},"107":{"tf":3.0},"109":{"tf":2.449489742783178},"112":{"tf":1.0},"113":{"tf":1.7320508075688772},"114":{"tf":2.23606797749979},"117":{"tf":1.0},"123":{"tf":2.0},"125":{"tf":1.4142135623730951},"130":{"tf":2.0},"131":{"tf":1.4142135623730951},"133":{"tf":1.4142135623730951},"134":{"tf":1.0},"146":{"tf":2.23606797749979},"147":{"tf":1.4142135623730951},"149":{"tf":2.6457513110645907},"150":{"tf":2.8284271247461903},"151":{"tf":3.0},"19":{"tf":1.0},"35":{"tf":1.4142135623730951},"43":{"tf":1.0},"44":{"tf":2.23606797749979},"45":{"tf":1.4142135623730951},"48":{"tf":2.23606797749979},"49":{"tf":1.4142135623730951},"50":{"tf":1.0},"52":{"tf":1.0},"53":{"tf":1.0},"54":{"tf":2.449489742783178},"55":{"tf":2.23606797749979},"56":{"tf":1.4142135623730951},"57":{"tf":1.0},"58":{"tf":1.0},"59":{"tf":1.0},"62":{"tf":1.0},"65":{"tf":1.4142135623730951},"66":{"tf":2.449489742783178},"69":{"tf":1.4142135623730951},"70":{"tf":2.449489742783178},"83":{"tf":2.0},"84":{"tf":2.8284271247461903},"85":{"tf":1.0},"91":{"tf":2.0}},"e":{"df":0,"docs":{},"v":{"a":{"df":0,"docs":{},"l":{"df":0,"docs":{},"u":{"a":{"df":0,"docs":{},"t":{"df":0,"docs":{},"i":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"df":0,"docs":{},"t":{"df":1,"docs":{"114":{"tf":1.0}}}}}}}},"df":1,"docs":{"114":{"tf":1.0}}}}},"df":0,"docs":{}}},"s":{"df":0,"docs":{},"y":{"df":0,"docs":{},"s":{"df":0,"docs":{},"t":{"df":0,"docs":{},"e":{"df":0,"docs":{},"m":{":":{":":{"<":{"df":0,"docs":{},"f":{"df":0,"docs":{},"r":{"df":0,"docs":{},"f":{"df":0,"docs":{},"i":{"df":0,"docs":{},"e":{"df":0,"docs":{},"l":{"d":{">":{":":{":":{"df":0,"docs":{},"n":{"df":0,"docs":{},"e":{"df":0,"docs":{},"w":{"df":2,"docs":{"44":{"tf":1.0},"45":{"tf":1.0}}}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}}}}}},"df":0,"docs":{}},"df":0,"docs":{}},"<":{"df":0,"docs":{},"f":{"df":0,"docs":{},"r":{"df":0,"docs":{},"f":{"df":0,"docs":{},"i":{"df":0,"docs":{},"e":{"df":0,"docs":{},"l":{"d":{"df":1,"docs":{"45":{"tf":1.0}}},"df":0,"docs":{}}}}}}}},"df":0,"docs":{}}}}}}}}}}},"df":0,"docs":{},"u":{"c":{"df":0,"docs":{},"t":{"df":30,"docs":{"112":{"tf":1.4142135623730951},"113":{"tf":1.0},"114":{"tf":2.0},"117":{"tf":1.0},"118":{"tf":1.0},"12":{"tf":1.0},"125":{"tf":1.0},"14":{"tf":1.4142135623730951},"142":{"tf":1.7320508075688772},"144":{"tf":1.0},"145":{"tf":1.4142135623730951},"146":{"tf":2.6457513110645907},"16":{"tf":1.4142135623730951},"20":{"tf":1.0},"21":{"tf":1.0},"41":{"tf":1.0},"53":{"tf":1.4142135623730951},"54":{"tf":1.4142135623730951},"55":{"tf":1.0},"56":{"tf":1.4142135623730951},"58":{"tf":1.4142135623730951},"60":{"tf":1.0},"62":{"tf":1.4142135623730951},"65":{"tf":1.0},"68":{"tf":1.0},"69":{"tf":1.7320508075688772},"84":{"tf":1.7320508075688772},"85":{"tf":1.4142135623730951},"91":{"tf":1.0},"94":{"tf":1.0}}}},"df":0,"docs":{}}}}},"t":{"a":{"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"df":8,"docs":{"13":{"tf":1.0},"146":{"tf":1.0},"35":{"tf":1.0},"48":{"tf":1.0},"50":{"tf":1.0},"65":{"tf":1.4142135623730951},"68":{"tf":1.4142135623730951},"70":{"tf":1.0}}}}},"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"df":0,"docs":{},"t":{"df":1,"docs":{"133":{"tf":1.0}}}},"x":{"df":0,"docs":{},"t":{"df":5,"docs":{"109":{"tf":1.7320508075688772},"14":{"tf":1.0},"7":{"tf":1.0},"80":{"tf":1.0},"84":{"tf":1.0}}}}},"i":{"df":0,"docs":{},"n":{"df":0,"docs":{},"u":{"df":4,"docs":{"102":{"tf":1.0},"130":{"tf":1.0},"134":{"tf":1.0},"146":{"tf":2.0}}}}},"r":{"df":0,"docs":{},"i":{"df":0,"docs":{},"v":{"df":1,"docs":{"49":{"tf":1.0}}}},"o":{"df":0,"docs":{},"l":{"df":1,"docs":{"118":{"tf":1.0}}}}}},"v":{"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"df":0,"docs":{},"i":{"df":2,"docs":{"35":{"tf":1.0},"92":{"tf":1.0}}},"t":{"df":1,"docs":{"95":{"tf":1.0}}}},"r":{"df":0,"docs":{},"s":{"df":1,"docs":{"14":{"tf":1.4142135623730951}}},"t":{"df":2,"docs":{"14":{"tf":1.0},"4":{"tf":1.0}}}}},"i":{"df":0,"docs":{},"n":{"c":{"df":9,"docs":{"21":{"tf":1.4142135623730951},"53":{"tf":1.0},"55":{"tf":1.0},"57":{"tf":1.0},"58":{"tf":1.0},"69":{"tf":1.4142135623730951},"73":{"tf":1.0},"75":{"tf":1.0},"80":{"tf":1.0}}},"df":0,"docs":{}}}}},"o":{"df":0,"docs":{},"r":{"d":{"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"df":4,"docs":{"14":{"tf":1.4142135623730951},"73":{"tf":1.0},"79":{"tf":1.0},"92":{"tf":1.0}}}}},"df":0,"docs":{}}},"p":{"df":0,"docs":{},"i":{"df":3,"docs":{"142":{"tf":1.7320508075688772},"146":{"tf":1.7320508075688772},"35":{"tf":1.0}}},"r":{"df":0,"docs":{},"i":{"df":0,"docs":{},"m":{"df":1,"docs":{"124":{"tf":1.0}}}}}},"r":{"df":0,"docs":{},"e":{"df":1,"docs":{"144":{"tf":1.0}}},"r":{"df":0,"docs":{},"e":{"c":{"df":0,"docs":{},"t":{"df":6,"docs":{"151":{"tf":1.0},"50":{"tf":1.0},"59":{"tf":1.0},"69":{"tf":1.0},"7":{"tf":1.4142135623730951},"72":{"tf":1.0}},"l":{"df":0,"docs":{},"i":{"df":7,"docs":{"104":{"tf":1.0},"117":{"tf":1.0},"146":{"tf":1.0},"151":{"tf":1.0},"58":{"tf":1.4142135623730951},"60":{"tf":1.0},"84":{"tf":1.0}}}}}},"df":0,"docs":{},"s":{"df":0,"docs":{},"p":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"d":{"df":18,"docs":{"10":{"tf":2.0},"108":{"tf":1.0},"11":{"tf":1.4142135623730951},"13":{"tf":1.0},"134":{"tf":1.0},"136":{"tf":1.0},"14":{"tf":1.0},"142":{"tf":1.4142135623730951},"146":{"tf":2.23606797749979},"149":{"tf":1.7320508075688772},"16":{"tf":1.4142135623730951},"23":{"tf":1.0},"36":{"tf":1.4142135623730951},"4":{"tf":1.0},"52":{"tf":1.0},"72":{"tf":1.0},"73":{"tf":1.0},"75":{"tf":1.0}}},"df":0,"docs":{}}}}}}}},"s":{"df":0,"docs":{},"e":{"df":0,"docs":{},"t":{"_":{"df":0,"docs":{},"o":{"df":0,"docs":{},"f":{"df":0,"docs":{},"f":{"df":0,"docs":{},"s":{"df":0,"docs":{},"e":{"df":0,"docs":{},"t":{"df":1,"docs":{"109":{"tf":1.0}}}}}}}}},"df":3,"docs":{"109":{"tf":1.0},"125":{"tf":2.449489742783178},"91":{"tf":1.0}}}},"t":{"df":1,"docs":{"151":{"tf":1.0}},"l":{"df":0,"docs":{},"i":{"df":1,"docs":{"80":{"tf":1.0}}}}}},"u":{"df":0,"docs":{},"l":{"d":{"df":0,"docs":{},"n":{"'":{"df":0,"docs":{},"t":{"df":1,"docs":{"103":{"tf":1.0}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"p":{"df":0,"docs":{},"l":{"df":1,"docs":{"105":{"tf":1.0}}}},"r":{"df":0,"docs":{},"s":{"df":3,"docs":{"12":{"tf":1.0},"125":{"tf":1.0},"14":{"tf":1.0}}}}}},"p":{"df":0,"docs":{},"u":{"_":{"a":{"df":0,"docs":{},"i":{"df":0,"docs":{},"r":{"_":{"d":{"df":0,"docs":{},"e":{"df":0,"docs":{},"f":{"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"df":0,"docs":{},"i":{"df":0,"docs":{},"t":{"df":0,"docs":{},"i":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"1":{"0":{".":{"df":0,"docs":{},"h":{"df":1,"docs":{"127":{"tf":1.0}}},"i":{"df":0,"docs":{},"n":{"df":0,"docs":{},"l":{"df":1,"docs":{"127":{"tf":1.0}}}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}}}}}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":1,"docs":{"3":{"tf":2.0}}}},"r":{"a":{"df":0,"docs":{},"f":{"df":0,"docs":{},"t":{"df":1,"docs":{"79":{"tf":1.0}}}},"t":{"df":0,"docs":{},"e":{"df":1,"docs":{"1":{"tf":1.4142135623730951}}}}},"df":0,"docs":{},"e":{"a":{"df":0,"docs":{},"t":{"df":5,"docs":{"108":{"tf":1.0},"146":{"tf":1.0},"44":{"tf":1.4142135623730951},"45":{"tf":1.0},"50":{"tf":1.0}},"i":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"df":1,"docs":{"34":{"tf":1.0}}}}}}},"df":0,"docs":{}},"i":{"df":0,"docs":{},"t":{"df":0,"docs":{},"i":{"c":{"df":1,"docs":{"146":{"tf":1.0}}},"df":0,"docs":{}}}},"u":{"c":{"df":0,"docs":{},"i":{"a":{"df":0,"docs":{},"l":{"df":1,"docs":{"71":{"tf":1.0}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"y":{"df":0,"docs":{},"p":{"df":0,"docs":{},"t":{"df":0,"docs":{},"o":{"df":1,"docs":{"1":{"tf":1.0}},"g":{"df":0,"docs":{},"r":{"a":{"df":0,"docs":{},"p":{"df":0,"docs":{},"h":{"df":3,"docs":{"19":{"tf":1.0},"4":{"tf":1.0},"71":{"tf":1.0}}}}},"df":0,"docs":{}}}}}}}},"s":{":":{":":{"c":{"df":0,"docs":{},"o":{"df":0,"docs":{},"m":{"df":0,"docs":{},"m":{"df":0,"docs":{},"i":{"df":0,"docs":{},"t":{"df":1,"docs":{"35":{"tf":3.0}}}}}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":1,"docs":{"35":{"tf":2.449489742783178}}},"u":{"df":0,"docs":{},"m":{"df":1,"docs":{"139":{"tf":1.4142135623730951}},"u":{"df":0,"docs":{},"l":{"df":3,"docs":{"138":{"tf":1.7320508075688772},"139":{"tf":1.0},"146":{"tf":1.7320508075688772}}}}},"r":{"df":0,"docs":{},"r":{"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"df":0,"docs":{},"t":{"df":7,"docs":{"107":{"tf":2.23606797749979},"109":{"tf":1.0},"121":{"tf":1.0},"133":{"tf":1.0},"40":{"tf":1.0},"66":{"tf":1.0},"84":{"tf":1.0}}}}}},"v":{"df":2,"docs":{"38":{"tf":1.0},"44":{"tf":1.0}}}},"s":{"df":0,"docs":{},"t":{"df":0,"docs":{},"o":{"df":0,"docs":{},"m":{"df":1,"docs":{"12":{"tf":1.7320508075688772}}}}}}},"y":{"c":{"df":0,"docs":{},"l":{"df":4,"docs":{"127":{"tf":1.0},"128":{"tf":1.0},"144":{"tf":1.0},"146":{"tf":1.0}},"i":{"c":{"df":1,"docs":{"38":{"tf":1.0}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"d":{"0":{"df":1,"docs":{"149":{"tf":1.7320508075688772}},"​":{":":{"=":{"d":{"df":0,"docs":{},"l":{"d":{"df":0,"docs":{},"e":{"df":1,"docs":{"94":{"tf":1.0}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"1":{"df":1,"docs":{"149":{"tf":1.7320508075688772}}},"2":{"df":1,"docs":{"149":{"tf":1.7320508075688772}}},"3":{"df":1,"docs":{"149":{"tf":1.7320508075688772}}},"=":{"(":{"d":{"1":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"…":{",":{"d":{"df":0,"docs":{},"m":{"df":3,"docs":{"73":{"tf":1.4142135623730951},"75":{"tf":1.0},"80":{"tf":1.0}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{},"y":{"1":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"…":{",":{"df":0,"docs":{},"y":{"df":0,"docs":{},"l":{"df":1,"docs":{"90":{"tf":1.0}}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{},"{":{"1":{",":{"df":0,"docs":{},"η":{",":{"df":0,"docs":{},"η":{"2":{",":{"df":0,"docs":{},"…":{",":{"df":0,"docs":{},"η":{"3":{"df":0,"docs":{},"n":{"df":0,"docs":{},"−":{"1":{"df":1,"docs":{"14":{"tf":1.0}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{},"g":{"df":0,"docs":{},"i":{"df":0,"docs":{},"​":{"df":0,"docs":{},"}":{"df":0,"docs":{},"i":{"=":{"0":{"2":{"df":0,"docs":{},"n":{"df":0,"docs":{},"−":{"1":{"df":1,"docs":{"84":{"tf":1.0}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}}}}}},"a":{"df":0,"docs":{},"t":{"a":{"df":2,"docs":{"146":{"tf":1.4142135623730951},"32":{"tf":1.0}}},"df":0,"docs":{}}},"df":11,"docs":{"114":{"tf":1.0},"14":{"tf":1.0},"142":{"tf":1.4142135623730951},"146":{"tf":2.23606797749979},"147":{"tf":1.4142135623730951},"149":{"tf":1.0},"3":{"tf":1.4142135623730951},"73":{"tf":1.0},"76":{"tf":1.0},"82":{"tf":1.0},"84":{"tf":1.0}},"e":{"c":{"df":0,"docs":{},"e":{"df":0,"docs":{},"i":{"df":0,"docs":{},"v":{"df":1,"docs":{"83":{"tf":1.0}}}}},"o":{"d":{"df":3,"docs":{"142":{"tf":1.0},"146":{"tf":2.0},"147":{"tf":1.0}},"e":{"/":{"df":0,"docs":{},"o":{"df":0,"docs":{},"p":{"c":{"df":0,"docs":{},"o":{"d":{"df":1,"docs":{"131":{"tf":1.4142135623730951}}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{},"m":{"df":0,"docs":{},"p":{"df":0,"docs":{},"o":{"df":0,"docs":{},"s":{"df":2,"docs":{"151":{"tf":1.0},"94":{"tf":1.4142135623730951}},"i":{"df":0,"docs":{},"t":{"df":4,"docs":{"113":{"tf":1.0},"116":{"tf":1.7320508075688772},"146":{"tf":1.0},"67":{"tf":1.7320508075688772}}}}}}}}}},"d":{"df":0,"docs":{},"i":{"c":{"df":2,"docs":{"146":{"tf":1.4142135623730951},"150":{"tf":1.0}}},"df":0,"docs":{}}},"df":0,"docs":{},"e":{"df":0,"docs":{},"p":{"(":{"df":0,"docs":{},"x":{")":{"=":{"df":0,"docs":{},"γ":{"1":{"df":0,"docs":{},"​":{"df":0,"docs":{},"x":{"df":0,"docs":{},"−":{"df":0,"docs":{},"z":{"df":0,"docs":{},"h":{"(":{"df":0,"docs":{},"x":{")":{"df":0,"docs":{},"−":{"df":0,"docs":{},"h":{"(":{"df":0,"docs":{},"z":{")":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"γ":{"2":{"df":0,"docs":{},"​":{"df":0,"docs":{},"x":{"df":0,"docs":{},"−":{"df":0,"docs":{},"z":{"df":0,"docs":{},"t":{"(":{"df":0,"docs":{},"x":{")":{"df":0,"docs":{},"−":{"df":0,"docs":{},"t":{"(":{"df":0,"docs":{},"z":{")":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"γ":{"3":{"df":0,"docs":{},"​":{"df":0,"docs":{},"x":{"df":0,"docs":{},"−":{"df":0,"docs":{},"z":{"df":0,"docs":{},"g":{"df":0,"docs":{},"t":{"(":{"df":0,"docs":{},"x":{")":{"df":0,"docs":{},"−":{"df":0,"docs":{},"t":{"(":{"df":0,"docs":{},"z":{"df":0,"docs":{},"g":{")":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"γ":{"4":{"df":0,"docs":{},"​":{"df":0,"docs":{},"x":{"df":0,"docs":{},"−":{"df":0,"docs":{},"z":{"df":0,"docs":{},"g":{"2":{"df":0,"docs":{},"t":{"(":{"df":0,"docs":{},"x":{")":{"df":0,"docs":{},"−":{"df":0,"docs":{},"t":{"(":{"df":0,"docs":{},"z":{"df":0,"docs":{},"g":{"2":{"df":1,"docs":{"59":{"tf":1.0}}},"df":0,"docs":{}}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"0":{"df":1,"docs":{"113":{"tf":1.0}},"​":{")":{"=":{"df":0,"docs":{},"γ":{"1":{"df":0,"docs":{},"​":{"df":0,"docs":{},"x":{"0":{"df":0,"docs":{},"​":{"df":0,"docs":{},"−":{"df":0,"docs":{},"z":{"df":0,"docs":{},"h":{"(":{"df":0,"docs":{},"x":{"0":{"df":0,"docs":{},"​":{")":{"df":0,"docs":{},"−":{"df":0,"docs":{},"h":{"(":{"df":0,"docs":{},"z":{")":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"γ":{"2":{"df":0,"docs":{},"​":{"df":0,"docs":{},"x":{"0":{"df":0,"docs":{},"​":{"df":0,"docs":{},"−":{"df":0,"docs":{},"z":{"df":0,"docs":{},"t":{"(":{"df":0,"docs":{},"x":{"0":{"df":0,"docs":{},"​":{")":{"df":0,"docs":{},"−":{"df":0,"docs":{},"t":{"(":{"df":0,"docs":{},"z":{")":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"γ":{"3":{"df":0,"docs":{},"​":{"df":0,"docs":{},"x":{"0":{"df":0,"docs":{},"​":{"df":0,"docs":{},"−":{"df":0,"docs":{},"z":{"df":0,"docs":{},"g":{"df":0,"docs":{},"t":{"(":{"df":0,"docs":{},"x":{"0":{"df":0,"docs":{},"​":{")":{"df":0,"docs":{},"−":{"df":0,"docs":{},"t":{"(":{"df":0,"docs":{},"z":{"df":0,"docs":{},"g":{")":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"γ":{"4":{"df":0,"docs":{},"​":{"df":0,"docs":{},"x":{"0":{"df":0,"docs":{},"​":{"df":0,"docs":{},"−":{"df":0,"docs":{},"z":{"df":0,"docs":{},"g":{"2":{"df":0,"docs":{},"t":{"(":{"df":0,"docs":{},"x":{"0":{"df":0,"docs":{},"​":{")":{"df":0,"docs":{},"−":{"df":0,"docs":{},"t":{"(":{"df":0,"docs":{},"z":{"df":0,"docs":{},"g":{"2":{"df":3,"docs":{"113":{"tf":1.0},"60":{"tf":1.0},"63":{"tf":1.0}}},"df":0,"docs":{}}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"_":{"0":{"df":1,"docs":{"63":{"tf":1.0}}},"df":0,"docs":{}},"df":3,"docs":{"112":{"tf":1.4142135623730951},"59":{"tf":1.4142135623730951},"62":{"tf":1.4142135623730951}}}},"df":13,"docs":{"100":{"tf":1.0},"112":{"tf":1.0},"119":{"tf":1.7320508075688772},"125":{"tf":1.0},"59":{"tf":1.7320508075688772},"60":{"tf":1.0},"62":{"tf":1.0},"65":{"tf":1.0},"68":{"tf":1.4142135623730951},"80":{"tf":1.0},"81":{"tf":1.4142135623730951},"95":{"tf":1.0},"97":{"tf":1.0}},"e":{"df":0,"docs":{},"r":{"df":2,"docs":{"103":{"tf":1.0},"69":{"tf":1.0}}}}}},"f":{"a":{"df":0,"docs":{},"u":{"df":0,"docs":{},"l":{"df":0,"docs":{},"t":{"df":1,"docs":{"9":{"tf":1.0}}}}}},"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"df":29,"docs":{"105":{"tf":1.7320508075688772},"108":{"tf":1.0},"109":{"tf":1.4142135623730951},"110":{"tf":1.0},"113":{"tf":1.0},"114":{"tf":1.0},"128":{"tf":1.0},"131":{"tf":1.0},"14":{"tf":1.7320508075688772},"142":{"tf":1.7320508075688772},"147":{"tf":2.449489742783178},"149":{"tf":1.0},"150":{"tf":2.0},"20":{"tf":1.0},"23":{"tf":1.0},"25":{"tf":1.0},"26":{"tf":1.4142135623730951},"28":{"tf":1.4142135623730951},"35":{"tf":1.4142135623730951},"5":{"tf":1.0},"50":{"tf":1.0},"54":{"tf":1.0},"55":{"tf":1.0},"59":{"tf":1.0},"8":{"tf":1.0},"90":{"tf":1.0},"94":{"tf":1.4142135623730951},"95":{"tf":1.0},"97":{"tf":1.7320508075688772}},"i":{"df":0,"docs":{},"t":{"df":3,"docs":{"14":{"tf":1.4142135623730951},"34":{"tf":1.0},"91":{"tf":1.4142135623730951}}}}}}},"g":{"df":0,"docs":{},"r":{"df":0,"docs":{},"e":{"df":18,"docs":{"109":{"tf":1.0},"114":{"tf":1.0},"119":{"tf":1.0},"125":{"tf":1.0},"14":{"tf":1.4142135623730951},"146":{"tf":1.0},"20":{"tf":2.23606797749979},"26":{"tf":1.0},"3":{"tf":1.4142135623730951},"68":{"tf":2.449489742783178},"73":{"tf":3.0},"74":{"tf":1.4142135623730951},"75":{"tf":1.7320508075688772},"78":{"tf":2.0},"79":{"tf":1.7320508075688772},"80":{"tf":1.0},"81":{"tf":1.0},"91":{"tf":1.4142135623730951}}}}},"l":{"df":0,"docs":{},"e":{"df":0,"docs":{},"g":{"df":2,"docs":{"151":{"tf":1.0},"7":{"tf":1.0}}}},"i":{"c":{"df":1,"docs":{"16":{"tf":1.0}}},"df":0,"docs":{}},"v":{"df":1,"docs":{"4":{"tf":1.0}}}},"m":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"df":0,"docs":{},"s":{"df":0,"docs":{},"t":{"df":0,"docs":{},"r":{"df":2,"docs":{"13":{"tf":1.0},"7":{"tf":1.0}}}}}},"s":{"df":0,"docs":{},"t":{"df":0,"docs":{},"r":{"df":1,"docs":{"7":{"tf":1.0}}}}}}},"n":{"df":0,"docs":{},"o":{"df":0,"docs":{},"m":{"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"df":4,"docs":{"114":{"tf":1.0},"123":{"tf":1.0},"125":{"tf":1.4142135623730951},"54":{"tf":1.0}}}}},"t":{"df":18,"docs":{"14":{"tf":1.0},"147":{"tf":1.0},"150":{"tf":1.0},"19":{"tf":1.0},"36":{"tf":1.0},"5":{"tf":3.0},"53":{"tf":1.0},"70":{"tf":1.4142135623730951},"72":{"tf":1.0},"75":{"tf":1.0},"77":{"tf":1.0},"82":{"tf":1.0},"83":{"tf":1.0},"84":{"tf":1.0},"88":{"tf":1.0},"90":{"tf":2.0},"91":{"tf":1.4142135623730951},"95":{"tf":1.4142135623730951}}}}},"p":{"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"d":{"df":9,"docs":{"10":{"tf":1.0},"109":{"tf":1.0},"13":{"tf":1.4142135623730951},"146":{"tf":1.7320508075688772},"70":{"tf":1.4142135623730951},"73":{"tf":1.4142135623730951},"8":{"tf":1.0},"84":{"tf":1.4142135623730951},"92":{"tf":1.0}}},"df":0,"docs":{}}}},"r":{"df":0,"docs":{},"i":{"df":0,"docs":{},"v":{"df":5,"docs":{"142":{"tf":1.0},"146":{"tf":1.7320508075688772},"147":{"tf":1.0},"5":{"tf":1.0},"91":{"tf":1.0}}}}},"s":{"c":{"df":0,"docs":{},"r":{"df":0,"docs":{},"i":{"b":{"df":12,"docs":{"142":{"tf":1.0},"145":{"tf":1.0},"147":{"tf":1.0},"23":{"tf":1.4142135623730951},"37":{"tf":1.0},"40":{"tf":1.0},"74":{"tf":1.0},"77":{"tf":1.0},"80":{"tf":1.0},"86":{"tf":1.0},"87":{"tf":1.0},"95":{"tf":1.0}}},"df":0,"docs":{},"p":{"df":0,"docs":{},"t":{"df":21,"docs":{"127":{"tf":1.0},"128":{"tf":1.0},"129":{"tf":1.0},"130":{"tf":1.0},"131":{"tf":1.0},"132":{"tf":1.0},"133":{"tf":1.0},"134":{"tf":1.0},"135":{"tf":1.0},"136":{"tf":1.0},"137":{"tf":1.0},"138":{"tf":1.0},"139":{"tf":1.0},"141":{"tf":1.0},"142":{"tf":1.4142135623730951},"143":{"tf":1.0},"144":{"tf":1.0},"145":{"tf":1.0},"146":{"tf":1.0},"16":{"tf":1.0},"82":{"tf":1.4142135623730951}}}}}}},"df":0,"docs":{},"i":{"df":0,"docs":{},"g":{"df":0,"docs":{},"n":{"df":2,"docs":{"10":{"tf":1.0},"151":{"tf":1.0}}}},"r":{"df":2,"docs":{"146":{"tf":1.0},"20":{"tf":1.0}}}}},"t":{"a":{"df":0,"docs":{},"i":{"df":0,"docs":{},"l":{"df":16,"docs":{"103":{"tf":1.0},"114":{"tf":1.0},"127":{"tf":1.0},"133":{"tf":1.0},"134":{"tf":1.0},"138":{"tf":1.0},"139":{"tf":1.4142135623730951},"143":{"tf":1.0},"144":{"tf":1.4142135623730951},"145":{"tf":1.7320508075688772},"146":{"tf":1.4142135623730951},"151":{"tf":1.0},"18":{"tf":1.4142135623730951},"34":{"tf":1.0},"37":{"tf":1.7320508075688772},"75":{"tf":1.0}}}}},"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":0,"docs":{},"m":{"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"df":1,"docs":{"91":{"tf":1.0}},"i":{"df":0,"docs":{},"s":{"df":0,"docs":{},"t":{"df":1,"docs":{"137":{"tf":1.0}}}}}}}}}}},"v":{"df":0,"docs":{},"i":{"c":{"df":1,"docs":{"151":{"tf":1.4142135623730951}}},"df":0,"docs":{}}}},"i":{"a":{"df":0,"docs":{},"g":{"df":0,"docs":{},"r":{"a":{"df":0,"docs":{},"m":{"df":1,"docs":{"133":{"tf":1.0}}}},"df":0,"docs":{}}}},"d":{"df":0,"docs":{},"n":{"'":{"df":0,"docs":{},"t":{"df":1,"docs":{"134":{"tf":1.0}}}},"df":0,"docs":{}}},"df":0,"docs":{},"f":{"df":0,"docs":{},"f":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":15,"docs":{"118":{"tf":1.0},"14":{"tf":1.7320508075688772},"146":{"tf":1.0},"149":{"tf":1.0},"20":{"tf":1.0},"3":{"tf":1.0},"50":{"tf":1.4142135623730951},"52":{"tf":1.0},"53":{"tf":1.0},"56":{"tf":1.0},"66":{"tf":1.4142135623730951},"70":{"tf":1.0},"73":{"tf":1.0},"80":{"tf":1.0},"9":{"tf":1.0}}}}}},"g":{"df":0,"docs":{},"e":{"df":0,"docs":{},"s":{"df":0,"docs":{},"t":{"df":1,"docs":{"7":{"tf":1.0}}}}}},"r":{"df":0,"docs":{},"e":{"c":{"df":0,"docs":{},"t":{"df":0,"docs":{},"l":{"df":0,"docs":{},"i":{"df":3,"docs":{"127":{"tf":1.0},"14":{"tf":1.0},"146":{"tf":1.4142135623730951}}}}}},"df":0,"docs":{}}},"s":{"c":{"df":0,"docs":{},"u":{"df":0,"docs":{},"s":{"df":0,"docs":{},"s":{"df":3,"docs":{"34":{"tf":1.0},"43":{"tf":1.0},"56":{"tf":1.0}}}}}},"df":0,"docs":{},"j":{"df":0,"docs":{},"o":{"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"df":0,"docs":{},"t":{"df":1,"docs":{"90":{"tf":1.0}}}}}}},"t":{"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"df":0,"docs":{},"g":{"df":0,"docs":{},"u":{"df":0,"docs":{},"i":{"df":0,"docs":{},"s":{"df":0,"docs":{},"h":{"df":1,"docs":{"9":{"tf":1.0}}}}}}}}}}},"v":{"df":0,"docs":{},"e":{"df":4,"docs":{"103":{"tf":1.0},"46":{"tf":1.0},"69":{"tf":1.0},"81":{"tf":1.0}}},"i":{"d":{"df":7,"docs":{"100":{"tf":1.0},"123":{"tf":1.4142135623730951},"125":{"tf":1.7320508075688772},"146":{"tf":1.4142135623730951},"38":{"tf":1.0},"79":{"tf":1.0},"99":{"tf":1.0}}},"df":0,"docs":{},"s":{"df":4,"docs":{"125":{"tf":1.4142135623730951},"146":{"tf":1.0},"69":{"tf":1.0},"99":{"tf":1.0}}}}},"​":{"=":{"df":0,"docs":{},"h":{"df":0,"docs":{},"ω":{"df":0,"docs":{},"i":{"df":2,"docs":{"73":{"tf":1.0},"75":{"tf":1.0}}}}}},"df":0,"docs":{},"∈":{"d":{"df":1,"docs":{"73":{"tf":1.0}}},"df":0,"docs":{}},"−":{"df":0,"docs":{},"z":{"df":1,"docs":{"79":{"tf":1.0}}}}}},"k":{"df":0,"docs":{},"​":{":":{"=":{"(":{"d":{"0":{"2":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"…":{",":{"d":{"df":0,"docs":{},"l":{"df":0,"docs":{},"−":{"1":{"2":{"df":1,"docs":{"94":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"−":{"1":{"df":0,"docs":{},"​":{"=":{"(":{"d":{"0":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"…":{",":{"d":{"2":{"df":0,"docs":{},"l":{"df":0,"docs":{},"−":{"1":{"df":1,"docs":{"94":{"tf":1.0}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"l":{"d":{"df":0,"docs":{},"e":{"df":1,"docs":{"83":{"tf":1.0}},"​":{"=":{"(":{"df":0,"docs":{},"h":{",":{"df":0,"docs":{},"h":{"df":0,"docs":{},"ω":{",":{"df":0,"docs":{},"h":{"df":0,"docs":{},"ω":{"2":{",":{"df":0,"docs":{},"…":{",":{"df":0,"docs":{},"h":{"df":0,"docs":{},"ω":{"2":{"df":0,"docs":{},"n":{"+":{"df":0,"docs":{},"l":{"df":1,"docs":{"82":{"tf":1.0}},"−":{"1":{"df":1,"docs":{"91":{"tf":1.0}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}},"o":{"c":{"df":0,"docs":{},"u":{"df":0,"docs":{},"m":{"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"df":0,"docs":{},"t":{"df":3,"docs":{"0":{"tf":1.0},"126":{"tf":1.4142135623730951},"46":{"tf":1.0}}}}}}}},"df":5,"docs":{"109":{"tf":1.0},"114":{"tf":1.0},"125":{"tf":1.7320508075688772},"146":{"tf":1.4142135623730951},"149":{"tf":1.0}},"e":{"df":0,"docs":{},"s":{"df":0,"docs":{},"n":{"'":{"df":0,"docs":{},"t":{"df":4,"docs":{"125":{"tf":1.0},"130":{"tf":1.0},"14":{"tf":1.0},"32":{"tf":1.0}}}},"df":0,"docs":{}}}},"m":{"a":{"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"df":24,"docs":{"101":{"tf":1.0},"112":{"tf":1.4142135623730951},"115":{"tf":1.0},"117":{"tf":1.7320508075688772},"119":{"tf":1.0},"125":{"tf":1.4142135623730951},"14":{"tf":2.0},"16":{"tf":1.0},"20":{"tf":1.0},"24":{"tf":1.0},"25":{"tf":1.0},"26":{"tf":1.0},"32":{"tf":1.4142135623730951},"35":{"tf":1.4142135623730951},"5":{"tf":1.0},"59":{"tf":1.4142135623730951},"60":{"tf":1.4142135623730951},"62":{"tf":1.4142135623730951},"66":{"tf":1.0},"68":{"tf":1.4142135623730951},"73":{"tf":1.7320508075688772},"84":{"tf":1.0},"91":{"tf":1.0},"94":{"tf":1.4142135623730951}}}}},"df":0,"docs":{}},"n":{"'":{"df":0,"docs":{},"t":{"df":9,"docs":{"111":{"tf":1.0},"114":{"tf":1.0},"116":{"tf":1.4142135623730951},"118":{"tf":1.0},"123":{"tf":1.4142135623730951},"125":{"tf":1.7320508075688772},"149":{"tf":1.0},"151":{"tf":1.0},"67":{"tf":1.0}}}},"df":0,"docs":{},"e":{"df":12,"docs":{"103":{"tf":1.0},"105":{"tf":1.0},"110":{"tf":1.0},"114":{"tf":1.4142135623730951},"118":{"tf":1.4142135623730951},"130":{"tf":1.0},"146":{"tf":1.4142135623730951},"151":{"tf":1.4142135623730951},"58":{"tf":1.0},"69":{"tf":1.0},"83":{"tf":1.0},"84":{"tf":1.0}}}},"t":{"df":2,"docs":{"65":{"tf":1.0},"66":{"tf":1.0}}},"w":{"df":0,"docs":{},"n":{"df":4,"docs":{"14":{"tf":1.4142135623730951},"16":{"tf":1.0},"21":{"tf":1.0},"35":{"tf":1.0}}}}},"s":{"df":1,"docs":{"94":{"tf":1.4142135623730951}},"t":{"_":{"a":{"d":{"d":{"df":0,"docs":{},"r":{"[":{"0":{"df":1,"docs":{"147":{"tf":1.0}}},"1":{"df":1,"docs":{"147":{"tf":1.0}}},"df":0,"docs":{}},"df":3,"docs":{"142":{"tf":1.0},"146":{"tf":1.7320508075688772},"147":{"tf":1.4142135623730951}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{},"r":{"df":0,"docs":{},"e":{"df":0,"docs":{},"g":{"df":1,"docs":{"146":{"tf":1.0}}}}}},"df":4,"docs":{"133":{"tf":2.0},"142":{"tf":1.0},"146":{"tf":2.23606797749979},"147":{"tf":1.4142135623730951}},"r":{"df":0,"docs":{},"e":{"df":0,"docs":{},"g":{"df":1,"docs":{"149":{"tf":1.0}}}}}},"​":{"=":{"(":{"1":{",":{"df":0,"docs":{},"g":{",":{"df":0,"docs":{},"…":{",":{"df":0,"docs":{},"g":{"2":{"df":0,"docs":{},"n":{"df":0,"docs":{},"−":{"1":{"df":1,"docs":{"91":{"tf":1.0}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{},"{":{"df":0,"docs":{},"g":{"df":0,"docs":{},"i":{"df":0,"docs":{},"}":{"df":0,"docs":{},"i":{"=":{"0":{"2":{"df":0,"docs":{},"n":{"df":0,"docs":{},"−":{"1":{"df":0,"docs":{},"​":{"df":0,"docs":{},"⊆":{"df":0,"docs":{},"f":{"df":1,"docs":{"82":{"tf":1.0}}}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}}}}},"df":0,"docs":{}}},"u":{"df":0,"docs":{},"e":{"df":2,"docs":{"13":{"tf":1.0},"4":{"tf":1.0}}},"m":{"df":0,"docs":{},"m":{"df":0,"docs":{},"i":{"df":5,"docs":{"133":{"tf":1.7320508075688772},"135":{"tf":1.7320508075688772},"146":{"tf":1.7320508075688772},"149":{"tf":1.0},"36":{"tf":1.7320508075688772}}}}},"r":{"df":0,"docs":{},"e":{"df":2,"docs":{"14":{"tf":1.0},"80":{"tf":1.0}}}}}},"df":0,"docs":{},"e":{"+":{"1":{")":{"df":0,"docs":{},"∗":{"df":0,"docs":{},"x":{"df":0,"docs":{},"−":{"1":{"df":1,"docs":{"7":{"tf":1.0}}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}},"=":{"2":{"df":1,"docs":{"9":{"tf":1.0}}},"df":0,"docs":{}},"a":{"c":{"df":0,"docs":{},"h":{"df":30,"docs":{"10":{"tf":1.0},"102":{"tf":1.0},"106":{"tf":1.0},"109":{"tf":1.7320508075688772},"114":{"tf":1.7320508075688772},"125":{"tf":1.0},"128":{"tf":1.0},"130":{"tf":1.4142135623730951},"131":{"tf":2.0},"133":{"tf":1.4142135623730951},"136":{"tf":1.0},"14":{"tf":1.0},"142":{"tf":1.4142135623730951},"146":{"tf":3.3166247903554},"147":{"tf":1.0},"149":{"tf":2.0},"150":{"tf":1.7320508075688772},"151":{"tf":2.23606797749979},"3":{"tf":1.0},"49":{"tf":1.0},"65":{"tf":1.4142135623730951},"66":{"tf":1.4142135623730951},"70":{"tf":1.0},"80":{"tf":1.7320508075688772},"82":{"tf":1.4142135623730951},"83":{"tf":1.0},"84":{"tf":1.0},"94":{"tf":1.4142135623730951},"95":{"tf":1.0},"99":{"tf":1.0}}}},"df":0,"docs":{},"r":{"df":0,"docs":{},"l":{"df":0,"docs":{},"i":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":2,"docs":{"59":{"tf":1.0},"80":{"tf":1.0}}}}}}},"s":{"df":1,"docs":{"70":{"tf":1.0}},"i":{"df":1,"docs":{"14":{"tf":1.0}},"l":{"df":0,"docs":{},"i":{"df":5,"docs":{"114":{"tf":1.0},"133":{"tf":1.0},"45":{"tf":1.0},"7":{"tf":1.0},"70":{"tf":1.0}}}}}}},"df":10,"docs":{"11":{"tf":1.0},"142":{"tf":1.4142135623730951},"146":{"tf":1.7320508075688772},"147":{"tf":1.4142135623730951},"34":{"tf":1.0},"35":{"tf":2.6457513110645907},"44":{"tf":2.6457513110645907},"45":{"tf":2.0},"7":{"tf":2.23606797749979},"9":{"tf":1.7320508075688772}},"f":{"df":0,"docs":{},"f":{"df":0,"docs":{},"e":{"c":{"df":0,"docs":{},"t":{"df":1,"docs":{"59":{"tf":1.0}}}},"df":0,"docs":{}},"i":{"c":{"df":0,"docs":{},"i":{"df":4,"docs":{"146":{"tf":1.0},"151":{"tf":1.0},"4":{"tf":1.0},"73":{"tf":1.0}}}},"df":0,"docs":{}}}},"i":{"df":1,"docs":{"123":{"tf":1.0}},"g":{"df":0,"docs":{},"h":{"df":0,"docs":{},"t":{"df":5,"docs":{"15":{"tf":1.0},"151":{"tf":1.0},"23":{"tf":1.0},"35":{"tf":1.4142135623730951},"8":{"tf":1.0}}}}}},"l":{"df":0,"docs":{},"e":{"df":0,"docs":{},"m":{"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"df":0,"docs":{},"t":{"df":30,"docs":{"101":{"tf":1.0},"107":{"tf":1.0},"121":{"tf":1.0},"123":{"tf":1.0},"125":{"tf":2.0},"128":{"tf":1.7320508075688772},"14":{"tf":5.477225575051661},"144":{"tf":1.0},"16":{"tf":1.0},"19":{"tf":2.449489742783178},"31":{"tf":1.0},"35":{"tf":1.7320508075688772},"41":{"tf":2.0},"52":{"tf":1.0},"54":{"tf":1.0},"55":{"tf":1.0},"68":{"tf":1.0},"7":{"tf":1.0},"70":{"tf":1.4142135623730951},"73":{"tf":1.0},"74":{"tf":1.4142135623730951},"77":{"tf":1.0},"79":{"tf":1.0},"82":{"tf":1.0},"84":{"tf":1.0},"91":{"tf":1.0},"92":{"tf":2.0},"95":{"tf":1.4142135623730951},"98":{"tf":1.4142135623730951},"99":{"tf":1.0}}}}}}}},"m":{"b":{"df":0,"docs":{},"e":{"d":{"df":1,"docs":{"130":{"tf":1.0}}},"df":0,"docs":{}}},"df":0,"docs":{},"p":{"df":0,"docs":{},"h":{"a":{"df":0,"docs":{},"s":{"df":1,"docs":{"5":{"tf":1.0}}}},"df":0,"docs":{}},"l":{"df":0,"docs":{},"o":{"df":0,"docs":{},"y":{"df":2,"docs":{"100":{"tf":1.0},"115":{"tf":1.0}}}}},"t":{"df":0,"docs":{},"i":{"df":2,"docs":{"134":{"tf":1.0},"36":{"tf":1.0}}}}},"u":{"df":0,"docs":{},"l":{"df":1,"docs":{"118":{"tf":1.0}}}}},"n":{"a":{"b":{"df":0,"docs":{},"l":{"df":2,"docs":{"146":{"tf":1.0},"4":{"tf":1.0}}}},"df":0,"docs":{}},"c":{"a":{"df":0,"docs":{},"p":{"df":0,"docs":{},"s":{"df":0,"docs":{},"u":{"df":0,"docs":{},"l":{"df":1,"docs":{"109":{"tf":1.0}}}}}}},"df":0,"docs":{},"o":{"d":{"df":15,"docs":{"10":{"tf":1.0},"105":{"tf":1.0},"107":{"tf":1.0},"11":{"tf":1.0},"133":{"tf":1.0},"48":{"tf":1.0},"50":{"tf":1.0},"52":{"tf":1.0},"54":{"tf":1.0},"55":{"tf":1.0},"66":{"tf":1.0},"69":{"tf":1.4142135623730951},"70":{"tf":1.4142135623730951},"72":{"tf":1.0},"84":{"tf":1.0}}},"df":0,"docs":{}}},"d":{"df":12,"docs":{"103":{"tf":1.0},"109":{"tf":1.0},"116":{"tf":1.0},"12":{"tf":1.0},"13":{"tf":1.0},"130":{"tf":1.0},"134":{"tf":1.0},"144":{"tf":1.0},"40":{"tf":1.0},"46":{"tf":1.0},"8":{"tf":1.0},"95":{"tf":1.0}},"i":{"a":{"df":0,"docs":{},"n":{"df":1,"docs":{"41":{"tf":1.0}}}},"df":0,"docs":{}}},"df":0,"docs":{},"f":{"df":0,"docs":{},"o":{"df":0,"docs":{},"r":{"c":{"df":5,"docs":{"146":{"tf":1.0},"150":{"tf":1.4142135623730951},"151":{"tf":2.0},"49":{"tf":1.0},"70":{"tf":1.0}}},"df":0,"docs":{}}}},"g":{"a":{"df":0,"docs":{},"g":{"df":3,"docs":{"75":{"tf":1.0},"77":{"tf":1.0},"86":{"tf":1.0}}}},"df":0,"docs":{}},"h":{"a":{"df":0,"docs":{},"n":{"c":{"df":1,"docs":{"100":{"tf":1.0}}},"df":0,"docs":{}}},"df":0,"docs":{}},"o":{"df":0,"docs":{},"u":{"df":0,"docs":{},"g":{"df":0,"docs":{},"h":{"df":3,"docs":{"114":{"tf":1.4142135623730951},"146":{"tf":1.0},"49":{"tf":1.0}}}}}},"s":{"df":0,"docs":{},"u":{"df":0,"docs":{},"r":{"df":1,"docs":{"83":{"tf":1.0}}}}},"t":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":1,"docs":{"14":{"tf":1.0}}}},"i":{"df":0,"docs":{},"r":{"df":4,"docs":{"12":{"tf":1.0},"14":{"tf":1.0},"50":{"tf":1.0},"68":{"tf":1.0}}},"t":{"df":0,"docs":{},"i":{"df":1,"docs":{"104":{"tf":1.0}}}}},"r":{"df":0,"docs":{},"i":{"df":7,"docs":{"134":{"tf":1.0},"141":{"tf":1.0},"142":{"tf":2.0},"147":{"tf":2.0},"36":{"tf":1.0},"70":{"tf":1.0},"91":{"tf":1.0}}}}}},"q":{"df":0,"docs":{},"u":{"a":{"df":0,"docs":{},"l":{"df":16,"docs":{"10":{"tf":1.4142135623730951},"101":{"tf":1.0},"106":{"tf":1.7320508075688772},"11":{"tf":1.0},"13":{"tf":1.0},"14":{"tf":5.830951894845301},"142":{"tf":1.4142135623730951},"146":{"tf":2.23606797749979},"147":{"tf":1.0},"16":{"tf":1.4142135623730951},"33":{"tf":1.0},"52":{"tf":1.0},"66":{"tf":1.0},"73":{"tf":1.0},"78":{"tf":1.4142135623730951},"95":{"tf":1.4142135623730951}}},"t":{"df":12,"docs":{"10":{"tf":1.7320508075688772},"11":{"tf":1.0},"12":{"tf":1.0},"13":{"tf":1.0},"14":{"tf":3.0},"149":{"tf":1.4142135623730951},"16":{"tf":1.7320508075688772},"4":{"tf":1.4142135623730951},"58":{"tf":1.0},"60":{"tf":1.0},"69":{"tf":1.0},"95":{"tf":1.0}}}},"df":0,"docs":{},"i":{"df":0,"docs":{},"v":{"a":{"df":0,"docs":{},"l":{"df":6,"docs":{"14":{"tf":2.8284271247461903},"16":{"tf":1.0},"56":{"tf":1.0},"69":{"tf":1.4142135623730951},"80":{"tf":1.0},"84":{"tf":1.0}}}},"df":0,"docs":{}}}}},"s":{"df":0,"docs":{},"s":{"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"c":{"df":1,"docs":{"133":{"tf":1.0}}},"df":0,"docs":{},"t":{"df":0,"docs":{},"i":{"df":1,"docs":{"49":{"tf":1.0}}}}}}}},"t":{"c":{"df":2,"docs":{"134":{"tf":1.0},"50":{"tf":1.0}}},"df":0,"docs":{}},"v":{"a":{"df":0,"docs":{},"l":{"df":0,"docs":{},"u":{"a":{"df":0,"docs":{},"t":{"df":0,"docs":{},"o":{"df":0,"docs":{},"r":{".":{"df":0,"docs":{},"e":{"df":0,"docs":{},"v":{"a":{"df":0,"docs":{},"l":{"df":0,"docs":{},"u":{"df":1,"docs":{"114":{"tf":1.0}}}}},"df":0,"docs":{}}}},"df":0,"docs":{}}}}},"df":39,"docs":{"10":{"tf":1.4142135623730951},"11":{"tf":1.4142135623730951},"112":{"tf":2.0},"113":{"tf":2.6457513110645907},"114":{"tf":4.358898943540674},"115":{"tf":1.0},"116":{"tf":1.0},"117":{"tf":1.7320508075688772},"119":{"tf":1.0},"121":{"tf":2.0},"123":{"tf":2.23606797749979},"125":{"tf":2.23606797749979},"13":{"tf":1.7320508075688772},"14":{"tf":1.4142135623730951},"149":{"tf":2.6457513110645907},"19":{"tf":1.4142135623730951},"28":{"tf":1.0},"35":{"tf":1.0},"52":{"tf":1.0},"58":{"tf":2.23606797749979},"59":{"tf":2.0},"60":{"tf":1.7320508075688772},"62":{"tf":1.7320508075688772},"63":{"tf":2.23606797749979},"66":{"tf":1.4142135623730951},"68":{"tf":2.449489742783178},"70":{"tf":1.0},"73":{"tf":1.7320508075688772},"74":{"tf":1.4142135623730951},"75":{"tf":1.0},"76":{"tf":1.0},"77":{"tf":1.4142135623730951},"78":{"tf":1.0},"79":{"tf":1.0},"80":{"tf":2.0},"85":{"tf":1.7320508075688772},"86":{"tf":1.0},"94":{"tf":1.0},"95":{"tf":2.0}}}}},"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"/":{"df":0,"docs":{},"o":{"d":{"d":{"df":2,"docs":{"113":{"tf":1.0},"116":{"tf":1.7320508075688772}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":10,"docs":{"101":{"tf":1.0},"114":{"tf":1.0},"119":{"tf":1.0},"146":{"tf":1.4142135623730951},"49":{"tf":1.0},"52":{"tf":1.0},"66":{"tf":1.0},"67":{"tf":1.0},"73":{"tf":1.0},"94":{"tf":1.0}}},"r":{"df":0,"docs":{},"y":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"df":1,"docs":{"103":{"tf":1.0}}}},"t":{"df":0,"docs":{},"h":{"df":4,"docs":{"146":{"tf":1.0},"16":{"tf":1.0},"21":{"tf":1.0},"70":{"tf":1.0}}}}}}},"i":{"d":{"df":1,"docs":{"14":{"tf":1.0}}},"df":0,"docs":{}},"o":{"df":0,"docs":{},"l":{"df":0,"docs":{},"u":{"df":0,"docs":{},"t":{"df":1,"docs":{"141":{"tf":1.0}}}}}}},"x":{"a":{"c":{"df":0,"docs":{},"t":{"df":2,"docs":{"146":{"tf":1.0},"95":{"tf":1.0}},"l":{"df":0,"docs":{},"i":{"df":4,"docs":{"114":{"tf":1.0},"20":{"tf":1.0},"21":{"tf":1.4142135623730951},"67":{"tf":1.0}}}}}},"df":0,"docs":{},"m":{"df":0,"docs":{},"p":{"df":0,"docs":{},"l":{"df":42,"docs":{"10":{"tf":1.4142135623730951},"100":{"tf":1.0},"101":{"tf":1.0},"103":{"tf":1.0},"104":{"tf":1.4142135623730951},"108":{"tf":1.0},"109":{"tf":1.4142135623730951},"11":{"tf":1.0},"110":{"tf":1.0},"111":{"tf":1.0},"114":{"tf":1.4142135623730951},"117":{"tf":1.0},"12":{"tf":1.0},"124":{"tf":1.0},"13":{"tf":1.0},"130":{"tf":1.0},"134":{"tf":1.0},"135":{"tf":1.0},"14":{"tf":2.0},"146":{"tf":1.4142135623730951},"149":{"tf":1.7320508075688772},"150":{"tf":1.0},"151":{"tf":1.4142135623730951},"20":{"tf":1.0},"34":{"tf":1.0},"40":{"tf":1.0},"44":{"tf":1.7320508075688772},"48":{"tf":1.0},"49":{"tf":1.7320508075688772},"5":{"tf":1.0},"50":{"tf":2.23606797749979},"52":{"tf":1.0},"64":{"tf":1.0},"66":{"tf":1.0},"68":{"tf":1.0},"7":{"tf":1.7320508075688772},"70":{"tf":1.4142135623730951},"73":{"tf":1.0},"74":{"tf":1.4142135623730951},"84":{"tf":1.0},"9":{"tf":1.0},"95":{"tf":2.0}}}}}},"c":{"df":0,"docs":{},"e":{"df":0,"docs":{},"p":{"df":0,"docs":{},"t":{"df":3,"docs":{"142":{"tf":1.4142135623730951},"44":{"tf":1.0},"72":{"tf":1.0}}}}}},"df":0,"docs":{},"e":{"c":{"df":0,"docs":{},"u":{"df":0,"docs":{},"t":{"df":30,"docs":{"10":{"tf":1.7320508075688772},"108":{"tf":1.0},"11":{"tf":1.0},"110":{"tf":1.7320508075688772},"127":{"tf":1.0},"13":{"tf":2.0},"131":{"tf":1.4142135623730951},"133":{"tf":1.0},"14":{"tf":1.0},"141":{"tf":1.0},"142":{"tf":1.7320508075688772},"143":{"tf":1.4142135623730951},"144":{"tf":2.23606797749979},"145":{"tf":1.0},"146":{"tf":3.605551275463989},"16":{"tf":1.7320508075688772},"22":{"tf":1.0},"34":{"tf":1.0},"4":{"tf":1.0},"43":{"tf":1.0},"48":{"tf":1.4142135623730951},"49":{"tf":1.0},"50":{"tf":2.449489742783178},"69":{"tf":1.7320508075688772},"7":{"tf":2.23606797749979},"8":{"tf":2.0},"83":{"tf":1.4142135623730951},"9":{"tf":2.449489742783178},"91":{"tf":1.4142135623730951},"94":{"tf":1.4142135623730951}}}}},"df":0,"docs":{},"m":{"df":0,"docs":{},"p":{"df":0,"docs":{},"t":{"df":1,"docs":{"123":{"tf":1.4142135623730951}}}}},"r":{"c":{"df":0,"docs":{},"i":{"df":0,"docs":{},"s":{"df":1,"docs":{"40":{"tf":1.0}}}}},"df":0,"docs":{}}},"i":{"df":0,"docs":{},"s":{"df":0,"docs":{},"t":{"df":4,"docs":{"106":{"tf":1.0},"14":{"tf":2.6457513110645907},"149":{"tf":1.0},"70":{"tf":1.0}}}}},"p":{"a":{"df":0,"docs":{},"n":{"d":{"df":1,"docs":{"14":{"tf":1.0}}},"df":0,"docs":{}}},"df":0,"docs":{},"e":{"c":{"df":0,"docs":{},"t":{"df":7,"docs":{"146":{"tf":2.23606797749979},"151":{"tf":1.0},"71":{"tf":1.0},"72":{"tf":1.0},"75":{"tf":1.0},"79":{"tf":1.0},"85":{"tf":1.0}}}},"df":0,"docs":{},"n":{"df":0,"docs":{},"s":{"df":2,"docs":{"123":{"tf":1.0},"21":{"tf":1.0}}}}},"l":{"a":{"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"df":7,"docs":{"103":{"tf":1.0},"117":{"tf":1.0},"118":{"tf":1.0},"144":{"tf":1.0},"146":{"tf":1.7320508075688772},"19":{"tf":1.0},"7":{"tf":1.0}}}},"n":{"df":4,"docs":{"114":{"tf":1.0},"128":{"tf":1.0},"146":{"tf":1.4142135623730951},"51":{"tf":1.0}}}},"df":0,"docs":{},"i":{"c":{"df":0,"docs":{},"i":{"df":0,"docs":{},"t":{"df":1,"docs":{"95":{"tf":1.0}},"l":{"df":0,"docs":{},"i":{"df":2,"docs":{"114":{"tf":1.4142135623730951},"5":{"tf":1.0}}}}}}},"df":0,"docs":{}},"o":{"df":0,"docs":{},"i":{"df":0,"docs":{},"t":{"df":1,"docs":{"10":{"tf":1.0}}}}}},"o":{"df":0,"docs":{},"n":{"df":1,"docs":{"45":{"tf":1.0}},"e":{"df":0,"docs":{},"n":{"df":0,"docs":{},"t":{"_":{"b":{"df":0,"docs":{},"i":{"df":0,"docs":{},"t":{"df":1,"docs":{"45":{"tf":1.0}}}}},"df":0,"docs":{}},"df":0,"docs":{},"i":{"df":2,"docs":{"124":{"tf":1.0},"45":{"tf":1.0}}}}}}},"s":{"df":1,"docs":{"40":{"tf":1.0}}}},"r":{"df":0,"docs":{},"e":{"df":0,"docs":{},"s":{"df":0,"docs":{},"s":{"df":7,"docs":{"107":{"tf":1.0},"12":{"tf":1.0},"123":{"tf":1.4142135623730951},"4":{"tf":1.0},"48":{"tf":1.4142135623730951},"70":{"tf":1.7320508075688772},"83":{"tf":1.0}}}}}}},"t":{"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"d":{"df":9,"docs":{"121":{"tf":1.0},"14":{"tf":1.0},"142":{"tf":1.7320508075688772},"146":{"tf":1.0},"147":{"tf":1.4142135623730951},"45":{"tf":1.0},"83":{"tf":1.4142135623730951},"91":{"tf":1.0},"94":{"tf":1.0}}},"df":0,"docs":{},"s":{"df":8,"docs":{"109":{"tf":1.0},"125":{"tf":1.0},"145":{"tf":1.0},"146":{"tf":1.4142135623730951},"49":{"tf":1.0},"68":{"tf":2.0},"84":{"tf":1.0},"91":{"tf":1.0}}}},"r":{"df":0,"docs":{},"n":{"df":2,"docs":{"151":{"tf":1.0},"34":{"tf":1.0}}}}},"r":{"a":{"c":{"df":0,"docs":{},"t":{"df":1,"docs":{"32":{"tf":1.4142135623730951}}}},"df":2,"docs":{"13":{"tf":1.7320508075688772},"146":{"tf":1.0}}},"df":0,"docs":{},"e":{"df":0,"docs":{},"m":{"df":3,"docs":{"50":{"tf":1.0},"68":{"tf":1.0},"79":{"tf":1.0}}}}}}}},"f":{"(":{"a":{")":{":":{"=":{"df":0,"docs":{},"p":{"(":{"a":{")":{"df":0,"docs":{},"q":{"(":{"a":{")":{"df":0,"docs":{},"−":{"1":{"df":1,"docs":{"90":{"tf":1.0}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":1,"docs":{"90":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},",":{"b":{")":{"=":{"(":{"a":{"df":0,"docs":{},"−":{"df":0,"docs":{},"y":{")":{"/":{"(":{"b":{"df":0,"docs":{},"−":{"df":0,"docs":{},"z":{"df":1,"docs":{"77":{"tf":1.0}}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}},"a":{"+":{"b":{"df":0,"docs":{},"−":{"1":{"df":1,"docs":{"74":{"tf":1.0}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"1":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"…":{",":{"a":{"df":0,"docs":{},"l":{"df":0,"docs":{},"​":{",":{"b":{")":{"=":{"df":0,"docs":{},"i":{"=":{"1":{"df":0,"docs":{},"∑":{"df":0,"docs":{},"l":{"df":0,"docs":{},"​":{"df":0,"docs":{},"γ":{"df":0,"docs":{},"i":{"df":0,"docs":{},"​":{"(":{"a":{"df":0,"docs":{},"i":{"df":0,"docs":{},"​":{"df":0,"docs":{},"−":{"df":0,"docs":{},"y":{"df":0,"docs":{},"i":{"df":0,"docs":{},"​":{")":{"/":{"(":{"b":{"df":0,"docs":{},"−":{"df":0,"docs":{},"z":{"df":0,"docs":{},"i":{"df":1,"docs":{"80":{"tf":1.0}}}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}}}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"d":{"df":1,"docs":{"90":{"tf":1.0}}},"df":0,"docs":{},"h":{")":{"=":{"0":{"df":1,"docs":{"16":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"p":{"(":{"d":{"1":{"df":0,"docs":{},"​":{")":{",":{"d":{"1":{"df":0,"docs":{},"​":{")":{",":{"df":0,"docs":{},"…":{",":{"df":0,"docs":{},"f":{"(":{"df":0,"docs":{},"p":{"(":{"d":{"df":0,"docs":{},"m":{"df":0,"docs":{},"​":{")":{",":{"d":{"df":0,"docs":{},"m":{"df":1,"docs":{"78":{"tf":1.0}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"1":{"df":0,"docs":{},"​":{"(":{"d":{"1":{"df":0,"docs":{},"​":{")":{",":{"df":0,"docs":{},"…":{",":{"df":0,"docs":{},"p":{"df":0,"docs":{},"l":{"df":0,"docs":{},"​":{"(":{"d":{"1":{"df":0,"docs":{},"​":{")":{",":{"d":{"1":{"df":0,"docs":{},"​":{")":{",":{"df":0,"docs":{},"…":{",":{"df":0,"docs":{},"f":{"(":{"df":0,"docs":{},"p":{"1":{"df":0,"docs":{},"​":{"(":{"d":{"df":0,"docs":{},"m":{"df":0,"docs":{},"​":{")":{",":{"df":0,"docs":{},"…":{",":{"df":0,"docs":{},"p":{"df":0,"docs":{},"l":{"df":0,"docs":{},"​":{"(":{"d":{"df":0,"docs":{},"m":{"df":0,"docs":{},"​":{")":{",":{"d":{"df":0,"docs":{},"m":{"df":1,"docs":{"80":{"tf":1.0}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"y":{"1":{"df":0,"docs":{},"​":{")":{",":{"df":0,"docs":{},"…":{",":{"df":0,"docs":{},"f":{"(":{"df":0,"docs":{},"y":{"df":0,"docs":{},"l":{"df":1,"docs":{"90":{"tf":1.0}}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},",":{"d":{"1":{"df":0,"docs":{},"​":{")":{",":{"df":0,"docs":{},"…":{",":{"df":0,"docs":{},"f":{"(":{"df":0,"docs":{},"y":{"df":0,"docs":{},"m":{"df":0,"docs":{},"​":{",":{"d":{"df":0,"docs":{},"m":{"df":1,"docs":{"74":{"tf":1.0}}}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"z":{")":{"=":{"df":0,"docs":{},"i":{"df":1,"docs":{"19":{"tf":1.0}}}},"df":0,"docs":{}},"df":1,"docs":{"69":{"tf":1.0}}}},"+":{"df":0,"docs":{},"g":{"]":{"1":{"df":0,"docs":{},"​":{"=":{"[":{"df":0,"docs":{},"f":{"]":{"1":{"df":0,"docs":{},"​":{"+":{"[":{"df":0,"docs":{},"g":{"]":{"1":{"df":1,"docs":{"19":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},",":{"df":0,"docs":{},"g":{"df":1,"docs":{"14":{"tf":1.0}}}},"/":{"df":0,"docs":{},"g":{"df":1,"docs":{"69":{"tf":1.0}}}},"0":{"df":1,"docs":{"131":{"tf":1.0}}},"1":{"5":{"df":0,"docs":{},"​":{"=":{"0":{"df":1,"docs":{"131":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{},"~":{"df":0,"docs":{},"​":{"=":{"0":{"df":1,"docs":{"131":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}},":":{"a":{"df":0,"docs":{},"→":{"df":0,"docs":{},"f":{"df":1,"docs":{"90":{"tf":1.4142135623730951}}}}},"df":0,"docs":{},"f":{"df":0,"docs":{},"×":{"d":{"df":0,"docs":{},"→":{"df":0,"docs":{},"f":{"df":1,"docs":{"74":{"tf":1.0}}}}},"df":0,"docs":{}}},"s":{"df":0,"docs":{},"e":{"df":0,"docs":{},"t":{"(":{"d":{")":{"df":0,"docs":{},"→":{"df":0,"docs":{},"f":{"df":1,"docs":{"90":{"tf":1.0}}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"=":{"a":{"(":{"df":0,"docs":{},"ζ":{")":{"df":0,"docs":{},"q":{"df":0,"docs":{},"l":{"df":0,"docs":{},"​":{"+":{"b":{"(":{"df":0,"docs":{},"ζ":{")":{"df":0,"docs":{},"q":{"df":0,"docs":{},"r":{"df":1,"docs":{"21":{"tf":1.0}}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{},"z":{"df":0,"docs":{},"h":{"df":0,"docs":{},"​":{"df":0,"docs":{},"t":{"df":1,"docs":{"16":{"tf":1.0}}}}}}},"]":{"1":{"df":3,"docs":{"19":{"tf":1.4142135623730951},"21":{"tf":1.0},"35":{"tf":1.0}}},"df":0,"docs":{}},"a":{"c":{"df":0,"docs":{},"e":{"b":{"df":0,"docs":{},"o":{"df":0,"docs":{},"o":{"df":0,"docs":{},"k":{"'":{"df":1,"docs":{"103":{"tf":1.0}}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"t":{"df":9,"docs":{"13":{"tf":1.0},"131":{"tf":1.0},"139":{"tf":1.0},"14":{"tf":2.8284271247461903},"146":{"tf":1.0},"149":{"tf":1.0},"16":{"tf":1.7320508075688772},"5":{"tf":1.0},"68":{"tf":1.0}},"o":{"df":0,"docs":{},"r":{"df":8,"docs":{"125":{"tf":1.0},"14":{"tf":1.7320508075688772},"146":{"tf":1.0},"28":{"tf":1.0},"3":{"tf":1.0},"68":{"tf":1.7320508075688772},"73":{"tf":1.0},"91":{"tf":2.0}}}}}},"df":0,"docs":{},"l":{"df":0,"docs":{},"s":{"df":1,"docs":{"104":{"tf":1.0}}}},"r":{"df":2,"docs":{"16":{"tf":1.0},"70":{"tf":1.0}}},"s":{"df":0,"docs":{},"h":{"df":0,"docs":{},"i":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"df":2,"docs":{"114":{"tf":1.0},"151":{"tf":1.0}}}}}},"t":{"df":2,"docs":{"123":{"tf":1.4142135623730951},"99":{"tf":1.0}},"e":{"df":0,"docs":{},"r":{"df":1,"docs":{"123":{"tf":1.0}}}}}}},"b":{"a":{"df":0,"docs":{},"t":{"c":{"df":0,"docs":{},"h":{"df":3,"docs":{"28":{"tf":1.0},"32":{"tf":1.7320508075688772},"33":{"tf":1.0}},"​":{"(":{"df":0,"docs":{},"ζ":{")":{"=":{"df":0,"docs":{},"t":{"df":0,"docs":{},"ˉ":{"+":{"df":0,"docs":{},"υ":{"df":0,"docs":{},"p":{"df":0,"docs":{},"ˉ":{"df":0,"docs":{},"​":{"df":0,"docs":{},"n":{"c":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"υ":{"2":{"a":{"df":0,"docs":{},"ˉ":{"+":{"df":0,"docs":{},"υ":{"3":{"b":{"df":0,"docs":{},"ˉ":{"+":{"df":0,"docs":{},"υ":{"4":{"c":{"df":0,"docs":{},"ˉ":{"+":{"df":0,"docs":{},"υ":{"5":{"df":0,"docs":{},"s":{"df":0,"docs":{},"ˉ":{"df":0,"docs":{},"σ":{"1":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"υ":{"6":{"df":0,"docs":{},"s":{"df":0,"docs":{},"ˉ":{"df":0,"docs":{},"σ":{"2":{"df":1,"docs":{"32":{"tf":1.0}}},"df":0,"docs":{}}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"]":{"1":{"df":1,"docs":{"32":{"tf":1.0}},"​":{"=":{"[":{"df":0,"docs":{},"t":{"df":0,"docs":{},"p":{"a":{"df":0,"docs":{},"r":{"df":0,"docs":{},"t":{"df":0,"docs":{},"i":{"a":{"df":0,"docs":{},"l":{"df":0,"docs":{},"​":{"]":{"1":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"υ":{"[":{"df":0,"docs":{},"p":{"df":0,"docs":{},"n":{"c":{"df":0,"docs":{},"​":{"]":{"1":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"υ":{"2":{"[":{"a":{"]":{"1":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"υ":{"3":{"[":{"b":{"]":{"1":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"υ":{"4":{"[":{"c":{"]":{"1":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"υ":{"5":{"[":{"df":0,"docs":{},"s":{"df":0,"docs":{},"σ":{"1":{"df":0,"docs":{},"​":{"]":{"1":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"υ":{"6":{"[":{"df":0,"docs":{},"s":{"df":0,"docs":{},"σ":{"2":{"df":0,"docs":{},"​":{"]":{"1":{"df":1,"docs":{"32":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":21,"docs":{"14":{"tf":2.6457513110645907},"141":{"tf":1.0},"142":{"tf":1.4142135623730951},"146":{"tf":1.7320508075688772},"147":{"tf":1.4142135623730951},"16":{"tf":1.4142135623730951},"19":{"tf":2.449489742783178},"21":{"tf":2.0},"35":{"tf":1.4142135623730951},"5":{"tf":1.7320508075688772},"69":{"tf":1.4142135623730951},"70":{"tf":1.4142135623730951},"73":{"tf":1.0},"74":{"tf":1.4142135623730951},"75":{"tf":1.0},"80":{"tf":1.0},"82":{"tf":1.0},"90":{"tf":1.7320508075688772},"91":{"tf":1.4142135623730951},"94":{"tf":2.0},"95":{"tf":1.4142135623730951}},"e":{"1":{"7":{":":{":":{"df":0,"docs":{},"n":{"df":0,"docs":{},"e":{"df":0,"docs":{},"w":{"(":{"1":{"df":1,"docs":{"108":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},":":{":":{"df":0,"docs":{},"f":{"df":0,"docs":{},"r":{"df":0,"docs":{},"o":{"df":0,"docs":{},"m":{"(":{"1":{"df":1,"docs":{"104":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"o":{"df":0,"docs":{},"n":{"df":1,"docs":{"104":{"tf":1.4142135623730951}}}}},"df":0,"docs":{}},"a":{"df":0,"docs":{},"t":{"df":0,"docs":{},"u":{"df":0,"docs":{},"r":{"df":1,"docs":{"70":{"tf":1.0}}}}}},"df":0,"docs":{},"e":{"d":{"df":1,"docs":{"34":{"tf":1.0}}},"df":0,"docs":{}},"t":{"c":{"df":0,"docs":{},"h":{"df":1,"docs":{"146":{"tf":1.4142135623730951}}}},"df":0,"docs":{}},"w":{"df":10,"docs":{"109":{"tf":1.0},"111":{"tf":1.0},"123":{"tf":1.4142135623730951},"124":{"tf":1.0},"13":{"tf":1.0},"14":{"tf":1.0},"16":{"tf":1.0},"32":{"tf":1.0},"37":{"tf":1.0},"64":{"tf":1.0}}}},"f":{"df":0,"docs":{},"t":{"df":4,"docs":{"121":{"tf":2.23606797749979},"2":{"tf":1.7320508075688772},"3":{"tf":1.7320508075688772},"99":{"tf":1.7320508075688772}}}},"i":{"a":{"df":0,"docs":{},"t":{"df":7,"docs":{"118":{"tf":1.4142135623730951},"39":{"tf":1.4142135623730951},"40":{"tf":1.0},"42":{"tf":2.23606797749979},"89":{"tf":1.0},"94":{"tf":1.0},"95":{"tf":1.0}}}},"b":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"a":{"c":{"c":{"df":0,"docs":{},"i":{"_":{"df":0,"docs":{},"t":{"df":0,"docs":{},"r":{"a":{"c":{"df":1,"docs":{"108":{"tf":1.0}},"e":{"(":{"[":{"df":0,"docs":{},"f":{"df":0,"docs":{},"e":{"1":{"7":{":":{":":{"df":0,"docs":{},"n":{"df":0,"docs":{},"e":{"df":0,"docs":{},"w":{"(":{"1":{"df":1,"docs":{"108":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"a":{"df":0,"docs":{},"i":{"df":0,"docs":{},"r":{"<":{"df":0,"docs":{},"f":{">":{">":{"(":{"&":{"df":0,"docs":{},"p":{"df":0,"docs":{},"r":{"df":0,"docs":{},"o":{"df":0,"docs":{},"o":{"df":0,"docs":{},"f":{"df":1,"docs":{"104":{"tf":1.0}}}}}}},"t":{"df":0,"docs":{},"r":{"a":{"c":{"df":1,"docs":{"104":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":18,"docs":{"104":{"tf":1.7320508075688772},"105":{"tf":1.4142135623730951},"106":{"tf":1.7320508075688772},"107":{"tf":1.0},"108":{"tf":1.0},"109":{"tf":1.0},"110":{"tf":1.0},"111":{"tf":1.0},"112":{"tf":1.0},"114":{"tf":1.7320508075688772},"117":{"tf":1.0},"49":{"tf":2.0},"50":{"tf":1.4142135623730951},"51":{"tf":1.7320508075688772},"64":{"tf":1.0},"66":{"tf":1.0},"68":{"tf":1.0},"84":{"tf":1.0}},"p":{"df":0,"docs":{},"u":{"b":{"df":0,"docs":{},"l":{"df":0,"docs":{},"i":{"c":{"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"df":0,"docs":{},"p":{"df":0,"docs":{},"u":{"df":0,"docs":{},"t":{"df":1,"docs":{"104":{"tf":1.0}}}}}}}},"df":0,"docs":{}}}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":1,"docs":{"131":{"tf":1.0}},"e":{"df":0,"docs":{},"l":{"d":{"df":22,"docs":{"108":{"tf":1.0},"109":{"tf":1.0},"114":{"tf":1.0},"14":{"tf":4.58257569495584},"16":{"tf":1.0},"19":{"tf":1.4142135623730951},"35":{"tf":1.0},"36":{"tf":1.0},"41":{"tf":2.449489742783178},"44":{"tf":1.0},"5":{"tf":1.0},"52":{"tf":1.0},"7":{"tf":1.4142135623730951},"70":{"tf":1.0},"73":{"tf":1.0},"74":{"tf":1.4142135623730951},"82":{"tf":1.0},"90":{"tf":1.0},"91":{"tf":1.0},"95":{"tf":1.0},"98":{"tf":1.0},"99":{"tf":1.0}},"e":{"df":0,"docs":{},"l":{"df":0,"docs":{},"e":{"df":0,"docs":{},"m":{"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"df":0,"docs":{},"t":{":":{":":{"df":0,"docs":{},"f":{"df":0,"docs":{},"r":{"df":0,"docs":{},"o":{"df":0,"docs":{},"m":{"(":{"2":{"_":{"df":0,"docs":{},"u":{"6":{"4":{"df":1,"docs":{"35":{"tf":1.4142135623730951}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"3":{"_":{"df":0,"docs":{},"u":{"6":{"4":{"df":1,"docs":{"35":{"tf":1.4142135623730951}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":1,"docs":{"44":{"tf":1.0}}},"4":{"df":1,"docs":{"44":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}}}}}},"df":0,"docs":{}},"<":{"df":0,"docs":{},"f":{"df":1,"docs":{"35":{"tf":3.1622776601683795}}}},"df":0,"docs":{}}}}}}}}},"df":0,"docs":{}}},"f":{"df":0,"docs":{},"t":{"df":0,"docs":{},"h":{"df":1,"docs":{"52":{"tf":1.0}}}}},"l":{"df":0,"docs":{},"e":{"df":2,"docs":{"144":{"tf":2.23606797749979},"145":{"tf":1.0}}},"l":{"df":6,"docs":{"13":{"tf":1.0},"130":{"tf":2.449489742783178},"134":{"tf":2.0},"139":{"tf":1.0},"146":{"tf":2.23606797749979},"36":{"tf":1.7320508075688772}}}},"n":{"a":{"df":0,"docs":{},"l":{"df":8,"docs":{"13":{"tf":1.0},"135":{"tf":1.0},"144":{"tf":1.4142135623730951},"146":{"tf":2.0},"147":{"tf":1.0},"16":{"tf":1.0},"35":{"tf":1.4142135623730951},"44":{"tf":1.0}}}},"d":{"df":3,"docs":{"128":{"tf":1.4142135623730951},"3":{"tf":1.4142135623730951},"88":{"tf":1.0}}},"df":0,"docs":{},"e":{"df":1,"docs":{"12":{"tf":1.0}}},"i":{"df":0,"docs":{},"t":{"df":10,"docs":{"14":{"tf":1.0},"19":{"tf":1.0},"5":{"tf":1.0},"52":{"tf":1.0},"69":{"tf":1.0},"7":{"tf":1.0},"70":{"tf":1.0},"90":{"tf":1.0},"91":{"tf":1.0},"98":{"tf":1.0}}}}},"r":{"df":0,"docs":{},"s":{"df":0,"docs":{},"t":{"_":{"df":0,"docs":{},"r":{"df":0,"docs":{},"o":{"df":0,"docs":{},"w":{"[":{"0":{"df":2,"docs":{"107":{"tf":1.4142135623730951},"114":{"tf":1.0}}},"df":0,"docs":{}},"df":2,"docs":{"107":{"tf":1.0},"114":{"tf":1.0}}}}}},"df":29,"docs":{"105":{"tf":1.0},"107":{"tf":1.4142135623730951},"108":{"tf":1.0},"114":{"tf":1.4142135623730951},"128":{"tf":1.0},"13":{"tf":1.7320508075688772},"130":{"tf":1.0},"134":{"tf":1.0},"135":{"tf":1.0},"14":{"tf":1.4142135623730951},"145":{"tf":1.0},"146":{"tf":1.7320508075688772},"147":{"tf":2.0},"149":{"tf":1.4142135623730951},"20":{"tf":1.0},"23":{"tf":1.4142135623730951},"31":{"tf":1.0},"32":{"tf":1.4142135623730951},"42":{"tf":1.0},"46":{"tf":1.0},"49":{"tf":1.4142135623730951},"52":{"tf":1.4142135623730951},"59":{"tf":1.7320508075688772},"66":{"tf":1.0},"69":{"tf":1.0},"70":{"tf":1.0},"8":{"tf":1.0},"83":{"tf":1.0},"9":{"tf":1.0}},"l":{"df":0,"docs":{},"i":{"df":1,"docs":{"32":{"tf":1.0}}}}}}},"t":{"df":2,"docs":{"103":{"tf":1.0},"133":{"tf":1.0}}},"v":{"df":0,"docs":{},"e":{"df":1,"docs":{"12":{"tf":1.0}}}},"x":{"df":5,"docs":{"146":{"tf":1.0},"5":{"tf":1.0},"73":{"tf":1.0},"88":{"tf":1.0},"91":{"tf":1.0}}},"​":{"df":0,"docs":{},"~":{"df":0,"docs":{},"​":{"=":{"df":0,"docs":{},"∑":{"df":0,"docs":{},"j":{"=":{"df":0,"docs":{},"i":{"1":{"4":{"df":0,"docs":{},"​":{"2":{"df":0,"docs":{},"j":{"df":0,"docs":{},"−":{"df":0,"docs":{},"i":{"df":0,"docs":{},"f":{"df":0,"docs":{},"i":{"df":1,"docs":{"131":{"tf":1.0}}}}}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{},"−":{"2":{"df":0,"docs":{},"f":{"df":0,"docs":{},"~":{"df":0,"docs":{},"​":{"df":0,"docs":{},"i":{"+":{"1":{"df":0,"docs":{},"​":{")":{"(":{"df":0,"docs":{},"f":{"df":0,"docs":{},"i":{"df":0,"docs":{},"​":{"df":0,"docs":{},"~":{"df":0,"docs":{},"​":{"df":0,"docs":{},"−":{"2":{"df":0,"docs":{},"f":{"df":0,"docs":{},"~":{"df":0,"docs":{},"​":{"df":0,"docs":{},"i":{"+":{"1":{"df":0,"docs":{},"​":{"df":0,"docs":{},"−":{"1":{")":{"=":{"0":{"df":1,"docs":{"131":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}}}}},"df":0,"docs":{}}}}}}}},"df":0,"docs":{}},"=":{"df":0,"docs":{},"f":{"df":0,"docs":{},"i":{"df":1,"docs":{"131":{"tf":1.0}}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}}}},"df":0,"docs":{}}}},"∗":{"(":{"df":0,"docs":{},"f":{"df":0,"docs":{},"i":{"df":0,"docs":{},"​":{"df":0,"docs":{},"−":{"1":{")":{"=":{"0":{"df":1,"docs":{"131":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}}}},"df":0,"docs":{}}}},"l":{"a":{"df":0,"docs":{},"g":{"a":{"df":1,"docs":{"149":{"tf":1.0}}},"b":{"df":1,"docs":{"149":{"tf":1.4142135623730951}}},"df":6,"docs":{"131":{"tf":2.23606797749979},"142":{"tf":1.4142135623730951},"146":{"tf":3.0},"147":{"tf":1.4142135623730951},"149":{"tf":3.1622776601683795},"150":{"tf":2.0}}}},"df":0,"docs":{},"e":{"df":0,"docs":{},"x":{"df":0,"docs":{},"i":{"b":{"df":0,"docs":{},"l":{"df":2,"docs":{"12":{"tf":1.4142135623730951},"4":{"tf":1.0}}}},"df":0,"docs":{}}}},"o":{"df":0,"docs":{},"w":{"df":2,"docs":{"103":{"tf":1.0},"50":{"tf":1.0}}}}},"n":{"df":6,"docs":{"104":{"tf":1.0},"106":{"tf":1.0},"107":{"tf":1.0},"114":{"tf":1.0},"44":{"tf":1.0},"45":{"tf":1.4142135623730951}}},"o":{"c":{"df":0,"docs":{},"u":{"df":1,"docs":{"149":{"tf":1.0}}}},"df":0,"docs":{},"l":{"df":0,"docs":{},"l":{"df":0,"docs":{},"o":{"df":0,"docs":{},"w":{"df":49,"docs":{"10":{"tf":1.4142135623730951},"101":{"tf":1.0},"102":{"tf":1.0},"103":{"tf":1.0},"11":{"tf":1.4142135623730951},"111":{"tf":1.0},"113":{"tf":1.0},"114":{"tf":1.7320508075688772},"123":{"tf":1.0},"128":{"tf":1.0},"13":{"tf":1.4142135623730951},"137":{"tf":1.0},"14":{"tf":3.7416573867739413},"142":{"tf":1.4142135623730951},"146":{"tf":1.4142135623730951},"147":{"tf":1.7320508075688772},"151":{"tf":1.0},"16":{"tf":2.0},"19":{"tf":1.4142135623730951},"21":{"tf":1.0},"22":{"tf":1.0},"24":{"tf":1.0},"31":{"tf":1.0},"32":{"tf":1.4142135623730951},"35":{"tf":1.7320508075688772},"37":{"tf":1.0},"42":{"tf":1.0},"44":{"tf":1.4142135623730951},"45":{"tf":1.0},"48":{"tf":1.0},"49":{"tf":1.0},"5":{"tf":1.0},"50":{"tf":1.0},"52":{"tf":1.0},"54":{"tf":1.0},"55":{"tf":1.0},"56":{"tf":1.0},"59":{"tf":1.4142135623730951},"69":{"tf":1.0},"7":{"tf":1.4142135623730951},"72":{"tf":1.0},"73":{"tf":1.0},"75":{"tf":1.0},"84":{"tf":1.0},"88":{"tf":1.0},"91":{"tf":1.0},"92":{"tf":1.0},"94":{"tf":1.7320508075688772},"95":{"tf":2.23606797749979}}}}}},"r":{"c":{"df":2,"docs":{"13":{"tf":1.4142135623730951},"20":{"tf":1.0}}},"df":0,"docs":{},"m":{"a":{"df":0,"docs":{},"l":{"df":3,"docs":{"141":{"tf":1.0},"142":{"tf":1.0},"69":{"tf":1.0}}},"t":{"df":1,"docs":{"69":{"tf":1.0}}}},"df":12,"docs":{"100":{"tf":1.0},"108":{"tf":1.0},"123":{"tf":1.0},"14":{"tf":2.23606797749979},"146":{"tf":1.0},"35":{"tf":1.0},"52":{"tf":1.0},"68":{"tf":1.0},"73":{"tf":1.0},"9":{"tf":1.0},"92":{"tf":1.0},"99":{"tf":1.0}},"e":{"df":0,"docs":{},"r":{"df":2,"docs":{"19":{"tf":1.0},"73":{"tf":1.0}}}},"u":{"df":0,"docs":{},"l":{"a":{"df":2,"docs":{"58":{"tf":1.0},"60":{"tf":1.0}}},"df":0,"docs":{}}}}},"u":{"df":0,"docs":{},"n":{"d":{"df":8,"docs":{"127":{"tf":1.0},"130":{"tf":1.4142135623730951},"133":{"tf":1.0},"134":{"tf":1.4142135623730951},"137":{"tf":1.0},"138":{"tf":1.0},"38":{"tf":1.0},"4":{"tf":1.0}}},"df":0,"docs":{}},"r":{"df":2,"docs":{"21":{"tf":1.0},"56":{"tf":1.0}},"i":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":3,"docs":{"123":{"tf":1.0},"3":{"tf":1.4142135623730951},"99":{"tf":1.0}}}}}}}},"p":{"df":6,"docs":{"136":{"tf":1.4142135623730951},"141":{"tf":1.0},"142":{"tf":1.0},"144":{"tf":1.0},"146":{"tf":1.4142135623730951},"147":{"tf":1.0}}},"r":{"a":{"df":0,"docs":{},"m":{"df":0,"docs":{},"e":{".":{"df":0,"docs":{},"g":{"df":0,"docs":{},"e":{"df":0,"docs":{},"t":{"_":{"df":0,"docs":{},"r":{"df":0,"docs":{},"o":{"df":0,"docs":{},"w":{"(":{"0":{"df":2,"docs":{"107":{"tf":1.0},"114":{"tf":1.0}}},"1":{"df":2,"docs":{"107":{"tf":1.0},"114":{"tf":1.0}}},"2":{"df":2,"docs":{"107":{"tf":1.0},"114":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}}}}},":":{":":{"df":0,"docs":{},"r":{"df":0,"docs":{},"e":{"a":{"d":{"_":{"df":0,"docs":{},"f":{"df":0,"docs":{},"r":{"df":0,"docs":{},"o":{"df":0,"docs":{},"m":{"_":{"df":0,"docs":{},"t":{"df":0,"docs":{},"r":{"a":{"c":{"df":1,"docs":{"114":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":7,"docs":{"107":{"tf":1.4142135623730951},"109":{"tf":1.0},"113":{"tf":1.0},"114":{"tf":2.8284271247461903},"117":{"tf":2.449489742783178},"149":{"tf":1.7320508075688772},"84":{"tf":1.0}}}}},"df":1,"docs":{"38":{"tf":1.0}},"e":{"df":0,"docs":{},"s":{"df":0,"docs":{},"h":{"df":1,"docs":{"40":{"tf":1.0}}}}},"i":{"_":{"df":0,"docs":{},"n":{"df":0,"docs":{},"u":{"df":0,"docs":{},"m":{"b":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"_":{"df":0,"docs":{},"o":{"df":0,"docs":{},"f":{"_":{"df":0,"docs":{},"q":{"df":0,"docs":{},"u":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":0,"docs":{},"i":{"df":1,"docs":{"109":{"tf":1.0}}}}}}}},"df":0,"docs":{}}}},"df":0,"docs":{}}}},"df":0,"docs":{}}}}},"d":{"a":{"df":0,"docs":{},"y":{"df":1,"docs":{"81":{"tf":1.0}}}},"df":0,"docs":{}},"df":22,"docs":{"102":{"tf":1.0},"109":{"tf":1.0},"112":{"tf":1.4142135623730951},"113":{"tf":1.0},"119":{"tf":1.4142135623730951},"125":{"tf":1.7320508075688772},"57":{"tf":2.0},"59":{"tf":1.7320508075688772},"62":{"tf":1.4142135623730951},"63":{"tf":1.0},"68":{"tf":2.0},"71":{"tf":1.0},"73":{"tf":2.23606797749979},"74":{"tf":2.0},"75":{"tf":1.0},"77":{"tf":1.4142135623730951},"78":{"tf":1.0},"80":{"tf":1.7320508075688772},"81":{"tf":1.7320508075688772},"91":{"tf":1.0},"94":{"tf":1.4142135623730951},"95":{"tf":1.0}},"e":{"df":0,"docs":{},"n":{"d":{"df":0,"docs":{},"l":{"df":0,"docs":{},"i":{"df":1,"docs":{"83":{"tf":1.0}}}}},"df":0,"docs":{}}}}},"u":{"df":0,"docs":{},"l":{"df":0,"docs":{},"l":{"df":2,"docs":{"10":{"tf":1.0},"131":{"tf":1.0}}}},"n":{"c":{"df":0,"docs":{},"t":{"df":0,"docs":{},"i":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"df":24,"docs":{"104":{"tf":1.4142135623730951},"108":{"tf":1.0},"110":{"tf":1.0},"111":{"tf":1.0},"113":{"tf":1.0},"114":{"tf":1.0},"118":{"tf":1.0},"142":{"tf":1.0},"146":{"tf":1.4142135623730951},"35":{"tf":1.7320508075688772},"40":{"tf":1.0},"45":{"tf":2.6457513110645907},"54":{"tf":1.0},"55":{"tf":1.0},"56":{"tf":1.0},"74":{"tf":1.7320508075688772},"77":{"tf":1.0},"80":{"tf":1.0},"84":{"tf":1.4142135623730951},"85":{"tf":1.0},"88":{"tf":1.0},"90":{"tf":1.7320508075688772},"91":{"tf":1.4142135623730951},"95":{"tf":1.0}}}}}}},"df":0,"docs":{}},"r":{"df":0,"docs":{},"t":{"df":0,"docs":{},"h":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":4,"docs":{"100":{"tf":1.0},"147":{"tf":1.0},"21":{"tf":1.0},"74":{"tf":1.0}},"m":{"df":0,"docs":{},"o":{"df":0,"docs":{},"r":{"df":1,"docs":{"13":{"tf":1.0}}}}}}}}}}}},"g":{"+":{"df":0,"docs":{},"ζ":{"df":0,"docs":{},"k":{"df":0,"docs":{},"​":{"df":0,"docs":{},"h":{"df":1,"docs":{"95":{"tf":1.4142135623730951}}}}}}},",":{"df":0,"docs":{},"h":{"df":1,"docs":{"95":{"tf":1.0}}}},"1":{"df":0,"docs":{},"p":{"df":0,"docs":{},"o":{"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"df":0,"docs":{},"t":{"df":1,"docs":{"35":{"tf":2.8284271247461903}}}}}}}},"3":{"df":1,"docs":{"124":{"tf":1.0}}},"=":{"df":0,"docs":{},"ω":{"b":{"df":1,"docs":{"91":{"tf":1.0}}},"df":0,"docs":{}}},"^":{"0":{"df":2,"docs":{"52":{"tf":1.0},"55":{"tf":1.0}}},"1":{"df":2,"docs":{"52":{"tf":1.0},"55":{"tf":1.0}}},"2":{"df":2,"docs":{"52":{"tf":1.4142135623730951},"55":{"tf":1.0}}},"3":{"df":2,"docs":{"52":{"tf":1.0},"55":{"tf":1.0}}},"4":{"df":2,"docs":{"52":{"tf":1.0},"55":{"tf":1.0}}},"5":{"df":2,"docs":{"52":{"tf":1.7320508075688772},"55":{"tf":1.0}}},"8":{"df":1,"docs":{"52":{"tf":1.0}}},"df":0,"docs":{},"i":{"df":2,"docs":{"52":{"tf":1.0},"62":{"tf":1.0}}}},"a":{"df":0,"docs":{},"m":{"df":0,"docs":{},"m":{"a":{"_":{"df":0,"docs":{},"i":{"df":1,"docs":{"59":{"tf":1.0}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"p":{"df":1,"docs":{"134":{"tf":1.0}}},"t":{"df":0,"docs":{},"e":{"df":8,"docs":{"10":{"tf":2.6457513110645907},"11":{"tf":1.0},"12":{"tf":2.449489742783178},"13":{"tf":1.0},"14":{"tf":1.0},"16":{"tf":1.4142135623730951},"20":{"tf":1.0},"9":{"tf":3.3166247903554}}},"h":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":1,"docs":{"126":{"tf":1.0}}}}}},"v":{"df":0,"docs":{},"e":{"df":2,"docs":{"113":{"tf":1.4142135623730951},"63":{"tf":1.4142135623730951}}}}},"df":13,"docs":{"124":{"tf":2.23606797749979},"125":{"tf":1.4142135623730951},"14":{"tf":2.449489742783178},"142":{"tf":1.4142135623730951},"146":{"tf":2.0},"147":{"tf":1.4142135623730951},"16":{"tf":1.0},"19":{"tf":2.0},"52":{"tf":2.23606797749979},"54":{"tf":1.0},"68":{"tf":2.0},"69":{"tf":1.0},"82":{"tf":1.0}},"e":{"df":0,"docs":{},"n":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":26,"docs":{"103":{"tf":1.0},"104":{"tf":1.4142135623730951},"108":{"tf":1.0},"118":{"tf":1.0},"119":{"tf":1.4142135623730951},"124":{"tf":2.0},"144":{"tf":1.4142135623730951},"146":{"tf":1.0},"149":{"tf":1.0},"34":{"tf":1.4142135623730951},"35":{"tf":1.4142135623730951},"4":{"tf":1.0},"44":{"tf":1.4142135623730951},"48":{"tf":1.0},"49":{"tf":1.0},"50":{"tf":1.7320508075688772},"52":{"tf":1.0},"65":{"tf":1.4142135623730951},"68":{"tf":1.0},"70":{"tf":1.0},"77":{"tf":1.0},"8":{"tf":1.0},"80":{"tf":1.4142135623730951},"82":{"tf":1.0},"84":{"tf":1.0},"90":{"tf":1.4142135623730951}}}}},"t":{"_":{"df":0,"docs":{},"r":{"df":0,"docs":{},"o":{"df":0,"docs":{},"w":{"df":1,"docs":{"107":{"tf":1.0}}}}}},"df":4,"docs":{"118":{"tf":1.4142135623730951},"151":{"tf":1.0},"75":{"tf":1.0},"82":{"tf":1.0}}}},"i":{":":{"0":{"df":0,"docs":{},"≤":{"df":0,"docs":{},"i":{"<":{"2":{"df":0,"docs":{},"n":{"df":1,"docs":{"112":{"tf":1.0}}}},"df":0,"docs":{},"n":{"df":1,"docs":{"124":{"tf":1.0}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":1,"docs":{"125":{"tf":1.4142135623730951}},"l":{"df":0,"docs":{},"l":{"df":0,"docs":{},"e":{"df":0,"docs":{},"t":{"df":0,"docs":{},"t":{"'":{"df":1,"docs":{"103":{"tf":1.0}}},"df":0,"docs":{}}}}}},"v":{"df":0,"docs":{},"e":{"df":8,"docs":{"114":{"tf":1.0},"14":{"tf":1.0},"149":{"tf":1.4142135623730951},"151":{"tf":1.7320508075688772},"19":{"tf":1.0},"46":{"tf":1.0},"50":{"tf":1.0},"7":{"tf":1.0}},"n":{"df":14,"docs":{"114":{"tf":1.7320508075688772},"128":{"tf":1.0},"130":{"tf":1.0},"14":{"tf":1.0},"146":{"tf":1.0},"16":{"tf":1.4142135623730951},"23":{"tf":1.0},"36":{"tf":1.0},"63":{"tf":1.0},"72":{"tf":1.0},"75":{"tf":1.0},"76":{"tf":1.0},"90":{"tf":1.0},"92":{"tf":1.0}}}}},"z":{"a":{"df":1,"docs":{"103":{"tf":1.0}}},"df":0,"docs":{}}},"k":{"df":1,"docs":{"124":{"tf":1.0}}},"l":{"df":0,"docs":{},"o":{"b":{"a":{"df":0,"docs":{},"l":{"df":1,"docs":{"70":{"tf":1.4142135623730951}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"n":{"+":{"1":{"=":{"df":0,"docs":{},"g":{"df":0,"docs":{},"g":{"df":0,"docs":{},"n":{"+":{"2":{"=":{"df":0,"docs":{},"g":{"2":{"df":1,"docs":{"124":{"tf":1.0}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}},"=":{"1":{"df":1,"docs":{"124":{"tf":1.0}}},"df":0,"docs":{}},"a":{"df":0,"docs":{},"r":{"df":0,"docs":{},"k":{"df":1,"docs":{"34":{"tf":1.0}}}}},"df":0,"docs":{}},"o":{"a":{"df":0,"docs":{},"l":{"df":4,"docs":{"103":{"tf":1.0},"46":{"tf":1.0},"50":{"tf":1.0},"77":{"tf":1.0}}}},"df":17,"docs":{"103":{"tf":1.0},"104":{"tf":1.4142135623730951},"109":{"tf":1.0},"111":{"tf":1.4142135623730951},"114":{"tf":1.0},"118":{"tf":1.0},"124":{"tf":1.0},"14":{"tf":1.7320508075688772},"42":{"tf":1.0},"46":{"tf":1.0},"48":{"tf":1.0},"49":{"tf":1.0},"50":{"tf":1.0},"51":{"tf":1.0},"53":{"tf":1.0},"57":{"tf":1.0},"68":{"tf":1.0}},"l":{"d":{"b":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":0,"docs":{},"g":{"df":4,"docs":{"103":{"tf":1.0},"19":{"tf":1.0},"35":{"tf":1.0},"38":{"tf":1.0}}}}}},"df":0,"docs":{}},"df":0,"docs":{}},"o":{"d":{"df":4,"docs":{"14":{"tf":1.0},"46":{"tf":1.0},"50":{"tf":1.0},"81":{"tf":1.0}}},"df":0,"docs":{}},"t":{"df":0,"docs":{},"h":{"df":0,"docs":{},"i":{"c":{"df":1,"docs":{"95":{"tf":1.0}}},"df":0,"docs":{}}}}},"p":{"df":0,"docs":{},"u":{"df":1,"docs":{"1":{"tf":1.0}}}},"r":{"a":{"df":0,"docs":{},"n":{"df":0,"docs":{},"t":{"df":1,"docs":{"14":{"tf":1.0}}}}},"df":0,"docs":{},"e":{"a":{"df":0,"docs":{},"t":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":2,"docs":{"3":{"tf":1.0},"68":{"tf":1.0}}}}}},"df":0,"docs":{},"e":{"df":0,"docs":{},"k":{"df":1,"docs":{"95":{"tf":1.0}}}}},"i":{"df":0,"docs":{},"n":{"d":{"df":4,"docs":{"88":{"tf":1.4142135623730951},"91":{"tf":1.0},"94":{"tf":1.0},"95":{"tf":1.0}},"i":{"df":0,"docs":{},"n":{"df":0,"docs":{},"g":{"_":{"df":0,"docs":{},"f":{"a":{"c":{"df":0,"docs":{},"t":{"df":0,"docs":{},"o":{"df":0,"docs":{},"r":{"df":1,"docs":{"88":{"tf":1.4142135623730951}}}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}}},"df":0,"docs":{}}},"o":{"df":0,"docs":{},"u":{"df":0,"docs":{},"p":{"df":3,"docs":{"142":{"tf":1.4142135623730951},"147":{"tf":1.0},"19":{"tf":2.23606797749979}}}}}}},"h":{"(":{"df":0,"docs":{},"h":{"(":{"df":0,"docs":{},"p":{"df":0,"docs":{},"r":{"df":0,"docs":{},"e":{"df":0,"docs":{},"f":{"df":0,"docs":{},"i":{"df":0,"docs":{},"x":{"df":1,"docs":{"88":{"tf":1.0}}}}}}}}},"df":0,"docs":{}},"x":{")":{"=":{"df":0,"docs":{},"h":{"1":{"df":0,"docs":{},"​":{"(":{"df":0,"docs":{},"x":{"2":{")":{"+":{"df":0,"docs":{},"x":{"df":0,"docs":{},"h":{"2":{"df":0,"docs":{},"​":{"(":{"df":0,"docs":{},"x":{"2":{"df":2,"docs":{"116":{"tf":1.0},"67":{"tf":1.0}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"β":{"1":{"df":0,"docs":{},"​":{"b":{"(":{"df":0,"docs":{},"x":{")":{"+":{"df":0,"docs":{},"β":{"2":{"df":0,"docs":{},"​":{"c":{"(":{"df":0,"docs":{},"x":{"df":1,"docs":{"56":{"tf":1.0}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"0":{"df":2,"docs":{"112":{"tf":1.0},"113":{"tf":1.0}}},"_":{"0":{"df":2,"docs":{"62":{"tf":1.0},"63":{"tf":1.0}}},"df":0,"docs":{}},"df":1,"docs":{"56":{"tf":1.0}}},"z":{")":{"=":{"df":0,"docs":{},"f":{"(":{"df":0,"docs":{},"z":{")":{"/":{"df":0,"docs":{},"g":{"(":{"df":0,"docs":{},"z":{"df":1,"docs":{"69":{"tf":1.0}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"β":{"1":{"df":0,"docs":{},"​":{"b":{"(":{"df":0,"docs":{},"z":{")":{"+":{"df":0,"docs":{},"β":{"2":{"df":0,"docs":{},"​":{"c":{"(":{"df":0,"docs":{},"z":{"df":3,"docs":{"113":{"tf":1.0},"58":{"tf":1.0},"63":{"tf":1.0}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":7,"docs":{"112":{"tf":1.0},"113":{"tf":1.4142135623730951},"59":{"tf":1.0},"62":{"tf":1.0},"63":{"tf":1.4142135623730951},"69":{"tf":1.0},"85":{"tf":1.0}}}},".":{"df":0,"docs":{},"t":{"df":0,"docs":{},"h":{"df":0,"docs":{},"i":{"df":1,"docs":{"14":{"tf":1.0}}}}}},"1":{"df":2,"docs":{"94":{"tf":1.4142135623730951},"95":{"tf":1.0}},"​":{"(":{"df":0,"docs":{},"z":{"2":{"df":1,"docs":{"94":{"tf":1.4142135623730951}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"2":{"df":2,"docs":{"94":{"tf":1.4142135623730951},"95":{"tf":1.0}},"​":{"(":{"df":0,"docs":{},"z":{"2":{"df":1,"docs":{"94":{"tf":1.4142135623730951}}},"df":0,"docs":{}}},"df":0,"docs":{}}},":":{"=":{"df":0,"docs":{},"k":{"df":0,"docs":{},"∑":{"df":0,"docs":{},"​":{"df":0,"docs":{},"β":{"df":0,"docs":{},"k":{"df":0,"docs":{},"t":{"df":0,"docs":{},"​":{"c":{"df":0,"docs":{},"k":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"j":{"df":0,"docs":{},"∑":{"df":0,"docs":{},"​":{"df":0,"docs":{},"β":{"df":0,"docs":{},"j":{"b":{"df":0,"docs":{},"​":{"b":{"df":0,"docs":{},"j":{"df":1,"docs":{"94":{"tf":1.0}}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}}}},"df":0,"docs":{}}}},"df":0,"docs":{}}}}}}}},"η":{"1":{"df":0,"docs":{},"z":{"2":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"z":{"df":0,"docs":{},"η":{"2":{"df":0,"docs":{},"z":{"2":{"df":1,"docs":{"95":{"tf":1.0}}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"=":{"df":0,"docs":{},"h":{"1":{"df":0,"docs":{},"​":{"(":{"df":0,"docs":{},"x":{"2":{")":{"+":{"df":0,"docs":{},"x":{"df":0,"docs":{},"h":{"2":{"df":0,"docs":{},"​":{"(":{"df":0,"docs":{},"x":{"2":{"df":1,"docs":{"94":{"tf":1.0}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"k":{"df":0,"docs":{},"∑":{"df":0,"docs":{},"​":{"df":0,"docs":{},"β":{"df":0,"docs":{},"k":{"df":0,"docs":{},"t":{"df":0,"docs":{},"​":{"c":{"df":0,"docs":{},"k":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"j":{"df":0,"docs":{},"∑":{"df":0,"docs":{},"​":{"df":0,"docs":{},"β":{"df":0,"docs":{},"j":{"b":{"df":0,"docs":{},"​":{"b":{"df":0,"docs":{},"j":{"df":1,"docs":{"95":{"tf":1.0}}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}}}},"df":0,"docs":{}}}},"df":0,"docs":{}}}}}}}},"s":{"df":0,"docs":{},"h":{"a":{"3":{"df":1,"docs":{"40":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}}},"{":{"1":{",":{"df":0,"docs":{},"ω":{",":{"df":0,"docs":{},"ω":{"2":{",":{"df":0,"docs":{},"…":{",":{"df":0,"docs":{},"ω":{"df":0,"docs":{},"k":{"df":0,"docs":{},"−":{"1":{"df":1,"docs":{"14":{"tf":1.4142135623730951}}},"df":0,"docs":{}}},"n":{"df":0,"docs":{},"−":{"1":{"df":3,"docs":{"14":{"tf":1.4142135623730951},"16":{"tf":1.0},"23":{"tf":1.0}}},"df":0,"docs":{}}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{},"h":{"0":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"…":{",":{"df":0,"docs":{},"h":{"df":0,"docs":{},"n":{"df":0,"docs":{},"​":{"df":0,"docs":{},"}":{"df":0,"docs":{},"⊂":{"df":0,"docs":{},"f":{"df":1,"docs":{"5":{"tf":1.0}}}}}}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"ω":{"df":0,"docs":{},"i":{":":{"0":{"df":0,"docs":{},"≤":{"df":0,"docs":{},"i":{"<":{"df":0,"docs":{},"n":{"df":1,"docs":{"14":{"tf":1.0}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"_":{"1":{"(":{"df":0,"docs":{},"x":{"df":1,"docs":{"67":{"tf":1.0}}}},"df":2,"docs":{"116":{"tf":1.7320508075688772},"67":{"tf":1.0}}},"2":{"(":{"df":0,"docs":{},"x":{"df":1,"docs":{"67":{"tf":1.0}}}},"df":2,"docs":{"116":{"tf":1.7320508075688772},"67":{"tf":1.0}}},"df":0,"docs":{}},"a":{"df":0,"docs":{},"l":{"df":0,"docs":{},"f":{"df":1,"docs":{"125":{"tf":1.0}}}},"n":{"d":{"c":{"df":0,"docs":{},"r":{"a":{"df":0,"docs":{},"f":{"df":0,"docs":{},"t":{"df":1,"docs":{"83":{"tf":1.0}}}}},"df":0,"docs":{}}},"df":4,"docs":{"114":{"tf":1.0},"117":{"tf":1.0},"52":{"tf":1.0},"56":{"tf":1.0}},"l":{"df":2,"docs":{"110":{"tf":1.0},"9":{"tf":1.0}}}},"df":0,"docs":{}},"p":{"df":0,"docs":{},"p":{"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"df":4,"docs":{"116":{"tf":1.0},"125":{"tf":1.0},"146":{"tf":1.0},"77":{"tf":1.0}}}},"i":{"df":1,"docs":{"35":{"tf":1.0}}}}},"r":{"d":{"df":2,"docs":{"14":{"tf":1.0},"20":{"tf":1.0}}},"df":0,"docs":{}},"s":{"df":0,"docs":{},"h":{"(":{"df":0,"docs":{},"m":{"df":0,"docs":{},"e":{"df":0,"docs":{},"s":{"df":0,"docs":{},"s":{"a":{"df":0,"docs":{},"g":{"df":0,"docs":{},"e":{"_":{"2":{"df":1,"docs":{"40":{"tf":1.7320508075688772}}},"3":{"df":1,"docs":{"40":{"tf":1.4142135623730951}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}}}}},"df":7,"docs":{"118":{"tf":1.0},"151":{"tf":1.0},"45":{"tf":1.0},"75":{"tf":1.7320508075688772},"88":{"tf":1.0},"91":{"tf":1.4142135623730951},"92":{"tf":1.0}},"e":{"df":0,"docs":{},"r":{"df":2,"docs":{"110":{"tf":1.0},"40":{"tf":2.6457513110645907}}}},"m":{"a":{"df":0,"docs":{},"p":{":":{":":{"df":0,"docs":{},"f":{"df":0,"docs":{},"r":{"df":0,"docs":{},"o":{"df":0,"docs":{},"m":{"(":{"[":{"(":{"df":0,"docs":{},"x":{"df":1,"docs":{"44":{"tf":1.0}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"v":{"df":0,"docs":{},"e":{"df":3,"docs":{"110":{"tf":1.0},"130":{"tf":1.0},"49":{"tf":1.0}},"n":{"'":{"df":0,"docs":{},"t":{"df":1,"docs":{"69":{"tf":1.0}}}},"df":0,"docs":{}}}}},"df":35,"docs":{"112":{"tf":1.4142135623730951},"113":{"tf":1.0},"114":{"tf":2.0},"116":{"tf":2.0},"117":{"tf":1.0},"118":{"tf":1.0},"125":{"tf":2.23606797749979},"134":{"tf":1.0},"14":{"tf":2.8284271247461903},"147":{"tf":1.4142135623730951},"20":{"tf":2.23606797749979},"24":{"tf":1.0},"25":{"tf":1.0},"26":{"tf":1.0},"32":{"tf":1.4142135623730951},"35":{"tf":1.0},"53":{"tf":1.0},"56":{"tf":2.449489742783178},"57":{"tf":2.23606797749979},"58":{"tf":2.0},"59":{"tf":1.7320508075688772},"60":{"tf":1.0},"62":{"tf":1.4142135623730951},"65":{"tf":1.0},"66":{"tf":1.0},"67":{"tf":1.7320508075688772},"69":{"tf":1.4142135623730951},"7":{"tf":1.4142135623730951},"73":{"tf":1.0},"75":{"tf":1.0},"82":{"tf":1.0},"84":{"tf":2.6457513110645907},"85":{"tf":2.0},"88":{"tf":1.0},"94":{"tf":1.4142135623730951}},"e":{"df":0,"docs":{},"l":{"df":0,"docs":{},"i":{"df":0,"docs":{},"o":{"df":1,"docs":{"42":{"tf":1.0}}}},"p":{"df":2,"docs":{"45":{"tf":1.0},"70":{"tf":1.0}},"e":{"df":0,"docs":{},"r":{"df":2,"docs":{"108":{"tf":1.0},"35":{"tf":1.0}}}}}},"n":{"c":{"df":2,"docs":{"125":{"tf":1.0},"130":{"tf":1.0}}},"df":0,"docs":{}},"r":{"df":0,"docs":{},"e":{"'":{"df":1,"docs":{"20":{"tf":1.0}}},"df":27,"docs":{"11":{"tf":1.0},"114":{"tf":1.0},"118":{"tf":1.0},"124":{"tf":1.0},"133":{"tf":1.4142135623730951},"135":{"tf":1.0},"137":{"tf":1.0},"14":{"tf":2.449489742783178},"142":{"tf":1.0},"151":{"tf":1.4142135623730951},"21":{"tf":1.0},"35":{"tf":1.4142135623730951},"4":{"tf":1.0},"40":{"tf":1.4142135623730951},"52":{"tf":1.0},"54":{"tf":1.0},"56":{"tf":1.0},"57":{"tf":1.0},"7":{"tf":1.4142135623730951},"70":{"tf":1.0},"72":{"tf":1.0},"75":{"tf":1.0},"88":{"tf":1.0},"9":{"tf":1.0},"90":{"tf":1.0},"94":{"tf":1.7320508075688772},"98":{"tf":1.0}}}},"u":{"df":0,"docs":{},"r":{"df":0,"docs":{},"i":{"df":0,"docs":{},"s":{"df":0,"docs":{},"t":{"df":4,"docs":{"118":{"tf":1.0},"40":{"tf":1.0},"42":{"tf":1.0},"89":{"tf":1.0}}}}}}}},"i":{"d":{"df":0,"docs":{},"e":{"df":1,"docs":{"19":{"tf":1.0}}}},"df":0,"docs":{},"g":{"df":0,"docs":{},"h":{"df":12,"docs":{"104":{"tf":1.7320508075688772},"105":{"tf":1.0},"106":{"tf":1.0},"107":{"tf":1.0},"108":{"tf":1.0},"109":{"tf":1.0},"110":{"tf":1.0},"35":{"tf":1.0},"46":{"tf":1.0},"59":{"tf":1.0},"69":{"tf":1.4142135623730951},"82":{"tf":1.4142135623730951}},"e":{"df":0,"docs":{},"r":{"df":4,"docs":{"125":{"tf":1.0},"146":{"tf":1.0},"20":{"tf":1.0},"68":{"tf":1.0}}}}}},"m":{"df":0,"docs":{},"s":{"df":0,"docs":{},"e":{"df":0,"docs":{},"l":{"df":0,"docs":{},"f":{"df":1,"docs":{"21":{"tf":1.4142135623730951}}}}}}}},"o":{"df":0,"docs":{},"l":{"d":{"df":20,"docs":{"10":{"tf":1.0},"107":{"tf":2.0},"108":{"tf":1.4142135623730951},"109":{"tf":1.7320508075688772},"11":{"tf":1.0},"123":{"tf":1.4142135623730951},"13":{"tf":1.0},"131":{"tf":1.0},"133":{"tf":1.0},"14":{"tf":2.23606797749979},"146":{"tf":1.4142135623730951},"16":{"tf":1.4142135623730951},"35":{"tf":1.0},"54":{"tf":1.0},"69":{"tf":1.0},"7":{"tf":1.0},"73":{"tf":2.23606797749979},"74":{"tf":2.0},"75":{"tf":1.0},"77":{"tf":1.0}}},"df":0,"docs":{},"e":{"df":3,"docs":{"130":{"tf":2.0},"134":{"tf":2.8284271247461903},"146":{"tf":2.6457513110645907}}}},"m":{"df":0,"docs":{},"o":{"df":0,"docs":{},"m":{"df":0,"docs":{},"o":{"df":0,"docs":{},"r":{"df":0,"docs":{},"p":{"df":0,"docs":{},"h":{"df":1,"docs":{"19":{"tf":1.0}}}}}}}}},"n":{"df":0,"docs":{},"e":{"df":0,"docs":{},"s":{"df":0,"docs":{},"t":{"df":4,"docs":{"118":{"tf":1.0},"20":{"tf":1.0},"77":{"tf":1.0},"78":{"tf":1.0}}}}}},"o":{"d":{"df":15,"docs":{"111":{"tf":1.7320508075688772},"112":{"tf":1.0},"113":{"tf":1.0},"114":{"tf":1.4142135623730951},"115":{"tf":1.0},"116":{"tf":1.0},"117":{"tf":1.0},"118":{"tf":1.0},"119":{"tf":1.0},"120":{"tf":1.0},"121":{"tf":1.0},"122":{"tf":1.0},"123":{"tf":1.0},"124":{"tf":1.0},"125":{"tf":1.0}}},"df":0,"docs":{}},"s":{"df":0,"docs":{},"t":{"df":1,"docs":{"0":{"tf":1.0}}}}},"u":{"df":0,"docs":{},"g":{"df":0,"docs":{},"e":{"df":2,"docs":{"123":{"tf":1.7320508075688772},"80":{"tf":1.0}}}}},"ω":{"df":0,"docs":{},"i":{":":{"0":{"df":0,"docs":{},"≤":{"df":0,"docs":{},"i":{"<":{"2":{"df":0,"docs":{},"n":{"df":1,"docs":{"125":{"tf":1.0}}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"∈":{"d":{"df":1,"docs":{"14":{"tf":1.4142135623730951}}},"df":0,"docs":{},"f":{"df":0,"docs":{},"∖":{"df":0,"docs":{},"{":{"df":0,"docs":{},"ω":{"df":0,"docs":{},"i":{"df":0,"docs":{},"}":{"df":0,"docs":{},"i":{"df":0,"docs":{},"≥":{"0":{"df":1,"docs":{"91":{"tf":1.0}}},"df":0,"docs":{}}}}}}}}},"h":{"df":2,"docs":{"14":{"tf":1.7320508075688772},"16":{"tf":1.0}}}}},"i":{"+":{"1":{"df":2,"docs":{"101":{"tf":1.0},"70":{"tf":1.4142135623730951}}},"2":{"df":0,"docs":{},"k":{"df":0,"docs":{},"−":{"1":{"df":1,"docs":{"101":{"tf":1.0}}},"df":0,"docs":{}}}},"df":0,"docs":{}},",":{"df":0,"docs":{},"j":{")":{",":{"df":0,"docs":{},"v":{"df":1,"docs":{"14":{"tf":1.4142135623730951}}}},"df":0,"docs":{},"∈":{"df":0,"docs":{},"i":{"df":1,"docs":{"14":{"tf":1.0}}}}},",":{"df":0,"docs":{},"k":{",":{"df":0,"docs":{},"l":{"df":4,"docs":{"11":{"tf":1.0},"13":{"tf":1.0},"14":{"tf":1.0},"16":{"tf":1.0}}}},"df":0,"docs":{}},"l":{"df":1,"docs":{"14":{"tf":1.0}}}},"df":1,"docs":{"14":{"tf":2.0}}}},".":{"df":6,"docs":{"107":{"tf":1.0},"114":{"tf":1.0},"125":{"tf":1.0},"52":{"tf":1.0},"59":{"tf":1.4142135623730951},"68":{"tf":1.0}}},":":{"0":{"df":0,"docs":{},"≤":{"df":0,"docs":{},"i":{"<":{"2":{"df":0,"docs":{},"n":{"+":{"1":{"df":1,"docs":{"125":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}},"=":{"0":{"df":0,"docs":{},"k":{"df":0,"docs":{},"−":{"1":{"df":0,"docs":{},"​":{"(":{"a":{"df":0,"docs":{},"i":{"df":1,"docs":{"14":{"tf":1.4142135623730951}},"​":{"+":{"df":0,"docs":{},"γ":{")":{"/":{"df":0,"docs":{},"∏":{"df":0,"docs":{},"i":{"=":{"0":{"df":0,"docs":{},"k":{"df":0,"docs":{},"−":{"1":{"df":0,"docs":{},"​":{"(":{"b":{"df":0,"docs":{},"i":{"df":1,"docs":{"14":{"tf":1.0}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"b":{"df":0,"docs":{},"i":{"df":1,"docs":{"14":{"tf":1.4142135623730951}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"∏":{"df":0,"docs":{},"k":{"df":0,"docs":{},"−":{"1":{"df":0,"docs":{},"​":{"(":{"a":{"df":0,"docs":{},"i":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"γ":{")":{"=":{"df":0,"docs":{},"i":{"=":{"0":{"df":0,"docs":{},"∏":{"df":0,"docs":{},"k":{"df":0,"docs":{},"−":{"1":{"df":0,"docs":{},"​":{"(":{"b":{"df":0,"docs":{},"i":{"df":1,"docs":{"14":{"tf":1.0}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"∑":{"df":0,"docs":{},"n":{"df":0,"docs":{},"​":{"df":0,"docs":{},"l":{"df":0,"docs":{},"i":{"df":0,"docs":{},"​":{"(":{"df":0,"docs":{},"ζ":{")":{"(":{"df":0,"docs":{},"p":{"df":0,"docs":{},"i":{")":{"df":0,"docs":{},"i":{"df":1,"docs":{"32":{"tf":1.0}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}}}}}},"1":{"df":1,"docs":{"101":{"tf":1.0}}},"df":0,"docs":{},"{":{"(":{"df":0,"docs":{},"i":{",":{"df":0,"docs":{},"j":{"df":1,"docs":{"14":{"tf":1.0}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"∣":{"df":0,"docs":{},"l":{"df":0,"docs":{},"j":{"df":1,"docs":{"142":{"tf":1.0}}}}}},">":{"df":0,"docs":{},"n":{"df":2,"docs":{"14":{"tf":1.0},"16":{"tf":1.0}}}},"b":{"df":0,"docs":{},"​":{"b":{"df":0,"docs":{},"i":{"df":0,"docs":{},"​":{"(":{"df":0,"docs":{},"x":{"df":1,"docs":{"114":{"tf":1.0}}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"d":{"df":0,"docs":{},"e":{"a":{"df":12,"docs":{"118":{"tf":1.0},"14":{"tf":1.0},"151":{"tf":1.0},"4":{"tf":1.0},"54":{"tf":1.0},"59":{"tf":1.0},"6":{"tf":1.4142135623730951},"65":{"tf":1.0},"66":{"tf":1.0},"67":{"tf":1.0},"7":{"tf":1.0},"79":{"tf":1.0}}},"df":0,"docs":{},"n":{"df":0,"docs":{},"t":{"df":0,"docs":{},"i":{"df":0,"docs":{},"f":{"df":0,"docs":{},"i":{"df":1,"docs":{"52":{"tf":1.0}}}}}}}}},"df":0,"docs":{},"l":{"df":0,"docs":{},"u":{"df":0,"docs":{},"s":{"df":0,"docs":{},"t":{"df":0,"docs":{},"r":{"df":1,"docs":{"111":{"tf":1.0}}}}}}},"m":{"a":{"df":0,"docs":{},"g":{"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"df":1,"docs":{"149":{"tf":1.0}}}}}},"df":0,"docs":{},"m":{"df":0,"docs":{},"e":{"d":{"df":0,"docs":{},"i":{"df":1,"docs":{"113":{"tf":1.0}}}},"df":0,"docs":{}}},"p":{"df":0,"docs":{},"l":{"df":0,"docs":{},"e":{"df":0,"docs":{},"m":{"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"df":0,"docs":{},"t":{"df":39,"docs":{"103":{"tf":2.23606797749979},"104":{"tf":1.0},"105":{"tf":1.4142135623730951},"106":{"tf":1.0},"107":{"tf":1.0},"108":{"tf":1.0},"109":{"tf":1.0},"110":{"tf":1.4142135623730951},"111":{"tf":1.4142135623730951},"112":{"tf":1.0},"113":{"tf":1.0},"114":{"tf":1.0},"115":{"tf":1.0},"116":{"tf":1.0},"117":{"tf":1.0},"118":{"tf":1.0},"119":{"tf":1.0},"120":{"tf":1.0},"121":{"tf":1.0},"122":{"tf":1.0},"123":{"tf":1.4142135623730951},"124":{"tf":1.4142135623730951},"125":{"tf":1.0},"127":{"tf":1.0},"19":{"tf":1.0},"34":{"tf":2.0},"35":{"tf":1.0},"36":{"tf":1.4142135623730951},"37":{"tf":2.0},"38":{"tf":1.0},"39":{"tf":1.0},"40":{"tf":1.4142135623730951},"41":{"tf":1.4142135623730951},"42":{"tf":1.0},"46":{"tf":1.0},"64":{"tf":1.0},"67":{"tf":1.0},"68":{"tf":1.0},"9":{"tf":1.0}}}}}}},"i":{"c":{"df":0,"docs":{},"i":{"df":0,"docs":{},"t":{"df":0,"docs":{},"l":{"df":0,"docs":{},"i":{"df":2,"docs":{"52":{"tf":1.0},"95":{"tf":1.0}}}}}}},"df":1,"docs":{"101":{"tf":1.0}}}},"o":{"df":0,"docs":{},"r":{"df":0,"docs":{},"t":{"df":10,"docs":{"10":{"tf":1.0},"105":{"tf":1.0},"124":{"tf":1.0},"134":{"tf":1.0},"14":{"tf":1.4142135623730951},"146":{"tf":1.7320508075688772},"16":{"tf":1.0},"68":{"tf":1.0},"73":{"tf":1.0},"92":{"tf":1.4142135623730951}}}}},"r":{"df":0,"docs":{},"o":{"df":0,"docs":{},"v":{"df":3,"docs":{"123":{"tf":1.0},"151":{"tf":1.0},"21":{"tf":1.0}}}}}}},"n":{"c":{"df":0,"docs":{},"l":{"df":0,"docs":{},"u":{"d":{"df":7,"docs":{"125":{"tf":1.7320508075688772},"13":{"tf":1.0},"146":{"tf":1.0},"149":{"tf":1.0},"23":{"tf":1.0},"66":{"tf":1.0},"84":{"tf":1.0}}},"df":0,"docs":{}}},"o":{"df":0,"docs":{},"r":{"df":0,"docs":{},"p":{"df":0,"docs":{},"o":{"df":0,"docs":{},"r":{"df":2,"docs":{"13":{"tf":1.4142135623730951},"147":{"tf":1.4142135623730951}}}}}}},"r":{"df":0,"docs":{},"e":{"a":{"df":0,"docs":{},"s":{"df":3,"docs":{"147":{"tf":1.4142135623730951},"88":{"tf":1.0},"94":{"tf":1.4142135623730951}}}},"d":{"df":1,"docs":{"50":{"tf":1.0}}},"df":0,"docs":{},"m":{"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"df":0,"docs":{},"t":{"df":1,"docs":{"142":{"tf":1.0}}}}}}}}},"d":{"df":0,"docs":{},"e":{"df":2,"docs":{"57":{"tf":1.0},"59":{"tf":1.4142135623730951}},"p":{"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"d":{"df":4,"docs":{"11":{"tf":1.0},"13":{"tf":1.4142135623730951},"150":{"tf":1.0},"80":{"tf":1.0}}},"df":0,"docs":{}}}},"x":{"df":11,"docs":{"101":{"tf":2.0},"109":{"tf":1.0},"11":{"tf":2.23606797749979},"13":{"tf":1.4142135623730951},"14":{"tf":1.4142135623730951},"149":{"tf":1.0},"36":{"tf":1.0},"72":{"tf":1.0},"80":{"tf":1.0},"94":{"tf":1.0},"95":{"tf":1.4142135623730951}}}},"i":{"c":{"df":4,"docs":{"11":{"tf":1.0},"14":{"tf":1.0},"28":{"tf":1.0},"95":{"tf":1.0}}},"df":0,"docs":{}},"u":{"c":{"df":2,"docs":{"14":{"tf":1.0},"90":{"tf":1.4142135623730951}}},"df":0,"docs":{}}},"df":2,"docs":{"146":{"tf":1.0},"151":{"tf":1.0}},"e":{"df":0,"docs":{},"f":{"df":0,"docs":{},"f":{"df":0,"docs":{},"i":{"c":{"df":0,"docs":{},"i":{"df":1,"docs":{"84":{"tf":1.0}}}},"df":0,"docs":{}}}}},"f":{"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"df":0,"docs":{},"i":{"df":0,"docs":{},"t":{"df":1,"docs":{"146":{"tf":1.4142135623730951}}}}}},"o":{"df":0,"docs":{},"r":{"df":0,"docs":{},"m":{"df":10,"docs":{"119":{"tf":1.0},"126":{"tf":1.0},"142":{"tf":1.0},"144":{"tf":1.7320508075688772},"145":{"tf":1.0},"146":{"tf":1.4142135623730951},"19":{"tf":1.0},"52":{"tf":1.0},"70":{"tf":1.0},"9":{"tf":1.0}}}}}},"g":{"df":0,"docs":{},"r":{"df":0,"docs":{},"e":{"d":{"df":0,"docs":{},"i":{"df":2,"docs":{"151":{"tf":1.0},"44":{"tf":1.0}}}},"df":0,"docs":{}}}},"i":{"df":0,"docs":{},"t":{"df":0,"docs":{},"i":{"df":5,"docs":{"146":{"tf":1.0},"151":{"tf":1.0},"31":{"tf":1.7320508075688772},"70":{"tf":1.0},"94":{"tf":1.0}}}}},"n":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":2,"docs":{"19":{"tf":1.0},"73":{"tf":1.0}}}}},"p":{"df":0,"docs":{},"u":{"df":0,"docs":{},"t":{"df":24,"docs":{"104":{"tf":2.449489742783178},"106":{"tf":1.4142135623730951},"11":{"tf":1.0},"110":{"tf":1.0},"121":{"tf":1.4142135623730951},"13":{"tf":2.6457513110645907},"14":{"tf":1.4142135623730951},"142":{"tf":1.0},"144":{"tf":1.0},"15":{"tf":1.7320508075688772},"151":{"tf":1.0},"16":{"tf":1.7320508075688772},"23":{"tf":1.7320508075688772},"26":{"tf":1.0},"3":{"tf":1.7320508075688772},"32":{"tf":1.4142135623730951},"34":{"tf":2.0},"35":{"tf":3.3166247903554},"36":{"tf":1.0},"42":{"tf":1.4142135623730951},"44":{"tf":2.449489742783178},"7":{"tf":3.1622776601683795},"9":{"tf":1.0},"95":{"tf":1.0}}}}},"s":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":0,"docs":{},"t":{"df":3,"docs":{"130":{"tf":1.0},"133":{"tf":1.4142135623730951},"134":{"tf":1.7320508075688772}}}}},"i":{"d":{"df":1,"docs":{"45":{"tf":1.0}}},"df":0,"docs":{},"g":{"df":0,"docs":{},"h":{"df":0,"docs":{},"t":{"df":1,"docs":{"103":{"tf":1.0}}}}}},"p":{"df":0,"docs":{},"e":{"c":{"df":0,"docs":{},"t":{"df":2,"docs":{"101":{"tf":1.0},"14":{"tf":1.0}}}},"df":0,"docs":{}}},"t":{"a":{"df":0,"docs":{},"n":{"c":{"df":5,"docs":{"13":{"tf":1.0},"14":{"tf":1.0},"16":{"tf":1.4142135623730951},"35":{"tf":2.6457513110645907},"8":{"tf":1.0}}},"df":0,"docs":{},"t":{"df":0,"docs":{},"i":{"df":4,"docs":{"106":{"tf":1.0},"109":{"tf":1.0},"110":{"tf":1.0},"35":{"tf":1.7320508075688772}}}}}},"df":3,"docs":{"142":{"tf":1.0},"146":{"tf":1.4142135623730951},"147":{"tf":1.4142135623730951}},"e":{"a":{"d":{"df":13,"docs":{"101":{"tf":1.0},"123":{"tf":1.0},"131":{"tf":1.0},"14":{"tf":1.0},"146":{"tf":1.0},"21":{"tf":1.0},"32":{"tf":1.0},"54":{"tf":1.0},"55":{"tf":1.0},"80":{"tf":1.4142135623730951},"84":{"tf":1.0},"9":{"tf":1.0},"92":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}},"r":{"df":0,"docs":{},"u":{"c":{"df":0,"docs":{},"t":{"df":8,"docs":{"130":{"tf":1.0},"131":{"tf":1.4142135623730951},"133":{"tf":2.449489742783178},"142":{"tf":1.4142135623730951},"146":{"tf":3.872983346207417},"147":{"tf":1.0},"151":{"tf":1.4142135623730951},"50":{"tf":1.0}}}},"df":0,"docs":{}}}}},"t":{"df":0,"docs":{},"e":{"df":0,"docs":{},"g":{"df":3,"docs":{"142":{"tf":1.0},"146":{"tf":1.0},"41":{"tf":1.4142135623730951}},"r":{"df":2,"docs":{"105":{"tf":1.0},"84":{"tf":1.0}}}},"n":{"df":0,"docs":{},"s":{"df":1,"docs":{"99":{"tf":1.0}}}},"r":{"a":{"c":{"df":0,"docs":{},"t":{"df":14,"docs":{"118":{"tf":1.4142135623730951},"127":{"tf":1.0},"128":{"tf":1.0},"137":{"tf":2.0},"145":{"tf":1.0},"146":{"tf":1.0},"70":{"tf":1.0},"71":{"tf":1.0},"73":{"tf":1.0},"75":{"tf":1.0},"77":{"tf":1.0},"82":{"tf":1.0},"91":{"tf":1.0},"95":{"tf":1.0}}}},"df":0,"docs":{}},"df":0,"docs":{},"e":{"df":0,"docs":{},"s":{"df":0,"docs":{},"t":{"df":1,"docs":{"14":{"tf":1.0}}}}},"l":{"df":0,"docs":{},"e":{"a":{"df":0,"docs":{},"v":{"df":2,"docs":{"149":{"tf":1.0},"150":{"tf":1.0}}}},"df":0,"docs":{}}},"m":{"df":0,"docs":{},"e":{"d":{"df":0,"docs":{},"i":{"df":2,"docs":{"11":{"tf":1.0},"48":{"tf":1.0}}}},"df":0,"docs":{}}},"n":{"df":4,"docs":{"118":{"tf":1.4142135623730951},"40":{"tf":2.23606797749979},"71":{"tf":1.0},"94":{"tf":1.0}}},"p":{"df":0,"docs":{},"o":{"df":0,"docs":{},"l":{"df":26,"docs":{"112":{"tf":1.0},"114":{"tf":2.6457513110645907},"121":{"tf":1.7320508075688772},"123":{"tf":2.0},"125":{"tf":1.0},"14":{"tf":3.0},"16":{"tf":1.0},"20":{"tf":1.4142135623730951},"24":{"tf":1.0},"25":{"tf":1.0},"26":{"tf":1.0},"3":{"tf":2.0},"32":{"tf":1.0},"35":{"tf":1.0},"36":{"tf":1.0},"5":{"tf":1.0},"52":{"tf":1.0},"54":{"tf":1.0},"62":{"tf":1.0},"68":{"tf":1.0},"73":{"tf":1.0},"82":{"tf":1.0},"83":{"tf":1.0},"84":{"tf":1.0},"91":{"tf":1.0},"94":{"tf":1.4142135623730951}}}},"r":{"df":0,"docs":{},"e":{"df":0,"docs":{},"t":{"df":2,"docs":{"41":{"tf":1.0},"70":{"tf":1.0}}}}}}}},"r":{"df":0,"docs":{},"o":{"d":{"df":0,"docs":{},"u":{"c":{"df":3,"docs":{"125":{"tf":1.0},"14":{"tf":1.0},"53":{"tf":1.0}},"t":{"df":2,"docs":{"0":{"tf":1.7320508075688772},"1":{"tf":1.0}}}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"v":{"a":{"df":0,"docs":{},"l":{"df":0,"docs":{},"i":{"d":{"df":2,"docs":{"79":{"tf":1.0},"9":{"tf":1.0}}},"df":0,"docs":{}}},"r":{"df":0,"docs":{},"i":{"df":1,"docs":{"20":{"tf":1.0}}}}},"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":0,"docs":{},"s":{"df":5,"docs":{"123":{"tf":1.0},"146":{"tf":1.0},"45":{"tf":1.0},"7":{"tf":1.0},"98":{"tf":2.0}}},"t":{"df":2,"docs":{"7":{"tf":1.0},"98":{"tf":1.0}}}}},"o":{"df":0,"docs":{},"l":{"df":0,"docs":{},"v":{"df":8,"docs":{"103":{"tf":1.0},"104":{"tf":1.0},"105":{"tf":1.0},"123":{"tf":1.4142135623730951},"149":{"tf":1.0},"21":{"tf":1.0},"53":{"tf":1.0},"70":{"tf":1.0}}}}}}},"o":{"df":1,"docs":{"151":{"tf":1.0}}},"s":{"c":{"df":0,"docs":{},"o":{"df":0,"docs":{},"m":{"df":0,"docs":{},"m":{"df":0,"docs":{},"i":{"df":0,"docs":{},"t":{"df":0,"docs":{},"m":{"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"df":0,"docs":{},"t":{"df":0,"docs":{},"s":{"c":{"df":0,"docs":{},"h":{"df":0,"docs":{},"e":{"df":0,"docs":{},"m":{"df":0,"docs":{},"e":{"<":{"df":0,"docs":{},"f":{"df":1,"docs":{"35":{"tf":1.7320508075688772}}}},"df":0,"docs":{}}}}}},"df":0,"docs":{}}}}}}}}}}}},"df":0,"docs":{},"f":{"df":0,"docs":{},"i":{"df":0,"docs":{},"e":{"df":0,"docs":{},"l":{"d":{"df":1,"docs":{"35":{"tf":2.23606797749979}}},"df":0,"docs":{}}}}},"n":{"'":{"df":0,"docs":{},"t":{"df":3,"docs":{"121":{"tf":1.0},"149":{"tf":1.0},"65":{"tf":1.0}}}},"df":0,"docs":{}},"r":{"a":{"df":0,"docs":{},"n":{"d":{"df":0,"docs":{},"o":{"df":0,"docs":{},"m":{"df":0,"docs":{},"f":{"df":0,"docs":{},"i":{"df":0,"docs":{},"e":{"df":0,"docs":{},"l":{"d":{"df":0,"docs":{},"e":{"df":0,"docs":{},"l":{"df":0,"docs":{},"e":{"df":0,"docs":{},"m":{"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"df":0,"docs":{},"t":{"df":0,"docs":{},"g":{"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"a":{"df":0,"docs":{},"t":{"df":0,"docs":{},"o":{"df":0,"docs":{},"r":{"<":{"df":0,"docs":{},"f":{"df":1,"docs":{"35":{"tf":1.0}}}},"df":0,"docs":{}}}}},"df":0,"docs":{}}}}}}}}}}}}}},"df":0,"docs":{}}}}}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"s":{"df":0,"docs":{},"u":{"df":2,"docs":{"146":{"tf":1.0},"151":{"tf":1.0}}}}},"t":{"'":{"df":15,"docs":{"107":{"tf":1.0},"11":{"tf":1.0},"124":{"tf":1.0},"125":{"tf":1.0},"14":{"tf":1.0},"146":{"tf":1.7320508075688772},"151":{"tf":1.0},"20":{"tf":1.0},"35":{"tf":1.0},"49":{"tf":1.4142135623730951},"50":{"tf":1.4142135623730951},"52":{"tf":1.0},"54":{"tf":1.0},"55":{"tf":1.0},"81":{"tf":1.0}}},"df":0,"docs":{},"e":{"df":0,"docs":{},"m":{"df":4,"docs":{"112":{"tf":1.0},"49":{"tf":1.0},"59":{"tf":1.4142135623730951},"62":{"tf":1.0}}},"r":{"df":2,"docs":{"102":{"tf":1.0},"114":{"tf":1.0}}}},"s":{"df":0,"docs":{},"e":{"df":0,"docs":{},"l":{"df":0,"docs":{},"f":{"df":3,"docs":{"10":{"tf":1.0},"14":{"tf":1.0},"58":{"tf":1.0}}}}}},"​":{"c":{"df":0,"docs":{},"i":{"df":0,"docs":{},"​":{"(":{"df":0,"docs":{},"x":{"df":1,"docs":{"114":{"tf":1.0}}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"∈":{"[":{"0":{",":{"2":{"df":0,"docs":{},"n":{"+":{"df":0,"docs":{},"k":{"df":1,"docs":{"92":{"tf":1.0}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"":{"=":{"df":0,"docs":{},"ω":{"df":0,"docs":{},"j":{"df":0,"docs":{},"k":{"1":{"df":0,"docs":{},"​":{"df":0,"docs":{},"":{"=":{"df":0,"docs":{},"ω":{"df":0,"docs":{},"l":{"df":0,"docs":{},"k":{"2":{"df":1,"docs":{"14":{"tf":1.0}}},"df":0,"docs":{}}}}},"df":0,"docs":{}}}},"df":0,"docs":{}}}}},"df":0,"docs":{}}},"j":{"=":{"1":{",":{"df":0,"docs":{},"…":{",":{"df":0,"docs":{},"m":{"df":4,"docs":{"91":{"tf":1.4142135623730951},"94":{"tf":1.4142135623730951},"95":{"tf":1.0},"97":{"tf":2.0}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{},"m":{"df":0,"docs":{},"′":{"+":{"1":{",":{"df":0,"docs":{},"…":{",":{"df":0,"docs":{},"m":{"df":0,"docs":{},"′":{"+":{"df":0,"docs":{},"m":{"df":2,"docs":{"94":{"tf":1.4142135623730951},"95":{"tf":1.0}}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}},"b":{"df":1,"docs":{"97":{"tf":1.4142135623730951}},"​":{":":{"=":{"df":0,"docs":{},"β":{"df":0,"docs":{},"j":{"+":{"df":0,"docs":{},"n":{"df":0,"docs":{},"t":{"df":0,"docs":{},"​":{"df":0,"docs":{},"−":{"1":{"df":1,"docs":{"97":{"tf":1.0}}},"df":0,"docs":{}}}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":9,"docs":{"106":{"tf":1.0},"14":{"tf":1.0},"147":{"tf":1.4142135623730951},"5":{"tf":1.0},"70":{"tf":1.4142135623730951},"83":{"tf":1.0},"85":{"tf":1.0},"92":{"tf":1.4142135623730951},"94":{"tf":1.4142135623730951}},"g":{"df":0,"docs":{},"z":{"df":1,"docs":{"95":{"tf":1.4142135623730951}}}},"o":{"b":{"df":3,"docs":{"114":{"tf":1.0},"118":{"tf":1.0},"20":{"tf":1.0}}},"df":0,"docs":{}},"u":{"df":0,"docs":{},"m":{"df":0,"docs":{},"p":{"df":2,"docs":{"146":{"tf":1.0},"50":{"tf":1.0}}}}},"z":{"df":1,"docs":{"95":{"tf":1.4142135623730951}}},"υ":{"df":0,"docs":{},"s":{"df":0,"docs":{},"​":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"t":{"df":0,"docs":{},"j":{"df":1,"docs":{"95":{"tf":1.0}}}}},"df":0,"docs":{}}}}},"​":{",":{"df":0,"docs":{},"γ":{"df":0,"docs":{},"j":{"df":1,"docs":{"97":{"tf":1.0}}}}},":":{"=":{"df":0,"docs":{},"α":{"df":0,"docs":{},"j":{"df":1,"docs":{"97":{"tf":1.0}}}}},"df":0,"docs":{}},"df":0,"docs":{}},"′":{"df":0,"docs":{},"​":{":":{"=":{"df":0,"docs":{},"α":{"df":0,"docs":{},"j":{"+":{"df":0,"docs":{},"m":{"df":0,"docs":{},"−":{"1":{"df":1,"docs":{"97":{"tf":1.0}}},"df":0,"docs":{}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"":{"=":{"df":0,"docs":{},"i":{"df":1,"docs":{"5":{"tf":1.0}}}},"df":0,"docs":{}}},"k":{"+":{"1":{"df":0,"docs":{},"υ":{"df":0,"docs":{},"s":{"2":{"df":0,"docs":{},"k":{"+":{"1":{"df":1,"docs":{"95":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"1":{"2":{"df":1,"docs":{"35":{"tf":1.0}}},"df":2,"docs":{"14":{"tf":1.0},"35":{"tf":1.4142135623730951}}},"2":{"df":2,"docs":{"14":{"tf":1.0},"35":{"tf":1.0}}},"<":{"df":0,"docs":{},"n":{"df":2,"docs":{"94":{"tf":1.0},"95":{"tf":1.0}},"−":{"1":{"df":1,"docs":{"95":{"tf":1.0}}},"df":0,"docs":{}}}},"=":{"0":{",":{"df":0,"docs":{},"…":{",":{"df":0,"docs":{},"n":{"df":0,"docs":{},"−":{"1":{"df":1,"docs":{"95":{"tf":1.0}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"1":{",":{"df":0,"docs":{},"…":{",":{"df":0,"docs":{},"n":{"df":2,"docs":{"94":{"tf":1.0},"95":{"tf":1.0}},"t":{"df":2,"docs":{"91":{"tf":1.4142135623730951},"97":{"tf":1.4142135623730951}}},"−":{"1":{"df":1,"docs":{"94":{"tf":1.0}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"3":{"df":1,"docs":{"101":{"tf":1.0}}},"df":0,"docs":{},"n":{"df":1,"docs":{"95":{"tf":1.0}}},"ω":{"0":{"df":1,"docs":{"14":{"tf":1.0}}},"df":0,"docs":{}}},">":{"1":{"df":1,"docs":{"102":{"tf":1.0}}},"df":0,"docs":{}},"a":{"df":0,"docs":{},"t":{"df":0,"docs":{},"e":{"df":3,"docs":{"19":{"tf":1.0},"35":{"tf":1.0},"38":{"tf":1.0}}}}},"df":6,"docs":{"101":{"tf":1.0},"124":{"tf":1.0},"14":{"tf":2.0},"147":{"tf":1.4142135623730951},"49":{"tf":1.0},"70":{"tf":1.4142135623730951}},"e":{"c":{"c":{"a":{"df":0,"docs":{},"k":{"2":{"5":{"6":{"(":{"df":0,"docs":{},"x":{"df":0,"docs":{},"∣":{"df":0,"docs":{},"∣":{"df":0,"docs":{},"i":{"df":2,"docs":{"94":{"tf":1.0},"95":{"tf":1.0}}}}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":1,"docs":{"151":{"tf":1.0}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{},"e":{"df":0,"docs":{},"p":{"df":5,"docs":{"118":{"tf":1.0},"124":{"tf":1.0},"20":{"tf":1.0},"45":{"tf":1.0},"50":{"tf":1.0}}}},"y":{"df":3,"docs":{"146":{"tf":1.0},"151":{"tf":1.0},"44":{"tf":1.0}}}},"i":{"df":0,"docs":{},"n":{"d":{"df":1,"docs":{"74":{"tf":1.0}}},"df":0,"docs":{}}},"n":{"df":0,"docs":{},"o":{"df":0,"docs":{},"w":{"df":14,"docs":{"114":{"tf":1.0},"119":{"tf":1.0},"146":{"tf":1.7320508075688772},"151":{"tf":1.0},"44":{"tf":1.0},"45":{"tf":1.0},"57":{"tf":1.0},"58":{"tf":1.0},"7":{"tf":1.0},"74":{"tf":1.0},"75":{"tf":1.0},"79":{"tf":1.0},"80":{"tf":1.0},"83":{"tf":1.0}},"l":{"df":0,"docs":{},"e":{"d":{"df":0,"docs":{},"g":{"df":3,"docs":{"20":{"tf":1.0},"4":{"tf":1.0},"7":{"tf":1.4142135623730951}}}},"df":0,"docs":{}}},"n":{"df":6,"docs":{"151":{"tf":1.0},"21":{"tf":1.0},"42":{"tf":1.0},"73":{"tf":1.0},"91":{"tf":1.0},"98":{"tf":1.0}}}}}},"t":{"df":1,"docs":{"97":{"tf":1.4142135623730951}},"​":{":":{"=":{"df":0,"docs":{},"β":{"df":0,"docs":{},"k":{"df":0,"docs":{},"−":{"1":{"df":1,"docs":{"97":{"tf":1.0}}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"z":{"df":0,"docs":{},"g":{":":{":":{"df":0,"docs":{},"n":{"df":0,"docs":{},"e":{"df":0,"docs":{},"w":{"(":{"df":0,"docs":{},"s":{"df":0,"docs":{},"r":{"df":2,"docs":{"35":{"tf":1.4142135623730951},"44":{"tf":1.0}}}}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":2,"docs":{"35":{"tf":2.23606797749979},"44":{"tf":1.4142135623730951}}}},"υ":{"df":0,"docs":{},"s":{"2":{"df":0,"docs":{},"k":{"df":1,"docs":{"102":{"tf":1.0}},"​":{"df":0,"docs":{},"​":{"df":0,"docs":{},"π":{"df":0,"docs":{},"k":{"df":0,"docs":{},"−":{"df":0,"docs":{},"υ":{"df":0,"docs":{},"s":{"2":{"df":0,"docs":{},"k":{"df":0,"docs":{},"​":{"df":0,"docs":{},"​":{"df":0,"docs":{},"​":{"=":{"df":0,"docs":{},"g":{"+":{"df":0,"docs":{},"υ":{"df":0,"docs":{},"s":{"2":{"df":0,"docs":{},"k":{"df":0,"docs":{},"​":{"df":0,"docs":{},"h":{"=":{"df":0,"docs":{},"g":{"df":0,"docs":{},"−":{"df":0,"docs":{},"υ":{"df":0,"docs":{},"s":{"2":{"df":0,"docs":{},"k":{"df":0,"docs":{},"​":{"df":0,"docs":{},"h":{"df":1,"docs":{"95":{"tf":1.0}}}}}},"df":0,"docs":{}}}}}},"df":0,"docs":{}}}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}}},"df":0,"docs":{}}}}}}}}}},"df":0,"docs":{}}},"−":{"1":{"df":3,"docs":{"14":{"tf":1.0},"94":{"tf":1.0},"95":{"tf":1.0}}},"df":0,"docs":{}},"≥":{"0":{"df":1,"docs":{"20":{"tf":1.0}}},"df":0,"docs":{}}},"l":{",":{"df":0,"docs":{},"r":{",":{"df":0,"docs":{},"o":{"df":2,"docs":{"10":{"tf":1.0},"9":{"tf":1.0}}}},"df":0,"docs":{}}},"=":{"df":0,"docs":{},"∣":{"d":{"df":0,"docs":{},"k":{"df":0,"docs":{},"−":{"1":{"df":0,"docs":{},"​":{"df":0,"docs":{},"∣":{"/":{"2":{"df":1,"docs":{"94":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"a":{"df":0,"docs":{},"g":{"df":0,"docs":{},"r":{"a":{"df":0,"docs":{},"n":{"df":0,"docs":{},"g":{"df":7,"docs":{"123":{"tf":1.0},"3":{"tf":1.7320508075688772},"32":{"tf":1.0},"35":{"tf":1.0},"5":{"tf":1.0},"73":{"tf":1.0},"99":{"tf":1.0}}}}},"df":0,"docs":{}}},"m":{"b":{"d":{"a":{"df":0,"docs":{},"w":{"df":0,"docs":{},"o":{"df":0,"docs":{},"r":{"df":0,"docs":{},"k":{"df":6,"docs":{"0":{"tf":1.0},"1":{"tf":1.7320508075688772},"103":{"tf":1.4142135623730951},"19":{"tf":1.0},"45":{"tf":1.7320508075688772},"87":{"tf":1.0}},"s":{"_":{"c":{"df":0,"docs":{},"r":{"df":0,"docs":{},"y":{"df":0,"docs":{},"p":{"df":0,"docs":{},"t":{"df":0,"docs":{},"o":{"df":1,"docs":{"38":{"tf":1.0}}}}}}}},"df":0,"docs":{},"m":{"a":{"df":0,"docs":{},"t":{"df":0,"docs":{},"h":{":":{":":{"df":0,"docs":{},"e":{"df":0,"docs":{},"l":{"df":0,"docs":{},"l":{"df":0,"docs":{},"i":{"df":0,"docs":{},"p":{"df":0,"docs":{},"t":{"df":0,"docs":{},"i":{"c":{"_":{"c":{"df":0,"docs":{},"u":{"df":0,"docs":{},"r":{"df":0,"docs":{},"v":{"df":0,"docs":{},"e":{":":{":":{"df":0,"docs":{},"s":{"df":0,"docs":{},"h":{"df":0,"docs":{},"o":{"df":0,"docs":{},"r":{"df":0,"docs":{},"t":{"_":{"df":0,"docs":{},"w":{"df":0,"docs":{},"e":{"df":0,"docs":{},"i":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":0,"docs":{},"s":{"df":0,"docs":{},"t":{"df":0,"docs":{},"r":{"a":{"df":0,"docs":{},"s":{"df":0,"docs":{},"s":{":":{":":{"c":{"df":0,"docs":{},"u":{"df":0,"docs":{},"r":{"df":0,"docs":{},"v":{"df":0,"docs":{},"e":{"df":0,"docs":{},"s":{":":{":":{"b":{"df":0,"docs":{},"l":{"df":0,"docs":{},"s":{"1":{"2":{"_":{"3":{"8":{"1":{":":{":":{"d":{"df":0,"docs":{},"e":{"df":0,"docs":{},"f":{"a":{"df":0,"docs":{},"u":{"df":0,"docs":{},"l":{"df":0,"docs":{},"t":{"_":{"df":0,"docs":{},"t":{"df":0,"docs":{},"y":{"df":0,"docs":{},"p":{"df":0,"docs":{},"e":{"df":0,"docs":{},"s":{":":{":":{"df":0,"docs":{},"f":{"df":0,"docs":{},"r":{"df":0,"docs":{},"f":{"df":0,"docs":{},"i":{"df":0,"docs":{},"e":{"df":0,"docs":{},"l":{"d":{"df":2,"docs":{"44":{"tf":1.0},"45":{"tf":1.0}}},"df":0,"docs":{}}}}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}}}}},"df":0,"docs":{}}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}}}}}}}}},"df":0,"docs":{}}}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}}}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}},"p":{"df":0,"docs":{},"l":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"df":0,"docs":{},"k":{":":{":":{"c":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"df":0,"docs":{},"s":{"df":0,"docs":{},"t":{"df":0,"docs":{},"r":{"a":{"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"df":0,"docs":{},"t":{"_":{"df":0,"docs":{},"s":{"df":0,"docs":{},"y":{"df":0,"docs":{},"s":{"df":0,"docs":{},"t":{"df":0,"docs":{},"e":{"df":0,"docs":{},"m":{":":{":":{"c":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"df":0,"docs":{},"s":{"df":0,"docs":{},"t":{"df":0,"docs":{},"r":{"a":{"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"df":0,"docs":{},"t":{"df":0,"docs":{},"s":{"df":0,"docs":{},"y":{"df":0,"docs":{},"s":{"df":0,"docs":{},"t":{"df":0,"docs":{},"e":{"df":0,"docs":{},"m":{"df":2,"docs":{"44":{"tf":1.0},"45":{"tf":1.0}}}}}}}}}}}},"df":0,"docs":{}}}}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}}}}}},"df":0,"docs":{}}}}},"df":0,"docs":{}}}}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}}}}},"df":0,"docs":{}}}}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"n":{"d":{"df":1,"docs":{"14":{"tf":1.4142135623730951}}},"df":0,"docs":{},"g":{"df":0,"docs":{},"u":{"a":{"df":0,"docs":{},"g":{"df":1,"docs":{"50":{"tf":1.0}}}},"df":0,"docs":{}}}},"s":{"df":0,"docs":{},"t":{"df":14,"docs":{"10":{"tf":1.0},"107":{"tf":1.0},"109":{"tf":1.0},"119":{"tf":1.0},"13":{"tf":1.4142135623730951},"134":{"tf":1.0},"14":{"tf":2.0},"142":{"tf":1.4142135623730951},"146":{"tf":2.0},"147":{"tf":1.0},"151":{"tf":1.0},"16":{"tf":1.0},"40":{"tf":1.0},"9":{"tf":1.0}}}},"t":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":3,"docs":{"102":{"tf":1.0},"19":{"tf":1.4142135623730951},"87":{"tf":1.0}}}},"t":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":2,"docs":{"19":{"tf":1.0},"73":{"tf":1.0}}}}}},"y":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":2,"docs":{"102":{"tf":1.4142135623730951},"119":{"tf":1.4142135623730951}}}},"o":{"df":0,"docs":{},"u":{"df":0,"docs":{},"t":{"df":18,"docs":{"127":{"tf":2.23606797749979},"128":{"tf":1.0},"129":{"tf":1.0},"130":{"tf":1.0},"131":{"tf":1.0},"132":{"tf":1.0},"133":{"tf":1.0},"134":{"tf":1.0},"135":{"tf":1.0},"136":{"tf":1.0},"137":{"tf":1.0},"138":{"tf":1.0},"139":{"tf":1.0},"142":{"tf":1.0},"146":{"tf":1.4142135623730951},"147":{"tf":1.0},"149":{"tf":1.0},"151":{"tf":1.0}}}}}}},"d":{"df":0,"docs":{},"e":{"_":{"d":{"df":0,"docs":{},"o":{"df":0,"docs":{},"m":{"a":{"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{".":{"df":0,"docs":{},"i":{"df":0,"docs":{},"t":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"(":{")":{".":{"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"df":0,"docs":{},"u":{"df":0,"docs":{},"m":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":1,"docs":{"114":{"tf":1.0}}}}}}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}}}},"df":0,"docs":{}}}},"df":0,"docs":{}}}},"df":0,"docs":{},"r":{"df":0,"docs":{},"o":{"df":0,"docs":{},"o":{"df":0,"docs":{},"t":{"df":0,"docs":{},"s":{"_":{"df":0,"docs":{},"o":{"df":0,"docs":{},"f":{"_":{"df":0,"docs":{},"u":{"df":0,"docs":{},"n":{"df":0,"docs":{},"i":{"df":0,"docs":{},"t":{"df":0,"docs":{},"y":{"_":{"c":{"df":0,"docs":{},"o":{"df":0,"docs":{},"s":{"df":0,"docs":{},"e":{"df":0,"docs":{},"t":{"df":1,"docs":{"114":{"tf":1.4142135623730951}}}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}}}}},"df":0,"docs":{}}}},"df":0,"docs":{}}}}}},"t":{"df":0,"docs":{},"r":{"a":{"c":{"df":1,"docs":{"114":{"tf":2.0}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":6,"docs":{"109":{"tf":1.4142135623730951},"114":{"tf":1.4142135623730951},"119":{"tf":1.0},"125":{"tf":1.7320508075688772},"149":{"tf":1.0},"68":{"tf":2.0}}}},"df":7,"docs":{"11":{"tf":1.0},"12":{"tf":1.0},"13":{"tf":1.0},"14":{"tf":1.0},"142":{"tf":1.0},"147":{"tf":1.4142135623730951},"80":{"tf":1.4142135623730951}},"e":{"a":{"d":{"df":3,"docs":{"151":{"tf":1.0},"94":{"tf":1.0},"95":{"tf":1.0}}},"df":0,"docs":{},"f":{"df":2,"docs":{"101":{"tf":1.4142135623730951},"92":{"tf":1.0}}},"r":{"df":0,"docs":{},"n":{"df":1,"docs":{"42":{"tf":1.0}}}},"v":{"df":7,"docs":{"101":{"tf":1.7320508075688772},"14":{"tf":1.0},"144":{"tf":1.0},"16":{"tf":1.0},"20":{"tf":1.0},"80":{"tf":1.0},"92":{"tf":1.0}}}},"df":0,"docs":{},"f":{"df":0,"docs":{},"t":{"df":8,"docs":{"109":{"tf":1.0},"11":{"tf":1.0},"134":{"tf":1.0},"16":{"tf":1.0},"20":{"tf":1.0},"59":{"tf":1.0},"88":{"tf":1.0},"9":{"tf":2.23606797749979}}}},"m":{"df":0,"docs":{},"m":{"a":{"df":1,"docs":{"14":{"tf":1.4142135623730951}}},"df":0,"docs":{}}},"n":{"df":0,"docs":{},"g":{"df":0,"docs":{},"h":{"df":0,"docs":{},"t":{"df":1,"docs":{"131":{"tf":1.0}}}},"t":{"df":0,"docs":{},"h":{"df":7,"docs":{"119":{"tf":1.0},"142":{"tf":1.4142135623730951},"146":{"tf":1.0},"147":{"tf":1.7320508075688772},"36":{"tf":1.0},"51":{"tf":1.0},"72":{"tf":1.0}}}}}},"q":{"df":3,"docs":{"52":{"tf":1.4142135623730951},"62":{"tf":1.0},"68":{"tf":2.0}}},"s":{"df":0,"docs":{},"s":{"df":6,"docs":{"14":{"tf":1.0},"146":{"tf":1.0},"151":{"tf":1.4142135623730951},"48":{"tf":1.0},"73":{"tf":2.6457513110645907},"82":{"tf":1.0}}}},"t":{"'":{"df":24,"docs":{"104":{"tf":1.0},"107":{"tf":1.0},"109":{"tf":1.0},"114":{"tf":1.0},"12":{"tf":1.0},"125":{"tf":1.0},"14":{"tf":2.23606797749979},"149":{"tf":2.23606797749979},"150":{"tf":1.4142135623730951},"151":{"tf":1.7320508075688772},"16":{"tf":1.0},"20":{"tf":1.0},"34":{"tf":1.0},"35":{"tf":1.0},"44":{"tf":1.0},"48":{"tf":1.0},"49":{"tf":1.0},"52":{"tf":1.0},"54":{"tf":1.0},"71":{"tf":1.0},"75":{"tf":1.0},"77":{"tf":1.0},"79":{"tf":1.0},"9":{"tf":1.0}}},"df":0,"docs":{},"t":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":4,"docs":{"146":{"tf":1.0},"149":{"tf":1.0},"5":{"tf":1.0},"95":{"tf":1.7320508075688772}}}}}},"v":{"df":0,"docs":{},"e":{"df":0,"docs":{},"l":{"df":12,"docs":{"104":{"tf":1.7320508075688772},"105":{"tf":1.0},"106":{"tf":1.0},"107":{"tf":1.0},"108":{"tf":1.0},"109":{"tf":1.0},"110":{"tf":1.0},"46":{"tf":1.0},"59":{"tf":1.0},"67":{"tf":1.0},"69":{"tf":1.7320508075688772},"82":{"tf":1.4142135623730951}}},"r":{"a":{"df":0,"docs":{},"g":{"df":1,"docs":{"83":{"tf":1.0}}}},"df":0,"docs":{}}}}},"i":{",":{"df":0,"docs":{},"j":{"df":0,"docs":{},"′":{"df":0,"docs":{},"′":{"df":0,"docs":{},"​":{"<":{"df":0,"docs":{},"m":{"a":{"d":{"d":{"df":0,"docs":{},"r":{",":{"df":0,"docs":{},"i":{"+":{"1":{",":{"df":0,"docs":{},"j":{"df":1,"docs":{"142":{"tf":1.0}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}}}},"b":{"df":0,"docs":{},"r":{"a":{"df":0,"docs":{},"r":{"df":0,"docs":{},"i":{"df":3,"docs":{"121":{"tf":1.0},"2":{"tf":1.0},"3":{"tf":1.0}}}}},"df":0,"docs":{}}},"df":3,"docs":{"146":{"tf":1.0},"32":{"tf":1.0},"5":{"tf":1.4142135623730951}},"m":{"b":{"df":1,"docs":{"151":{"tf":1.4142135623730951}}},"df":0,"docs":{}},"n":{"df":0,"docs":{},"e":{"a":{"df":0,"docs":{},"r":{"df":6,"docs":{"14":{"tf":1.0},"21":{"tf":2.0},"28":{"tf":1.0},"32":{"tf":1.0},"84":{"tf":1.0},"85":{"tf":1.4142135623730951}}}},"df":1,"docs":{"35":{"tf":1.0}}},"k":{"df":1,"docs":{"151":{"tf":1.0}}}},"o":{"df":0,"docs":{},"r":{"df":1,"docs":{"103":{"tf":1.0}}}},"s":{"df":0,"docs":{},"t":{"df":4,"docs":{"114":{"tf":1.0},"119":{"tf":1.0},"146":{"tf":1.0},"59":{"tf":1.0}}}},"t":{"df":0,"docs":{},"t":{"df":0,"docs":{},"l":{"df":2,"docs":{"107":{"tf":1.0},"14":{"tf":1.0}}}}},"​":{"(":{"df":0,"docs":{},"h":{"df":0,"docs":{},"i":{"df":0,"docs":{},"​":{")":{"=":{"1":{"df":1,"docs":{"5":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"j":{"df":0,"docs":{},"​":{")":{"=":{"0":{"df":1,"docs":{"5":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}}},"o":{"c":{"a":{"df":0,"docs":{},"l":{"df":2,"docs":{"70":{"tf":2.0},"83":{"tf":1.0}}}},"df":0,"docs":{}},"df":0,"docs":{},"g":{"df":0,"docs":{},"i":{"c":{"df":5,"docs":{"146":{"tf":1.0},"151":{"tf":1.0},"16":{"tf":1.0},"36":{"tf":1.0},"4":{"tf":1.0}}},"df":0,"docs":{}}},"n":{"df":0,"docs":{},"g":{"df":2,"docs":{"114":{"tf":1.0},"73":{"tf":1.0}},"e":{"df":0,"docs":{},"r":{"df":2,"docs":{"13":{"tf":1.0},"146":{"tf":1.4142135623730951}}}}}},"o":{"df":0,"docs":{},"k":{"df":8,"docs":{"106":{"tf":1.0},"107":{"tf":1.0},"108":{"tf":1.0},"114":{"tf":1.0},"14":{"tf":1.0},"149":{"tf":2.23606797749979},"21":{"tf":1.0},"51":{"tf":1.0}}},"p":{"df":2,"docs":{"102":{"tf":1.0},"146":{"tf":2.0}}},"s":{"df":1,"docs":{"16":{"tf":1.0}}}},"s":{"df":0,"docs":{},"t":{"df":1,"docs":{"49":{"tf":1.0}}}},"t":{"df":2,"docs":{"50":{"tf":1.0},"53":{"tf":1.0}}},"w":{"df":5,"docs":{"125":{"tf":1.0},"35":{"tf":1.0},"68":{"tf":2.0},"81":{"tf":1.0},"91":{"tf":1.0}},"e":{"df":0,"docs":{},"s":{"df":0,"docs":{},"t":{"df":1,"docs":{"3":{"tf":1.0}}}}}}},"p":{"df":0,"docs":{},"u":{"b":{"df":2,"docs":{"142":{"tf":1.0},"147":{"tf":1.0}}},"df":0,"docs":{}}},"u":{"c":{"df":0,"docs":{},"k":{"df":0,"docs":{},"i":{"df":0,"docs":{},"l":{"df":0,"docs":{},"i":{"df":1,"docs":{"146":{"tf":1.0}}}}}}},"df":0,"docs":{}},"×":{"3":{"df":1,"docs":{"141":{"tf":1.0}}},"df":0,"docs":{}},"′":{":":{"=":{"df":0,"docs":{},"⌈":{"df":0,"docs":{},"l":{"df":0,"docs":{},"p":{"df":0,"docs":{},"u":{"b":{"df":0,"docs":{},"​":{"/":{"4":{"df":1,"docs":{"142":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}}}},"df":0,"docs":{}},"df":0,"docs":{},"′":{"=":{"(":{"df":0,"docs":{},"l":{"0":{"df":0,"docs":{},"′":{"df":0,"docs":{},"′":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"l":{"1":{"df":0,"docs":{},"′":{"df":0,"docs":{},"′":{"df":0,"docs":{},"​":{",":{".":{".":{".":{",":{"df":0,"docs":{},"l":{"df":0,"docs":{},"j":{"df":0,"docs":{},"′":{"df":0,"docs":{},"′":{"df":0,"docs":{},"​":{")":{"df":0,"docs":{},"t":{"df":1,"docs":{"142":{"tf":1.0}}}},"df":0,"docs":{}}}}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"m":{",":{"df":0,"docs":{},"v":{")":{"=":{"(":{"0":{",":{"0":{"df":1,"docs":{"146":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":1,"docs":{"146":{"tf":1.0}}}},"/":{"2":{"df":1,"docs":{"133":{"tf":1.0}}},"df":0,"docs":{}},"0":{"df":0,"docs":{},"​":{"=":{"df":0,"docs":{},"r":{"0":{"df":1,"docs":{"151":{"tf":1.0}}},"df":0,"docs":{}}},"df":0,"docs":{}}},":":{"=":{"df":0,"docs":{},"m":{"df":0,"docs":{},"′":{"+":{"df":0,"docs":{},"m":{"df":1,"docs":{"91":{"tf":1.0}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"=":{"2":{"df":0,"docs":{},"m":{"df":3,"docs":{"73":{"tf":1.0},"75":{"tf":1.0},"80":{"tf":1.0}}}},"5":{"2":{"df":1,"docs":{"147":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{},"n":{"+":{"df":0,"docs":{},"k":{"df":1,"docs":{"20":{"tf":1.0}}}},"df":0,"docs":{}}},"^":{"df":0,"docs":{},"j":{"df":1,"docs":{"94":{"tf":1.0}}}},"a":{"c":{"df":0,"docs":{},"h":{"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"df":2,"docs":{"151":{"tf":1.7320508075688772},"50":{"tf":1.4142135623730951}}}}}},"d":{"d":{"df":0,"docs":{},"r":{",":{"df":0,"docs":{},"i":{"+":{"1":{",":{"df":0,"docs":{},"j":{"df":0,"docs":{},"​":{"df":0,"docs":{},"−":{"df":0,"docs":{},"m":{"a":{"d":{"d":{"df":0,"docs":{},"r":{",":{"df":0,"docs":{},"i":{",":{"df":0,"docs":{},"j":{"df":0,"docs":{},"​":{">":{"1":{"df":1,"docs":{"142":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}}}},"df":0,"docs":{}},"df":0,"docs":{}},",":{"df":0,"docs":{},"j":{"df":0,"docs":{},"​":{"<":{"df":0,"docs":{},"l":{"0":{",":{"df":0,"docs":{},"j":{"df":1,"docs":{"142":{"tf":1.0}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":1,"docs":{"142":{"tf":1.0}}}},"df":0,"docs":{},"e":{"df":1,"docs":{"130":{"tf":1.0}}}},"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"df":14,"docs":{"0":{"tf":1.0},"104":{"tf":1.4142135623730951},"111":{"tf":1.0},"113":{"tf":1.0},"128":{"tf":1.0},"129":{"tf":1.4142135623730951},"133":{"tf":1.0},"145":{"tf":1.4142135623730951},"146":{"tf":2.0},"21":{"tf":1.0},"4":{"tf":1.0},"44":{"tf":1.0},"45":{"tf":1.0},"94":{"tf":1.0}},"l":{"df":0,"docs":{},"i":{"df":1,"docs":{"34":{"tf":1.0}}}},"t":{"a":{"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"df":1,"docs":{"13":{"tf":1.0}}}}},"df":0,"docs":{}}}},"j":{"df":0,"docs":{},"o":{"df":0,"docs":{},"r":{"df":3,"docs":{"108":{"tf":1.0},"147":{"tf":1.4142135623730951},"69":{"tf":1.0}}}}},"k":{"df":0,"docs":{},"e":{"df":15,"docs":{"125":{"tf":1.0},"146":{"tf":1.7320508075688772},"149":{"tf":1.0},"151":{"tf":1.0},"20":{"tf":1.0},"45":{"tf":1.0},"48":{"tf":1.0},"56":{"tf":1.0},"70":{"tf":1.0},"71":{"tf":1.0},"77":{"tf":1.0},"80":{"tf":1.0},"83":{"tf":1.0},"89":{"tf":1.0},"95":{"tf":1.0}}}},"l":{"df":0,"docs":{},"f":{"df":0,"docs":{},"o":{"df":0,"docs":{},"r":{"df":0,"docs":{},"m":{"df":1,"docs":{"11":{"tf":1.0}}}}}},"i":{"c":{"df":0,"docs":{},"i":{"df":1,"docs":{"77":{"tf":1.0}}}},"df":0,"docs":{}}},"n":{"df":0,"docs":{},"i":{"df":6,"docs":{"108":{"tf":1.0},"109":{"tf":1.0},"146":{"tf":1.7320508075688772},"20":{"tf":1.0},"70":{"tf":1.0},"98":{"tf":1.0}},"p":{"df":0,"docs":{},"u":{"df":0,"docs":{},"l":{"df":1,"docs":{"118":{"tf":1.0}}}}}}},"p":{"df":3,"docs":{"101":{"tf":1.0},"113":{"tf":1.4142135623730951},"14":{"tf":2.23606797749979}}},"r":{"df":0,"docs":{},"k":{"df":1,"docs":{"3":{"tf":1.0}}}},"t":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":0,"docs":{},"i":{"df":1,"docs":{"144":{"tf":1.4142135623730951}}}}},"h":{"/":{"df":0,"docs":{},"s":{"df":0,"docs":{},"r":{"c":{"/":{"df":0,"docs":{},"p":{"df":0,"docs":{},"o":{"df":0,"docs":{},"l":{"df":0,"docs":{},"y":{"df":0,"docs":{},"n":{"df":0,"docs":{},"o":{"df":0,"docs":{},"m":{"df":0,"docs":{},"i":{"a":{"df":0,"docs":{},"l":{".":{"df":0,"docs":{},"r":{"df":0,"docs":{},"s":{":":{"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"df":0,"docs":{},"t":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":0,"docs":{},"p":{"df":0,"docs":{},"o":{"df":0,"docs":{},"l":{"a":{"df":0,"docs":{},"t":{"df":0,"docs":{},"e":{"_":{"df":0,"docs":{},"f":{"df":0,"docs":{},"f":{"df":0,"docs":{},"t":{"df":1,"docs":{"3":{"tf":1.0}}}}}},"df":0,"docs":{}}}},"df":1,"docs":{"3":{"tf":1.0}}}}}}}}}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}}}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":2,"docs":{"1":{"tf":1.0},"146":{"tf":1.0}},"e":{"df":0,"docs":{},"m":{"a":{"df":0,"docs":{},"t":{"df":4,"docs":{"131":{"tf":1.0},"46":{"tf":1.0},"70":{"tf":1.0},"8":{"tf":1.0}}}},"df":0,"docs":{}}}},"r":{"df":0,"docs":{},"i":{"c":{"df":9,"docs":{"12":{"tf":1.0},"13":{"tf":2.0},"14":{"tf":1.7320508075688772},"147":{"tf":1.0},"16":{"tf":2.23606797749979},"34":{"tf":1.4142135623730951},"36":{"tf":1.4142135623730951},"8":{"tf":1.4142135623730951},"9":{"tf":1.4142135623730951}}},"df":0,"docs":{},"x":{"df":24,"docs":{"10":{"tf":2.23606797749979},"11":{"tf":2.449489742783178},"12":{"tf":1.7320508075688772},"13":{"tf":2.6457513110645907},"14":{"tf":2.0},"141":{"tf":1.0},"142":{"tf":2.23606797749979},"147":{"tf":2.8284271247461903},"16":{"tf":2.449489742783178},"20":{"tf":1.7320508075688772},"23":{"tf":1.4142135623730951},"26":{"tf":1.0},"32":{"tf":1.0},"34":{"tf":1.0},"35":{"tf":1.0},"36":{"tf":2.0},"5":{"tf":1.0},"69":{"tf":1.7320508075688772},"70":{"tf":1.0},"8":{"tf":1.4142135623730951},"83":{"tf":1.0},"9":{"tf":1.7320508075688772},"91":{"tf":2.0},"94":{"tf":1.4142135623730951}}}}},"t":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":3,"docs":{"14":{"tf":1.0},"146":{"tf":1.0},"16":{"tf":1.0}}}}}},"x":{"df":5,"docs":{"103":{"tf":1.0},"119":{"tf":1.0},"130":{"tf":1.0},"134":{"tf":1.0},"146":{"tf":1.0}},"i":{"df":0,"docs":{},"m":{"df":0,"docs":{},"u":{"df":0,"docs":{},"m":{"df":3,"docs":{"142":{"tf":1.0},"36":{"tf":1.0},"38":{"tf":1.4142135623730951}}}}}}}},"df":12,"docs":{"133":{"tf":1.4142135623730951},"14":{"tf":1.0},"141":{"tf":1.0},"142":{"tf":2.449489742783178},"146":{"tf":2.449489742783178},"20":{"tf":1.0},"5":{"tf":1.0},"70":{"tf":1.4142135623730951},"72":{"tf":1.0},"73":{"tf":1.7320508075688772},"75":{"tf":1.0},"91":{"tf":1.7320508075688772}},"e":{"a":{"df":0,"docs":{},"n":{"df":32,"docs":{"101":{"tf":1.0},"105":{"tf":1.0},"106":{"tf":1.0},"108":{"tf":1.0},"114":{"tf":1.0},"123":{"tf":1.0},"124":{"tf":1.4142135623730951},"127":{"tf":1.0},"131":{"tf":1.0},"133":{"tf":1.4142135623730951},"134":{"tf":1.0},"14":{"tf":1.7320508075688772},"142":{"tf":1.0},"146":{"tf":1.4142135623730951},"147":{"tf":1.0},"21":{"tf":1.0},"23":{"tf":1.0},"28":{"tf":1.0},"35":{"tf":1.0},"36":{"tf":1.0},"52":{"tf":1.0},"65":{"tf":1.4142135623730951},"66":{"tf":1.0},"68":{"tf":1.4142135623730951},"70":{"tf":1.0},"72":{"tf":1.0},"73":{"tf":1.0},"75":{"tf":1.0},"78":{"tf":1.0},"9":{"tf":1.0},"92":{"tf":1.0},"95":{"tf":1.0}}}},"c":{"df":0,"docs":{},"h":{"a":{"df":0,"docs":{},"n":{"df":1,"docs":{"4":{"tf":1.0}}}},"df":0,"docs":{}}},"df":0,"docs":{},"m":{"_":{"a":{"df":3,"docs":{"142":{"tf":2.0},"146":{"tf":1.0},"147":{"tf":1.4142135623730951}}},"df":0,"docs":{},"p":{"df":1,"docs":{"147":{"tf":1.0}}},"v":{"df":3,"docs":{"142":{"tf":1.7320508075688772},"146":{"tf":1.0},"147":{"tf":1.4142135623730951}}}},"df":3,"docs":{"133":{"tf":1.4142135623730951},"134":{"tf":1.7320508075688772},"135":{"tf":1.4142135623730951}},"o":{"df":0,"docs":{},"r":{"df":0,"docs":{},"i":{"df":11,"docs":{"127":{"tf":1.0},"133":{"tf":3.7416573867739413},"134":{"tf":2.8284271247461903},"135":{"tf":2.0},"141":{"tf":1.0},"142":{"tf":1.7320508075688772},"144":{"tf":2.23606797749979},"146":{"tf":5.5677643628300215},"147":{"tf":2.8284271247461903},"149":{"tf":1.0},"151":{"tf":3.1622776601683795}}},"y":{"_":{"c":{"df":0,"docs":{},"e":{"df":0,"docs":{},"l":{"df":1,"docs":{"134":{"tf":1.0}},"l":{".":{"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"df":0,"docs":{},"l":{"df":1,"docs":{"134":{"tf":1.0}}}}}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"n":{"df":0,"docs":{},"t":{"a":{"df":0,"docs":{},"l":{"df":1,"docs":{"50":{"tf":1.0}}}},"df":0,"docs":{},"i":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"df":14,"docs":{"142":{"tf":1.0},"146":{"tf":1.4142135623730951},"15":{"tf":1.0},"35":{"tf":1.0},"37":{"tf":1.0},"51":{"tf":1.0},"55":{"tf":1.0},"58":{"tf":1.0},"7":{"tf":1.0},"70":{"tf":1.0},"77":{"tf":1.0},"83":{"tf":1.0},"84":{"tf":1.0},"92":{"tf":1.0}}}}}}},"r":{"df":0,"docs":{},"g":{"df":1,"docs":{"12":{"tf":1.0}}},"k":{"df":0,"docs":{},"l":{"df":13,"docs":{"101":{"tf":1.7320508075688772},"110":{"tf":1.0},"112":{"tf":1.4142135623730951},"113":{"tf":1.4142135623730951},"119":{"tf":1.0},"60":{"tf":1.4142135623730951},"62":{"tf":1.4142135623730951},"63":{"tf":1.4142135623730951},"72":{"tf":1.4142135623730951},"76":{"tf":1.0},"80":{"tf":2.449489742783178},"91":{"tf":1.0},"92":{"tf":2.23606797749979}}}}},"s":{"df":0,"docs":{},"s":{"a":{"df":0,"docs":{},"g":{"df":2,"docs":{"40":{"tf":1.4142135623730951},"42":{"tf":1.0}},"e":{"_":{"1":{"df":1,"docs":{"40":{"tf":2.23606797749979}}},"2":{"df":1,"docs":{"40":{"tf":1.4142135623730951}}},"3":{"df":1,"docs":{"40":{"tf":1.0}}},"4":{"df":1,"docs":{"40":{"tf":1.4142135623730951}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"t":{"df":1,"docs":{"121":{"tf":1.0}},"h":{"df":0,"docs":{},"o":{"d":{"df":12,"docs":{"105":{"tf":1.0},"106":{"tf":1.4142135623730951},"107":{"tf":1.7320508075688772},"113":{"tf":1.0},"114":{"tf":2.23606797749979},"117":{"tf":1.0},"123":{"tf":1.0},"14":{"tf":1.4142135623730951},"3":{"tf":1.7320508075688772},"35":{"tf":1.4142135623730951},"40":{"tf":1.7320508075688772},"70":{"tf":1.0}}},"df":0,"docs":{}}}}},"i":{"+":{"1":{"df":0,"docs":{},"​":{"=":{"df":0,"docs":{},"m":{"df":0,"docs":{},"i":{"df":0,"docs":{},"​":{"+":{"1":{"df":1,"docs":{"151":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}}},"df":0,"docs":{}},",":{"df":0,"docs":{},"j":{"df":1,"docs":{"5":{"tf":1.0}}}},"d":{"d":{"df":0,"docs":{},"l":{"df":1,"docs":{"35":{"tf":1.0}}}},"df":0,"docs":{}},"df":1,"docs":{"151":{"tf":1.0}},"l":{"df":0,"docs":{},"l":{"df":0,"docs":{},"i":{"df":0,"docs":{},"s":{"df":0,"docs":{},"e":{"c":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"d":{"df":1,"docs":{"3":{"tf":1.0}}},"df":0,"docs":{}}}},"df":0,"docs":{}}}}}},"n":{"d":{"df":4,"docs":{"124":{"tf":1.0},"146":{"tf":1.0},"49":{"tf":1.0},"50":{"tf":1.0}}},"df":0,"docs":{},"i":{"df":0,"docs":{},"m":{"df":0,"docs":{},"u":{"df":0,"docs":{},"m":{"df":1,"docs":{"142":{"tf":1.0}}}}}},"u":{"df":0,"docs":{},"t":{"df":1,"docs":{"14":{"tf":1.0}}}}},"s":{"df":0,"docs":{},"s":{"df":4,"docs":{"128":{"tf":1.0},"130":{"tf":1.0},"146":{"tf":1.0},"151":{"tf":1.0}}}},"x":{"df":2,"docs":{"150":{"tf":1.0},"66":{"tf":1.0}}}},"j":{"df":1,"docs":{"94":{"tf":1.0}}},"m":{"df":0,"docs":{},"e":{"df":0,"docs":{},"m":{"df":1,"docs":{"147":{"tf":1.0}},"r":{"a":{"df":0,"docs":{},"p":{"2":{"df":1,"docs":{"147":{"tf":1.4142135623730951}}},"df":0,"docs":{}}},"df":0,"docs":{},"e":{"df":0,"docs":{},"p":{"df":0,"docs":{},"l":{"df":1,"docs":{"147":{"tf":1.0}},"s":{"df":0,"docs":{},"o":{"df":0,"docs":{},"r":{"df":0,"docs":{},"t":{"df":1,"docs":{"147":{"tf":1.4142135623730951}}}}}},"​":{"df":0,"docs":{},"∈":{"df":0,"docs":{},"f":{"2":{"df":0,"docs":{},"n":{"+":{"2":{"df":0,"docs":{},"×":{"2":{"df":1,"docs":{"147":{"tf":1.0}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}}}}}},"​":{"df":0,"docs":{},"∈":{"df":0,"docs":{},"f":{"2":{"df":0,"docs":{},"n":{"+":{"2":{"df":0,"docs":{},"×":{"2":{"df":1,"docs":{"147":{"tf":1.0}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}}}}},"o":{"d":{"df":0,"docs":{},"e":{"df":1,"docs":{"146":{"tf":1.4142135623730951}},"l":{"df":1,"docs":{"50":{"tf":1.0}}},"r":{"df":0,"docs":{},"n":{"df":1,"docs":{"42":{"tf":1.0}}}}},"i":{"df":0,"docs":{},"f":{"df":0,"docs":{},"i":{"df":4,"docs":{"102":{"tf":1.0},"146":{"tf":1.0},"45":{"tf":1.0},"59":{"tf":1.4142135623730951}}}}},"u":{"df":0,"docs":{},"l":{"df":1,"docs":{"38":{"tf":1.0}},"o":{"df":1,"docs":{"41":{"tf":1.0}}}}}},"df":0,"docs":{},"f":{"df":0,"docs":{},"f":{"df":0,"docs":{},"s":{"df":0,"docs":{},"e":{"df":0,"docs":{},"t":{"df":1,"docs":{"142":{"tf":1.4142135623730951}}}}}}},"m":{"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"df":0,"docs":{},"t":{"df":5,"docs":{"10":{"tf":1.0},"19":{"tf":1.0},"34":{"tf":1.0},"70":{"tf":1.0},"79":{"tf":1.0}}}}}},"r":{"df":0,"docs":{},"e":{"df":27,"docs":{"103":{"tf":1.0},"114":{"tf":1.7320508075688772},"118":{"tf":1.0},"12":{"tf":1.0},"130":{"tf":1.0},"133":{"tf":1.0},"135":{"tf":1.0},"14":{"tf":2.0},"146":{"tf":1.7320508075688772},"150":{"tf":1.0},"151":{"tf":2.0},"16":{"tf":1.0},"21":{"tf":1.0},"34":{"tf":1.0},"41":{"tf":1.4142135623730951},"42":{"tf":1.0},"45":{"tf":1.0},"64":{"tf":1.0},"66":{"tf":1.0},"68":{"tf":1.0},"7":{"tf":1.0},"73":{"tf":1.4142135623730951},"74":{"tf":1.0},"80":{"tf":1.0},"82":{"tf":1.0},"83":{"tf":1.0},"84":{"tf":1.4142135623730951}},"o":{"df":0,"docs":{},"v":{"df":1,"docs":{"14":{"tf":1.0}}}}}},"v":{"df":0,"docs":{},"e":{"df":4,"docs":{"128":{"tf":1.4142135623730951},"149":{"tf":1.0},"50":{"tf":1.0},"53":{"tf":1.0}},"m":{"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"df":0,"docs":{},"t":{"df":1,"docs":{"149":{"tf":1.0}}}}}}}}},"r":{"a":{"df":0,"docs":{},"n":{"df":0,"docs":{},"g":{"df":0,"docs":{},"e":{"c":{"df":0,"docs":{},"h":{"df":0,"docs":{},"e":{"c":{"df":0,"docs":{},"k":{"df":0,"docs":{},"r":{"a":{"df":0,"docs":{},"p":{"2":{"df":1,"docs":{"147":{"tf":1.4142135623730951}}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}}}},"df":0,"docs":{}}}},"p":{"2":{"df":3,"docs":{"147":{"tf":1.0},"83":{"tf":1.0},"94":{"tf":1.0}},"​":{"df":0,"docs":{},"∈":{"df":0,"docs":{},"f":{"2":{"df":0,"docs":{},"n":{"df":0,"docs":{},"×":{"df":0,"docs":{},"m":{"df":1,"docs":{"94":{"tf":1.0}}}}}},"df":0,"docs":{}}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"s":{"df":1,"docs":{"3":{"tf":4.0}}},"u":{"c":{"df":0,"docs":{},"h":{"df":6,"docs":{"116":{"tf":1.0},"123":{"tf":1.4142135623730951},"125":{"tf":1.0},"21":{"tf":1.0},"37":{"tf":1.0},"66":{"tf":1.0}}}},"df":0,"docs":{},"l":{"df":3,"docs":{"142":{"tf":1.0},"146":{"tf":1.7320508075688772},"147":{"tf":1.0}},"t":{"df":0,"docs":{},"i":{"df":1,"docs":{"139":{"tf":1.4142135623730951}},"p":{"df":0,"docs":{},"l":{"df":16,"docs":{"10":{"tf":1.4142135623730951},"101":{"tf":1.0},"106":{"tf":1.0},"107":{"tf":1.0},"12":{"tf":1.0},"14":{"tf":1.0},"142":{"tf":1.4142135623730951},"149":{"tf":1.4142135623730951},"151":{"tf":1.0},"44":{"tf":1.0},"65":{"tf":2.0},"66":{"tf":2.0},"7":{"tf":1.0},"9":{"tf":1.0},"92":{"tf":1.0},"98":{"tf":1.0}},"i":{"df":4,"docs":{"125":{"tf":1.0},"14":{"tf":1.0},"45":{"tf":1.0},"52":{"tf":1.0}}}}},"v":{"a":{"df":0,"docs":{},"r":{"df":0,"docs":{},"i":{"df":2,"docs":{"70":{"tf":1.0},"84":{"tf":1.0}}}}},"df":0,"docs":{}}}}},"t":{"a":{"df":0,"docs":{},"t":{"df":1,"docs":{"118":{"tf":1.4142135623730951}}}},"df":3,"docs":{"114":{"tf":1.0},"44":{"tf":1.0},"45":{"tf":1.7320508075688772}}}},"′":{",":{"df":0,"docs":{},"v":{"df":1,"docs":{"146":{"tf":1.0}}}},"=":{"3":{"3":{"df":1,"docs":{"147":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{},"′":{"=":{"1":{"8":{"df":1,"docs":{"147":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"∈":{"df":0,"docs":{},"f":{"(":{"df":0,"docs":{},"l":{"+":{"df":0,"docs":{},"l":{"df":0,"docs":{},"′":{")":{"df":0,"docs":{},"×":{"3":{"3":{"df":1,"docs":{"142":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}}},"+":{"df":0,"docs":{},"l":{"df":0,"docs":{},"′":{"df":0,"docs":{},"′":{")":{"df":0,"docs":{},"×":{"3":{"3":{"df":1,"docs":{"142":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{},"l":{"df":0,"docs":{},"×":{"3":{"3":{"df":1,"docs":{"142":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"−":{"1":{"df":2,"docs":{"73":{"tf":2.23606797749979},"79":{"tf":1.0}}},"df":0,"docs":{}},"≥":{"df":0,"docs":{},"n":{"df":1,"docs":{"20":{"tf":1.0}}}}},"n":{"+":{"1":{"df":1,"docs":{"26":{"tf":1.0}}},"df":0,"docs":{},"m":{"+":{"1":{")":{"df":0,"docs":{},"×":{"3":{"df":1,"docs":{"14":{"tf":1.0}}},"5":{"df":1,"docs":{"14":{"tf":1.0}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"<":{"df":0,"docs":{},"m":{"df":3,"docs":{"73":{"tf":1.0},"75":{"tf":1.0},"80":{"tf":1.0}}}},"=":{"2":{"df":0,"docs":{},"n":{"df":3,"docs":{"73":{"tf":1.0},"75":{"tf":1.0},"80":{"tf":1.0}}}},"3":{"df":1,"docs":{"51":{"tf":1.0}}},"df":0,"docs":{},"n":{"+":{"df":0,"docs":{},"m":{"+":{"1":{"df":1,"docs":{"14":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"a":{"df":0,"docs":{},"i":{"df":0,"docs":{},"v":{"df":3,"docs":{"101":{"tf":1.0},"123":{"tf":1.0},"3":{"tf":1.0}}}},"m":{"df":0,"docs":{},"e":{"df":3,"docs":{"102":{"tf":1.0},"13":{"tf":1.0},"130":{"tf":1.0}}}},"r":{"df":0,"docs":{},"r":{"df":0,"docs":{},"o":{"df":0,"docs":{},"w":{"df":1,"docs":{"50":{"tf":1.0}}}}}},"t":{"df":0,"docs":{},"u":{"df":0,"docs":{},"r":{"df":2,"docs":{"101":{"tf":1.0},"84":{"tf":1.0}}}}}},"c":{"df":1,"docs":{"28":{"tf":1.0}}},"df":14,"docs":{"124":{"tf":2.23606797749979},"127":{"tf":1.0},"14":{"tf":2.449489742783178},"151":{"tf":1.0},"16":{"tf":1.0},"20":{"tf":1.0},"23":{"tf":1.4142135623730951},"32":{"tf":1.0},"35":{"tf":1.7320508075688772},"68":{"tf":1.4142135623730951},"75":{"tf":1.4142135623730951},"78":{"tf":1.4142135623730951},"79":{"tf":1.0},"91":{"tf":1.4142135623730951}},"e":{"a":{"df":0,"docs":{},"r":{"df":0,"docs":{},"e":{"df":0,"docs":{},"s":{"df":0,"docs":{},"t":{"df":1,"docs":{"142":{"tf":1.0}}}}}}},"c":{"df":0,"docs":{},"e":{"df":0,"docs":{},"s":{"df":0,"docs":{},"s":{"a":{"df":0,"docs":{},"r":{"df":0,"docs":{},"i":{"df":6,"docs":{"103":{"tf":1.0},"110":{"tf":1.0},"112":{"tf":1.0},"13":{"tf":1.0},"133":{"tf":1.0},"33":{"tf":1.0}},"l":{"df":0,"docs":{},"i":{"df":1,"docs":{"14":{"tf":1.0}}}}}}},"df":0,"docs":{}}}}},"df":0,"docs":{},"e":{"d":{"df":42,"docs":{"102":{"tf":1.4142135623730951},"105":{"tf":1.0},"107":{"tf":2.0},"109":{"tf":1.0},"111":{"tf":1.4142135623730951},"114":{"tf":2.0},"117":{"tf":1.4142135623730951},"118":{"tf":1.4142135623730951},"119":{"tf":1.4142135623730951},"123":{"tf":1.0},"125":{"tf":1.0},"130":{"tf":1.0},"133":{"tf":1.0},"14":{"tf":1.4142135623730951},"144":{"tf":1.0},"146":{"tf":3.3166247903554},"149":{"tf":1.0},"151":{"tf":1.7320508075688772},"19":{"tf":1.0},"20":{"tf":1.0},"21":{"tf":1.0},"32":{"tf":2.0},"34":{"tf":1.0},"35":{"tf":1.4142135623730951},"36":{"tf":1.4142135623730951},"44":{"tf":1.4142135623730951},"48":{"tf":1.0},"5":{"tf":1.0},"57":{"tf":1.0},"58":{"tf":1.4142135623730951},"59":{"tf":1.0},"60":{"tf":1.7320508075688772},"68":{"tf":1.7320508075688772},"69":{"tf":1.0},"70":{"tf":1.7320508075688772},"74":{"tf":1.4142135623730951},"75":{"tf":1.0},"8":{"tf":1.4142135623730951},"80":{"tf":1.7320508075688772},"82":{"tf":1.0},"88":{"tf":1.0},"9":{"tf":1.0}}},"df":0,"docs":{}},"g":{"df":0,"docs":{},"l":{"df":0,"docs":{},"i":{"df":0,"docs":{},"g":{"df":1,"docs":{"73":{"tf":1.0}}}}}},"v":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":1,"docs":{"95":{"tf":1.0}}}}},"w":{"_":{"df":0,"docs":{},"s":{"df":0,"docs":{},"i":{"df":0,"docs":{},"m":{"df":0,"docs":{},"p":{"df":0,"docs":{},"l":{"df":1,"docs":{"106":{"tf":1.0}}}}}}}},"df":10,"docs":{"106":{"tf":1.0},"13":{"tf":1.0},"14":{"tf":1.0},"142":{"tf":1.0},"146":{"tf":1.7320508075688772},"20":{"tf":1.4142135623730951},"59":{"tf":1.0},"68":{"tf":1.0},"83":{"tf":1.4142135623730951},"94":{"tf":1.0}}},"x":{"df":0,"docs":{},"t":{"df":15,"docs":{"10":{"tf":1.0},"102":{"tf":1.4142135623730951},"118":{"tf":1.0},"128":{"tf":1.0},"134":{"tf":1.4142135623730951},"14":{"tf":1.4142135623730951},"144":{"tf":1.0},"149":{"tf":1.0},"23":{"tf":1.0},"35":{"tf":1.0},"49":{"tf":1.0},"66":{"tf":1.0},"74":{"tf":1.0},"77":{"tf":1.0},"9":{"tf":1.0}}}}},"i":{"c":{"df":0,"docs":{},"e":{"df":1,"docs":{"14":{"tf":1.0}}}},"df":0,"docs":{}},"o":{"df":0,"docs":{},"n":{"c":{"df":2,"docs":{"119":{"tf":1.0},"88":{"tf":1.4142135623730951}}},"df":1,"docs":{"28":{"tf":1.4142135623730951}},"i":{"df":0,"docs":{},"n":{"df":0,"docs":{},"t":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"a":{"c":{"df":0,"docs":{},"t":{"df":1,"docs":{"89":{"tf":1.0}}}},"df":0,"docs":{}},"df":0,"docs":{}}}}}},"z":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":0,"docs":{},"o":{"df":2,"docs":{"73":{"tf":1.0},"75":{"tf":1.0}}}}}}},"t":{"a":{"df":0,"docs":{},"t":{"df":11,"docs":{"142":{"tf":1.0},"147":{"tf":1.0},"34":{"tf":1.0},"35":{"tf":1.0},"5":{"tf":1.7320508075688772},"70":{"tf":1.4142135623730951},"87":{"tf":1.0},"9":{"tf":1.0},"90":{"tf":1.7320508075688772},"92":{"tf":2.0},"95":{"tf":1.4142135623730951}}}},"df":0,"docs":{},"e":{"df":14,"docs":{"104":{"tf":1.0},"125":{"tf":1.7320508075688772},"128":{"tf":1.0},"131":{"tf":1.0},"146":{"tf":1.0},"44":{"tf":1.0},"68":{"tf":1.0},"74":{"tf":1.0},"79":{"tf":1.0},"80":{"tf":1.4142135623730951},"84":{"tf":1.0},"92":{"tf":1.0},"95":{"tf":1.0},"96":{"tf":1.4142135623730951}}},"h":{"df":1,"docs":{"114":{"tf":1.0}}},"i":{"c":{"df":8,"docs":{"10":{"tf":1.0},"114":{"tf":1.0},"118":{"tf":1.0},"13":{"tf":1.0},"14":{"tf":1.4142135623730951},"21":{"tf":1.0},"68":{"tf":1.0},"7":{"tf":1.0}}},"df":0,"docs":{}}},"w":{"df":17,"docs":{"11":{"tf":1.4142135623730951},"12":{"tf":1.0},"121":{"tf":1.0},"123":{"tf":1.0},"13":{"tf":1.4142135623730951},"14":{"tf":2.23606797749979},"146":{"tf":1.0},"149":{"tf":1.7320508075688772},"150":{"tf":1.0},"33":{"tf":1.0},"44":{"tf":1.0},"58":{"tf":1.0},"65":{"tf":1.0},"66":{"tf":1.0},"7":{"tf":1.0},"77":{"tf":1.0},"83":{"tf":1.0}}}},"u":{"df":0,"docs":{},"m":{"_":{"c":{"df":0,"docs":{},"o":{"df":0,"docs":{},"l":{"df":1,"docs":{"108":{"tf":1.7320508075688772}}}}},"df":0,"docs":{},"t":{"df":0,"docs":{},"r":{"a":{"df":0,"docs":{},"n":{"df":0,"docs":{},"s":{"df":0,"docs":{},"i":{"df":0,"docs":{},"t":{"df":0,"docs":{},"i":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"_":{"c":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"df":0,"docs":{},"s":{"df":0,"docs":{},"t":{"df":0,"docs":{},"r":{"a":{"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"df":0,"docs":{},"t":{"df":1,"docs":{"109":{"tf":1.4142135623730951}}}}}},"df":0,"docs":{}}}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}}}}}}},"df":0,"docs":{}}}},"b":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":25,"docs":{"106":{"tf":1.0},"109":{"tf":2.0},"118":{"tf":1.7320508075688772},"119":{"tf":1.0},"124":{"tf":1.4142135623730951},"125":{"tf":1.0},"128":{"tf":1.4142135623730951},"133":{"tf":1.0},"142":{"tf":1.0},"144":{"tf":1.0},"146":{"tf":1.4142135623730951},"147":{"tf":1.0},"149":{"tf":1.0},"151":{"tf":1.7320508075688772},"21":{"tf":1.0},"32":{"tf":1.0},"35":{"tf":1.0},"49":{"tf":2.449489742783178},"50":{"tf":1.0},"56":{"tf":1.0},"59":{"tf":1.0},"69":{"tf":1.0},"73":{"tf":1.4142135623730951},"88":{"tf":1.0},"91":{"tf":2.6457513110645907}}}}},"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":4,"docs":{"114":{"tf":1.0},"123":{"tf":1.0},"125":{"tf":1.4142135623730951},"55":{"tf":1.0}}}}}},"×":{"1":{"df":2,"docs":{"14":{"tf":1.0},"16":{"tf":1.4142135623730951}}},"3":{"df":3,"docs":{"14":{"tf":1.0},"16":{"tf":1.4142135623730951},"23":{"tf":1.0}}},"df":0,"docs":{}},"−":{"1":{")":{"df":0,"docs":{},"t":{"df":1,"docs":{"33":{"tf":1.0}}}},"df":7,"docs":{"14":{"tf":1.0},"20":{"tf":1.0},"73":{"tf":1.4142135623730951},"74":{"tf":1.4142135623730951},"78":{"tf":1.4142135623730951},"79":{"tf":1.0},"80":{"tf":1.0}}},"df":0,"docs":{}}},"o":{"b":{"df":0,"docs":{},"j":{"df":0,"docs":{},"e":{"c":{"df":0,"docs":{},"t":{"df":2,"docs":{"87":{"tf":1.0},"89":{"tf":1.0}}}},"df":0,"docs":{}}},"s":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":0,"docs":{},"v":{"df":3,"docs":{"13":{"tf":1.0},"151":{"tf":1.0},"7":{"tf":1.0}}}}}},"t":{"a":{"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"df":14,"docs":{"114":{"tf":1.4142135623730951},"124":{"tf":1.0},"14":{"tf":1.7320508075688772},"142":{"tf":1.7320508075688772},"146":{"tf":1.7320508075688772},"32":{"tf":1.0},"34":{"tf":1.0},"44":{"tf":1.4142135623730951},"7":{"tf":1.0},"83":{"tf":1.0},"84":{"tf":1.4142135623730951},"91":{"tf":1.0},"94":{"tf":1.7320508075688772},"95":{"tf":1.0}}}}},"df":0,"docs":{}},"v":{"df":0,"docs":{},"i":{"df":0,"docs":{},"o":{"df":0,"docs":{},"u":{"df":3,"docs":{"107":{"tf":1.0},"14":{"tf":1.0},"16":{"tf":1.0}}}}}}},"c":{"c":{"df":0,"docs":{},"u":{"df":0,"docs":{},"r":{"df":1,"docs":{"41":{"tf":1.0}},"r":{"df":1,"docs":{"14":{"tf":1.7320508075688772}}}}}},"df":0,"docs":{}},"d":{"d":{"df":4,"docs":{"119":{"tf":1.0},"67":{"tf":1.0},"7":{"tf":1.0},"94":{"tf":1.0}}},"df":0,"docs":{}},"df":4,"docs":{"11":{"tf":1.0},"12":{"tf":1.0},"13":{"tf":1.0},"14":{"tf":1.0}},"f":{"df":0,"docs":{},"f":{"0":{"df":1,"docs":{"130":{"tf":1.0}}},"1":{"df":1,"docs":{"130":{"tf":1.0}}},"2":{"df":1,"docs":{"130":{"tf":1.0}}},"_":{"d":{"df":0,"docs":{},"s":{"df":0,"docs":{},"t":{"df":4,"docs":{"130":{"tf":1.4142135623730951},"142":{"tf":1.0},"146":{"tf":1.7320508075688772},"147":{"tf":1.0}}}}},"df":0,"docs":{},"o":{"df":0,"docs":{},"p":{"0":{"df":4,"docs":{"130":{"tf":1.4142135623730951},"142":{"tf":1.0},"146":{"tf":1.7320508075688772},"147":{"tf":1.0}}},"1":{"df":4,"docs":{"130":{"tf":1.4142135623730951},"142":{"tf":1.0},"146":{"tf":1.7320508075688772},"147":{"tf":1.0}}},"df":0,"docs":{}}}},"df":0,"docs":{},"s":{"df":0,"docs":{},"e":{"df":0,"docs":{},"t":{"df":12,"docs":{"109":{"tf":1.0},"125":{"tf":2.23606797749979},"128":{"tf":1.0},"130":{"tf":2.23606797749979},"133":{"tf":3.4641016151377544},"134":{"tf":1.0},"136":{"tf":2.449489742783178},"139":{"tf":1.4142135623730951},"142":{"tf":1.4142135623730951},"146":{"tf":2.23606797749979},"147":{"tf":1.7320508075688772},"151":{"tf":1.0}},"s":{"/":{"df":0,"docs":{},"r":{"a":{"df":0,"docs":{},"n":{"df":0,"docs":{},"g":{"df":1,"docs":{"146":{"tf":1.0}}}}},"df":0,"docs":{}}},"_":{"b":{"df":1,"docs":{"147":{"tf":1.0}}},"df":0,"docs":{},"p":{"df":1,"docs":{"147":{"tf":1.0}}}},"df":0,"docs":{}}}}}}},"m":{"df":0,"docs":{},"e":{"df":0,"docs":{},"g":{"a":{"^":{"2":{"df":1,"docs":{"68":{"tf":1.4142135623730951}}},"df":0,"docs":{},"i":{"df":1,"docs":{"68":{"tf":1.4142135623730951}}},"{":{"1":{"6":{"df":1,"docs":{"68":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":2,"docs":{"35":{"tf":1.0},"68":{"tf":1.7320508075688772}}},"df":0,"docs":{}}},"i":{"df":0,"docs":{},"s":{"df":0,"docs":{},"s":{"df":1,"docs":{"64":{"tf":1.4142135623730951}}}}}},"n":{"c":{"df":7,"docs":{"111":{"tf":1.0},"134":{"tf":1.0},"44":{"tf":1.0},"67":{"tf":1.0},"8":{"tf":1.0},"80":{"tf":1.4142135623730951},"98":{"tf":1.0}}},"df":47,"docs":{"10":{"tf":1.0},"101":{"tf":1.0},"102":{"tf":1.0},"105":{"tf":1.0},"106":{"tf":1.0},"107":{"tf":2.0},"109":{"tf":1.0},"11":{"tf":1.4142135623730951},"114":{"tf":2.0},"118":{"tf":1.0},"123":{"tf":1.0},"124":{"tf":1.0},"125":{"tf":1.4142135623730951},"13":{"tf":2.0},"130":{"tf":1.0},"131":{"tf":1.0},"132":{"tf":1.0},"133":{"tf":1.0},"135":{"tf":1.0},"136":{"tf":1.0},"14":{"tf":2.449489742783178},"142":{"tf":1.0},"145":{"tf":1.0},"146":{"tf":3.3166247903554},"149":{"tf":2.23606797749979},"151":{"tf":1.7320508075688772},"19":{"tf":1.0},"21":{"tf":1.0},"22":{"tf":1.0},"34":{"tf":1.0},"49":{"tf":2.23606797749979},"5":{"tf":1.0},"50":{"tf":1.0},"51":{"tf":1.0},"52":{"tf":1.4142135623730951},"57":{"tf":1.0},"59":{"tf":1.4142135623730951},"65":{"tf":2.0},"66":{"tf":1.7320508075688772},"68":{"tf":1.0},"70":{"tf":1.0},"73":{"tf":1.4142135623730951},"80":{"tf":2.0},"84":{"tf":2.0},"9":{"tf":1.7320508075688772},"92":{"tf":1.0},"99":{"tf":1.0}},"w":{"a":{"df":0,"docs":{},"r":{"d":{"df":1,"docs":{"52":{"tf":1.0}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"o":{"d":{"df":2,"docs":{"113":{"tf":1.0},"117":{"tf":1.4142135623730951}}},"df":0,"docs":{}},"p":{"0":{"_":{"a":{"d":{"d":{"df":0,"docs":{},"r":{"[":{"0":{"df":1,"docs":{"147":{"tf":1.0}}},"df":0,"docs":{}},"df":3,"docs":{"142":{"tf":1.0},"146":{"tf":2.0},"147":{"tf":1.4142135623730951}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{},"r":{"df":0,"docs":{},"e":{"df":0,"docs":{},"g":{"df":1,"docs":{"146":{"tf":1.0}}}}}},"df":4,"docs":{"133":{"tf":2.0},"142":{"tf":1.0},"146":{"tf":2.0},"147":{"tf":1.4142135623730951}},"r":{"df":0,"docs":{},"e":{"df":0,"docs":{},"g":{"df":1,"docs":{"149":{"tf":1.0}}}}}},"1":{"_":{"a":{"d":{"d":{"df":0,"docs":{},"r":{"[":{"0":{"df":1,"docs":{"147":{"tf":1.0}}},"df":0,"docs":{}},"df":3,"docs":{"142":{"tf":1.0},"146":{"tf":1.7320508075688772},"147":{"tf":1.4142135623730951}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{},"s":{"df":0,"docs":{},"r":{"c":{"df":1,"docs":{"146":{"tf":1.0}}},"df":0,"docs":{}}}},"df":4,"docs":{"133":{"tf":2.0},"142":{"tf":1.0},"146":{"tf":2.0},"147":{"tf":1.4142135623730951}}},"c":{"df":0,"docs":{},"o":{"d":{"df":2,"docs":{"146":{"tf":1.7320508075688772},"149":{"tf":1.0}}},"df":0,"docs":{}}},"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"(":{"a":{",":{"a":{"b":{"df":0,"docs":{},"i":{"df":1,"docs":{"92":{"tf":1.0}}}},"df":0,"docs":{}},"df":0,"docs":{},"i":{"df":2,"docs":{"92":{"tf":1.4142135623730951},"95":{"tf":1.0}}}},"df":0,"docs":{}},"df":0,"docs":{},"f":{"df":1,"docs":{"19":{"tf":1.0}}},"h":{"1":{"df":0,"docs":{},"​":{"(":{"d":{"df":0,"docs":{},"l":{"d":{"df":0,"docs":{},"e":{"df":0,"docs":{},"​":{")":{",":{"df":0,"docs":{},"υ":{"df":1,"docs":{"94":{"tf":1.0}}},"−":{"df":0,"docs":{},"υ":{"df":1,"docs":{"94":{"tf":1.0}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"2":{"df":0,"docs":{},"​":{"(":{"d":{"df":0,"docs":{},"l":{"d":{"df":0,"docs":{},"e":{"df":0,"docs":{},"​":{")":{",":{"df":0,"docs":{},"υ":{"df":1,"docs":{"94":{"tf":1.0}}},"−":{"df":0,"docs":{},"υ":{"df":1,"docs":{"94":{"tf":1.0}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"p":{"(":{"d":{")":{",":{"df":0,"docs":{},"h":{"df":0,"docs":{},"ω":{"df":0,"docs":{},"i":{"df":1,"docs":{"101":{"tf":1.0}}}}},"−":{"df":0,"docs":{},"h":{"df":0,"docs":{},"ω":{"df":0,"docs":{},"i":{"df":1,"docs":{"101":{"tf":1.0}}}}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{},"k":{"df":0,"docs":{},"​":{"(":{"d":{"df":0,"docs":{},"k":{"df":0,"docs":{},"​":{")":{",":{"df":0,"docs":{},"υ":{"df":0,"docs":{},"s":{"2":{"df":0,"docs":{},"k":{"df":1,"docs":{"94":{"tf":1.0}}}},"df":0,"docs":{}}},"−":{"df":0,"docs":{},"υ":{"df":0,"docs":{},"s":{"2":{"df":0,"docs":{},"k":{"df":1,"docs":{"94":{"tf":1.0}}}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"t":{"df":0,"docs":{},"j":{"df":0,"docs":{},"​":{"(":{"d":{"df":0,"docs":{},"l":{"d":{"df":0,"docs":{},"e":{"df":0,"docs":{},"​":{")":{",":{"df":0,"docs":{},"υ":{"df":2,"docs":{"94":{"tf":1.0},"95":{"tf":1.0}}},"−":{"df":0,"docs":{},"υ":{"df":1,"docs":{"94":{"tf":1.0}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":17,"docs":{"101":{"tf":1.4142135623730951},"102":{"tf":1.0},"119":{"tf":1.0},"19":{"tf":1.0},"21":{"tf":2.8284271247461903},"28":{"tf":1.4142135623730951},"33":{"tf":1.4142135623730951},"35":{"tf":1.4142135623730951},"69":{"tf":1.0},"71":{"tf":1.0},"72":{"tf":1.0},"75":{"tf":2.0},"77":{"tf":1.4142135623730951},"80":{"tf":2.23606797749979},"86":{"tf":1.7320508075688772},"94":{"tf":1.0},"95":{"tf":1.7320508075688772}}},"r":{"a":{"df":0,"docs":{},"n":{"d":{"df":3,"docs":{"11":{"tf":1.0},"20":{"tf":1.0},"9":{"tf":1.4142135623730951}}},"df":0,"docs":{}}},"df":5,"docs":{"13":{"tf":1.0},"45":{"tf":2.0},"50":{"tf":1.0},"92":{"tf":2.23606797749979},"99":{"tf":1.0}}}},"s":{"_":{"df":0,"docs":{},"m":{"df":0,"docs":{},"u":{"df":0,"docs":{},"l":{"df":1,"docs":{"136":{"tf":1.4142135623730951}}}}}},"df":0,"docs":{}},"t":{"df":0,"docs":{},"i":{"df":0,"docs":{},"m":{"df":9,"docs":{"12":{"tf":1.0},"136":{"tf":1.0},"146":{"tf":1.4142135623730951},"21":{"tf":1.0},"67":{"tf":1.0},"80":{"tf":1.0},"94":{"tf":1.7320508075688772},"96":{"tf":1.4142135623730951},"99":{"tf":1.0}}},"o":{"df":0,"docs":{},"n":{"df":4,"docs":{"104":{"tf":2.0},"109":{"tf":1.4142135623730951},"110":{"tf":1.0},"91":{"tf":1.0}}}}}}},"r":{"d":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"_":{"df":0,"docs":{},"r":{"_":{"df":0,"docs":{},"m":{"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"df":0,"docs":{},"u":{"df":0,"docs":{},"s":{"_":{"1":{"_":{"df":0,"docs":{},"r":{"df":0,"docs":{},"o":{"df":0,"docs":{},"o":{"df":0,"docs":{},"t":{"_":{"df":0,"docs":{},"u":{"df":0,"docs":{},"n":{"df":1,"docs":{"44":{"tf":1.0}}}}},"df":0,"docs":{}}}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}}}}},"df":0,"docs":{}}},"df":16,"docs":{"101":{"tf":2.23606797749979},"118":{"tf":1.0},"130":{"tf":1.0},"132":{"tf":1.0},"134":{"tf":1.0},"135":{"tf":1.0},"142":{"tf":1.4142135623730951},"146":{"tf":2.449489742783178},"147":{"tf":1.4142135623730951},"20":{"tf":1.0},"36":{"tf":1.0},"38":{"tf":1.7320508075688772},"41":{"tf":1.7320508075688772},"45":{"tf":1.0},"94":{"tf":1.4142135623730951},"95":{"tf":1.0}}}}},"df":0,"docs":{},"g":{"a":{"df":0,"docs":{},"n":{"df":2,"docs":{"149":{"tf":1.0},"69":{"tf":1.0}}}},"df":0,"docs":{}},"i":{"df":0,"docs":{},"g":{"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"df":6,"docs":{"13":{"tf":1.0},"146":{"tf":1.0},"19":{"tf":1.4142135623730951},"74":{"tf":1.0},"83":{"tf":1.0},"84":{"tf":1.0}}}}}}},"t":{"df":0,"docs":{},"h":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":4,"docs":{"107":{"tf":1.0},"109":{"tf":1.0},"149":{"tf":1.0},"41":{"tf":1.0}},"w":{"df":0,"docs":{},"i":{"df":0,"docs":{},"s":{"df":5,"docs":{"104":{"tf":1.0},"124":{"tf":1.0},"19":{"tf":1.0},"33":{"tf":1.0},"7":{"tf":1.0}}}}}}}}},"u":{"df":0,"docs":{},"t":{"c":{"df":0,"docs":{},"o":{"df":0,"docs":{},"m":{"df":1,"docs":{"118":{"tf":1.0}}}}},"df":16,"docs":{"101":{"tf":1.0},"112":{"tf":1.0},"115":{"tf":1.0},"117":{"tf":1.7320508075688772},"119":{"tf":1.0},"16":{"tf":1.0},"41":{"tf":1.0},"42":{"tf":1.0},"50":{"tf":1.0},"59":{"tf":1.4142135623730951},"60":{"tf":1.0},"62":{"tf":1.0},"66":{"tf":1.0},"7":{"tf":1.0},"72":{"tf":1.0},"79":{"tf":1.0}},"p":{"df":0,"docs":{},"u":{"df":0,"docs":{},"t":{"df":16,"docs":{"104":{"tf":1.0},"11":{"tf":1.0},"13":{"tf":1.7320508075688772},"146":{"tf":1.0},"151":{"tf":1.0},"16":{"tf":1.0},"19":{"tf":1.0},"32":{"tf":1.0},"33":{"tf":2.0},"34":{"tf":1.4142135623730951},"35":{"tf":2.0},"4":{"tf":1.0},"40":{"tf":1.4142135623730951},"7":{"tf":2.8284271247461903},"74":{"tf":1.0},"9":{"tf":2.449489742783178}}}}},"s":{"df":0,"docs":{},"i":{"d":{"df":1,"docs":{"20":{"tf":1.0}}},"df":0,"docs":{}}}}},"v":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":16,"docs":{"103":{"tf":1.0},"104":{"tf":1.4142135623730951},"109":{"tf":1.0},"111":{"tf":1.0},"112":{"tf":1.0},"125":{"tf":2.0},"131":{"tf":1.4142135623730951},"149":{"tf":1.4142135623730951},"44":{"tf":1.0},"62":{"tf":1.0},"68":{"tf":1.4142135623730951},"70":{"tf":1.0},"80":{"tf":1.0},"82":{"tf":1.4142135623730951},"83":{"tf":1.0},"84":{"tf":1.0}},"v":{"df":0,"docs":{},"i":{"df":0,"docs":{},"e":{"df":0,"docs":{},"w":{"df":19,"docs":{"69":{"tf":1.7320508075688772},"70":{"tf":1.0},"71":{"tf":1.0},"72":{"tf":1.0},"73":{"tf":1.0},"74":{"tf":1.0},"75":{"tf":1.0},"76":{"tf":1.0},"77":{"tf":1.0},"78":{"tf":1.0},"79":{"tf":1.0},"80":{"tf":1.0},"81":{"tf":1.0},"82":{"tf":1.0},"83":{"tf":1.0},"84":{"tf":1.0},"85":{"tf":1.0},"86":{"tf":1.0},"92":{"tf":1.0}}}}}},"w":{"df":0,"docs":{},"h":{"df":0,"docs":{},"e":{"df":0,"docs":{},"l":{"df":0,"docs":{},"m":{"df":3,"docs":{"14":{"tf":2.6457513110645907},"19":{"tf":1.0},"85":{"tf":1.0}}}}}}}}}}},"p":{"(":{"1":{")":{"=":{"1":{"df":0,"docs":{},"p":{"(":{"df":0,"docs":{},"g":{")":{"=":{"1":{"df":1,"docs":{"54":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"d":{"1":{"df":0,"docs":{},"​":{")":{",":{"df":0,"docs":{},"…":{",":{"df":0,"docs":{},"p":{"(":{"d":{"df":0,"docs":{},"m":{"df":3,"docs":{"73":{"tf":1.4142135623730951},"76":{"tf":1.0},"80":{"tf":1.0}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{},"i":{"df":0,"docs":{},"​":{")":{"=":{"df":0,"docs":{},"y":{"df":0,"docs":{},"i":{"df":1,"docs":{"73":{"tf":1.0}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{},"h":{")":{",":{"df":0,"docs":{},"p":{"(":{"df":0,"docs":{},"h":{"df":0,"docs":{},"ω":{")":{",":{"df":0,"docs":{},"p":{"(":{"df":0,"docs":{},"h":{"df":0,"docs":{},"ω":{"2":{")":{",":{"df":0,"docs":{},"…":{",":{"df":0,"docs":{},"p":{"(":{"df":0,"docs":{},"h":{"df":0,"docs":{},"ω":{"2":{"df":0,"docs":{},"k":{"df":0,"docs":{},"−":{"1":{"df":1,"docs":{"101":{"tf":1.0}}},"df":0,"docs":{}}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{},"ω":{"df":0,"docs":{},"i":{"df":1,"docs":{"101":{"tf":1.4142135623730951}}},"σ":{"(":{"df":0,"docs":{},"i":{"df":1,"docs":{"101":{"tf":1.4142135623730951}}}},"df":0,"docs":{}}}},"x":{"df":1,"docs":{"54":{"tf":1.0}}},"z":{"df":2,"docs":{"77":{"tf":1.0},"78":{"tf":1.0}}},"ζ":{")":{"=":{"a":{"(":{"df":0,"docs":{},"ζ":{")":{"df":0,"docs":{},"q":{"df":0,"docs":{},"l":{"df":0,"docs":{},"​":{"(":{"df":0,"docs":{},"ζ":{")":{"+":{"b":{"(":{"df":0,"docs":{},"ζ":{")":{"df":0,"docs":{},"q":{"df":0,"docs":{},"r":{"df":1,"docs":{"21":{"tf":1.0}}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{},"p":{"df":0,"docs":{},"ˉ":{"df":0,"docs":{},"​":{"c":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"p":{"df":0,"docs":{},"ˉ":{"df":0,"docs":{},"​":{"df":0,"docs":{},"n":{"c":{"df":1,"docs":{"32":{"tf":1.0}}},"df":0,"docs":{}}}}}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"z":{"df":0,"docs":{},"h":{"df":0,"docs":{},"​":{"(":{"df":0,"docs":{},"ζ":{")":{"df":0,"docs":{},"t":{"df":1,"docs":{"21":{"tf":1.0}}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}},"−":{"df":0,"docs":{},"h":{"df":0,"docs":{},"ω":{"df":0,"docs":{},"i":{"df":1,"docs":{"101":{"tf":1.0}}},"σ":{"(":{"df":0,"docs":{},"i":{"df":1,"docs":{"101":{"tf":1.0}}}},"df":0,"docs":{}}}}}},",":{"a":{",":{"b":{",":{"df":0,"docs":{},"z":{"df":1,"docs":{"5":{"tf":1.0}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{},"q":{"df":0,"docs":{},"∈":{"df":0,"docs":{},"f":{"[":{"df":0,"docs":{},"x":{"df":1,"docs":{"90":{"tf":1.0}}}},"df":0,"docs":{}}}}},".":{"df":0,"docs":{},"i":{"df":1,"docs":{"32":{"tf":1.0}}}},"/":{"df":0,"docs":{},"q":{"df":1,"docs":{"90":{"tf":1.0}}}},"0":{"df":1,"docs":{"94":{"tf":1.0}}},"1":{"df":0,"docs":{},"​":{"(":{"d":{"1":{"df":0,"docs":{},"​":{")":{"df":0,"docs":{},"∣":{"df":0,"docs":{},"∣":{"df":0,"docs":{},"…":{"df":0,"docs":{},"∣":{"df":0,"docs":{},"∣":{"df":0,"docs":{},"p":{"df":0,"docs":{},"l":{"df":0,"docs":{},"​":{"(":{"d":{"1":{"df":0,"docs":{},"​":{")":{",":{"df":0,"docs":{},"…":{",":{"df":0,"docs":{},"p":{"df":0,"docs":{},"l":{"df":0,"docs":{},"​":{"(":{"d":{"df":0,"docs":{},"m":{"df":0,"docs":{},"​":{")":{"df":0,"docs":{},"∣":{"df":0,"docs":{},"∣":{"df":0,"docs":{},"…":{"df":0,"docs":{},"∣":{"df":0,"docs":{},"∣":{"df":0,"docs":{},"p":{"df":0,"docs":{},"n":{"df":0,"docs":{},"​":{"(":{"d":{"df":0,"docs":{},"m":{"df":1,"docs":{"80":{"tf":1.0}}}},"df":0,"docs":{}},"df":0,"docs":{}}}}}}}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}}}}}}}},"df":0,"docs":{}}},"df":0,"docs":{},"i":{"df":0,"docs":{},"​":{")":{",":{"df":0,"docs":{},"…":{",":{"df":0,"docs":{},"p":{"df":0,"docs":{},"l":{"df":0,"docs":{},"​":{"(":{"d":{"df":0,"docs":{},"i":{"df":1,"docs":{"80":{"tf":1.0}}}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}}},"df":0,"docs":{},"∣":{"df":0,"docs":{},"∣":{"df":0,"docs":{},"…":{"df":0,"docs":{},"∣":{"df":0,"docs":{},"∣":{"df":0,"docs":{},"p":{"df":0,"docs":{},"l":{"df":0,"docs":{},"​":{"(":{"d":{"df":0,"docs":{},"i":{"df":1,"docs":{"80":{"tf":1.0}}}},"df":0,"docs":{}},"df":0,"docs":{}}}}}}}}}},"df":0,"docs":{}}}},"df":0,"docs":{},"z":{"1":{"df":0,"docs":{},"​":{")":{",":{"df":0,"docs":{},"…":{",":{"df":0,"docs":{},"p":{"df":0,"docs":{},"k":{"df":0,"docs":{},"​":{"(":{"df":0,"docs":{},"z":{"df":0,"docs":{},"l":{"df":1,"docs":{"80":{"tf":1.0}}}}},"df":0,"docs":{}}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{},"p":{"2":{"df":0,"docs":{},"​":{"df":0,"docs":{},"p":{"3":{"df":0,"docs":{},"​":{"df":0,"docs":{},"​":{"=":{"a":{"df":0,"docs":{},"q":{"df":0,"docs":{},"l":{"df":0,"docs":{},"​":{"+":{"b":{"df":0,"docs":{},"q":{"df":0,"docs":{},"r":{"df":0,"docs":{},"​":{"+":{"a":{"b":{"df":0,"docs":{},"q":{"df":0,"docs":{},"m":{"df":0,"docs":{},"​":{"+":{"c":{"df":0,"docs":{},"q":{"df":0,"docs":{},"o":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"q":{"c":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"p":{"df":0,"docs":{},"i":{"=":{"(":{"a":{"+":{"df":0,"docs":{},"β":{"df":0,"docs":{},"x":{"+":{"df":0,"docs":{},"γ":{")":{"(":{"b":{"+":{"df":0,"docs":{},"β":{"df":0,"docs":{},"k":{"1":{"df":0,"docs":{},"​":{"df":0,"docs":{},"x":{"+":{"df":0,"docs":{},"γ":{")":{"(":{"c":{"+":{"df":0,"docs":{},"β":{"df":0,"docs":{},"k":{"2":{"df":0,"docs":{},"​":{"df":0,"docs":{},"x":{"+":{"df":0,"docs":{},"γ":{")":{"df":0,"docs":{},"z":{"df":0,"docs":{},"−":{"(":{"a":{"+":{"df":0,"docs":{},"β":{"df":0,"docs":{},"s":{"df":0,"docs":{},"σ":{"1":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"γ":{")":{"(":{"b":{"+":{"df":0,"docs":{},"β":{"df":0,"docs":{},"s":{"df":0,"docs":{},"σ":{"2":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"γ":{")":{"(":{"c":{"+":{"df":0,"docs":{},"β":{"df":0,"docs":{},"s":{"df":0,"docs":{},"σ":{"3":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"γ":{")":{"df":0,"docs":{},"z":{"(":{"df":0,"docs":{},"ω":{"df":0,"docs":{},"x":{")":{"=":{"(":{"df":0,"docs":{},"z":{"df":0,"docs":{},"−":{"1":{")":{"df":0,"docs":{},"l":{"1":{"df":1,"docs":{"26":{"tf":1.0}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}}},"df":0,"docs":{}}}},"=":{"1":{"df":1,"docs":{"73":{"tf":1.0}}},"a":{"df":0,"docs":{},"q":{"df":0,"docs":{},"l":{"df":0,"docs":{},"​":{"+":{"b":{"df":0,"docs":{},"q":{"df":0,"docs":{},"r":{"df":0,"docs":{},"​":{"+":{"a":{"b":{"df":0,"docs":{},"q":{"df":0,"docs":{},"m":{"df":1,"docs":{"21":{"tf":1.0}}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{},"p":{"1":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"α":{"df":0,"docs":{},"p":{"2":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"α":{"2":{"df":0,"docs":{},"p":{"3":{"df":1,"docs":{"26":{"tf":1.0}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"t":{"df":0,"docs":{},"z":{"df":0,"docs":{},"h":{"df":1,"docs":{"26":{"tf":1.0}}}}},"{":{"df":0,"docs":{},"p":{"1":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"…":{",":{"df":0,"docs":{},"p":{"df":0,"docs":{},"l":{"df":1,"docs":{"80":{"tf":1.0}}},"n":{"df":1,"docs":{"80":{"tf":1.0}}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"^":{"df":0,"docs":{},"​":{"df":0,"docs":{},"n":{"c":{"1":{"df":0,"docs":{},"​":{"]":{"1":{"df":0,"docs":{},"​":{"[":{"df":0,"docs":{},"p":{"^":{"df":0,"docs":{},"​":{"df":0,"docs":{},"n":{"c":{"2":{"df":0,"docs":{},"​":{"]":{"1":{"df":0,"docs":{},"​":{"[":{"df":0,"docs":{},"p":{"^":{"df":0,"docs":{},"​":{"df":0,"docs":{},"n":{"c":{"3":{"df":0,"docs":{},"​":{"]":{"1":{"df":0,"docs":{},"​":{"df":0,"docs":{},"​":{"=":{"a":{"df":0,"docs":{},"ˉ":{"[":{"df":0,"docs":{},"q":{"df":0,"docs":{},"l":{"df":0,"docs":{},"​":{"]":{"1":{"df":0,"docs":{},"​":{"+":{"b":{"df":0,"docs":{},"ˉ":{"[":{"df":0,"docs":{},"q":{"df":0,"docs":{},"r":{"df":0,"docs":{},"​":{"]":{"1":{"df":0,"docs":{},"​":{"+":{"(":{"a":{"df":0,"docs":{},"ˉ":{"b":{"df":0,"docs":{},"ˉ":{")":{"[":{"df":0,"docs":{},"q":{"df":0,"docs":{},"m":{"df":0,"docs":{},"​":{"]":{"1":{"df":0,"docs":{},"​":{"+":{"c":{"df":0,"docs":{},"ˉ":{"[":{"df":0,"docs":{},"q":{"df":0,"docs":{},"o":{"df":0,"docs":{},"​":{"]":{"1":{"df":0,"docs":{},"​":{"+":{"[":{"df":0,"docs":{},"q":{"c":{"df":0,"docs":{},"​":{"]":{"1":{"df":0,"docs":{},"​":{"=":{"(":{"a":{"df":0,"docs":{},"ˉ":{"+":{"df":0,"docs":{},"β":{"df":0,"docs":{},"ζ":{"+":{"df":0,"docs":{},"γ":{")":{"(":{"b":{"df":0,"docs":{},"ˉ":{"+":{"df":0,"docs":{},"β":{"df":0,"docs":{},"k":{"1":{"df":0,"docs":{},"​":{"df":0,"docs":{},"ζ":{"+":{"df":0,"docs":{},"γ":{")":{"(":{"c":{"df":0,"docs":{},"ˉ":{"+":{"df":0,"docs":{},"β":{"df":0,"docs":{},"k":{"2":{"df":0,"docs":{},"​":{"df":0,"docs":{},"ζ":{"+":{"df":0,"docs":{},"γ":{")":{"[":{"df":0,"docs":{},"z":{"]":{"1":{"df":0,"docs":{},"​":{"df":0,"docs":{},"−":{"(":{"a":{"df":0,"docs":{},"ˉ":{"+":{"df":0,"docs":{},"β":{"df":0,"docs":{},"s":{"df":0,"docs":{},"ˉ":{"df":0,"docs":{},"σ":{"1":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"γ":{")":{"(":{"b":{"df":0,"docs":{},"ˉ":{"+":{"df":0,"docs":{},"β":{"df":0,"docs":{},"s":{"df":0,"docs":{},"ˉ":{"df":0,"docs":{},"σ":{"2":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"γ":{")":{"df":0,"docs":{},"β":{"df":0,"docs":{},"z":{"df":0,"docs":{},"ˉ":{"df":0,"docs":{},"ω":{"df":0,"docs":{},"​":{"[":{"df":0,"docs":{},"s":{"df":0,"docs":{},"σ":{"3":{"df":0,"docs":{},"​":{"]":{"1":{"df":0,"docs":{},"​":{"=":{"df":0,"docs":{},"l":{"1":{"df":0,"docs":{},"​":{"(":{"df":0,"docs":{},"ζ":{")":{"[":{"df":0,"docs":{},"z":{"]":{"1":{"df":1,"docs":{"32":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}}}}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{},"p":{"^":{"df":0,"docs":{},"​":{"df":0,"docs":{},"n":{"c":{"2":{"df":0,"docs":{},"​":{"df":0,"docs":{},"p":{"^":{"df":0,"docs":{},"​":{"df":0,"docs":{},"n":{"c":{"3":{"df":0,"docs":{},"​":{"df":0,"docs":{},"​":{"=":{"a":{"df":0,"docs":{},"ˉ":{"df":0,"docs":{},"q":{"df":0,"docs":{},"l":{"df":0,"docs":{},"​":{"+":{"b":{"df":0,"docs":{},"ˉ":{"df":0,"docs":{},"q":{"df":0,"docs":{},"r":{"df":0,"docs":{},"​":{"+":{"a":{"df":0,"docs":{},"ˉ":{"b":{"df":0,"docs":{},"ˉ":{"df":0,"docs":{},"q":{"df":0,"docs":{},"m":{"df":0,"docs":{},"​":{"+":{"c":{"df":0,"docs":{},"ˉ":{"df":0,"docs":{},"q":{"df":0,"docs":{},"o":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"q":{"c":{"df":0,"docs":{},"​":{"=":{"(":{"a":{"df":0,"docs":{},"ˉ":{"+":{"df":0,"docs":{},"β":{"df":0,"docs":{},"ζ":{"+":{"df":0,"docs":{},"γ":{")":{"(":{"b":{"df":0,"docs":{},"ˉ":{"+":{"df":0,"docs":{},"β":{"df":0,"docs":{},"k":{"1":{"df":0,"docs":{},"​":{"df":0,"docs":{},"ζ":{"+":{"df":0,"docs":{},"γ":{")":{"(":{"c":{"df":0,"docs":{},"ˉ":{"+":{"df":0,"docs":{},"β":{"df":0,"docs":{},"k":{"2":{"df":0,"docs":{},"​":{"df":0,"docs":{},"ζ":{"+":{"df":0,"docs":{},"γ":{")":{"df":0,"docs":{},"z":{"df":0,"docs":{},"−":{"(":{"a":{"df":0,"docs":{},"ˉ":{"+":{"df":0,"docs":{},"β":{"df":0,"docs":{},"s":{"df":0,"docs":{},"ˉ":{"df":0,"docs":{},"σ":{"1":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"γ":{")":{"(":{"b":{"df":0,"docs":{},"ˉ":{"+":{"df":0,"docs":{},"β":{"df":0,"docs":{},"s":{"df":0,"docs":{},"ˉ":{"df":0,"docs":{},"σ":{"2":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"γ":{")":{"df":0,"docs":{},"β":{"df":0,"docs":{},"z":{"df":0,"docs":{},"ˉ":{"df":0,"docs":{},"ω":{"df":0,"docs":{},"​":{"df":0,"docs":{},"s":{"df":0,"docs":{},"σ":{"3":{"df":0,"docs":{},"​":{"=":{"df":0,"docs":{},"l":{"1":{"df":0,"docs":{},"​":{"(":{"df":0,"docs":{},"ζ":{")":{"df":0,"docs":{},"z":{"df":1,"docs":{"28":{"tf":1.0}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}}}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"_":{"df":0,"docs":{},"n":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"_":{"c":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"df":0,"docs":{},"s":{"df":0,"docs":{},"t":{"a":{"df":0,"docs":{},"n":{"df":0,"docs":{},"t":{"_":{"df":0,"docs":{},"z":{"df":0,"docs":{},"e":{"df":0,"docs":{},"t":{"a":{"df":1,"docs":{"35":{"tf":1.0}}},"df":0,"docs":{}}}}},"df":0,"docs":{}}}},"df":1,"docs":{"35":{"tf":1.0}}}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"a":{"c":{"df":0,"docs":{},"k":{"a":{"df":0,"docs":{},"g":{"df":1,"docs":{"38":{"tf":1.0}}}},"df":0,"docs":{}}},"d":{"df":5,"docs":{"101":{"tf":1.0},"121":{"tf":1.0},"142":{"tf":1.0},"146":{"tf":2.0},"36":{"tf":2.0}}},"df":0,"docs":{},"i":{"df":0,"docs":{},"r":{"df":9,"docs":{"102":{"tf":1.0},"135":{"tf":1.0},"14":{"tf":3.4641016151377544},"146":{"tf":2.23606797749979},"151":{"tf":1.0},"3":{"tf":1.0},"38":{"tf":1.0},"92":{"tf":1.0},"95":{"tf":1.0}}}},"p":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":3,"docs":{"146":{"tf":1.4142135623730951},"16":{"tf":1.0},"4":{"tf":1.0}}}},"i":{"df":0,"docs":{},"n":{"df":0,"docs":{},"i":{"df":1,"docs":{"103":{"tf":1.0}}}}}},"r":{"a":{"df":0,"docs":{},"m":{"df":0,"docs":{},"e":{"df":0,"docs":{},"t":{"df":7,"docs":{"109":{"tf":1.7320508075688772},"128":{"tf":1.0},"68":{"tf":1.0},"73":{"tf":1.4142135623730951},"75":{"tf":1.0},"80":{"tf":1.4142135623730951},"91":{"tf":1.0}}}}}},"df":0,"docs":{},"t":{"df":22,"docs":{"114":{"tf":1.0},"117":{"tf":1.0},"123":{"tf":1.0},"13":{"tf":1.0},"14":{"tf":1.0},"146":{"tf":1.0},"147":{"tf":1.0},"149":{"tf":1.0},"151":{"tf":1.0},"16":{"tf":1.0},"28":{"tf":1.0},"32":{"tf":1.0},"35":{"tf":1.7320508075688772},"50":{"tf":1.0},"53":{"tf":1.0},"58":{"tf":1.0},"69":{"tf":1.0},"7":{"tf":1.0},"70":{"tf":1.0},"73":{"tf":1.0},"9":{"tf":1.0},"94":{"tf":1.0}},"i":{"a":{"df":0,"docs":{},"l":{"df":1,"docs":{"28":{"tf":1.7320508075688772}}}},"c":{"df":0,"docs":{},"u":{"df":0,"docs":{},"l":{"a":{"df":0,"docs":{},"r":{"df":14,"docs":{"10":{"tf":1.4142135623730951},"11":{"tf":1.0},"13":{"tf":1.4142135623730951},"134":{"tf":1.0},"146":{"tf":1.4142135623730951},"21":{"tf":1.0},"22":{"tf":1.0},"28":{"tf":1.0},"35":{"tf":1.4142135623730951},"36":{"tf":1.0},"43":{"tf":1.0},"69":{"tf":1.0},"70":{"tf":1.4142135623730951},"8":{"tf":1.4142135623730951}}}},"df":0,"docs":{}}}},"df":5,"docs":{"19":{"tf":1.4142135623730951},"4":{"tf":1.0},"7":{"tf":1.0},"73":{"tf":1.4142135623730951},"74":{"tf":1.0}}}}},"s":{"df":0,"docs":{},"s":{"df":10,"docs":{"11":{"tf":1.0},"110":{"tf":1.4142135623730951},"114":{"tf":1.4142135623730951},"117":{"tf":1.0},"144":{"tf":1.0},"33":{"tf":1.0},"40":{"tf":2.0},"50":{"tf":1.4142135623730951},"57":{"tf":1.0},"75":{"tf":1.0}}}},"t":{"df":0,"docs":{},"h":{"df":7,"docs":{"101":{"tf":1.7320508075688772},"102":{"tf":1.0},"35":{"tf":1.0},"72":{"tf":1.4142135623730951},"80":{"tf":1.4142135623730951},"92":{"tf":1.4142135623730951},"95":{"tf":1.7320508075688772}}}}},"c":{"[":{"0":{"df":1,"docs":{"147":{"tf":1.0}}},"1":{"df":1,"docs":{"147":{"tf":1.0}}},"df":0,"docs":{}},"_":{"df":0,"docs":{},"j":{"df":0,"docs":{},"n":{"df":0,"docs":{},"z":{"df":1,"docs":{"146":{"tf":1.0}}}}},"u":{"df":0,"docs":{},"p":{"d":{"a":{"df":0,"docs":{},"t":{"df":1,"docs":{"146":{"tf":1.0}}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":8,"docs":{"133":{"tf":1.0},"136":{"tf":1.7320508075688772},"141":{"tf":1.0},"142":{"tf":1.0},"144":{"tf":1.0},"146":{"tf":2.23606797749979},"147":{"tf":1.4142135623730951},"19":{"tf":1.4142135623730951}},"i":{"df":1,"docs":{"142":{"tf":1.0}},"​":{",":{"a":{"df":0,"docs":{},"p":{"df":0,"docs":{},"i":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"f":{"df":0,"docs":{},"p":{"df":0,"docs":{},"i":{"df":1,"docs":{"142":{"tf":1.0}}}}}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":14,"docs":{"101":{"tf":1.0},"147":{"tf":2.0},"21":{"tf":2.6457513110645907},"28":{"tf":1.0},"32":{"tf":1.4142135623730951},"33":{"tf":1.0},"54":{"tf":1.4142135623730951},"73":{"tf":2.6457513110645907},"75":{"tf":2.8284271247461903},"76":{"tf":2.0},"77":{"tf":1.4142135623730951},"78":{"tf":1.4142135623730951},"79":{"tf":1.7320508075688772},"80":{"tf":2.449489742783178}},"e":{"df":0,"docs":{},"r":{"df":3,"docs":{"107":{"tf":1.0},"65":{"tf":1.0},"9":{"tf":1.0}},"f":{"df":0,"docs":{},"o":{"df":0,"docs":{},"r":{"df":0,"docs":{},"m":{"df":10,"docs":{"100":{"tf":1.0},"109":{"tf":1.0},"123":{"tf":1.0},"125":{"tf":1.4142135623730951},"151":{"tf":1.0},"35":{"tf":1.0},"66":{"tf":1.0},"7":{"tf":1.0},"82":{"tf":1.0},"99":{"tf":1.0}}}}}},"m":{"df":0,"docs":{},"u":{"df":0,"docs":{},"t":{"df":6,"docs":{"101":{"tf":1.4142135623730951},"138":{"tf":1.0},"139":{"tf":1.4142135623730951},"14":{"tf":2.0},"146":{"tf":2.0},"147":{"tf":1.4142135623730951}}}}}}},"h":{"a":{"df":0,"docs":{},"n":{"df":0,"docs":{},"t":{"df":0,"docs":{},"o":{"df":0,"docs":{},"m":{"d":{"a":{"df":0,"docs":{},"t":{"a":{"<":{"df":0,"docs":{},"f":{"df":1,"docs":{"35":{"tf":1.4142135623730951}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":1,"docs":{"35":{"tf":1.4142135623730951}}}}}},"s":{"df":0,"docs":{},"e":{"df":4,"docs":{"127":{"tf":1.0},"22":{"tf":1.0},"35":{"tf":1.0},"94":{"tf":1.4142135623730951}}}}},"df":0,"docs":{}},"i":{")":{"df":0,"docs":{},"i":{"df":0,"docs":{},"​":{"=":{"0":{"df":2,"docs":{"14":{"tf":1.0},"16":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":7,"docs":{"13":{"tf":2.0},"14":{"tf":1.4142135623730951},"16":{"tf":2.449489742783178},"26":{"tf":1.4142135623730951},"32":{"tf":2.449489742783178},"36":{"tf":1.0},"80":{"tf":1.0}},"e":{"c":{"df":2,"docs":{"114":{"tf":1.0},"116":{"tf":1.0}}},"df":0,"docs":{}},"t":{"df":0,"docs":{},"f":{"a":{"df":0,"docs":{},"l":{"df":1,"docs":{"42":{"tf":1.0}}}},"df":0,"docs":{}}},"​":{":":{"=":{"df":0,"docs":{},"j":{"=":{"0":{"df":0,"docs":{},"∏":{"df":0,"docs":{},"i":{"df":0,"docs":{},"​":{"df":0,"docs":{},"z":{"df":0,"docs":{},"−":{"(":{"a":{"df":0,"docs":{},"i":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"α":{"df":0,"docs":{},"v":{"df":0,"docs":{},"i":{"df":0,"docs":{},"​":{")":{"df":0,"docs":{},"z":{"df":0,"docs":{},"−":{"(":{"a":{"df":0,"docs":{},"i":{"df":0,"docs":{},"′":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"α":{"df":0,"docs":{},"v":{"df":0,"docs":{},"i":{"df":1,"docs":{"147":{"tf":1.0}}}}}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}}}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"′":{"df":0,"docs":{},"​":{":":{"=":{"df":0,"docs":{},"j":{"=":{"0":{"df":0,"docs":{},"∏":{"df":0,"docs":{},"i":{"df":0,"docs":{},"​":{"df":0,"docs":{},"z":{"df":0,"docs":{},"′":{"df":0,"docs":{},"−":{"b":{"df":0,"docs":{},"i":{"df":0,"docs":{},"​":{"df":0,"docs":{},"z":{"df":0,"docs":{},"′":{"df":0,"docs":{},"−":{"b":{"df":0,"docs":{},"i":{"df":1,"docs":{"147":{"tf":1.0}}}},"df":0,"docs":{}}}}}}},"df":0,"docs":{}}}}}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"j":{"b":{"df":2,"docs":{"70":{"tf":1.0},"91":{"tf":1.0}}},"df":0,"docs":{}},"k":{"+":{"1":{"df":0,"docs":{},"​":{"(":{"df":0,"docs":{},"υ":{"2":{"df":0,"docs":{},"k":{"+":{"1":{"df":1,"docs":{"102":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":3,"docs":{"102":{"tf":1.4142135623730951},"94":{"tf":1.0},"95":{"tf":1.0}},"t":{"df":2,"docs":{"70":{"tf":1.0},"91":{"tf":1.0}},"​":{"(":{"df":0,"docs":{},"t":{"df":0,"docs":{},"i":{",":{"0":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"…":{",":{"df":0,"docs":{},"t":{"df":0,"docs":{},"i":{",":{"df":0,"docs":{},"m":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"t":{"df":0,"docs":{},"i":{"+":{"1":{",":{"0":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"…":{",":{"df":0,"docs":{},"t":{"df":0,"docs":{},"i":{"+":{"1":{",":{"df":0,"docs":{},"m":{"df":0,"docs":{},"​":{")":{"=":{"0":{"df":1,"docs":{"70":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"x":{"1":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"…":{",":{"df":0,"docs":{},"x":{"df":0,"docs":{},"m":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"y":{"1":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"…":{",":{"df":0,"docs":{},"y":{"df":0,"docs":{},"m":{"df":1,"docs":{"70":{"tf":1.0}}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"​":{"(":{"df":0,"docs":{},"υ":{"2":{"df":0,"docs":{},"k":{"+":{"1":{"df":1,"docs":{"102":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},":":{"=":{"df":0,"docs":{},"p":{"df":0,"docs":{},"k":{"df":0,"docs":{},"−":{"1":{"df":0,"docs":{},"o":{"d":{"d":{"df":0,"docs":{},"​":{"(":{"df":0,"docs":{},"x":{")":{"+":{"df":0,"docs":{},"ζ":{"df":0,"docs":{},"k":{"df":0,"docs":{},"−":{"1":{"df":0,"docs":{},"​":{"df":0,"docs":{},"p":{"df":0,"docs":{},"k":{"df":0,"docs":{},"−":{"1":{"df":0,"docs":{},"e":{"df":0,"docs":{},"v":{"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"df":0,"docs":{},"​":{"(":{"df":0,"docs":{},"x":{"df":1,"docs":{"94":{"tf":1.0}}}},"df":0,"docs":{}}}}}}},"df":0,"docs":{}}}}}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"]":{":":{"=":{"c":{"df":0,"docs":{},"o":{"df":0,"docs":{},"m":{"df":0,"docs":{},"m":{"df":0,"docs":{},"i":{"df":0,"docs":{},"t":{"(":{"df":0,"docs":{},"p":{"df":0,"docs":{},"k":{"df":0,"docs":{},"​":{"(":{"d":{"df":0,"docs":{},"k":{"df":1,"docs":{"94":{"tf":1.0}}}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}}}}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"−":{"1":{"df":1,"docs":{"94":{"tf":1.0}},"​":{"=":{"df":0,"docs":{},"p":{"df":0,"docs":{},"k":{"df":0,"docs":{},"−":{"1":{"df":0,"docs":{},"o":{"d":{"d":{"df":0,"docs":{},"​":{"(":{"df":0,"docs":{},"x":{"2":{")":{"+":{"df":0,"docs":{},"x":{"df":0,"docs":{},"p":{"df":0,"docs":{},"k":{"df":0,"docs":{},"−":{"1":{"df":0,"docs":{},"e":{"df":0,"docs":{},"v":{"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"df":0,"docs":{},"​":{"(":{"df":0,"docs":{},"x":{"2":{"df":1,"docs":{"94":{"tf":1.0}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}}}},"df":0,"docs":{}}}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"l":{"a":{"c":{"df":0,"docs":{},"e":{"df":3,"docs":{"123":{"tf":1.4142135623730951},"146":{"tf":1.0},"151":{"tf":1.0}}}},"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"df":14,"docs":{"127":{"tf":2.0},"128":{"tf":1.0},"129":{"tf":1.0},"130":{"tf":1.0},"131":{"tf":1.0},"132":{"tf":1.0},"133":{"tf":1.0},"134":{"tf":1.0},"135":{"tf":1.0},"136":{"tf":1.0},"137":{"tf":1.0},"138":{"tf":1.0},"139":{"tf":1.0},"149":{"tf":1.0}}}},"y":{"df":1,"docs":{"16":{"tf":1.0}}}},"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"df":0,"docs":{},"t":{"df":0,"docs":{},"i":{"df":1,"docs":{"130":{"tf":1.0}}}}}},"o":{"df":0,"docs":{},"n":{"df":0,"docs":{},"k":{"df":42,"docs":{"10":{"tf":1.0},"11":{"tf":1.0},"12":{"tf":1.7320508075688772},"13":{"tf":1.0},"14":{"tf":1.7320508075688772},"15":{"tf":1.0},"16":{"tf":1.0},"17":{"tf":1.0},"18":{"tf":1.0},"19":{"tf":1.4142135623730951},"20":{"tf":1.0},"21":{"tf":1.7320508075688772},"22":{"tf":1.0},"23":{"tf":1.0},"24":{"tf":1.0},"25":{"tf":1.0},"26":{"tf":1.0},"27":{"tf":1.0},"28":{"tf":1.0},"29":{"tf":1.0},"30":{"tf":1.0},"31":{"tf":1.0},"32":{"tf":1.0},"33":{"tf":1.0},"34":{"tf":1.7320508075688772},"35":{"tf":1.0},"36":{"tf":1.0},"37":{"tf":1.0},"38":{"tf":1.0},"39":{"tf":1.0},"4":{"tf":2.23606797749979},"40":{"tf":1.0},"41":{"tf":1.0},"42":{"tf":1.0},"43":{"tf":1.0},"44":{"tf":1.4142135623730951},"45":{"tf":1.0},"5":{"tf":1.0},"6":{"tf":1.0},"7":{"tf":2.0},"8":{"tf":1.7320508075688772},"9":{"tf":1.0}}}}},"u":{"df":0,"docs":{},"g":{"df":1,"docs":{"70":{"tf":1.0}}}}},"n":{"c":{"df":0,"docs":{},"​":{"]":{"1":{"df":1,"docs":{"32":{"tf":1.4142135623730951}},"​":{"=":{"[":{"df":0,"docs":{},"p":{"df":0,"docs":{},"n":{"c":{"1":{"df":0,"docs":{},"​":{"]":{"1":{"df":0,"docs":{},"​":{"+":{"[":{"df":0,"docs":{},"p":{"df":0,"docs":{},"n":{"c":{"2":{"df":0,"docs":{},"​":{"]":{"1":{"df":0,"docs":{},"​":{"+":{"[":{"df":0,"docs":{},"p":{"df":0,"docs":{},"n":{"c":{"3":{"df":0,"docs":{},"​":{"]":{"1":{"df":1,"docs":{"32":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{},"t":{"df":0,"docs":{},"p":{"a":{"df":0,"docs":{},"r":{"df":0,"docs":{},"t":{"df":0,"docs":{},"i":{"a":{"df":0,"docs":{},"l":{"df":0,"docs":{},"​":{"df":0,"docs":{},"​":{"=":{"df":0,"docs":{},"p":{"df":0,"docs":{},"n":{"c":{"1":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"α":{"df":0,"docs":{},"p":{"df":0,"docs":{},"n":{"c":{"2":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"α":{"2":{"df":0,"docs":{},"p":{"df":0,"docs":{},"n":{"c":{"3":{"df":0,"docs":{},"​":{"=":{"df":0,"docs":{},"t":{"df":0,"docs":{},"l":{"df":0,"docs":{},"o":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"ζ":{"df":0,"docs":{},"n":{"+":{"2":{"df":0,"docs":{},"t":{"df":0,"docs":{},"m":{"df":0,"docs":{},"i":{"d":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"ζ":{"2":{"(":{"df":0,"docs":{},"n":{"+":{"2":{")":{"df":0,"docs":{},"t":{"df":0,"docs":{},"h":{"df":0,"docs":{},"i":{"df":1,"docs":{"28":{"tf":1.0}}}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}}}},"df":0,"docs":{}}}}},"df":0,"docs":{}}}}},"df":1,"docs":{"94":{"tf":1.4142135623730951}},"​":{"df":0,"docs":{},"∈":{"df":0,"docs":{},"f":{"df":1,"docs":{"94":{"tf":1.0}}}}},"−":{"1":{"df":1,"docs":{"146":{"tf":1.0}},"​":{"=":{"1":{"df":1,"docs":{"146":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"o":{"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"df":0,"docs":{},"t":{"df":23,"docs":{"101":{"tf":1.0},"112":{"tf":1.4142135623730951},"114":{"tf":2.8284271247461903},"115":{"tf":1.0},"117":{"tf":1.0},"123":{"tf":1.7320508075688772},"133":{"tf":1.0},"142":{"tf":1.0},"149":{"tf":1.0},"19":{"tf":1.0},"3":{"tf":1.0},"40":{"tf":1.0},"49":{"tf":1.0},"58":{"tf":1.4142135623730951},"59":{"tf":1.0},"60":{"tf":1.7320508075688772},"62":{"tf":1.4142135623730951},"68":{"tf":1.7320508075688772},"69":{"tf":1.4142135623730951},"80":{"tf":1.0},"85":{"tf":1.0},"88":{"tf":1.0},"95":{"tf":1.0}},"e":{"df":0,"docs":{},"r":{"df":3,"docs":{"142":{"tf":1.4142135623730951},"146":{"tf":2.449489742783178},"147":{"tf":1.4142135623730951}}}},"l":{"df":0,"docs":{},"e":{"df":0,"docs":{},"s":{"df":0,"docs":{},"s":{"df":1,"docs":{"7":{"tf":1.0}}}}}}}}},"l":{"df":0,"docs":{},"i":{"df":1,"docs":{"119":{"tf":1.0}}},"y":{"df":0,"docs":{},"l":{"df":0,"docs":{},"o":{"df":0,"docs":{},"g":{"a":{"df":0,"docs":{},"r":{"df":0,"docs":{},"i":{"df":0,"docs":{},"t":{"df":0,"docs":{},"h":{"df":0,"docs":{},"m":{"df":1,"docs":{"81":{"tf":1.0}}}}}}}},"df":0,"docs":{}}}},"n":{"df":0,"docs":{},"o":{"df":0,"docs":{},"m":{"df":0,"docs":{},"i":{"a":{"df":0,"docs":{},"l":{"'":{"df":3,"docs":{"19":{"tf":1.0},"3":{"tf":1.0},"68":{"tf":1.0}}},"<":{"df":0,"docs":{},"f":{"df":0,"docs":{},"i":{"df":0,"docs":{},"e":{"df":0,"docs":{},"l":{"d":{"df":0,"docs":{},"e":{"df":0,"docs":{},"l":{"df":0,"docs":{},"e":{"df":0,"docs":{},"m":{"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"df":0,"docs":{},"t":{"<":{"df":0,"docs":{},"f":{"df":1,"docs":{"35":{"tf":2.8284271247461903}}}},"df":0,"docs":{}}}}}}}}},"df":0,"docs":{}}}}}},"df":0,"docs":{}}},"df":70,"docs":{"100":{"tf":1.4142135623730951},"101":{"tf":1.0},"102":{"tf":1.0},"112":{"tf":2.23606797749979},"113":{"tf":1.7320508075688772},"114":{"tf":3.4641016151377544},"116":{"tf":1.0},"117":{"tf":1.4142135623730951},"119":{"tf":1.7320508075688772},"121":{"tf":1.0},"123":{"tf":3.4641016151377544},"124":{"tf":1.0},"125":{"tf":2.6457513110645907},"14":{"tf":5.916079783099616},"149":{"tf":1.0},"15":{"tf":1.0},"16":{"tf":3.0},"19":{"tf":3.3166247903554},"20":{"tf":3.1622776601683795},"21":{"tf":2.0},"23":{"tf":1.0},"24":{"tf":1.4142135623730951},"25":{"tf":1.4142135623730951},"26":{"tf":1.0},"28":{"tf":1.4142135623730951},"3":{"tf":2.449489742783178},"32":{"tf":1.0},"35":{"tf":3.605551275463989},"4":{"tf":1.4142135623730951},"42":{"tf":1.0},"48":{"tf":2.0},"5":{"tf":2.6457513110645907},"52":{"tf":2.449489742783178},"53":{"tf":2.449489742783178},"54":{"tf":2.6457513110645907},"55":{"tf":2.23606797749979},"56":{"tf":1.7320508075688772},"57":{"tf":1.7320508075688772},"58":{"tf":1.0},"59":{"tf":3.1622776601683795},"60":{"tf":1.0},"62":{"tf":2.23606797749979},"65":{"tf":2.23606797749979},"66":{"tf":2.0},"67":{"tf":1.4142135623730951},"68":{"tf":1.7320508075688772},"69":{"tf":2.8284271247461903},"70":{"tf":1.7320508075688772},"71":{"tf":2.449489742783178},"73":{"tf":2.6457513110645907},"74":{"tf":1.7320508075688772},"75":{"tf":3.1622776601683795},"76":{"tf":1.0},"77":{"tf":1.0},"78":{"tf":1.4142135623730951},"79":{"tf":1.4142135623730951},"8":{"tf":1.4142135623730951},"80":{"tf":3.7416573867739413},"81":{"tf":1.0},"82":{"tf":1.0},"83":{"tf":1.0},"84":{"tf":4.242640687119285},"85":{"tf":1.4142135623730951},"86":{"tf":1.0},"90":{"tf":1.4142135623730951},"91":{"tf":1.4142135623730951},"94":{"tf":2.449489742783178},"95":{"tf":2.0},"97":{"tf":1.0},"99":{"tf":1.0}}}}}}}},"o":{"df":0,"docs":{},"l":{"df":5,"docs":{"130":{"tf":1.4142135623730951},"133":{"tf":1.4142135623730951},"134":{"tf":1.7320508075688772},"135":{"tf":1.4142135623730951},"138":{"tf":1.4142135623730951}}}},"p":{"df":0,"docs":{},"u":{"df":0,"docs":{},"l":{"a":{"df":0,"docs":{},"r":{"df":2,"docs":{"19":{"tf":1.0},"4":{"tf":1.0}}}},"df":0,"docs":{}}}},"s":{"df":0,"docs":{},"e":{"df":0,"docs":{},"i":{"d":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"df":1,"docs":{"151":{"tf":1.0}}}}},"df":0,"docs":{}}},"i":{"df":0,"docs":{},"t":{"df":4,"docs":{"101":{"tf":1.4142135623730951},"14":{"tf":1.4142135623730951},"151":{"tf":1.7320508075688772},"72":{"tf":1.0}}}},"s":{"df":0,"docs":{},"i":{"b":{"df":0,"docs":{},"l":{"df":6,"docs":{"114":{"tf":1.0},"149":{"tf":1.4142135623730951},"36":{"tf":1.0},"38":{"tf":1.0},"50":{"tf":1.0},"71":{"tf":1.0}}}},"df":0,"docs":{}}}},"w":{"(":{"df":0,"docs":{},"s":{"df":0,"docs":{},"y":{"df":0,"docs":{},"s":{"df":0,"docs":{},"t":{"df":0,"docs":{},"e":{"df":0,"docs":{},"m":{"df":1,"docs":{"45":{"tf":1.0}}}}}}}},"x":{"df":1,"docs":{"45":{"tf":1.0}}}},"df":1,"docs":{"45":{"tf":1.0}},"e":{"df":0,"docs":{},"r":{"df":15,"docs":{"121":{"tf":1.0},"124":{"tf":1.4142135623730951},"125":{"tf":1.0},"142":{"tf":1.0},"146":{"tf":1.0},"149":{"tf":1.0},"28":{"tf":1.4142135623730951},"36":{"tf":1.0},"38":{"tf":1.4142135623730951},"52":{"tf":1.0},"68":{"tf":1.7320508075688772},"73":{"tf":1.0},"75":{"tf":1.0},"95":{"tf":1.0},"97":{"tf":1.0}}}}}},"r":{"a":{"c":{"df":0,"docs":{},"t":{"df":0,"docs":{},"i":{"c":{"df":1,"docs":{"149":{"tf":1.0}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{},"e":{"c":{"df":0,"docs":{},"e":{"d":{"df":4,"docs":{"49":{"tf":1.0},"52":{"tf":1.0},"70":{"tf":1.0},"84":{"tf":1.0}}},"df":0,"docs":{}},"i":{"df":0,"docs":{},"s":{"df":6,"docs":{"14":{"tf":1.0},"22":{"tf":1.0},"34":{"tf":1.0},"73":{"tf":1.4142135623730951},"74":{"tf":1.0},"87":{"tf":1.0}}}},"o":{"df":0,"docs":{},"m":{"df":0,"docs":{},"p":{"df":0,"docs":{},"u":{"df":0,"docs":{},"t":{"df":2,"docs":{"34":{"tf":1.0},"56":{"tf":1.0}}}}}}}},"d":{"df":0,"docs":{},"e":{"df":0,"docs":{},"f":{"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"df":1,"docs":{"70":{"tf":1.0}}}}}},"i":{"c":{"df":0,"docs":{},"t":{"df":1,"docs":{"118":{"tf":1.0}}}},"df":0,"docs":{}}},"df":0,"docs":{},"f":{"df":0,"docs":{},"i":{"df":0,"docs":{},"x":{"df":1,"docs":{"88":{"tf":1.0}}}}},"i":{"df":0,"docs":{},"m":{"a":{"df":0,"docs":{},"g":{"df":1,"docs":{"7":{"tf":1.0}}}},"df":0,"docs":{}}},"p":{"df":0,"docs":{},"r":{"df":0,"docs":{},"o":{"c":{"df":0,"docs":{},"e":{"df":0,"docs":{},"s":{"df":0,"docs":{},"s":{"df":6,"docs":{"15":{"tf":1.7320508075688772},"23":{"tf":1.0},"35":{"tf":1.7320508075688772},"42":{"tf":1.0},"70":{"tf":1.0},"83":{"tf":1.0}}}}}},"df":0,"docs":{}}}},"t":{"df":0,"docs":{},"t":{"df":0,"docs":{},"i":{"df":5,"docs":{"113":{"tf":1.0},"132":{"tf":1.0},"14":{"tf":1.0},"151":{"tf":1.0},"37":{"tf":1.0}}}}},"v":{"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"df":0,"docs":{},"t":{"df":2,"docs":{"42":{"tf":1.0},"71":{"tf":1.0}}}}},"i":{"df":0,"docs":{},"o":{"df":0,"docs":{},"u":{"df":10,"docs":{"107":{"tf":1.7320508075688772},"109":{"tf":1.0},"11":{"tf":1.0},"14":{"tf":2.6457513110645907},"145":{"tf":1.0},"149":{"tf":1.4142135623730951},"45":{"tf":1.0},"70":{"tf":1.0},"84":{"tf":1.0},"86":{"tf":1.0}}}}}}},"i":{"df":0,"docs":{},"m":{"a":{"df":0,"docs":{},"r":{"df":0,"docs":{},"i":{"df":1,"docs":{"70":{"tf":1.0}}}}},"df":0,"docs":{},"e":{"df":2,"docs":{"14":{"tf":1.0},"41":{"tf":1.0}}},"i":{"df":0,"docs":{},"t":{"df":17,"docs":{"114":{"tf":1.0},"124":{"tf":3.872983346207417},"125":{"tf":1.7320508075688772},"14":{"tf":2.23606797749979},"16":{"tf":1.0},"23":{"tf":1.0},"34":{"tf":1.0},"35":{"tf":1.0},"36":{"tf":1.0},"38":{"tf":1.0},"5":{"tf":1.0},"52":{"tf":1.4142135623730951},"68":{"tf":1.4142135623730951},"71":{"tf":1.0},"73":{"tf":1.0},"75":{"tf":1.0},"91":{"tf":1.0}}}}},"o":{"df":0,"docs":{},"r":{"df":1,"docs":{"91":{"tf":1.0}},"i":{"df":1,"docs":{"101":{"tf":1.0}}}}},"v":{"a":{"df":0,"docs":{},"t":{"df":5,"docs":{"34":{"tf":1.0},"35":{"tf":1.7320508075688772},"44":{"tf":1.7320508075688772},"69":{"tf":1.0},"7":{"tf":2.0}}}},"df":0,"docs":{}}},"o":{"b":{"a":{"b":{"df":0,"docs":{},"l":{"df":5,"docs":{"111":{"tf":1.0},"14":{"tf":2.6457513110645907},"19":{"tf":1.0},"41":{"tf":1.0},"85":{"tf":1.0}}}},"df":0,"docs":{}},"df":0,"docs":{},"l":{"df":0,"docs":{},"e":{"df":0,"docs":{},"m":{"df":4,"docs":{"125":{"tf":1.4142135623730951},"149":{"tf":1.0},"21":{"tf":1.0},"50":{"tf":1.0}}}}}},"c":{"df":0,"docs":{},"e":{"df":2,"docs":{"33":{"tf":1.0},"80":{"tf":1.0}},"s":{"df":0,"docs":{},"s":{"df":9,"docs":{"14":{"tf":1.0},"151":{"tf":1.0},"20":{"tf":1.4142135623730951},"4":{"tf":1.0},"40":{"tf":1.0},"69":{"tf":1.0},"77":{"tf":1.0},"8":{"tf":1.0},"94":{"tf":1.0}}}}}},"d":{"df":0,"docs":{},"u":{"c":{"df":5,"docs":{"141":{"tf":1.0},"19":{"tf":1.4142135623730951},"20":{"tf":1.0},"75":{"tf":1.0},"8":{"tf":1.0}},"t":{"df":6,"docs":{"123":{"tf":1.4142135623730951},"138":{"tf":1.7320508075688772},"139":{"tf":1.7320508075688772},"14":{"tf":1.4142135623730951},"144":{"tf":1.0},"146":{"tf":2.449489742783178}}}},"df":0,"docs":{}}},"df":0,"docs":{},"g":{"df":0,"docs":{},"r":{"a":{"df":0,"docs":{},"m":{"df":25,"docs":{"10":{"tf":1.0},"12":{"tf":1.4142135623730951},"127":{"tf":1.0},"13":{"tf":2.23606797749979},"133":{"tf":1.4142135623730951},"14":{"tf":1.0},"141":{"tf":1.0},"142":{"tf":1.0},"144":{"tf":1.7320508075688772},"146":{"tf":2.0},"147":{"tf":1.0},"16":{"tf":1.0},"23":{"tf":1.0},"34":{"tf":1.4142135623730951},"35":{"tf":1.4142135623730951},"4":{"tf":1.0},"43":{"tf":1.0},"44":{"tf":1.0},"45":{"tf":1.4142135623730951},"50":{"tf":2.0},"69":{"tf":1.0},"7":{"tf":3.605551275463989},"8":{"tf":1.4142135623730951},"9":{"tf":1.7320508075688772},"91":{"tf":1.4142135623730951}}}},"df":0,"docs":{},"e":{"df":0,"docs":{},"s":{"df":0,"docs":{},"s":{"df":1,"docs":{"0":{"tf":1.0}}}}}}},"o":{"df":0,"docs":{},"f":{"<":{"df":0,"docs":{},"f":{"df":1,"docs":{"35":{"tf":1.0}}}},"_":{"df":0,"docs":{},"o":{"df":0,"docs":{},"p":{"df":0,"docs":{},"t":{"df":2,"docs":{"104":{"tf":1.4142135623730951},"110":{"tf":1.4142135623730951}},"i":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"df":0,"docs":{},"s":{")":{".":{"df":0,"docs":{},"u":{"df":0,"docs":{},"n":{"df":0,"docs":{},"w":{"df":0,"docs":{},"r":{"a":{"df":0,"docs":{},"p":{"df":1,"docs":{"104":{"tf":1.0}}}},"df":0,"docs":{}}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}}}}}}},"df":39,"docs":{"102":{"tf":1.7320508075688772},"104":{"tf":3.0},"109":{"tf":1.4142135623730951},"110":{"tf":2.0},"112":{"tf":1.0},"113":{"tf":1.0},"119":{"tf":2.23606797749979},"13":{"tf":1.0},"14":{"tf":1.0},"144":{"tf":1.0},"146":{"tf":1.7320508075688772},"16":{"tf":1.0},"19":{"tf":1.4142135623730951},"21":{"tf":2.449489742783178},"22":{"tf":1.0},"28":{"tf":1.4142135623730951},"29":{"tf":1.7320508075688772},"32":{"tf":1.0},"33":{"tf":1.7320508075688772},"34":{"tf":1.4142135623730951},"35":{"tf":2.8284271247461903},"4":{"tf":1.0},"42":{"tf":1.0},"44":{"tf":1.7320508075688772},"50":{"tf":2.0},"57":{"tf":1.0},"59":{"tf":1.0},"60":{"tf":1.0},"61":{"tf":1.0},"62":{"tf":1.0},"63":{"tf":1.0},"7":{"tf":2.0},"77":{"tf":1.0},"8":{"tf":1.0},"80":{"tf":1.4142135623730951},"88":{"tf":1.0},"91":{"tf":1.0},"94":{"tf":1.4142135623730951},"95":{"tf":1.4142135623730951}},"o":{"df":0,"docs":{},"p":{"df":0,"docs":{},"t":{"df":1,"docs":{"109":{"tf":1.4142135623730951}},"i":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"df":0,"docs":{},"s":{":":{":":{"d":{"df":0,"docs":{},"e":{"df":0,"docs":{},"f":{"a":{"df":0,"docs":{},"u":{"df":0,"docs":{},"l":{"df":0,"docs":{},"t":{"_":{"df":0,"docs":{},"t":{"df":0,"docs":{},"e":{"df":0,"docs":{},"s":{"df":0,"docs":{},"t":{"_":{"df":0,"docs":{},"o":{"df":0,"docs":{},"p":{"df":0,"docs":{},"t":{"df":1,"docs":{"104":{"tf":1.0}}}}}},"df":0,"docs":{}}}}}},"df":0,"docs":{}}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}}}}}}}},"p":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":0,"docs":{},"l":{"df":0,"docs":{},"i":{"df":2,"docs":{"146":{"tf":1.0},"85":{"tf":1.0}}}},"t":{"df":0,"docs":{},"i":{"df":8,"docs":{"14":{"tf":1.0},"146":{"tf":1.0},"19":{"tf":1.4142135623730951},"20":{"tf":1.0},"52":{"tf":1.0},"70":{"tf":2.0},"73":{"tf":1.0},"84":{"tf":1.0}}}}}},"o":{"df":0,"docs":{},"r":{"df":0,"docs":{},"t":{"df":1,"docs":{"73":{"tf":1.0}}}}}},"t":{"df":0,"docs":{},"o":{"c":{"df":0,"docs":{},"o":{"df":0,"docs":{},"l":{"df":64,"docs":{"100":{"tf":1.0},"101":{"tf":1.7320508075688772},"102":{"tf":1.4142135623730951},"109":{"tf":1.7320508075688772},"118":{"tf":1.4142135623730951},"123":{"tf":1.0},"14":{"tf":1.0},"142":{"tf":1.0},"16":{"tf":1.7320508075688772},"17":{"tf":1.7320508075688772},"18":{"tf":1.0},"19":{"tf":1.0},"20":{"tf":1.7320508075688772},"21":{"tf":1.0},"22":{"tf":1.0},"23":{"tf":1.0},"24":{"tf":1.0},"25":{"tf":1.0},"26":{"tf":1.0},"27":{"tf":1.0},"28":{"tf":1.0},"29":{"tf":1.0},"30":{"tf":1.0},"31":{"tf":1.0},"32":{"tf":1.0},"33":{"tf":1.0},"34":{"tf":1.0},"37":{"tf":1.0},"4":{"tf":1.0},"57":{"tf":1.4142135623730951},"58":{"tf":1.0},"59":{"tf":1.0},"68":{"tf":1.0},"69":{"tf":2.449489742783178},"70":{"tf":1.7320508075688772},"71":{"tf":1.7320508075688772},"72":{"tf":1.0},"73":{"tf":2.6457513110645907},"74":{"tf":1.4142135623730951},"75":{"tf":2.449489742783178},"76":{"tf":1.0},"77":{"tf":1.7320508075688772},"78":{"tf":1.4142135623730951},"79":{"tf":1.0},"80":{"tf":2.0},"81":{"tf":1.0},"82":{"tf":2.0},"83":{"tf":1.4142135623730951},"84":{"tf":1.7320508075688772},"85":{"tf":1.4142135623730951},"86":{"tf":2.0},"87":{"tf":2.0},"88":{"tf":1.7320508075688772},"89":{"tf":1.4142135623730951},"90":{"tf":1.0},"91":{"tf":1.0},"92":{"tf":1.4142135623730951},"93":{"tf":1.7320508075688772},"94":{"tf":1.4142135623730951},"95":{"tf":1.0},"96":{"tf":1.0},"97":{"tf":1.0},"98":{"tf":1.0},"99":{"tf":1.0}}}}},"df":0,"docs":{}}},"v":{"a":{"b":{"df":0,"docs":{},"l":{"df":1,"docs":{"149":{"tf":1.4142135623730951}}}},"df":0,"docs":{}},"df":0,"docs":{},"e":{"(":{"&":{"df":0,"docs":{},"t":{"df":0,"docs":{},"r":{"a":{"c":{"df":0,"docs":{},"e":{"_":{"df":0,"docs":{},"t":{"df":1,"docs":{"110":{"tf":1.0}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}},":":{":":{"<":{"df":0,"docs":{},"f":{"df":1,"docs":{"104":{"tf":1.0}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":34,"docs":{"102":{"tf":1.0},"103":{"tf":1.0},"104":{"tf":1.4142135623730951},"105":{"tf":1.0},"108":{"tf":1.0},"109":{"tf":1.0},"110":{"tf":2.0},"111":{"tf":1.0},"113":{"tf":1.0},"114":{"tf":1.4142135623730951},"118":{"tf":1.0},"138":{"tf":1.0},"14":{"tf":1.0},"144":{"tf":1.0},"146":{"tf":1.7320508075688772},"151":{"tf":1.7320508075688772},"16":{"tf":1.0},"19":{"tf":1.4142135623730951},"23":{"tf":1.7320508075688772},"34":{"tf":1.0},"35":{"tf":1.0},"4":{"tf":1.0},"42":{"tf":1.0},"43":{"tf":1.0},"44":{"tf":1.0},"46":{"tf":1.0},"48":{"tf":1.0},"49":{"tf":1.0},"50":{"tf":1.0},"70":{"tf":1.0},"73":{"tf":1.4142135623730951},"74":{"tf":1.0},"84":{"tf":1.0},"85":{"tf":1.0}},"r":{"'":{"df":2,"docs":{"35":{"tf":1.0},"7":{"tf":1.0}}},".":{"df":0,"docs":{},"p":{"df":0,"docs":{},"r":{"df":0,"docs":{},"o":{"df":0,"docs":{},"v":{"df":1,"docs":{"35":{"tf":1.4142135623730951}},"e":{"(":{"&":{"df":0,"docs":{},"w":{"df":0,"docs":{},"i":{"df":0,"docs":{},"t":{"df":1,"docs":{"44":{"tf":1.0}}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}}}}},":":{":":{"df":0,"docs":{},"n":{"df":0,"docs":{},"e":{"df":0,"docs":{},"w":{"(":{"df":0,"docs":{},"k":{"df":0,"docs":{},"z":{"df":0,"docs":{},"g":{".":{"c":{"df":0,"docs":{},"l":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"df":2,"docs":{"35":{"tf":1.4142135623730951},"44":{"tf":1.0}}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"<":{"df":0,"docs":{},"f":{"df":1,"docs":{"35":{"tf":1.0}}}},"df":71,"docs":{"101":{"tf":1.0},"102":{"tf":1.4142135623730951},"103":{"tf":1.4142135623730951},"104":{"tf":1.4142135623730951},"111":{"tf":1.4142135623730951},"112":{"tf":1.4142135623730951},"113":{"tf":2.0},"117":{"tf":1.4142135623730951},"118":{"tf":2.8284271247461903},"126":{"tf":2.0},"127":{"tf":2.0},"128":{"tf":1.0},"129":{"tf":1.0},"13":{"tf":1.4142135623730951},"130":{"tf":1.0},"131":{"tf":1.4142135623730951},"132":{"tf":1.0},"133":{"tf":1.0},"134":{"tf":1.0},"135":{"tf":1.0},"136":{"tf":1.0},"137":{"tf":1.0},"138":{"tf":1.0},"139":{"tf":1.0},"144":{"tf":1.4142135623730951},"146":{"tf":1.7320508075688772},"147":{"tf":1.4142135623730951},"151":{"tf":1.4142135623730951},"20":{"tf":2.0},"21":{"tf":2.449489742783178},"23":{"tf":1.0},"31":{"tf":1.0},"32":{"tf":1.0},"34":{"tf":1.0},"35":{"tf":2.23606797749979},"44":{"tf":1.4142135623730951},"45":{"tf":1.0},"46":{"tf":1.7320508075688772},"50":{"tf":1.4142135623730951},"51":{"tf":1.0},"53":{"tf":1.0},"56":{"tf":1.0},"57":{"tf":1.0},"58":{"tf":2.0},"59":{"tf":2.0},"60":{"tf":2.0},"61":{"tf":1.0},"62":{"tf":1.4142135623730951},"63":{"tf":2.0},"66":{"tf":1.4142135623730951},"69":{"tf":1.7320508075688772},"7":{"tf":1.4142135623730951},"71":{"tf":1.0},"72":{"tf":1.7320508075688772},"73":{"tf":1.4142135623730951},"74":{"tf":1.0},"75":{"tf":2.449489742783178},"77":{"tf":2.23606797749979},"78":{"tf":1.0},"79":{"tf":2.0},"80":{"tf":1.7320508075688772},"82":{"tf":1.0},"83":{"tf":2.23606797749979},"84":{"tf":2.0},"85":{"tf":1.0},"86":{"tf":1.0},"88":{"tf":1.0},"91":{"tf":2.23606797749979},"94":{"tf":1.4142135623730951},"95":{"tf":1.0},"97":{"tf":1.0}}}},"i":{"d":{"df":19,"docs":{"103":{"tf":1.0},"109":{"tf":1.0},"112":{"tf":2.0},"113":{"tf":1.7320508075688772},"117":{"tf":1.0},"118":{"tf":1.0},"125":{"tf":1.0},"34":{"tf":1.0},"48":{"tf":1.0},"50":{"tf":1.4142135623730951},"57":{"tf":1.0},"58":{"tf":1.0},"59":{"tf":1.7320508075688772},"60":{"tf":1.4142135623730951},"62":{"tf":2.0},"63":{"tf":1.7320508075688772},"66":{"tf":1.4142135623730951},"68":{"tf":1.0},"80":{"tf":1.0}}},"df":0,"docs":{}}}}},"u":{"b":{"_":{"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"df":0,"docs":{},"p":{"df":0,"docs":{},"u":{"df":0,"docs":{},"t":{"df":2,"docs":{"104":{"tf":1.7320508075688772},"110":{"tf":1.4142135623730951}}}}}}}},"df":4,"docs":{"133":{"tf":1.4142135623730951},"135":{"tf":1.4142135623730951},"35":{"tf":6.928203230275509},"45":{"tf":1.0}},"l":{"c":{"df":1,"docs":{"135":{"tf":1.0}}},"df":0,"docs":{},"i":{"c":{"_":{"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"df":0,"docs":{},"p":{"df":0,"docs":{},"u":{"df":0,"docs":{},"t":{"df":2,"docs":{"35":{"tf":2.449489742783178},"44":{"tf":1.7320508075688772}}}}}}}},"df":26,"docs":{"104":{"tf":2.0},"106":{"tf":1.4142135623730951},"110":{"tf":1.0},"13":{"tf":2.23606797749979},"133":{"tf":2.23606797749979},"135":{"tf":1.4142135623730951},"14":{"tf":1.4142135623730951},"142":{"tf":1.0},"144":{"tf":1.4142135623730951},"146":{"tf":2.8284271247461903},"147":{"tf":1.0},"151":{"tf":1.0},"16":{"tf":1.7320508075688772},"23":{"tf":1.4142135623730951},"26":{"tf":1.0},"32":{"tf":1.4142135623730951},"34":{"tf":1.4142135623730951},"35":{"tf":1.4142135623730951},"36":{"tf":1.0},"42":{"tf":1.0},"44":{"tf":2.0},"58":{"tf":1.0},"69":{"tf":1.0},"7":{"tf":1.0},"94":{"tf":1.0},"95":{"tf":1.0}},"l":{"df":0,"docs":{},"i":{"df":2,"docs":{"144":{"tf":1.0},"19":{"tf":1.0}}}}},"df":0,"docs":{},"s":{"df":0,"docs":{},"h":{"df":1,"docs":{"22":{"tf":1.0}}}}}}},"df":0,"docs":{},"r":{"df":0,"docs":{},"p":{"df":0,"docs":{},"o":{"df":0,"docs":{},"s":{"df":4,"docs":{"149":{"tf":1.0},"34":{"tf":1.0},"50":{"tf":1.4142135623730951},"56":{"tf":1.0}}}}}},"t":{"df":7,"docs":{"10":{"tf":1.0},"13":{"tf":1.0},"14":{"tf":1.4142135623730951},"147":{"tf":1.0},"151":{"tf":1.0},"45":{"tf":1.0},"9":{"tf":1.4142135623730951}}}},"ˉ":{"df":0,"docs":{},"​":{"c":{"df":0,"docs":{},"​":{":":{"=":{"df":0,"docs":{},"p":{"df":0,"docs":{},"i":{"(":{"df":0,"docs":{},"ζ":{")":{"+":{"df":0,"docs":{},"α":{"df":0,"docs":{},"z":{"df":0,"docs":{},"ˉ":{"df":0,"docs":{},"ω":{"df":0,"docs":{},"​":{"(":{"c":{"df":0,"docs":{},"ˉ":{"+":{"df":0,"docs":{},"γ":{")":{"(":{"a":{"df":0,"docs":{},"ˉ":{"+":{"df":0,"docs":{},"β":{"df":0,"docs":{},"s":{"df":0,"docs":{},"ˉ":{"df":0,"docs":{},"σ":{"1":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"γ":{")":{"(":{"b":{"df":0,"docs":{},"ˉ":{"+":{"df":0,"docs":{},"β":{"df":0,"docs":{},"s":{"df":0,"docs":{},"ˉ":{"df":0,"docs":{},"σ":{"2":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"γ":{")":{"df":0,"docs":{},"−":{"df":0,"docs":{},"α":{"2":{"df":0,"docs":{},"l":{"1":{"df":1,"docs":{"32":{"tf":1.0}}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{},"n":{"c":{"df":1,"docs":{"32":{"tf":1.0}},"​":{":":{"=":{"df":0,"docs":{},"p":{"df":0,"docs":{},"n":{"c":{"df":1,"docs":{"28":{"tf":1.0}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"∈":{"df":0,"docs":{},"f":{"[":{"df":0,"docs":{},"x":{"df":1,"docs":{"90":{"tf":1.0}}}},"df":0,"docs":{}}},"−":{"df":0,"docs":{},"y":{"=":{"(":{"df":0,"docs":{},"x":{"df":0,"docs":{},"−":{"df":0,"docs":{},"z":{")":{"df":0,"docs":{},"q":{"df":1,"docs":{"78":{"tf":1.0}}}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"q":{"(":{"d":{"1":{"df":0,"docs":{},"​":{")":{",":{"df":0,"docs":{},"…":{",":{"df":0,"docs":{},"q":{"(":{"d":{"df":0,"docs":{},"m":{"df":1,"docs":{"78":{"tf":1.0}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},",":{"df":0,"docs":{},"v":{",":{"df":0,"docs":{},"t":{",":{"df":0,"docs":{},"p":{"df":0,"docs":{},"i":{"df":1,"docs":{"36":{"tf":1.0}}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"c":{"_":{"1":{"df":1,"docs":{"35":{"tf":1.0}}},"df":0,"docs":{}},"df":4,"docs":{"10":{"tf":2.23606797749979},"12":{"tf":1.0},"13":{"tf":2.0},"35":{"tf":1.0}}},"df":15,"docs":{"10":{"tf":1.7320508075688772},"11":{"tf":1.0},"12":{"tf":1.0},"13":{"tf":1.7320508075688772},"14":{"tf":1.0},"16":{"tf":1.4142135623730951},"34":{"tf":1.4142135623730951},"36":{"tf":1.0},"74":{"tf":1.0},"78":{"tf":1.4142135623730951},"8":{"tf":1.0},"80":{"tf":1.4142135623730951},"9":{"tf":1.0},"90":{"tf":1.0},"91":{"tf":1.0}},"k":{"df":0,"docs":{},"t":{"df":0,"docs":{},"​":{"(":{"df":0,"docs":{},"x":{")":{"=":{"df":0,"docs":{},"p":{"df":0,"docs":{},"k":{"df":0,"docs":{},"t":{"df":0,"docs":{},"​":{"(":{"df":0,"docs":{},"t":{"1":{"df":0,"docs":{},"​":{"(":{"df":0,"docs":{},"x":{")":{",":{"df":0,"docs":{},"…":{",":{"df":0,"docs":{},"t":{"df":0,"docs":{},"m":{"df":0,"docs":{},"​":{"(":{"df":0,"docs":{},"x":{")":{",":{"df":0,"docs":{},"t":{"1":{"df":0,"docs":{},"​":{"(":{"df":0,"docs":{},"g":{"df":0,"docs":{},"x":{")":{",":{"df":0,"docs":{},"…":{",":{"df":0,"docs":{},"t":{"df":0,"docs":{},"m":{"df":0,"docs":{},"​":{"(":{"df":0,"docs":{},"ω":{"df":0,"docs":{},"x":{"df":1,"docs":{"84":{"tf":1.0}}}}},"df":0,"docs":{}}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"l":{"_":{"1":{"df":1,"docs":{"35":{"tf":1.0}}},"df":0,"docs":{}},"df":5,"docs":{"10":{"tf":2.23606797749979},"12":{"tf":1.0},"13":{"tf":1.4142135623730951},"21":{"tf":2.0},"35":{"tf":1.0}},"​":{"(":{"df":0,"docs":{},"ζ":{")":{",":{"df":0,"docs":{},"q":{"df":0,"docs":{},"r":{"df":1,"docs":{"21":{"tf":1.0}}}}},"df":0,"docs":{}},"df":0,"docs":{}}},",":{"df":0,"docs":{},"q":{"df":0,"docs":{},"r":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"q":{"df":0,"docs":{},"m":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"q":{"df":0,"docs":{},"o":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"q":{"c":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"s":{"df":0,"docs":{},"σ":{"1":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"s":{"df":0,"docs":{},"σ":{"2":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"s":{"df":0,"docs":{},"σ":{"3":{"df":2,"docs":{"15":{"tf":1.0},"16":{"tf":1.0}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}},"df":0,"docs":{}}},"o":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"q":{"df":0,"docs":{},"m":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"q":{"c":{"df":1,"docs":{"10":{"tf":1.0}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}},"df":0,"docs":{}}}}},"df":0,"docs":{}}}}},"]":{"1":{"df":1,"docs":{"21":{"tf":1.0}},"​":{",":{"[":{"df":0,"docs":{},"q":{"df":0,"docs":{},"r":{"df":0,"docs":{},"​":{"]":{"1":{"df":0,"docs":{},"​":{",":{"[":{"df":0,"docs":{},"q":{"df":0,"docs":{},"m":{"df":0,"docs":{},"​":{"]":{"1":{"df":0,"docs":{},"​":{",":{"[":{"df":0,"docs":{},"q":{"df":0,"docs":{},"o":{"df":0,"docs":{},"​":{"]":{"1":{"df":0,"docs":{},"​":{",":{"[":{"df":0,"docs":{},"q":{"c":{"df":0,"docs":{},"​":{"]":{"1":{"df":0,"docs":{},"​":{",":{"[":{"df":0,"docs":{},"s":{"df":0,"docs":{},"σ":{"1":{"df":0,"docs":{},"​":{"]":{"1":{"df":0,"docs":{},"​":{",":{"[":{"df":0,"docs":{},"s":{"df":0,"docs":{},"σ":{"2":{"df":0,"docs":{},"​":{"]":{"1":{"df":0,"docs":{},"​":{",":{"[":{"df":0,"docs":{},"s":{"df":0,"docs":{},"σ":{"3":{"df":0,"docs":{},"​":{"]":{"1":{"df":1,"docs":{"22":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"m":{"_":{"1":{"df":1,"docs":{"35":{"tf":1.0}}},"df":0,"docs":{}},"df":4,"docs":{"10":{"tf":2.23606797749979},"12":{"tf":1.0},"13":{"tf":1.4142135623730951},"35":{"tf":1.0}}},"o":{"_":{"1":{"df":1,"docs":{"35":{"tf":1.0}}},"df":0,"docs":{}},"df":4,"docs":{"10":{"tf":2.23606797749979},"12":{"tf":1.0},"13":{"tf":1.4142135623730951},"35":{"tf":1.0}}},"r":{"_":{"1":{"df":1,"docs":{"35":{"tf":1.0}}},"df":0,"docs":{}},"df":5,"docs":{"10":{"tf":2.23606797749979},"12":{"tf":1.0},"13":{"tf":1.4142135623730951},"21":{"tf":2.0},"35":{"tf":1.0}},"​":{"]":{"1":{"df":1,"docs":{"21":{"tf":1.4142135623730951}}},"df":0,"docs":{}},"df":0,"docs":{}}},"u":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":0,"docs":{},"i":{"df":4,"docs":{"109":{"tf":1.0},"119":{"tf":1.0},"91":{"tf":1.0},"94":{"tf":1.0}}}},"s":{"df":0,"docs":{},"t":{"df":0,"docs":{},"i":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"df":1,"docs":{"14":{"tf":1.0}}}}}}}},"i":{"c":{"df":0,"docs":{},"k":{"df":1,"docs":{"131":{"tf":1.0}}}},"df":0,"docs":{},"t":{"df":1,"docs":{"146":{"tf":1.0}}}},"o":{"df":0,"docs":{},"t":{"df":0,"docs":{},"i":{"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"df":0,"docs":{},"t":{"df":2,"docs":{"35":{"tf":1.7320508075688772},"69":{"tf":1.0}}}}}}}}}},"r":{"0":{"df":1,"docs":{"151":{"tf":1.4142135623730951}}},"a":{"d":{"df":0,"docs":{},"i":{"df":0,"docs":{},"x":{"df":1,"docs":{"121":{"tf":1.0}}}}},"df":0,"docs":{},"n":{"d":{"df":0,"docs":{},"o":{"df":0,"docs":{},"m":{"_":{"df":0,"docs":{},"g":{"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":1,"docs":{"35":{"tf":2.23606797749979}}}}}}}},"df":19,"docs":{"118":{"tf":1.4142135623730951},"14":{"tf":2.8284271247461903},"16":{"tf":2.0},"20":{"tf":1.7320508075688772},"24":{"tf":1.0},"25":{"tf":1.0},"26":{"tf":1.0},"3":{"tf":1.0},"35":{"tf":1.0},"58":{"tf":1.0},"60":{"tf":1.4142135623730951},"69":{"tf":1.4142135623730951},"70":{"tf":1.4142135623730951},"79":{"tf":1.0},"83":{"tf":1.0},"84":{"tf":1.0},"85":{"tf":1.0},"94":{"tf":1.4142135623730951},"95":{"tf":1.4142135623730951}},"l":{"df":0,"docs":{},"i":{"df":3,"docs":{"118":{"tf":1.0},"59":{"tf":1.0},"85":{"tf":1.0}}}}}}},"df":0,"docs":{},"g":{"df":9,"docs":{"130":{"tf":1.4142135623730951},"134":{"tf":1.0},"138":{"tf":1.4142135623730951},"14":{"tf":1.4142135623730951},"144":{"tf":1.0},"146":{"tf":2.23606797749979},"147":{"tf":1.4142135623730951},"151":{"tf":2.449489742783178},"70":{"tf":1.0}},"e":{"_":{"c":{"df":0,"docs":{},"h":{"df":0,"docs":{},"e":{"c":{"df":0,"docs":{},"k":{"_":{"c":{"df":0,"docs":{},"e":{"df":0,"docs":{},"l":{"df":0,"docs":{},"l":{".":{"df":0,"docs":{},"h":{"df":1,"docs":{"130":{"tf":1.0}}},"i":{"df":0,"docs":{},"n":{"df":0,"docs":{},"l":{"df":1,"docs":{"130":{"tf":1.0}}}}}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"p":{"2":{"df":1,"docs":{"83":{"tf":1.0}}},"df":3,"docs":{"147":{"tf":1.4142135623730951},"91":{"tf":1.7320508075688772},"94":{"tf":1.0}}},"t":{"df":0,"docs":{},"i":{"df":0,"docs":{},"o":{"df":2,"docs":{"149":{"tf":1.0},"151":{"tf":1.0}},"n":{"df":3,"docs":{"54":{"tf":1.0},"55":{"tf":1.0},"56":{"tf":1.0}}}}}},"w":{"df":2,"docs":{"144":{"tf":1.4142135623730951},"40":{"tf":1.0}}}},"c":{"1":{"6":{"/":{"df":0,"docs":{},"s":{"df":0,"docs":{},"o":{"df":0,"docs":{},"r":{"df":0,"docs":{},"t":{"df":1,"docs":{"132":{"tf":1.4142135623730951}}}}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":5,"docs":{"130":{"tf":1.7320508075688772},"131":{"tf":1.4142135623730951},"132":{"tf":1.4142135623730951},"138":{"tf":1.4142135623730951},"146":{"tf":1.0}},"m":{"a":{"df":0,"docs":{},"x":{"df":1,"docs":{"146":{"tf":1.0}}}},"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"df":1,"docs":{"146":{"tf":1.0}}}}}},"df":10,"docs":{"11":{"tf":1.0},"12":{"tf":1.0},"13":{"tf":1.0},"14":{"tf":1.0},"142":{"tf":2.6457513110645907},"35":{"tf":1.7320508075688772},"38":{"tf":1.0},"9":{"tf":1.0},"91":{"tf":1.0},"92":{"tf":1.4142135623730951}},"e":{"a":{"c":{"df":0,"docs":{},"h":{"df":1,"docs":{"146":{"tf":1.0}}}},"d":{"df":2,"docs":{"111":{"tf":1.4142135623730951},"69":{"tf":1.0}},"e":{"df":0,"docs":{},"r":{"df":1,"docs":{"125":{"tf":1.0}}}},"i":{"df":1,"docs":{"146":{"tf":1.0}}}},"df":0,"docs":{},"l":{"df":4,"docs":{"128":{"tf":1.7320508075688772},"135":{"tf":1.0},"146":{"tf":1.0},"149":{"tf":1.0}},"i":{"df":0,"docs":{},"t":{"df":0,"docs":{},"i":{"df":1,"docs":{"146":{"tf":1.0}}}}},"l":{"df":0,"docs":{},"i":{"df":4,"docs":{"116":{"tf":1.0},"123":{"tf":1.0},"14":{"tf":1.0},"50":{"tf":1.0}}}}},"r":{"df":0,"docs":{},"r":{"a":{"df":0,"docs":{},"n":{"df":0,"docs":{},"g":{"df":1,"docs":{"14":{"tf":1.0}}}}},"df":0,"docs":{}}},"s":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"df":6,"docs":{"117":{"tf":1.0},"123":{"tf":1.0},"146":{"tf":1.0},"20":{"tf":1.0},"50":{"tf":1.0},"56":{"tf":1.0}}}}}},"c":{"a":{"df":0,"docs":{},"l":{"c":{"df":0,"docs":{},"u":{"df":0,"docs":{},"l":{"df":1,"docs":{"146":{"tf":1.0}}}}},"df":7,"docs":{"111":{"tf":1.0},"125":{"tf":1.0},"14":{"tf":2.0},"16":{"tf":1.0},"54":{"tf":1.0},"91":{"tf":1.0},"95":{"tf":1.0}}},"p":{"df":46,"docs":{"10":{"tf":1.0},"103":{"tf":1.0},"105":{"tf":1.0},"11":{"tf":1.0},"111":{"tf":1.0},"116":{"tf":1.0},"12":{"tf":1.0},"13":{"tf":1.0},"14":{"tf":1.0},"15":{"tf":1.0},"16":{"tf":1.0},"34":{"tf":1.4142135623730951},"35":{"tf":1.0},"36":{"tf":1.0},"4":{"tf":1.0},"46":{"tf":1.0},"47":{"tf":1.7320508075688772},"48":{"tf":1.0},"49":{"tf":1.0},"5":{"tf":1.0},"50":{"tf":1.0},"51":{"tf":1.0},"52":{"tf":1.0},"53":{"tf":1.0},"54":{"tf":1.0},"55":{"tf":1.0},"56":{"tf":1.0},"57":{"tf":1.0},"58":{"tf":1.0},"59":{"tf":1.0},"6":{"tf":1.0},"60":{"tf":1.0},"61":{"tf":1.0},"62":{"tf":1.0},"63":{"tf":1.0},"64":{"tf":1.0},"65":{"tf":1.0},"66":{"tf":1.0},"67":{"tf":1.0},"68":{"tf":1.0},"69":{"tf":1.0},"7":{"tf":1.4142135623730951},"70":{"tf":1.0},"8":{"tf":1.0},"84":{"tf":1.0},"9":{"tf":1.0}}}},"df":0,"docs":{},"e":{"df":0,"docs":{},"i":{"df":0,"docs":{},"v":{"df":5,"docs":{"114":{"tf":1.0},"21":{"tf":1.0},"77":{"tf":1.0},"79":{"tf":1.0},"95":{"tf":1.0}}}}},"o":{"df":0,"docs":{},"g":{"df":0,"docs":{},"n":{"df":1,"docs":{"16":{"tf":1.0}}}},"m":{"df":0,"docs":{},"m":{"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"d":{"df":1,"docs":{"69":{"tf":1.0}}},"df":0,"docs":{}}}}},"n":{"df":0,"docs":{},"s":{"df":0,"docs":{},"t":{"df":0,"docs":{},"r":{"df":0,"docs":{},"u":{"c":{"df":0,"docs":{},"t":{"df":8,"docs":{"102":{"tf":1.4142135623730951},"112":{"tf":1.0},"113":{"tf":1.0},"114":{"tf":1.7320508075688772},"115":{"tf":1.0},"117":{"tf":1.0},"63":{"tf":1.0},"95":{"tf":1.0}}}},"df":0,"docs":{}}}}}},"v":{"df":3,"docs":{"114":{"tf":1.7320508075688772},"124":{"tf":1.0},"95":{"tf":1.0}}}},"u":{"df":0,"docs":{},"r":{"df":0,"docs":{},"s":{"df":2,"docs":{"14":{"tf":1.0},"25":{"tf":1.0}}}}}},"d":{"df":0,"docs":{},"u":{"c":{"df":6,"docs":{"101":{"tf":1.0},"12":{"tf":1.0},"14":{"tf":1.7320508075688772},"144":{"tf":1.0},"21":{"tf":1.4142135623730951},"80":{"tf":1.7320508075688772}}},"df":0,"docs":{},"n":{"d":{"df":2,"docs":{"102":{"tf":1.4142135623730951},"70":{"tf":1.0}}},"df":0,"docs":{}}}},"df":4,"docs":{"136":{"tf":1.4142135623730951},"142":{"tf":1.4142135623730951},"146":{"tf":2.449489742783178},"147":{"tf":1.4142135623730951}},"f":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":11,"docs":{"126":{"tf":1.0},"130":{"tf":1.0},"134":{"tf":1.0},"139":{"tf":1.0},"146":{"tf":1.0},"3":{"tf":1.0},"70":{"tf":1.0},"81":{"tf":1.4142135623730951},"83":{"tf":1.0},"87":{"tf":1.0},"95":{"tf":2.0}}}},"l":{"df":0,"docs":{},"e":{"c":{"df":0,"docs":{},"t":{"df":1,"docs":{"133":{"tf":1.0}}}},"df":0,"docs":{}}},"o":{"df":0,"docs":{},"r":{"df":0,"docs":{},"m":{"df":0,"docs":{},"u":{"df":0,"docs":{},"l":{"df":1,"docs":{"16":{"tf":1.0}}}}}}}},"g":{"a":{"df":0,"docs":{},"r":{"d":{"df":1,"docs":{"126":{"tf":1.0}}},"df":0,"docs":{}}},"df":1,"docs":{"146":{"tf":1.4142135623730951}},"i":{"df":0,"docs":{},"s":{"df":0,"docs":{},"t":{"df":8,"docs":{"127":{"tf":1.0},"133":{"tf":1.0},"136":{"tf":2.0},"141":{"tf":1.0},"144":{"tf":1.4142135623730951},"146":{"tf":1.4142135623730951},"149":{"tf":1.0},"50":{"tf":1.0}},"e":{"df":0,"docs":{},"r":{"df":0,"docs":{},"s":{"df":0,"docs":{},"t":{"df":1,"docs":{"146":{"tf":1.0}}}}}}}}}},"j":{"df":0,"docs":{},"e":{"c":{"df":0,"docs":{},"t":{"df":4,"docs":{"19":{"tf":1.0},"33":{"tf":1.0},"7":{"tf":1.0},"92":{"tf":1.0}}}},"df":0,"docs":{}}},"l":{"a":{"df":0,"docs":{},"t":{"df":6,"docs":{"109":{"tf":2.0},"13":{"tf":1.0},"136":{"tf":1.0},"146":{"tf":1.0},"68":{"tf":1.0},"69":{"tf":1.0}},"i":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"df":0,"docs":{},"s":{"df":0,"docs":{},"h":{"df":0,"docs":{},"i":{"df":0,"docs":{},"p":{"df":4,"docs":{"107":{"tf":1.0},"151":{"tf":1.0},"53":{"tf":1.0},"66":{"tf":1.0}}}}}}}}}}},"df":2,"docs":{"146":{"tf":1.0},"70":{"tf":1.0}},"e":{"df":0,"docs":{},"v":{"df":2,"docs":{"139":{"tf":1.0},"87":{"tf":1.0}}}},"i":{"df":2,"docs":{"34":{"tf":1.0},"4":{"tf":1.0}}},"o":{"c":{"df":1,"docs":{"144":{"tf":1.0}}},"df":0,"docs":{}}},"m":{"a":{"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"df":6,"docs":{"125":{"tf":1.0},"130":{"tf":1.0},"59":{"tf":1.0},"65":{"tf":1.0},"66":{"tf":1.0},"67":{"tf":1.0}}}}},"df":0,"docs":{},"e":{"df":0,"docs":{},"m":{"b":{"df":1,"docs":{"114":{"tf":1.4142135623730951}}},"df":0,"docs":{}}},"i":{"df":0,"docs":{},"n":{"d":{"df":1,"docs":{"68":{"tf":1.0}}},"df":0,"docs":{}}},"o":{"df":0,"docs":{},"v":{"df":2,"docs":{"118":{"tf":1.0},"13":{"tf":1.0}}}}},"p":{"df":0,"docs":{},"e":{"a":{"df":0,"docs":{},"t":{"df":2,"docs":{"135":{"tf":1.0},"83":{"tf":1.0}}}},"df":0,"docs":{}},"h":{"df":0,"docs":{},"r":{"a":{"df":0,"docs":{},"s":{"df":1,"docs":{"14":{"tf":1.0}}}},"df":0,"docs":{}}},"l":{"a":{"c":{"df":3,"docs":{"135":{"tf":1.4142135623730951},"28":{"tf":1.0},"98":{"tf":1.0}}},"df":0,"docs":{},"y":{"df":1,"docs":{"95":{"tf":1.0}}}},"df":0,"docs":{}},"r":{"df":0,"docs":{},"e":{"df":0,"docs":{},"s":{"df":7,"docs":{"10":{"tf":2.23606797749979},"106":{"tf":1.0},"127":{"tf":1.4142135623730951},"146":{"tf":1.7320508075688772},"41":{"tf":1.0},"44":{"tf":1.0},"82":{"tf":1.0}},"e":{"df":0,"docs":{},"n":{"df":0,"docs":{},"t":{"df":6,"docs":{"101":{"tf":1.7320508075688772},"12":{"tf":1.0},"128":{"tf":1.0},"146":{"tf":1.7320508075688772},"4":{"tf":1.4142135623730951},"48":{"tf":1.4142135623730951}}}}}}}}},"q":{"df":0,"docs":{},"u":{"df":0,"docs":{},"i":{"df":0,"docs":{},"r":{"df":8,"docs":{"105":{"tf":1.0},"109":{"tf":1.0},"114":{"tf":1.0},"121":{"tf":1.0},"134":{"tf":1.0},"151":{"tf":1.0},"53":{"tf":1.0},"61":{"tf":1.0}}}}}},"s":{"_":{"df":0,"docs":{},"l":{"df":0,"docs":{},"o":{"df":0,"docs":{},"g":{"df":1,"docs":{"146":{"tf":1.4142135623730951}}}}}},"c":{"df":0,"docs":{},"u":{"df":1,"docs":{"14":{"tf":1.0}}}},"df":0,"docs":{},"e":{"df":0,"docs":{},"t":{"df":1,"docs":{"40":{"tf":1.0}}}},"h":{"a":{"df":0,"docs":{},"p":{"df":1,"docs":{"147":{"tf":1.7320508075688772}}}},"df":0,"docs":{}},"o":{"df":0,"docs":{},"u":{"df":0,"docs":{},"r":{"c":{"df":1,"docs":{"42":{"tf":1.0}}},"df":0,"docs":{}}}},"p":{"df":0,"docs":{},"e":{"c":{"df":0,"docs":{},"t":{"df":9,"docs":{"109":{"tf":1.0},"14":{"tf":2.449489742783178},"142":{"tf":1.0},"144":{"tf":1.0},"146":{"tf":1.4142135623730951},"147":{"tf":1.0},"16":{"tf":1.0},"3":{"tf":1.0},"32":{"tf":1.0}}}},"df":0,"docs":{}},"o":{"df":0,"docs":{},"n":{"df":0,"docs":{},"s":{"df":1,"docs":{"13":{"tf":1.0}}}}}},"t":{"df":2,"docs":{"114":{"tf":1.4142135623730951},"84":{"tf":1.0}}},"u":{"df":0,"docs":{},"l":{"df":0,"docs":{},"t":{"_":{"df":0,"docs":{},"t":{"df":0,"docs":{},"i":{"df":0,"docs":{},"m":{"df":0,"docs":{},"e":{"df":0,"docs":{},"s":{"_":{"b":{"a":{"df":0,"docs":{},"s":{"df":1,"docs":{"45":{"tf":1.4142135623730951}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}}}}},"df":23,"docs":{"11":{"tf":1.0},"112":{"tf":1.0},"13":{"tf":1.0},"135":{"tf":1.0},"14":{"tf":1.4142135623730951},"142":{"tf":1.4142135623730951},"146":{"tf":1.0},"147":{"tf":1.7320508075688772},"151":{"tf":1.4142135623730951},"16":{"tf":1.4142135623730951},"3":{"tf":1.0},"40":{"tf":1.4142135623730951},"41":{"tf":1.0},"45":{"tf":2.449489742783178},"5":{"tf":1.0},"56":{"tf":1.0},"62":{"tf":1.0},"73":{"tf":1.0},"75":{"tf":1.0},"77":{"tf":1.4142135623730951},"8":{"tf":1.0},"84":{"tf":1.4142135623730951},"9":{"tf":1.4142135623730951}}}}}},"t":{"df":0,"docs":{},"u":{"df":0,"docs":{},"r":{"df":0,"docs":{},"n":{"df":9,"docs":{"106":{"tf":1.0},"107":{"tf":1.4142135623730951},"114":{"tf":1.7320508075688772},"146":{"tf":1.0},"35":{"tf":1.4142135623730951},"40":{"tf":1.0},"74":{"tf":1.0},"80":{"tf":1.0},"92":{"tf":1.7320508075688772}}}}}},"u":{"df":0,"docs":{},"s":{"df":1,"docs":{"151":{"tf":1.0}}}},"v":{"df":0,"docs":{},"e":{"a":{"df":0,"docs":{},"l":{"df":6,"docs":{"19":{"tf":1.0},"20":{"tf":1.0},"69":{"tf":1.0},"7":{"tf":1.4142135623730951},"72":{"tf":1.0},"80":{"tf":1.0}}}},"df":0,"docs":{},"r":{"df":0,"docs":{},"s":{"df":1,"docs":{"101":{"tf":2.449489742783178}}}}},"o":{"df":0,"docs":{},"l":{"df":0,"docs":{},"v":{"df":1,"docs":{"104":{"tf":1.0}}}}}}},"i":{"d":{"df":1,"docs":{"14":{"tf":1.0}}},"df":0,"docs":{},"g":{"df":0,"docs":{},"h":{"df":0,"docs":{},"t":{"df":6,"docs":{"11":{"tf":1.0},"118":{"tf":1.0},"121":{"tf":1.0},"14":{"tf":1.0},"16":{"tf":1.0},"9":{"tf":2.23606797749979}}}}}},"m":{"a":{"df":0,"docs":{},"x":{"df":1,"docs":{"142":{"tf":1.4142135623730951}}}},"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"df":1,"docs":{"142":{"tf":1.4142135623730951}}}}},"n":{"df":0,"docs":{},"g":{"df":1,"docs":{"118":{"tf":1.0}}}},"o":{"df":0,"docs":{},"o":{"df":0,"docs":{},"t":{"df":29,"docs":{"112":{"tf":1.4142135623730951},"113":{"tf":1.0},"114":{"tf":1.4142135623730951},"119":{"tf":1.4142135623730951},"123":{"tf":2.6457513110645907},"124":{"tf":4.123105625617661},"125":{"tf":2.8284271247461903},"14":{"tf":3.0},"16":{"tf":1.0},"23":{"tf":1.0},"34":{"tf":1.0},"36":{"tf":1.0},"38":{"tf":1.0},"5":{"tf":1.0},"52":{"tf":2.0},"54":{"tf":1.7320508075688772},"60":{"tf":1.0},"62":{"tf":1.4142135623730951},"63":{"tf":1.0},"68":{"tf":3.0},"72":{"tf":1.7320508075688772},"73":{"tf":1.0},"75":{"tf":1.0},"76":{"tf":1.0},"80":{"tf":1.0},"82":{"tf":1.4142135623730951},"90":{"tf":1.0},"91":{"tf":1.0},"92":{"tf":2.0}}}},"u":{"df":0,"docs":{},"n":{"d":{"df":14,"docs":{"24":{"tf":1.4142135623730951},"25":{"tf":1.4142135623730951},"26":{"tf":1.4142135623730951},"27":{"tf":1.4142135623730951},"28":{"tf":1.4142135623730951},"35":{"tf":2.23606797749979},"37":{"tf":1.0},"82":{"tf":1.7320508075688772},"83":{"tf":2.0},"84":{"tf":2.0},"85":{"tf":1.7320508075688772},"86":{"tf":2.0},"91":{"tf":1.0},"94":{"tf":3.1622776601683795}}},"df":0,"docs":{}}},"w":{"df":32,"docs":{"10":{"tf":3.1622776601683795},"104":{"tf":1.0},"106":{"tf":1.7320508075688772},"107":{"tf":2.23606797749979},"108":{"tf":1.4142135623730951},"109":{"tf":1.7320508075688772},"119":{"tf":1.0},"12":{"tf":1.0},"125":{"tf":1.0},"127":{"tf":1.7320508075688772},"128":{"tf":2.0},"13":{"tf":3.1622776601683795},"130":{"tf":2.23606797749979},"133":{"tf":3.605551275463989},"134":{"tf":2.449489742783178},"136":{"tf":2.449489742783178},"139":{"tf":1.4142135623730951},"142":{"tf":2.449489742783178},"147":{"tf":2.6457513110645907},"149":{"tf":2.449489742783178},"20":{"tf":1.0},"23":{"tf":1.0},"36":{"tf":2.449489742783178},"49":{"tf":1.7320508075688772},"5":{"tf":1.0},"52":{"tf":2.0},"66":{"tf":1.7320508075688772},"69":{"tf":1.0},"70":{"tf":1.7320508075688772},"84":{"tf":1.4142135623730951},"9":{"tf":1.0},"91":{"tf":1.4142135623730951}}}},"u":{"df":0,"docs":{},"f":{"df":0,"docs":{},"f":{"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"df":0,"docs":{},"i":{"'":{"df":1,"docs":{"100":{"tf":1.7320508075688772}}},"df":0,"docs":{}}}}}},"g":{"df":1,"docs":{"64":{"tf":1.0}}},"l":{"df":0,"docs":{},"e":{"df":4,"docs":{"100":{"tf":1.7320508075688772},"48":{"tf":1.4142135623730951},"70":{"tf":1.0},"85":{"tf":1.0}}}},"n":{"df":5,"docs":{"144":{"tf":1.0},"44":{"tf":1.0},"80":{"tf":1.7320508075688772},"86":{"tf":1.4142135623730951},"94":{"tf":1.0}},"n":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":1,"docs":{"104":{"tf":1.0}}}}}}},"−":{"1":{"df":1,"docs":{"38":{"tf":1.0}}},"df":0,"docs":{}}},"s":{"1":{"_":{"1":{"df":1,"docs":{"35":{"tf":1.0}}},"df":0,"docs":{},"l":{"a":{"df":0,"docs":{},"g":{"df":0,"docs":{},"r":{"a":{"df":0,"docs":{},"n":{"df":0,"docs":{},"g":{"df":1,"docs":{"35":{"tf":1.0}}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"z":{"df":0,"docs":{},"e":{"df":0,"docs":{},"t":{"a":{"df":1,"docs":{"35":{"tf":1.0}}},"df":0,"docs":{}}}}},"df":1,"docs":{"35":{"tf":1.0}}},"2":{"_":{"1":{"df":1,"docs":{"35":{"tf":1.0}}},"df":0,"docs":{},"l":{"a":{"df":0,"docs":{},"g":{"df":0,"docs":{},"r":{"a":{"df":0,"docs":{},"n":{"df":0,"docs":{},"g":{"df":1,"docs":{"35":{"tf":1.0}}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"z":{"df":0,"docs":{},"e":{"df":0,"docs":{},"t":{"a":{"df":1,"docs":{"35":{"tf":1.0}}},"df":0,"docs":{}}}}},"df":1,"docs":{"35":{"tf":1.0}},"k":{"df":1,"docs":{"102":{"tf":1.4142135623730951}}}},"3":{"_":{"1":{"df":1,"docs":{"35":{"tf":1.0}}},"df":0,"docs":{},"l":{"a":{"df":0,"docs":{},"g":{"df":0,"docs":{},"r":{"a":{"df":0,"docs":{},"n":{"df":0,"docs":{},"g":{"df":1,"docs":{"35":{"tf":1.0}}}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":1,"docs":{"35":{"tf":1.0}}},"=":{"0":{",":{"df":0,"docs":{},"…":{",":{"df":0,"docs":{},"q":{"df":0,"docs":{},"−":{"1":{"df":2,"docs":{"94":{"tf":1.0},"95":{"tf":1.7320508075688772}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"_":{"df":0,"docs":{},"σ":{"1":{"df":1,"docs":{"35":{"tf":1.0}}},"2":{"df":1,"docs":{"35":{"tf":1.0}}},"df":0,"docs":{}}},"a":{"df":0,"docs":{},"k":{"df":0,"docs":{},"e":{"df":1,"docs":{"21":{"tf":1.0}}}},"m":{"df":0,"docs":{},"e":{"df":33,"docs":{"10":{"tf":1.0},"101":{"tf":1.0},"104":{"tf":1.0},"110":{"tf":1.0},"115":{"tf":1.0},"118":{"tf":2.0},"12":{"tf":1.0},"124":{"tf":1.0},"125":{"tf":1.0},"132":{"tf":1.0},"133":{"tf":1.0},"135":{"tf":1.0},"14":{"tf":1.7320508075688772},"146":{"tf":1.7320508075688772},"149":{"tf":2.23606797749979},"151":{"tf":1.0},"31":{"tf":1.0},"36":{"tf":1.0},"52":{"tf":1.0},"54":{"tf":1.0},"55":{"tf":1.0},"59":{"tf":1.0},"60":{"tf":1.0},"65":{"tf":1.0},"66":{"tf":1.4142135623730951},"67":{"tf":1.0},"68":{"tf":1.0},"69":{"tf":1.0},"75":{"tf":1.0},"80":{"tf":1.0},"84":{"tf":1.7320508075688772},"95":{"tf":1.0},"97":{"tf":1.0}}},"p":{"df":0,"docs":{},"l":{"df":19,"docs":{"112":{"tf":1.4142135623730951},"118":{"tf":1.7320508075688772},"147":{"tf":1.0},"20":{"tf":1.0},"24":{"tf":1.0},"25":{"tf":1.4142135623730951},"26":{"tf":1.4142135623730951},"27":{"tf":1.0},"28":{"tf":1.0},"32":{"tf":2.0},"56":{"tf":1.0},"59":{"tf":1.0},"62":{"tf":1.4142135623730951},"83":{"tf":1.4142135623730951},"84":{"tf":1.0},"89":{"tf":1.0},"94":{"tf":2.6457513110645907},"95":{"tf":2.6457513110645907},"97":{"tf":2.23606797749979}}}}},"t":{"df":0,"docs":{},"i":{"df":0,"docs":{},"s":{"df":0,"docs":{},"f":{"df":0,"docs":{},"i":{"df":28,"docs":{"10":{"tf":1.0},"11":{"tf":1.0},"113":{"tf":1.4142135623730951},"114":{"tf":1.0},"116":{"tf":1.0},"12":{"tf":1.0},"124":{"tf":1.0},"130":{"tf":1.0},"14":{"tf":1.0},"146":{"tf":1.0},"19":{"tf":1.0},"20":{"tf":1.4142135623730951},"48":{"tf":1.0},"49":{"tf":1.4142135623730951},"52":{"tf":1.7320508075688772},"53":{"tf":1.4142135623730951},"54":{"tf":1.7320508075688772},"55":{"tf":1.4142135623730951},"56":{"tf":1.0},"57":{"tf":1.0},"59":{"tf":1.0},"63":{"tf":1.4142135623730951},"66":{"tf":1.0},"67":{"tf":1.0},"68":{"tf":1.0},"69":{"tf":1.0},"75":{"tf":1.0},"84":{"tf":1.4142135623730951}}}}}}},"v":{"df":0,"docs":{},"e":{"df":2,"docs":{"151":{"tf":1.0},"21":{"tf":1.0}}}},"w":{"df":3,"docs":{"10":{"tf":1.0},"14":{"tf":1.0},"16":{"tf":1.0}}},"y":{"df":1,"docs":{"56":{"tf":1.0}}}},"c":{"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"a":{"df":0,"docs":{},"r":{"df":0,"docs":{},"i":{"df":0,"docs":{},"o":{"df":1,"docs":{"100":{"tf":1.0}}}}}},"df":0,"docs":{}}},"h":{"df":0,"docs":{},"e":{"df":0,"docs":{},"m":{"df":0,"docs":{},"e":{"df":11,"docs":{"19":{"tf":2.0},"35":{"tf":1.7320508075688772},"38":{"tf":2.0},"44":{"tf":1.0},"71":{"tf":2.6457513110645907},"74":{"tf":1.0},"75":{"tf":1.4142135623730951},"81":{"tf":1.0},"82":{"tf":1.0},"86":{"tf":1.0},"92":{"tf":1.0}}}}},"w":{"a":{"df":0,"docs":{},"r":{"df":0,"docs":{},"t":{"df":0,"docs":{},"z":{"df":1,"docs":{"14":{"tf":1.4142135623730951}}}}}},"df":0,"docs":{}}}},"df":2,"docs":{"92":{"tf":1.4142135623730951},"95":{"tf":1.7320508075688772}},"e":{"c":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"d":{"_":{"df":0,"docs":{},"r":{"df":0,"docs":{},"o":{"df":0,"docs":{},"w":{"[":{"0":{"df":2,"docs":{"107":{"tf":1.4142135623730951},"114":{"tf":1.0}}},"df":0,"docs":{}},"df":2,"docs":{"107":{"tf":1.0},"114":{"tf":1.0}}}}}},"df":13,"docs":{"11":{"tf":1.0},"13":{"tf":1.0},"14":{"tf":1.7320508075688772},"145":{"tf":1.0},"147":{"tf":1.0},"149":{"tf":1.4142135623730951},"3":{"tf":1.0},"49":{"tf":1.0},"59":{"tf":1.0},"66":{"tf":1.0},"70":{"tf":1.0},"9":{"tf":1.0},"91":{"tf":1.0}}},"df":0,"docs":{}}},"r":{"df":0,"docs":{},"e":{"df":0,"docs":{},"t":{"df":2,"docs":{"20":{"tf":1.0},"21":{"tf":1.0}}}}},"t":{"df":0,"docs":{},"i":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"df":34,"docs":{"103":{"tf":1.0},"11":{"tf":1.0},"111":{"tf":1.0},"117":{"tf":1.0},"126":{"tf":1.0},"130":{"tf":1.0},"131":{"tf":1.0},"138":{"tf":1.0},"139":{"tf":1.0},"14":{"tf":2.6457513110645907},"142":{"tf":1.0},"144":{"tf":1.0},"145":{"tf":1.0},"146":{"tf":4.0},"19":{"tf":1.0},"21":{"tf":1.0},"23":{"tf":1.0},"34":{"tf":1.4142135623730951},"37":{"tf":1.0},"4":{"tf":1.0},"43":{"tf":1.0},"45":{"tf":1.0},"46":{"tf":1.0},"49":{"tf":1.0},"69":{"tf":1.4142135623730951},"70":{"tf":1.0},"74":{"tf":1.0},"77":{"tf":1.0},"80":{"tf":1.0},"82":{"tf":1.0},"84":{"tf":1.0},"87":{"tf":1.0},"9":{"tf":1.0},"95":{"tf":1.0}}}}}},"u":{"df":0,"docs":{},"r":{"df":5,"docs":{"109":{"tf":1.4142135623730951},"151":{"tf":1.0},"68":{"tf":1.0},"73":{"tf":1.0},"91":{"tf":1.0}}}}},"df":0,"docs":{},"e":{"df":15,"docs":{"101":{"tf":1.0},"134":{"tf":1.0},"14":{"tf":2.0},"149":{"tf":1.0},"151":{"tf":1.0},"16":{"tf":1.4142135623730951},"20":{"tf":1.0},"3":{"tf":1.4142135623730951},"34":{"tf":1.0},"71":{"tf":1.0},"75":{"tf":1.0},"77":{"tf":1.0},"80":{"tf":1.0},"9":{"tf":1.7320508075688772},"98":{"tf":1.0}},"m":{"df":2,"docs":{"20":{"tf":1.0},"66":{"tf":1.0}},"i":{"df":0,"docs":{},"n":{"df":0,"docs":{},"g":{"df":0,"docs":{},"l":{"df":0,"docs":{},"i":{"df":1,"docs":{"20":{"tf":1.0}}}}}}}},"n":{"df":7,"docs":{"10":{"tf":1.0},"130":{"tf":1.0},"131":{"tf":1.0},"133":{"tf":1.0},"14":{"tf":1.0},"149":{"tf":1.0},"16":{"tf":1.0}}}},"l":{"df":0,"docs":{},"f":{".":{"a":{"df":0,"docs":{},"i":{"df":0,"docs":{},"r":{".":{"c":{"df":0,"docs":{},"o":{"df":0,"docs":{},"m":{"df":0,"docs":{},"p":{"df":0,"docs":{},"u":{"df":0,"docs":{},"t":{"df":0,"docs":{},"e":{"_":{"df":0,"docs":{},"t":{"df":0,"docs":{},"r":{"a":{"df":0,"docs":{},"n":{"df":0,"docs":{},"s":{"df":0,"docs":{},"i":{"df":0,"docs":{},"t":{"df":0,"docs":{},"i":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"(":{"&":{"df":0,"docs":{},"f":{"df":0,"docs":{},"r":{"a":{"df":0,"docs":{},"m":{"df":1,"docs":{"114":{"tf":1.0}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}}}}}}}},"df":0,"docs":{}}}},"df":0,"docs":{}}}}}},"n":{"df":0,"docs":{},"t":{"df":0,"docs":{},"e":{"df":0,"docs":{},"x":{"df":0,"docs":{},"t":{"(":{")":{".":{"df":0,"docs":{},"t":{"df":0,"docs":{},"r":{"a":{"df":0,"docs":{},"n":{"df":0,"docs":{},"s":{"df":0,"docs":{},"i":{"df":0,"docs":{},"t":{"df":0,"docs":{},"i":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"_":{"df":0,"docs":{},"o":{"df":0,"docs":{},"f":{"df":0,"docs":{},"f":{"df":0,"docs":{},"s":{"df":0,"docs":{},"e":{"df":0,"docs":{},"t":{"df":1,"docs":{"114":{"tf":1.0}}}}}}}}},"df":0,"docs":{}}}}}}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{},"p":{"df":0,"docs":{},"u":{"b":{"_":{"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"df":0,"docs":{},"p":{"df":0,"docs":{},"u":{"df":0,"docs":{},"t":{"df":0,"docs":{},"s":{".":{"a":{"0":{".":{"c":{"df":0,"docs":{},"l":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"df":1,"docs":{"106":{"tf":1.0}}}}}},"df":0,"docs":{}},"df":0,"docs":{}},"1":{".":{"c":{"df":0,"docs":{},"l":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"df":1,"docs":{"106":{"tf":1.0}}}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}},":":{":":{"df":0,"docs":{},"r":{"a":{"df":0,"docs":{},"p":{"c":{"df":0,"docs":{},"h":{"a":{"df":0,"docs":{},"l":{"df":0,"docs":{},"l":{"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"df":0,"docs":{},"g":{"df":2,"docs":{"106":{"tf":1.0},"107":{"tf":1.0}}}}}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":3,"docs":{"106":{"tf":1.0},"107":{"tf":1.0},"114":{"tf":1.0}}}},"n":{"d":{"df":13,"docs":{"101":{"tf":1.4142135623730951},"102":{"tf":1.0},"147":{"tf":1.4142135623730951},"21":{"tf":2.0},"7":{"tf":1.4142135623730951},"72":{"tf":1.4142135623730951},"75":{"tf":1.7320508075688772},"77":{"tf":1.0},"79":{"tf":1.4142135623730951},"80":{"tf":1.4142135623730951},"83":{"tf":1.0},"84":{"tf":1.0},"94":{"tf":1.0}}},"df":0,"docs":{},"s":{"df":4,"docs":{"19":{"tf":1.0},"45":{"tf":1.0},"70":{"tf":1.0},"77":{"tf":1.0}},"i":{"b":{"df":0,"docs":{},"l":{"df":1,"docs":{"7":{"tf":1.0}}}},"df":0,"docs":{}}},"t":{"df":1,"docs":{"91":{"tf":1.0}}}},"q":{"df":0,"docs":{},"u":{"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"c":{"df":5,"docs":{"105":{"tf":1.0},"16":{"tf":1.0},"49":{"tf":1.7320508075688772},"51":{"tf":1.0},"9":{"tf":1.0}}},"df":0,"docs":{}}}}},"r":{"df":0,"docs":{},"v":{"df":4,"docs":{"101":{"tf":1.0},"7":{"tf":1.0},"83":{"tf":1.0},"95":{"tf":1.0}}}},"t":{"(":{"d":{"df":1,"docs":{"90":{"tf":1.0}}},"df":0,"docs":{}},"df":24,"docs":{"106":{"tf":1.4142135623730951},"112":{"tf":1.0},"119":{"tf":1.0},"124":{"tf":1.4142135623730951},"125":{"tf":2.0},"14":{"tf":6.48074069840786},"142":{"tf":1.7320508075688772},"149":{"tf":1.0},"150":{"tf":1.4142135623730951},"151":{"tf":1.0},"3":{"tf":1.0},"4":{"tf":1.0},"45":{"tf":1.0},"62":{"tf":1.0},"65":{"tf":1.0},"68":{"tf":2.0},"69":{"tf":1.0},"73":{"tf":1.0},"8":{"tf":1.4142135623730951},"80":{"tf":1.4142135623730951},"82":{"tf":1.7320508075688772},"83":{"tf":1.0},"90":{"tf":1.4142135623730951},"92":{"tf":1.0}},"u":{"df":0,"docs":{},"p":{"(":{"&":{"c":{"df":0,"docs":{},"o":{"df":0,"docs":{},"m":{"df":0,"docs":{},"m":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"_":{"df":0,"docs":{},"p":{"df":0,"docs":{},"r":{"df":0,"docs":{},"e":{"df":0,"docs":{},"p":{"df":0,"docs":{},"r":{"df":0,"docs":{},"o":{"c":{"df":0,"docs":{},"e":{"df":0,"docs":{},"s":{"df":0,"docs":{},"s":{"df":0,"docs":{},"e":{"d":{"_":{"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"df":0,"docs":{},"p":{"df":0,"docs":{},"u":{"df":0,"docs":{},"t":{"df":1,"docs":{"35":{"tf":1.4142135623730951}}}}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}}}},"df":0,"docs":{}}}}}}}},"df":1,"docs":{"44":{"tf":1.0}}}}}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":4,"docs":{"22":{"tf":1.7320508075688772},"34":{"tf":1.0},"35":{"tf":1.4142135623730951},"44":{"tf":1.0}}}}},"v":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":7,"docs":{"10":{"tf":1.0},"124":{"tf":1.0},"45":{"tf":1.0},"65":{"tf":1.4142135623730951},"66":{"tf":1.0},"70":{"tf":1.4142135623730951},"80":{"tf":1.4142135623730951}}}}}},"h":{"a":{"2":{"5":{"6":{"(":{"df":1,"docs":{"7":{"tf":1.0}}},"df":1,"docs":{"7":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{},"h":{"a":{"df":0,"docs":{},"r":{"df":1,"docs":{"103":{"tf":1.0}}}},"df":0,"docs":{}},"m":{"df":0,"docs":{},"i":{"df":0,"docs":{},"r":{"df":7,"docs":{"118":{"tf":1.4142135623730951},"39":{"tf":1.4142135623730951},"40":{"tf":1.0},"42":{"tf":2.23606797749979},"89":{"tf":1.0},"94":{"tf":1.0},"95":{"tf":1.0}}}}},"p":{"df":0,"docs":{},"e":{"df":2,"docs":{"73":{"tf":1.0},"9":{"tf":1.0}}}},"r":{"df":0,"docs":{},"e":{"df":4,"docs":{"104":{"tf":1.0},"150":{"tf":2.0},"151":{"tf":1.4142135623730951},"19":{"tf":1.0}}}}},"df":0,"docs":{},"o":{"df":0,"docs":{},"w":{"c":{"a":{"df":0,"docs":{},"s":{"df":1,"docs":{"12":{"tf":1.0}}}},"df":0,"docs":{}},"df":11,"docs":{"114":{"tf":1.0},"14":{"tf":1.0},"53":{"tf":1.0},"54":{"tf":1.7320508075688772},"55":{"tf":1.0},"56":{"tf":1.0},"57":{"tf":1.0},"58":{"tf":1.0},"59":{"tf":2.0},"60":{"tf":1.4142135623730951},"69":{"tf":1.0}},"n":{"df":1,"docs":{"149":{"tf":1.0}}}}},"u":{"df":0,"docs":{},"f":{"df":0,"docs":{},"f":{"df":0,"docs":{},"l":{"df":1,"docs":{"79":{"tf":1.0}}}}}}},"i":{"d":{"df":0,"docs":{},"e":{"df":9,"docs":{"112":{"tf":1.4142135623730951},"113":{"tf":1.4142135623730951},"146":{"tf":1.0},"58":{"tf":1.0},"60":{"tf":1.0},"62":{"tf":1.4142135623730951},"63":{"tf":1.4142135623730951},"64":{"tf":1.0},"80":{"tf":1.0}}}},"df":0,"docs":{},"g":{"df":0,"docs":{},"n":{"a":{"df":0,"docs":{},"t":{"df":0,"docs":{},"u":{"df":0,"docs":{},"r":{"df":1,"docs":{"151":{"tf":1.0}}}}}},"df":0,"docs":{}}},"m":{"df":0,"docs":{},"i":{"df":0,"docs":{},"l":{"a":{"df":0,"docs":{},"r":{"df":5,"docs":{"114":{"tf":1.0},"14":{"tf":1.4142135623730951},"146":{"tf":1.0},"151":{"tf":1.0},"85":{"tf":1.0}},"l":{"df":0,"docs":{},"i":{"df":5,"docs":{"13":{"tf":1.0},"139":{"tf":1.0},"14":{"tf":1.4142135623730951},"80":{"tf":1.0},"92":{"tf":1.0}}}}}},"df":0,"docs":{}}},"p":{"df":0,"docs":{},"l":{"df":8,"docs":{"110":{"tf":1.0},"117":{"tf":1.0},"118":{"tf":1.0},"128":{"tf":1.0},"20":{"tf":1.0},"44":{"tf":1.7320508075688772},"45":{"tf":1.0},"49":{"tf":1.0}},"e":{"_":{"df":0,"docs":{},"f":{"df":0,"docs":{},"i":{"b":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"a":{"c":{"c":{"df":0,"docs":{},"i":{":":{":":{"df":0,"docs":{},"f":{"df":0,"docs":{},"i":{"b":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"a":{"c":{"c":{"df":0,"docs":{},"i":{"_":{"df":0,"docs":{},"t":{"df":0,"docs":{},"r":{"a":{"c":{"df":0,"docs":{},"e":{"(":{"[":{"df":0,"docs":{},"f":{"df":0,"docs":{},"e":{":":{":":{"df":0,"docs":{},"f":{"df":0,"docs":{},"r":{"df":0,"docs":{},"o":{"df":0,"docs":{},"m":{"(":{"1":{"df":1,"docs":{"104":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}}},"df":0,"docs":{},"r":{"df":2,"docs":{"14":{"tf":1.0},"149":{"tf":1.0}}}},"i":{"c":{"df":2,"docs":{"21":{"tf":1.0},"64":{"tf":1.0}}},"df":4,"docs":{"106":{"tf":1.0},"109":{"tf":1.0},"114":{"tf":1.0},"125":{"tf":1.0}},"f":{"df":2,"docs":{"149":{"tf":1.0},"64":{"tf":1.4142135623730951}},"i":{"df":1,"docs":{"56":{"tf":1.0}}}}}}},"u":{"df":0,"docs":{},"l":{"df":0,"docs":{},"t":{"a":{"df":0,"docs":{},"n":{"df":3,"docs":{"101":{"tf":1.0},"59":{"tf":1.0},"80":{"tf":1.0}}}},"df":0,"docs":{}}}}},"n":{"df":0,"docs":{},"g":{"df":0,"docs":{},"l":{"df":14,"docs":{"101":{"tf":1.4142135623730951},"12":{"tf":1.7320508075688772},"128":{"tf":1.0},"146":{"tf":1.0},"16":{"tf":1.0},"28":{"tf":1.0},"33":{"tf":1.0},"35":{"tf":1.0},"5":{"tf":1.0},"69":{"tf":1.0},"80":{"tf":1.4142135623730951},"84":{"tf":1.0},"92":{"tf":1.0},"97":{"tf":1.4142135623730951}}}}},"t":{"df":1,"docs":{"13":{"tf":1.0}},"e":{"df":1,"docs":{"0":{"tf":1.0}}},"u":{"a":{"df":0,"docs":{},"t":{"df":2,"docs":{"14":{"tf":1.0},"70":{"tf":1.0}}}},"df":0,"docs":{}}},"z":{"df":0,"docs":{},"e":{"df":9,"docs":{"12":{"tf":1.0},"121":{"tf":1.4142135623730951},"14":{"tf":1.0},"141":{"tf":1.0},"147":{"tf":2.6457513110645907},"23":{"tf":1.0},"3":{"tf":2.0},"36":{"tf":1.0},"80":{"tf":1.0}}}}},"k":{"df":0,"docs":{},"e":{"df":0,"docs":{},"t":{"c":{"df":0,"docs":{},"h":{"df":1,"docs":{"79":{"tf":1.0}}}},"df":0,"docs":{}}},"i":{"df":0,"docs":{},"p":{"df":1,"docs":{"102":{"tf":1.0}}}}},"l":{"df":0,"docs":{},"i":{"df":0,"docs":{},"g":{"df":0,"docs":{},"h":{"df":0,"docs":{},"t":{"df":0,"docs":{},"l":{"df":0,"docs":{},"i":{"df":1,"docs":{"59":{"tf":1.0}}}}}}}},"o":{"df":0,"docs":{},"w":{"df":2,"docs":{"3":{"tf":1.0},"98":{"tf":1.0}}}}},"m":{"a":{"df":0,"docs":{},"l":{"df":0,"docs":{},"l":{"df":2,"docs":{"151":{"tf":1.0},"74":{"tf":1.0}},"e":{"df":0,"docs":{},"r":{"df":2,"docs":{"123":{"tf":1.0},"142":{"tf":1.0}}},"s":{"df":0,"docs":{},"t":{"df":1,"docs":{"142":{"tf":1.0}}}}}}}},"df":0,"docs":{}},"o":{"df":0,"docs":{},"l":{"df":0,"docs":{},"u":{"df":0,"docs":{},"t":{"df":4,"docs":{"101":{"tf":1.0},"14":{"tf":1.0},"4":{"tf":1.0},"44":{"tf":1.0}}}},"v":{"df":2,"docs":{"146":{"tf":1.0},"95":{"tf":1.0}}}},"m":{"df":0,"docs":{},"e":{"df":0,"docs":{},"h":{"df":0,"docs":{},"o":{"df":0,"docs":{},"w":{"df":2,"docs":{"114":{"tf":1.0},"70":{"tf":1.0}}}}},"o":{"df":0,"docs":{},"n":{"df":1,"docs":{"114":{"tf":1.0}}}},"t":{"df":0,"docs":{},"h":{"df":6,"docs":{"130":{"tf":1.0},"14":{"tf":1.4142135623730951},"146":{"tf":2.23606797749979},"45":{"tf":1.0},"50":{"tf":1.0},"95":{"tf":1.0}}},"i":{"df":0,"docs":{},"m":{"df":2,"docs":{"48":{"tf":1.0},"52":{"tf":1.0}}}}},"w":{"df":0,"docs":{},"h":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":1,"docs":{"21":{"tf":1.0}}}}}}}},"p":{"df":0,"docs":{},"h":{"df":0,"docs":{},"i":{"df":0,"docs":{},"s":{"df":0,"docs":{},"t":{"df":1,"docs":{"12":{"tf":1.0}}}}}}},"r":{"df":0,"docs":{},"t":{"df":6,"docs":{"132":{"tf":1.7320508075688772},"135":{"tf":2.0},"138":{"tf":1.0},"14":{"tf":1.0},"147":{"tf":2.23606797749979},"75":{"tf":1.0}}}},"u":{"df":0,"docs":{},"n":{"d":{"df":3,"docs":{"7":{"tf":1.0},"79":{"tf":1.7320508075688772},"88":{"tf":1.0}}},"df":0,"docs":{}},"r":{"c":{"df":3,"docs":{"118":{"tf":1.0},"127":{"tf":1.0},"34":{"tf":1.0}}},"df":0,"docs":{}}}},"p":{"a":{"c":{"df":0,"docs":{},"e":{"df":1,"docs":{"149":{"tf":1.0}}}},"df":0,"docs":{}},"df":0,"docs":{},"e":{"a":{"df":0,"docs":{},"k":{"df":1,"docs":{"16":{"tf":1.0}}}},"c":{"df":0,"docs":{},"i":{"a":{"df":0,"docs":{},"l":{"df":2,"docs":{"120":{"tf":1.4142135623730951},"151":{"tf":1.0}}}},"df":0,"docs":{},"f":{"df":14,"docs":{"100":{"tf":1.0},"106":{"tf":1.0},"108":{"tf":1.0},"109":{"tf":1.0},"114":{"tf":1.0},"13":{"tf":1.4142135623730951},"146":{"tf":1.0},"151":{"tf":1.7320508075688772},"19":{"tf":1.0},"49":{"tf":1.0},"70":{"tf":1.4142135623730951},"73":{"tf":1.0},"91":{"tf":1.0},"94":{"tf":1.0}},"i":{"df":7,"docs":{"105":{"tf":1.0},"106":{"tf":1.0},"107":{"tf":1.4142135623730951},"109":{"tf":1.4142135623730951},"128":{"tf":1.0},"146":{"tf":1.0},"44":{"tf":1.0}}}}}},"df":0,"docs":{}},"l":{"df":0,"docs":{},"i":{"df":0,"docs":{},"t":{"df":2,"docs":{"151":{"tf":1.0},"82":{"tf":1.0}}}}}},"q":{"df":0,"docs":{},"u":{"a":{"df":0,"docs":{},"r":{"df":1,"docs":{"45":{"tf":1.0}}},"s":{"df":0,"docs":{},"h":{"df":2,"docs":{"12":{"tf":1.0},"14":{"tf":1.0}}}}},"df":0,"docs":{}}},"r":{"c":{"df":1,"docs":{"146":{"tf":1.0}}},"df":2,"docs":{"35":{"tf":1.4142135623730951},"44":{"tf":1.0}}},"t":{"a":{"c":{"df":0,"docs":{},"k":{"df":2,"docs":{"14":{"tf":1.0},"147":{"tf":1.7320508075688772}}}},"df":0,"docs":{},"g":{"df":0,"docs":{},"e":{"df":1,"docs":{"145":{"tf":1.4142135623730951}}}},"n":{"d":{"df":2,"docs":{"13":{"tf":1.0},"28":{"tf":1.0}}},"df":0,"docs":{}},"r":{"df":0,"docs":{},"k":{"df":95,"docs":{"100":{"tf":1.0},"101":{"tf":1.0},"102":{"tf":1.0},"103":{"tf":1.7320508075688772},"104":{"tf":1.4142135623730951},"105":{"tf":1.0},"106":{"tf":1.0},"107":{"tf":1.0},"108":{"tf":1.0},"109":{"tf":2.0},"110":{"tf":1.0},"111":{"tf":1.0},"112":{"tf":1.0},"113":{"tf":1.0},"114":{"tf":1.0},"115":{"tf":1.0},"116":{"tf":1.0},"117":{"tf":1.0},"118":{"tf":1.0},"119":{"tf":1.0},"120":{"tf":1.0},"121":{"tf":1.0},"122":{"tf":1.0},"123":{"tf":1.4142135623730951},"124":{"tf":1.0},"125":{"tf":1.0},"126":{"tf":1.0},"127":{"tf":1.0},"128":{"tf":1.0},"129":{"tf":1.0},"130":{"tf":1.0},"131":{"tf":1.0},"132":{"tf":1.0},"133":{"tf":1.0},"134":{"tf":1.0},"135":{"tf":1.0},"136":{"tf":1.0},"137":{"tf":1.0},"138":{"tf":1.0},"139":{"tf":1.0},"142":{"tf":1.0},"46":{"tf":2.0},"47":{"tf":1.7320508075688772},"48":{"tf":1.0},"49":{"tf":1.4142135623730951},"50":{"tf":1.7320508075688772},"51":{"tf":1.4142135623730951},"52":{"tf":1.0},"53":{"tf":1.0},"54":{"tf":1.0},"55":{"tf":1.0},"56":{"tf":1.0},"57":{"tf":1.0},"58":{"tf":1.0},"59":{"tf":1.0},"60":{"tf":1.0},"61":{"tf":1.4142135623730951},"62":{"tf":1.0},"63":{"tf":1.0},"64":{"tf":1.0},"65":{"tf":1.0},"66":{"tf":1.0},"67":{"tf":1.0},"68":{"tf":1.0},"69":{"tf":1.4142135623730951},"70":{"tf":1.0},"71":{"tf":1.4142135623730951},"72":{"tf":1.0},"73":{"tf":1.4142135623730951},"74":{"tf":2.0},"75":{"tf":1.4142135623730951},"76":{"tf":1.0},"77":{"tf":1.0},"78":{"tf":1.0},"79":{"tf":1.0},"80":{"tf":1.4142135623730951},"81":{"tf":1.0},"82":{"tf":1.0},"83":{"tf":1.0},"84":{"tf":1.4142135623730951},"85":{"tf":1.0},"86":{"tf":1.0},"87":{"tf":2.0},"88":{"tf":1.0},"89":{"tf":1.0},"90":{"tf":1.0},"91":{"tf":1.0},"92":{"tf":1.0},"93":{"tf":1.0},"94":{"tf":1.0},"95":{"tf":1.0},"96":{"tf":1.0},"97":{"tf":1.0},"98":{"tf":1.0},"99":{"tf":1.0}},"w":{"a":{"df":0,"docs":{},"r":{"df":1,"docs":{"103":{"tf":1.0}},"e":{"'":{"df":1,"docs":{"126":{"tf":1.0}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"t":{"df":12,"docs":{"109":{"tf":1.0},"14":{"tf":1.4142135623730951},"149":{"tf":1.0},"151":{"tf":1.0},"16":{"tf":1.0},"40":{"tf":1.0},"50":{"tf":1.0},"69":{"tf":1.4142135623730951},"8":{"tf":1.0},"82":{"tf":1.0},"94":{"tf":1.0},"95":{"tf":1.0}}}},"t":{"df":0,"docs":{},"e":{"df":12,"docs":{"118":{"tf":1.7320508075688772},"127":{"tf":2.0},"133":{"tf":1.0},"144":{"tf":1.4142135623730951},"146":{"tf":1.7320508075688772},"149":{"tf":1.0},"40":{"tf":2.23606797749979},"48":{"tf":1.0},"70":{"tf":3.0},"84":{"tf":1.0},"88":{"tf":1.7320508075688772},"94":{"tf":1.0}},"m":{"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"df":0,"docs":{},"t":{"df":5,"docs":{"11":{"tf":1.4142135623730951},"14":{"tf":1.0},"146":{"tf":1.4142135623730951},"34":{"tf":1.0},"84":{"tf":1.0}}}}}}}}},"df":0,"docs":{},"e":{"df":0,"docs":{},"p":{"df":27,"docs":{"102":{"tf":1.0},"107":{"tf":1.7320508075688772},"111":{"tf":1.0},"113":{"tf":1.0},"127":{"tf":2.0},"128":{"tf":1.4142135623730951},"130":{"tf":1.7320508075688772},"131":{"tf":1.0},"133":{"tf":4.123105625617661},"136":{"tf":2.449489742783178},"139":{"tf":1.4142135623730951},"144":{"tf":1.0},"146":{"tf":2.449489742783178},"147":{"tf":1.4142135623730951},"149":{"tf":1.4142135623730951},"16":{"tf":1.0},"31":{"tf":1.0},"32":{"tf":1.0},"48":{"tf":1.0},"49":{"tf":1.0},"51":{"tf":2.449489742783178},"52":{"tf":1.0},"53":{"tf":1.4142135623730951},"57":{"tf":1.0},"61":{"tf":1.0},"70":{"tf":1.0},"95":{"tf":2.0}}}},"i":{"c":{"df":0,"docs":{},"k":{"df":1,"docs":{"7":{"tf":1.0}}}},"df":0,"docs":{},"l":{"df":0,"docs":{},"l":{"df":3,"docs":{"0":{"tf":1.0},"125":{"tf":1.0},"58":{"tf":1.0}}}}},"o":{"df":0,"docs":{},"n":{"df":0,"docs":{},"e":{"df":14,"docs":{"126":{"tf":2.0},"127":{"tf":2.23606797749979},"128":{"tf":1.0},"129":{"tf":1.0},"130":{"tf":1.4142135623730951},"131":{"tf":1.4142135623730951},"132":{"tf":1.0},"133":{"tf":1.0},"134":{"tf":1.0},"135":{"tf":1.0},"136":{"tf":1.0},"137":{"tf":1.0},"138":{"tf":1.0},"139":{"tf":1.0}}}},"r":{"df":0,"docs":{},"e":{"df":5,"docs":{"136":{"tf":1.0},"146":{"tf":1.4142135623730951},"151":{"tf":1.0},"35":{"tf":2.0},"70":{"tf":1.0}}}}},"r":{"a":{"df":0,"docs":{},"i":{"df":0,"docs":{},"g":{"df":0,"docs":{},"h":{"df":0,"docs":{},"t":{"df":0,"docs":{},"f":{"df":0,"docs":{},"o":{"df":0,"docs":{},"r":{"df":0,"docs":{},"w":{"a":{"df":0,"docs":{},"r":{"d":{"df":4,"docs":{"132":{"tf":1.0},"14":{"tf":1.4142135623730951},"146":{"tf":1.0},"151":{"tf":1.0}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}}}}}},"t":{"df":0,"docs":{},"e":{"df":0,"docs":{},"g":{"df":0,"docs":{},"i":{"df":1,"docs":{"40":{"tf":1.4142135623730951}}}}}}},"df":0,"docs":{},"i":{"c":{"df":0,"docs":{},"t":{"df":0,"docs":{},"l":{"df":0,"docs":{},"i":{"df":1,"docs":{"73":{"tf":1.4142135623730951}}}}}},"df":0,"docs":{},"n":{"df":0,"docs":{},"g":{"df":3,"docs":{"41":{"tf":1.0},"88":{"tf":1.7320508075688772},"91":{"tf":1.0}}}}},"o":{"df":0,"docs":{},"n":{"df":0,"docs":{},"g":{"df":3,"docs":{"42":{"tf":1.4142135623730951},"94":{"tf":1.0},"95":{"tf":1.0}}}}},"u":{"c":{"df":0,"docs":{},"t":{"df":8,"docs":{"106":{"tf":1.4142135623730951},"107":{"tf":1.0},"108":{"tf":1.0},"109":{"tf":1.4142135623730951},"110":{"tf":1.0},"114":{"tf":1.4142135623730951},"118":{"tf":1.0},"35":{"tf":3.3166247903554}},"u":{"df":0,"docs":{},"r":{"df":2,"docs":{"146":{"tf":1.0},"83":{"tf":1.0}}}}}},"df":0,"docs":{}}},"u":{"df":0,"docs":{},"f":{"df":0,"docs":{},"f":{"df":1,"docs":{"56":{"tf":1.0}}}}}},"u":{"b":{"c":{"df":0,"docs":{},"o":{"df":0,"docs":{},"l":{"df":0,"docs":{},"u":{"df":0,"docs":{},"m":{"df":0,"docs":{},"n":{"df":4,"docs":{"146":{"tf":1.4142135623730951},"148":{"tf":1.4142135623730951},"150":{"tf":2.0},"151":{"tf":1.0}}}}}}}},"df":2,"docs":{"133":{"tf":1.7320508075688772},"134":{"tf":1.0}},"g":{"df":0,"docs":{},"r":{"df":0,"docs":{},"o":{"df":0,"docs":{},"u":{"df":0,"docs":{},"p":{"df":1,"docs":{"38":{"tf":1.0}}}}}}},"m":{"a":{"df":0,"docs":{},"t":{"df":0,"docs":{},"r":{"df":0,"docs":{},"i":{"df":0,"docs":{},"x":{"df":2,"docs":{"142":{"tf":1.7320508075688772},"147":{"tf":1.7320508075688772}}}}}}},"df":0,"docs":{}},"p":{"df":0,"docs":{},"r":{"df":0,"docs":{},"o":{"df":0,"docs":{},"g":{"df":0,"docs":{},"r":{"a":{"df":0,"docs":{},"m":{"df":1,"docs":{"151":{"tf":1.4142135623730951}}}},"df":0,"docs":{}}}}}},"s":{"c":{"df":0,"docs":{},"r":{"df":0,"docs":{},"i":{"df":0,"docs":{},"p":{"df":0,"docs":{},"t":{"df":1,"docs":{"28":{"tf":1.4142135623730951}}}}}}},"df":0,"docs":{},"e":{"c":{"df":0,"docs":{},"t":{"df":1,"docs":{"146":{"tf":1.4142135623730951}}}},"df":0,"docs":{},"q":{"df":0,"docs":{},"u":{"df":3,"docs":{"11":{"tf":1.0},"114":{"tf":1.0},"40":{"tf":1.0}}}},"t":{"df":3,"docs":{"128":{"tf":1.4142135623730951},"150":{"tf":1.0},"90":{"tf":1.4142135623730951}}}}},"t":{"a":{"b":{"df":0,"docs":{},"l":{"df":1,"docs":{"127":{"tf":1.0}}}},"df":0,"docs":{}},"df":0,"docs":{},"r":{"a":{"c":{"df":0,"docs":{},"t":{"df":1,"docs":{"9":{"tf":1.4142135623730951}}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"c":{"c":{"df":0,"docs":{},"e":{"df":1,"docs":{"78":{"tf":1.0}}}},"df":0,"docs":{},"h":{"df":23,"docs":{"100":{"tf":1.0},"109":{"tf":1.0},"11":{"tf":1.0},"124":{"tf":1.4142135623730951},"13":{"tf":1.0},"14":{"tf":3.3166247903554},"144":{"tf":1.0},"151":{"tf":1.0},"16":{"tf":2.23606797749979},"19":{"tf":1.0},"20":{"tf":1.7320508075688772},"26":{"tf":1.0},"44":{"tf":1.0},"45":{"tf":1.4142135623730951},"5":{"tf":1.0},"66":{"tf":1.0},"70":{"tf":1.0},"71":{"tf":1.0},"73":{"tf":1.7320508075688772},"79":{"tf":1.0},"88":{"tf":1.0},"94":{"tf":1.7320508075688772},"95":{"tf":1.0}}}},"df":0,"docs":{},"f":{"df":0,"docs":{},"f":{"df":0,"docs":{},"i":{"df":0,"docs":{},"x":{"df":1,"docs":{"35":{"tf":1.0}}}}}},"m":{"df":6,"docs":{"114":{"tf":1.0},"125":{"tf":1.0},"19":{"tf":1.0},"49":{"tf":1.0},"52":{"tf":1.0},"65":{"tf":1.0}},"m":{"a":{"df":0,"docs":{},"n":{"d":{"df":1,"docs":{"59":{"tf":1.4142135623730951}}},"df":0,"docs":{}},"r":{"df":1,"docs":{"61":{"tf":1.0}},"i":{"df":8,"docs":{"131":{"tf":1.0},"138":{"tf":1.0},"14":{"tf":1.0},"146":{"tf":1.0},"150":{"tf":1.0},"61":{"tf":1.4142135623730951},"69":{"tf":1.0},"81":{"tf":1.0}}}}},"df":0,"docs":{}}},"p":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":0,"docs":{},"s":{"c":{"df":0,"docs":{},"r":{"df":0,"docs":{},"i":{"df":0,"docs":{},"p":{"df":0,"docs":{},"t":{"df":1,"docs":{"95":{"tf":1.4142135623730951}}}}}}},"df":0,"docs":{}}}},"p":{"df":0,"docs":{},"o":{"df":0,"docs":{},"r":{"df":0,"docs":{},"t":{"df":3,"docs":{"107":{"tf":1.0},"121":{"tf":1.0},"34":{"tf":1.0}}}},"s":{"df":5,"docs":{"14":{"tf":2.23606797749979},"45":{"tf":1.0},"73":{"tf":1.4142135623730951},"74":{"tf":1.4142135623730951},"79":{"tf":1.0}}}}}},"r":{"df":0,"docs":{},"e":{"df":2,"docs":{"125":{"tf":1.0},"151":{"tf":1.0}}}}},"w":{"df":0,"docs":{},"e":{"df":0,"docs":{},"e":{"df":0,"docs":{},"p":{"df":1,"docs":{"64":{"tf":1.0}}}}}},"y":{"df":0,"docs":{},"m":{"b":{"df":0,"docs":{},"o":{"df":0,"docs":{},"l":{"df":3,"docs":{"36":{"tf":1.0},"5":{"tf":2.0},"9":{"tf":1.0}}}}},"df":0,"docs":{},"m":{"df":0,"docs":{},"e":{"df":0,"docs":{},"t":{"df":0,"docs":{},"r":{"df":1,"docs":{"73":{"tf":1.0}}}}}}},"s":{"df":0,"docs":{},"t":{"df":0,"docs":{},"e":{"df":0,"docs":{},"m":{"'":{"df":1,"docs":{"70":{"tf":1.0}}},".":{"a":{"df":0,"docs":{},"s":{"df":0,"docs":{},"s":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":0,"docs":{},"t":{"_":{"df":0,"docs":{},"e":{"df":0,"docs":{},"q":{"(":{"&":{"df":0,"docs":{},"i":{"df":2,"docs":{"44":{"tf":1.0},"45":{"tf":1.0}}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}}}}}},"df":0,"docs":{},"i":{"df":0,"docs":{},"f":{"_":{"df":0,"docs":{},"e":{"df":0,"docs":{},"l":{"df":0,"docs":{},"s":{"df":0,"docs":{},"e":{"(":{"&":{"df":0,"docs":{},"e":{"df":0,"docs":{},"x":{"df":0,"docs":{},"p":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"df":0,"docs":{},"t":{"_":{"b":{"df":0,"docs":{},"i":{"df":0,"docs":{},"t":{"df":0,"docs":{},"s":{"[":{"df":0,"docs":{},"i":{"df":1,"docs":{"45":{"tf":1.0}}}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}}}}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}}}},"df":0,"docs":{}}},"m":{"df":0,"docs":{},"u":{"df":0,"docs":{},"l":{"(":{"&":{"df":0,"docs":{},"r":{"df":0,"docs":{},"e":{"df":0,"docs":{},"s":{"df":0,"docs":{},"u":{"df":0,"docs":{},"l":{"df":0,"docs":{},"t":{"df":1,"docs":{"45":{"tf":1.4142135623730951}}}}}}}},"x":{"df":1,"docs":{"44":{"tf":1.0}}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"n":{"df":0,"docs":{},"e":{"df":0,"docs":{},"w":{"_":{"c":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"df":0,"docs":{},"s":{"df":0,"docs":{},"t":{"a":{"df":0,"docs":{},"n":{"df":0,"docs":{},"t":{"(":{"df":0,"docs":{},"f":{"df":0,"docs":{},"i":{"df":0,"docs":{},"e":{"df":0,"docs":{},"l":{"d":{"df":0,"docs":{},"e":{"df":0,"docs":{},"l":{"df":0,"docs":{},"e":{"df":0,"docs":{},"m":{"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"df":0,"docs":{},"t":{":":{":":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"df":1,"docs":{"45":{"tf":1.0}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}}}}}}},"df":0,"docs":{}}}}}},"df":0,"docs":{}}}},"df":0,"docs":{}}}}}},"df":0,"docs":{},"p":{"df":0,"docs":{},"u":{"b":{"df":0,"docs":{},"l":{"df":0,"docs":{},"i":{"c":{"_":{"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"df":0,"docs":{},"p":{"df":0,"docs":{},"u":{"df":0,"docs":{},"t":{"df":2,"docs":{"44":{"tf":1.4142135623730951},"45":{"tf":1.4142135623730951}}}}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"u":{"3":{"2":{"(":{"&":{"df":0,"docs":{},"e":{"df":0,"docs":{},"x":{"df":0,"docs":{},"p":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"df":1,"docs":{"45":{"tf":1.0}}}}}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"v":{"a":{"df":0,"docs":{},"r":{"df":0,"docs":{},"i":{"df":2,"docs":{"44":{"tf":1.0},"45":{"tf":1.0}}}}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"p":{"df":0,"docs":{},"u":{"b":{"df":0,"docs":{},"l":{"df":0,"docs":{},"i":{"c":{"_":{"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"df":0,"docs":{},"p":{"df":0,"docs":{},"u":{"df":0,"docs":{},"t":{"_":{"df":0,"docs":{},"v":{"a":{"df":0,"docs":{},"l":{"df":0,"docs":{},"u":{"df":0,"docs":{},"e":{"df":0,"docs":{},"s":{"(":{"&":{"a":{"df":0,"docs":{},"s":{"df":0,"docs":{},"s":{"df":0,"docs":{},"i":{"df":0,"docs":{},"g":{"df":0,"docs":{},"n":{"df":1,"docs":{"44":{"tf":1.0}}}}}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"s":{"df":0,"docs":{},"o":{"df":0,"docs":{},"l":{"df":0,"docs":{},"v":{"df":0,"docs":{},"e":{"(":{"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"df":0,"docs":{},"p":{"df":0,"docs":{},"u":{"df":0,"docs":{},"t":{"df":0,"docs":{},"s":{")":{".":{"df":0,"docs":{},"u":{"df":0,"docs":{},"n":{"df":0,"docs":{},"w":{"df":0,"docs":{},"r":{"a":{"df":0,"docs":{},"p":{"df":1,"docs":{"44":{"tf":1.0}}}},"df":0,"docs":{}}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}}}}}},"df":0,"docs":{}}}}}}},"df":17,"docs":{"103":{"tf":1.0},"104":{"tf":1.0},"118":{"tf":1.0},"119":{"tf":1.0},"144":{"tf":1.0},"146":{"tf":1.0},"151":{"tf":1.0},"4":{"tf":1.4142135623730951},"42":{"tf":1.0},"43":{"tf":1.0},"44":{"tf":2.8284271247461903},"45":{"tf":2.449489742783178},"46":{"tf":1.0},"48":{"tf":1.4142135623730951},"49":{"tf":1.0},"70":{"tf":1.0},"95":{"tf":1.0}}}}}}},"σ":{"1":{"df":1,"docs":{"14":{"tf":1.0}},"​":{"]":{"1":{"df":0,"docs":{},"​":{",":{"[":{"df":0,"docs":{},"s":{"df":0,"docs":{},"σ":{"2":{"df":0,"docs":{},"​":{"]":{"1":{"df":0,"docs":{},"​":{",":{"[":{"df":0,"docs":{},"s":{"df":0,"docs":{},"σ":{"3":{"]":{"1":{"df":0,"docs":{},"​":{",":{"[":{"df":0,"docs":{},"q":{"df":0,"docs":{},"l":{"df":0,"docs":{},"​":{"]":{"1":{"df":0,"docs":{},"​":{",":{"[":{"df":0,"docs":{},"q":{"df":0,"docs":{},"r":{"df":0,"docs":{},"​":{"]":{"1":{"df":0,"docs":{},"​":{",":{"[":{"df":0,"docs":{},"q":{"df":0,"docs":{},"m":{"df":0,"docs":{},"​":{"]":{"1":{"df":0,"docs":{},"​":{",":{"[":{"df":0,"docs":{},"q":{"df":0,"docs":{},"o":{"df":0,"docs":{},"​":{"]":{"1":{"df":0,"docs":{},"​":{",":{"[":{"df":0,"docs":{},"q":{"c":{"df":0,"docs":{},"​":{"]":{"1":{"df":1,"docs":{"24":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{},"​":{"]":{"1":{"df":0,"docs":{},"​":{",":{"[":{"df":0,"docs":{},"q":{"df":0,"docs":{},"l":{"df":0,"docs":{},"​":{"]":{"1":{"df":0,"docs":{},"​":{",":{"[":{"df":0,"docs":{},"q":{"df":0,"docs":{},"r":{"df":0,"docs":{},"​":{"]":{"1":{"df":0,"docs":{},"​":{",":{"[":{"df":0,"docs":{},"q":{"df":0,"docs":{},"m":{"df":0,"docs":{},"​":{"]":{"1":{"df":0,"docs":{},"​":{",":{"[":{"df":0,"docs":{},"q":{"df":0,"docs":{},"o":{"df":0,"docs":{},"​":{"]":{"1":{"df":0,"docs":{},"​":{",":{"[":{"df":0,"docs":{},"q":{"c":{"df":0,"docs":{},"​":{"]":{"1":{"df":1,"docs":{"31":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"2":{"df":1,"docs":{"14":{"tf":1.0}}},"3":{"df":1,"docs":{"14":{"tf":1.0}}},"df":0,"docs":{},"i":{"df":1,"docs":{"35":{"tf":1.0}}}},"​":{":":{"=":{"df":0,"docs":{},"h":{"df":0,"docs":{},"ω":{"df":0,"docs":{},"ι":{"df":2,"docs":{"94":{"tf":1.0},"95":{"tf":1.0}}}}}},"df":0,"docs":{}},"df":0,"docs":{},"∈":{"[":{"0":{",":{"2":{"df":0,"docs":{},"n":{"+":{"df":0,"docs":{},"l":{"df":0,"docs":{},"−":{"1":{"df":2,"docs":{"94":{"tf":1.0},"95":{"tf":1.0}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}},"t":{"(":{"1":{")":{"df":0,"docs":{},"−":{"df":0,"docs":{},"p":{"(":{"1":{")":{"=":{"1":{"df":0,"docs":{},"−":{"1":{"=":{"0":{"df":1,"docs":{"54":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{},"g":{")":{"df":0,"docs":{},"−":{"df":0,"docs":{},"p":{"(":{"df":0,"docs":{},"g":{")":{"=":{"1":{"df":0,"docs":{},"−":{"1":{"=":{"0":{"df":1,"docs":{"54":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"^":{"0":{"df":2,"docs":{"52":{"tf":1.0},"54":{"tf":1.0}}},"df":0,"docs":{}},"df":2,"docs":{"52":{"tf":1.0},"54":{"tf":1.0}},"i":{")":{"=":{"a":{"df":0,"docs":{},"i":{"df":1,"docs":{"52":{"tf":1.0}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"x":{")":{"'":{"df":1,"docs":{"114":{"tf":1.0}}},"df":0,"docs":{}},"0":{"df":2,"docs":{"112":{"tf":1.4142135623730951},"113":{"tf":1.4142135623730951}},"​":{")":{"df":0,"docs":{},"t":{"(":{"df":0,"docs":{},"x":{"0":{"df":0,"docs":{},"​":{"df":0,"docs":{},"g":{")":{"df":0,"docs":{},"t":{"(":{"df":0,"docs":{},"x":{"0":{"df":0,"docs":{},"​":{"df":0,"docs":{},"g":{"2":{"df":1,"docs":{"114":{"tf":1.0}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{},"g":{"2":{")":{"df":0,"docs":{},"−":{"df":0,"docs":{},"t":{"(":{"df":0,"docs":{},"x":{"0":{"df":0,"docs":{},"​":{"df":0,"docs":{},"g":{")":{"df":0,"docs":{},"−":{"df":0,"docs":{},"t":{"(":{"df":0,"docs":{},"x":{"0":{"df":1,"docs":{"114":{"tf":1.0}}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"_":{"0":{"df":3,"docs":{"60":{"tf":1.7320508075688772},"62":{"tf":1.4142135623730951},"63":{"tf":1.4142135623730951}}},"df":0,"docs":{}},"df":3,"docs":{"35":{"tf":1.7320508075688772},"52":{"tf":1.4142135623730951},"54":{"tf":1.0}},"g":{"2":{")":{"df":0,"docs":{},"−":{"df":0,"docs":{},"t":{"(":{"df":0,"docs":{},"x":{"df":0,"docs":{},"g":{")":{"df":0,"docs":{},"−":{"df":0,"docs":{},"t":{"(":{"df":0,"docs":{},"x":{"df":1,"docs":{"114":{"tf":1.0}}}},"df":0,"docs":{}}}},"df":0,"docs":{}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":1,"docs":{"52":{"tf":1.0}}}},"z":{"df":7,"docs":{"112":{"tf":1.0},"117":{"tf":1.0},"58":{"tf":1.0},"59":{"tf":1.0},"60":{"tf":1.0},"62":{"tf":1.0},"63":{"tf":1.0}},"g":{"2":{"df":2,"docs":{"112":{"tf":1.0},"117":{"tf":1.0}}},"^":{"2":{"df":5,"docs":{"58":{"tf":1.0},"59":{"tf":1.0},"60":{"tf":1.0},"62":{"tf":1.0},"63":{"tf":1.0}}},"df":0,"docs":{}},"df":7,"docs":{"112":{"tf":1.0},"117":{"tf":1.0},"58":{"tf":1.0},"59":{"tf":1.0},"60":{"tf":1.0},"62":{"tf":1.0},"63":{"tf":1.0}}}},"ω":{"0":{")":{"df":0,"docs":{},"t":{"(":{"df":0,"docs":{},"ω":{"1":{")":{"df":0,"docs":{},"…":{"df":0,"docs":{},"t":{"(":{"df":0,"docs":{},"ω":{"1":{"5":{"df":1,"docs":{"114":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"2":{")":{"df":0,"docs":{},"t":{"(":{"df":0,"docs":{},"ω":{"4":{"df":1,"docs":{"114":{"tf":1.0}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{},"g":{"2":{")":{"df":0,"docs":{},"−":{"df":0,"docs":{},"t":{"(":{"df":0,"docs":{},"ω":{"0":{"df":0,"docs":{},"g":{")":{"df":0,"docs":{},"−":{"df":0,"docs":{},"t":{"(":{"df":0,"docs":{},"ω":{"0":{")":{"df":0,"docs":{},"t":{"(":{"df":0,"docs":{},"ω":{"df":0,"docs":{},"g":{"2":{")":{"df":0,"docs":{},"−":{"df":0,"docs":{},"t":{"(":{"df":0,"docs":{},"ω":{"df":0,"docs":{},"g":{")":{"df":0,"docs":{},"−":{"df":0,"docs":{},"t":{"(":{"df":0,"docs":{},"ω":{")":{"df":0,"docs":{},"t":{"(":{"df":0,"docs":{},"ω":{"2":{"df":0,"docs":{},"g":{"2":{")":{"df":0,"docs":{},"−":{"df":0,"docs":{},"t":{"(":{"df":0,"docs":{},"ω":{"2":{"df":0,"docs":{},"g":{")":{"df":0,"docs":{},"−":{"df":0,"docs":{},"t":{"(":{"df":0,"docs":{},"ω":{"2":{")":{"df":0,"docs":{},"⋮":{"df":0,"docs":{},"t":{"(":{"df":0,"docs":{},"ω":{"1":{"5":{"df":0,"docs":{},"g":{"2":{")":{"df":0,"docs":{},"−":{"df":0,"docs":{},"t":{"(":{"df":0,"docs":{},"ω":{"1":{"5":{"df":0,"docs":{},"g":{")":{"df":0,"docs":{},"−":{"df":0,"docs":{},"t":{"(":{"df":0,"docs":{},"ω":{"1":{"5":{"df":1,"docs":{"114":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"2":{"df":0,"docs":{},"i":{")":{":":{"0":{"df":0,"docs":{},"≤":{"df":0,"docs":{},"i":{"df":0,"docs":{},"≤":{"1":{"5":{"df":0,"docs":{},"}":{"=":{"df":0,"docs":{},"{":{"df":0,"docs":{},"t":{"(":{"df":0,"docs":{},"g":{"df":0,"docs":{},"i":{")":{":":{"0":{"df":0,"docs":{},"≤":{"df":0,"docs":{},"i":{"df":0,"docs":{},"≤":{"7":{"df":1,"docs":{"68":{"tf":1.0}}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"4":{")":{"df":0,"docs":{},"−":{"df":0,"docs":{},"t":{"(":{"df":0,"docs":{},"ω":{"2":{")":{"df":0,"docs":{},"−":{"df":0,"docs":{},"t":{"(":{"df":0,"docs":{},"ω":{"0":{")":{"df":0,"docs":{},"t":{"(":{"df":0,"docs":{},"ω":{"5":{")":{"df":0,"docs":{},"−":{"df":0,"docs":{},"t":{"(":{"df":0,"docs":{},"ω":{"3":{")":{"df":0,"docs":{},"−":{"df":0,"docs":{},"t":{"(":{"df":0,"docs":{},"ω":{")":{"df":0,"docs":{},"t":{"(":{"df":0,"docs":{},"ω":{"6":{")":{"df":0,"docs":{},"−":{"df":0,"docs":{},"t":{"(":{"df":0,"docs":{},"ω":{"4":{")":{"df":0,"docs":{},"−":{"df":0,"docs":{},"t":{"(":{"df":0,"docs":{},"ω":{"2":{")":{"df":0,"docs":{},"⋮":{"df":0,"docs":{},"t":{"(":{"df":0,"docs":{},"ω":{"3":{")":{"df":0,"docs":{},"−":{"df":0,"docs":{},"t":{"(":{"df":0,"docs":{},"ω":{")":{"df":0,"docs":{},"−":{"df":0,"docs":{},"t":{"(":{"df":0,"docs":{},"ω":{"1":{"5":{"df":1,"docs":{"114":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"0":{",":{"df":0,"docs":{},"i":{"df":1,"docs":{"20":{"tf":1.0}}},"j":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"s":{"df":0,"docs":{},"σ":{"1":{"df":0,"docs":{},"​":{"(":{"df":0,"docs":{},"ω":{"df":0,"docs":{},"i":{")":{"df":0,"docs":{},"β":{"+":{"df":0,"docs":{},"γ":{")":{"(":{"df":0,"docs":{},"t":{"0":{",":{"df":0,"docs":{},"j":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"s":{"df":0,"docs":{},"σ":{"2":{"df":0,"docs":{},"​":{"(":{"df":0,"docs":{},"ω":{"df":0,"docs":{},"i":{")":{"df":0,"docs":{},"β":{"+":{"df":0,"docs":{},"γ":{")":{"(":{"df":0,"docs":{},"t":{"0":{",":{"df":0,"docs":{},"j":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"s":{"df":0,"docs":{},"σ":{"3":{"df":0,"docs":{},"​":{"(":{"df":0,"docs":{},"ω":{"df":0,"docs":{},"i":{")":{"df":0,"docs":{},"β":{"+":{"df":0,"docs":{},"γ":{")":{":":{"0":{"df":0,"docs":{},"≤":{"df":0,"docs":{},"i":{"<":{"df":0,"docs":{},"n":{"df":1,"docs":{"14":{"tf":1.0}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"ω":{"df":0,"docs":{},"i":{"df":0,"docs":{},"β":{"+":{"df":0,"docs":{},"γ":{")":{"(":{"df":0,"docs":{},"t":{"1":{",":{"df":0,"docs":{},"j":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"ω":{"df":0,"docs":{},"i":{"df":0,"docs":{},"k":{"1":{"df":0,"docs":{},"​":{"df":0,"docs":{},"β":{"+":{"df":0,"docs":{},"γ":{")":{"(":{"df":0,"docs":{},"t":{"2":{",":{"df":0,"docs":{},"j":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"ω":{"df":0,"docs":{},"i":{"df":0,"docs":{},"k":{"2":{"df":0,"docs":{},"​":{"df":0,"docs":{},"β":{"+":{"df":0,"docs":{},"γ":{")":{":":{"0":{"df":0,"docs":{},"≤":{"df":0,"docs":{},"i":{"<":{"df":0,"docs":{},"n":{"df":1,"docs":{"14":{"tf":1.0}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}}},"df":0,"docs":{}}}},"df":4,"docs":{"136":{"tf":1.0},"142":{"tf":1.0},"146":{"tf":2.0},"147":{"tf":1.0}}},"1":{"df":5,"docs":{"136":{"tf":1.0},"142":{"tf":1.0},"146":{"tf":1.7320508075688772},"147":{"tf":1.0},"16":{"tf":1.0}}},"2":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"t":{"3":{"df":1,"docs":{"16":{"tf":1.0}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"=":{"df":0,"docs":{},"t":{"df":0,"docs":{},"l":{"df":0,"docs":{},"o":{"df":0,"docs":{},"′":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"x":{"df":0,"docs":{},"n":{"+":{"2":{"df":0,"docs":{},"t":{"df":0,"docs":{},"m":{"df":0,"docs":{},"i":{"d":{"df":0,"docs":{},"′":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"x":{"2":{"(":{"df":0,"docs":{},"n":{"+":{"2":{")":{"df":0,"docs":{},"t":{"df":0,"docs":{},"h":{"df":0,"docs":{},"i":{"df":1,"docs":{"26":{"tf":1.0}}}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}}}}}},"_":{"1":{"(":{"df":0,"docs":{},"z":{"df":0,"docs":{},"g":{"df":1,"docs":{"66":{"tf":1.0}}}}},"df":0,"docs":{}},"2":{"(":{"df":0,"docs":{},"z":{"df":1,"docs":{"66":{"tf":1.0}}}},"df":0,"docs":{}},"df":0,"docs":{},"h":{"df":0,"docs":{},"i":{"_":{"1":{"df":1,"docs":{"35":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}}},"i":{"(":{"df":0,"docs":{},"x":{"df":1,"docs":{"65":{"tf":1.0}}}},"df":0,"docs":{}},"l":{"df":0,"docs":{},"o":{"_":{"1":{"df":1,"docs":{"35":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}}},"m":{"df":0,"docs":{},"i":{"d":{"_":{"1":{"df":1,"docs":{"35":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"z":{"df":0,"docs":{},"e":{"df":0,"docs":{},"t":{"a":{"df":1,"docs":{"35":{"tf":1.0}}},"df":0,"docs":{}}}}},"a":{"b":{"df":0,"docs":{},"l":{"df":9,"docs":{"108":{"tf":1.7320508075688772},"119":{"tf":1.0},"127":{"tf":1.0},"137":{"tf":1.0},"145":{"tf":1.0},"48":{"tf":1.0},"49":{"tf":1.4142135623730951},"70":{"tf":1.0},"9":{"tf":1.0}}}},"c":{"df":0,"docs":{},"k":{"df":0,"docs":{},"l":{"df":1,"docs":{"64":{"tf":1.0}}}}},"df":0,"docs":{},"k":{"df":0,"docs":{},"e":{"df":23,"docs":{"104":{"tf":1.4142135623730951},"113":{"tf":2.0},"123":{"tf":1.0},"13":{"tf":1.4142135623730951},"14":{"tf":2.0},"149":{"tf":1.0},"151":{"tf":1.4142135623730951},"19":{"tf":1.7320508075688772},"20":{"tf":1.7320508075688772},"34":{"tf":1.0},"41":{"tf":1.0},"44":{"tf":1.0},"51":{"tf":1.0},"52":{"tf":1.4142135623730951},"56":{"tf":1.0},"63":{"tf":1.4142135623730951},"68":{"tf":1.4142135623730951},"7":{"tf":1.4142135623730951},"70":{"tf":1.4142135623730951},"74":{"tf":1.0},"8":{"tf":1.0},"80":{"tf":1.0},"9":{"tf":1.0}},"n":{"df":1,"docs":{"20":{"tf":1.0}}}}},"l":{"df":0,"docs":{},"k":{"df":4,"docs":{"105":{"tf":1.0},"107":{"tf":1.0},"116":{"tf":1.0},"52":{"tf":1.4142135623730951}}}}},"df":27,"docs":{"11":{"tf":1.0},"112":{"tf":1.7320508075688772},"114":{"tf":1.4142135623730951},"13":{"tf":1.0},"14":{"tf":1.7320508075688772},"142":{"tf":1.7320508075688772},"147":{"tf":1.4142135623730951},"16":{"tf":2.449489742783178},"20":{"tf":1.4142135623730951},"23":{"tf":1.0},"24":{"tf":1.0},"26":{"tf":1.0},"28":{"tf":1.0},"32":{"tf":1.7320508075688772},"34":{"tf":1.4142135623730951},"35":{"tf":1.4142135623730951},"36":{"tf":1.4142135623730951},"52":{"tf":1.7320508075688772},"60":{"tf":1.0},"62":{"tf":1.7320508075688772},"68":{"tf":2.23606797749979},"69":{"tf":1.4142135623730951},"70":{"tf":2.0},"83":{"tf":1.4142135623730951},"84":{"tf":1.7320508075688772},"91":{"tf":1.7320508075688772},"94":{"tf":1.0}},"e":{"c":{"df":0,"docs":{},"h":{"df":0,"docs":{},"n":{"df":0,"docs":{},"i":{"c":{"df":1,"docs":{"52":{"tf":1.0}}},"df":0,"docs":{},"q":{"df":0,"docs":{},"u":{"df":1,"docs":{"88":{"tf":1.0}}}}}}}},"df":0,"docs":{},"l":{"df":0,"docs":{},"l":{"df":3,"docs":{"109":{"tf":1.0},"125":{"tf":1.0},"49":{"tf":1.0}}}},"m":{"df":0,"docs":{},"p":{"df":0,"docs":{},"o":{"df":0,"docs":{},"r":{"a":{"df":0,"docs":{},"r":{"df":0,"docs":{},"i":{"df":3,"docs":{"142":{"tf":1.0},"146":{"tf":1.4142135623730951},"147":{"tf":1.0}},"l":{"df":0,"docs":{},"i":{"df":1,"docs":{"34":{"tf":1.4142135623730951}}}}}}},"df":0,"docs":{}}}}},"r":{"df":0,"docs":{},"m":{"df":14,"docs":{"114":{"tf":1.4142135623730951},"123":{"tf":1.0},"125":{"tf":1.0},"128":{"tf":1.0},"14":{"tf":1.0},"144":{"tf":1.4142135623730951},"16":{"tf":1.0},"20":{"tf":1.0},"52":{"tf":1.4142135623730951},"65":{"tf":1.0},"66":{"tf":1.0},"67":{"tf":1.4142135623730951},"80":{"tf":1.0},"85":{"tf":1.0}},"i":{"df":0,"docs":{},"n":{"df":0,"docs":{},"o":{"df":0,"docs":{},"l":{"df":0,"docs":{},"o":{"df":0,"docs":{},"g":{"df":1,"docs":{"34":{"tf":1.0}}}}}}}}}},"s":{"df":0,"docs":{},"t":{"_":{"c":{"df":0,"docs":{},"o":{"df":0,"docs":{},"m":{"df":0,"docs":{},"m":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"_":{"df":0,"docs":{},"p":{"df":0,"docs":{},"r":{"df":0,"docs":{},"e":{"df":0,"docs":{},"p":{"df":0,"docs":{},"r":{"df":0,"docs":{},"o":{"c":{"df":0,"docs":{},"e":{"df":0,"docs":{},"s":{"df":0,"docs":{},"s":{"df":0,"docs":{},"e":{"d":{"_":{"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"df":0,"docs":{},"p":{"df":0,"docs":{},"u":{"df":0,"docs":{},"t":{"_":{"2":{"df":1,"docs":{"35":{"tf":1.4142135623730951}}},"df":0,"docs":{}},"df":0,"docs":{}}}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}}}},"df":0,"docs":{}}}}}}}},"df":0,"docs":{}}}}}}},"df":0,"docs":{},"p":{"df":0,"docs":{},"r":{"df":0,"docs":{},"o":{"df":0,"docs":{},"v":{"df":0,"docs":{},"e":{"_":{"df":0,"docs":{},"f":{"df":0,"docs":{},"i":{"b":{"df":1,"docs":{"104":{"tf":1.0}}},"df":0,"docs":{}}}},"df":0,"docs":{}}}}}},"s":{"df":0,"docs":{},"r":{"df":0,"docs":{},"s":{"(":{"c":{"df":0,"docs":{},"o":{"df":0,"docs":{},"m":{"df":0,"docs":{},"m":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{".":{"df":0,"docs":{},"n":{"df":1,"docs":{"44":{"tf":1.0}}}},"_":{"df":0,"docs":{},"p":{"df":0,"docs":{},"r":{"df":0,"docs":{},"e":{"df":0,"docs":{},"p":{"df":0,"docs":{},"r":{"df":0,"docs":{},"o":{"c":{"df":0,"docs":{},"e":{"df":0,"docs":{},"s":{"df":0,"docs":{},"s":{"df":0,"docs":{},"e":{"d":{"_":{"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"df":0,"docs":{},"p":{"df":0,"docs":{},"u":{"df":0,"docs":{},"t":{".":{"df":0,"docs":{},"n":{"df":1,"docs":{"35":{"tf":1.4142135623730951}}}},"df":0,"docs":{}}}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}}}},"df":0,"docs":{}}}}}}}},"df":0,"docs":{}}}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"w":{"df":0,"docs":{},"i":{"df":0,"docs":{},"t":{"df":0,"docs":{},"n":{"df":0,"docs":{},"e":{"df":0,"docs":{},"s":{"df":0,"docs":{},"s":{"_":{"2":{"(":{"df":0,"docs":{},"x":{"df":1,"docs":{"35":{"tf":1.7320508075688772}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}}}}}}},"df":5,"docs":{"104":{"tf":1.0},"114":{"tf":1.0},"34":{"tf":1.0},"35":{"tf":1.4142135623730951},"81":{"tf":1.0}},"h":{"a":{"df":0,"docs":{},"s":{"df":0,"docs":{},"h":{"df":1,"docs":{"110":{"tf":1.0}}}}},"df":0,"docs":{}},"r":{"a":{"df":0,"docs":{},"n":{"d":{"df":0,"docs":{},"o":{"df":0,"docs":{},"m":{"df":0,"docs":{},"f":{"df":0,"docs":{},"i":{"df":0,"docs":{},"e":{"df":0,"docs":{},"l":{"d":{"df":0,"docs":{},"g":{"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":2,"docs":{"35":{"tf":1.4142135623730951},"44":{"tf":1.0}}}}}}}},"df":0,"docs":{}}}}}}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}},"h":{"a":{"df":0,"docs":{},"n":{"df":0,"docs":{},"k":{"df":2,"docs":{"103":{"tf":1.0},"81":{"tf":1.0}}}},"t":{"'":{"df":10,"docs":{"142":{"tf":1.4142135623730951},"146":{"tf":1.0},"147":{"tf":1.0},"151":{"tf":1.0},"16":{"tf":1.0},"46":{"tf":1.0},"54":{"tf":1.0},"79":{"tf":1.0},"9":{"tf":1.0},"95":{"tf":1.0}}},"df":0,"docs":{}}},"df":17,"docs":{"112":{"tf":1.0},"123":{"tf":1.0},"124":{"tf":2.8284271247461903},"125":{"tf":1.7320508075688772},"14":{"tf":3.0},"142":{"tf":1.0},"16":{"tf":1.0},"23":{"tf":1.0},"35":{"tf":1.0},"49":{"tf":1.4142135623730951},"52":{"tf":1.7320508075688772},"62":{"tf":1.0},"65":{"tf":1.0},"68":{"tf":2.23606797749979},"70":{"tf":1.7320508075688772},"91":{"tf":1.0},"92":{"tf":2.0}},"e":{"df":0,"docs":{},"o":{"df":0,"docs":{},"r":{"df":0,"docs":{},"e":{"df":0,"docs":{},"m":{"df":1,"docs":{"73":{"tf":1.0}}}}}},"r":{"df":0,"docs":{},"e":{"'":{"df":6,"docs":{"102":{"tf":1.0},"116":{"tf":1.0},"14":{"tf":1.0},"146":{"tf":1.0},"22":{"tf":1.0},"7":{"tf":1.0}}},"df":0,"docs":{},"f":{"df":0,"docs":{},"o":{"df":0,"docs":{},"r":{"df":8,"docs":{"101":{"tf":1.0},"12":{"tf":1.0},"20":{"tf":1.0},"36":{"tf":1.0},"38":{"tf":1.0},"41":{"tf":1.0},"66":{"tf":1.0},"94":{"tf":1.0}}}}}}},"y":{"'":{"df":0,"docs":{},"r":{"df":2,"docs":{"3":{"tf":1.0},"68":{"tf":1.0}}}},"df":0,"docs":{}}},"i":{"df":1,"docs":{"26":{"tf":1.0}},"n":{"df":0,"docs":{},"g":{"df":16,"docs":{"104":{"tf":1.0},"109":{"tf":1.4142135623730951},"111":{"tf":1.0},"113":{"tf":1.0},"124":{"tf":1.0},"14":{"tf":1.7320508075688772},"16":{"tf":1.4142135623730951},"21":{"tf":1.0},"49":{"tf":1.4142135623730951},"50":{"tf":1.0},"52":{"tf":1.0},"53":{"tf":1.0},"56":{"tf":1.0},"58":{"tf":1.0},"59":{"tf":1.0},"68":{"tf":1.0}}},"k":{"df":4,"docs":{"118":{"tf":1.4142135623730951},"151":{"tf":1.4142135623730951},"52":{"tf":1.0},"70":{"tf":1.0}}}},"r":{"d":{"_":{"df":0,"docs":{},"r":{"df":0,"docs":{},"o":{"df":0,"docs":{},"w":{"[":{"0":{"df":1,"docs":{"107":{"tf":1.0}}},"df":0,"docs":{}},"df":2,"docs":{"107":{"tf":1.0},"114":{"tf":1.0}}}}}},"df":1,"docs":{"13":{"tf":1.0}}},"df":0,"docs":{}}},"o":{"df":0,"docs":{},"s":{"df":0,"docs":{},"e":{"df":3,"docs":{"102":{"tf":1.0},"14":{"tf":1.0},"3":{"tf":1.0}}}},"u":{"df":0,"docs":{},"g":{"df":0,"docs":{},"h":{"df":6,"docs":{"114":{"tf":1.0},"118":{"tf":1.0},"49":{"tf":1.0},"52":{"tf":1.0},"57":{"tf":1.0},"66":{"tf":1.0}}}}}},"r":{"df":0,"docs":{},"e":{"df":0,"docs":{},"e":{"df":16,"docs":{"107":{"tf":1.0},"12":{"tf":1.0},"128":{"tf":1.0},"130":{"tf":1.4142135623730951},"141":{"tf":1.0},"142":{"tf":1.4142135623730951},"144":{"tf":1.4142135623730951},"149":{"tf":1.0},"150":{"tf":1.0},"21":{"tf":1.4142135623730951},"3":{"tf":1.0},"44":{"tf":1.0},"58":{"tf":1.0},"69":{"tf":1.0},"84":{"tf":1.0},"9":{"tf":1.0}}}},"o":{"df":0,"docs":{},"u":{"df":0,"docs":{},"g":{"df":0,"docs":{},"h":{"df":8,"docs":{"105":{"tf":1.0},"106":{"tf":1.0},"111":{"tf":1.0},"113":{"tf":1.0},"4":{"tf":1.0},"46":{"tf":1.0},"48":{"tf":1.7320508075688772},"51":{"tf":1.0}},"o":{"df":0,"docs":{},"u":{"df":0,"docs":{},"t":{"df":4,"docs":{"118":{"tf":1.7320508075688772},"124":{"tf":1.4142135623730951},"49":{"tf":1.0},"7":{"tf":1.0}}}}}}}}}},"u":{"df":3,"docs":{"57":{"tf":1.0},"58":{"tf":1.0},"83":{"tf":1.0}}}},"i":{",":{"df":0,"docs":{},"j":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"η":{"3":{"df":0,"docs":{},"i":{"+":{"df":0,"docs":{},"j":{"df":0,"docs":{},"β":{"+":{"df":0,"docs":{},"γ":{":":{"(":{"df":0,"docs":{},"i":{",":{"df":0,"docs":{},"j":{")":{"df":0,"docs":{},"∈":{"df":0,"docs":{},"i":{"df":1,"docs":{"14":{"tf":1.0}}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"k":{"+":{"df":0,"docs":{},"l":{"df":0,"docs":{},"β":{"+":{"df":0,"docs":{},"γ":{":":{"(":{"df":0,"docs":{},"i":{",":{"df":0,"docs":{},"j":{")":{"df":0,"docs":{},"∈":{"df":0,"docs":{},"i":{",":{"df":0,"docs":{},"σ":{"(":{"(":{"df":0,"docs":{},"i":{",":{"df":0,"docs":{},"j":{")":{")":{"=":{"(":{"df":0,"docs":{},"k":{",":{"df":0,"docs":{},"l":{"df":1,"docs":{"14":{"tf":1.0}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"=":{"df":0,"docs":{},"t":{"df":0,"docs":{},"j":{"df":0,"docs":{},"​":{"(":{"df":0,"docs":{},"g":{"df":0,"docs":{},"i":{"df":2,"docs":{"83":{"tf":1.0},"84":{"tf":1.0}}}}},"df":0,"docs":{}}},"k":{",":{"df":0,"docs":{},"l":{"df":4,"docs":{"11":{"tf":1.0},"13":{"tf":1.0},"14":{"tf":1.0},"16":{"tf":1.0}}}},"df":0,"docs":{}},"σ":{"(":{"(":{"df":0,"docs":{},"i":{",":{"df":0,"docs":{},"j":{"df":1,"docs":{"14":{"tf":1.0}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}}},"df":1,"docs":{"16":{"tf":1.0}},"j":{"df":1,"docs":{"70":{"tf":1.0}}},"m":{"df":0,"docs":{},"e":{"df":10,"docs":{"101":{"tf":1.0},"118":{"tf":2.0},"133":{"tf":1.0},"146":{"tf":1.4142135623730951},"149":{"tf":1.0},"22":{"tf":1.0},"3":{"tf":1.4142135623730951},"45":{"tf":1.0},"59":{"tf":1.0},"80":{"tf":2.0}}}}},"j":{"df":5,"docs":{"70":{"tf":1.7320508075688772},"83":{"tf":1.4142135623730951},"84":{"tf":1.0},"94":{"tf":1.7320508075688772},"95":{"tf":2.6457513110645907}},"​":{"(":{"df":0,"docs":{},"g":{"df":0,"docs":{},"i":{")":{"=":{"df":0,"docs":{},"m":{"^":{"df":0,"docs":{},"i":{",":{"df":0,"docs":{},"j":{"df":1,"docs":{"94":{"tf":1.0}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"t":{"df":0,"docs":{},"i":{",":{"df":0,"docs":{},"j":{"df":1,"docs":{"94":{"tf":1.0}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}},"z":{"df":3,"docs":{"85":{"tf":1.0},"94":{"tf":1.4142135623730951},"95":{"tf":1.0}}}},"z":{"df":3,"docs":{"85":{"tf":1.0},"94":{"tf":1.4142135623730951},"95":{"tf":1.4142135623730951}}}},",":{"df":0,"docs":{},"τ":{"df":0,"docs":{},"j":{"df":0,"docs":{},"z":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"τ":{"df":0,"docs":{},"j":{"df":0,"docs":{},"g":{"df":0,"docs":{},"z":{"df":0,"docs":{},"​":{":":{"0":{"df":0,"docs":{},"≤":{"df":0,"docs":{},"j":{"<":{"df":0,"docs":{},"m":{"df":0,"docs":{},"}":{",":{"df":0,"docs":{},"h":{"1":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"η":{"1":{"df":0,"docs":{},"z":{"2":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"h":{"2":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"η":{"2":{"df":0,"docs":{},"z":{"2":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"{":{"df":0,"docs":{},"p":{"df":0,"docs":{},"k":{"df":0,"docs":{},"​":{":":{"1":{"df":0,"docs":{},"≤":{"df":0,"docs":{},"k":{"<":{"df":0,"docs":{},"n":{"df":0,"docs":{},"}":{",":{"df":0,"docs":{},"π":{",":{"df":0,"docs":{},"y":{",":{"df":0,"docs":{},"{":{"(":{"df":0,"docs":{},"τ":{"df":0,"docs":{},"j":{"df":0,"docs":{},"υ":{"df":0,"docs":{},"s":{"df":0,"docs":{},"​":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"t":{"df":0,"docs":{},"j":{"df":0,"docs":{},"​":{")":{":":{"0":{"df":0,"docs":{},"≤":{"df":0,"docs":{},"j":{"<":{"df":0,"docs":{},"m":{",":{"0":{"df":0,"docs":{},"≤":{"df":0,"docs":{},"s":{"<":{"df":0,"docs":{},"q":{"df":0,"docs":{},"}":{",":{"df":0,"docs":{},"{":{"(":{"df":0,"docs":{},"η":{"1":{"df":0,"docs":{},"υ":{"df":0,"docs":{},"s":{"df":0,"docs":{},"​":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"h":{"1":{"df":0,"docs":{},"​":{")":{":":{"0":{"df":0,"docs":{},"≤":{"df":0,"docs":{},"s":{"<":{"df":0,"docs":{},"q":{"df":0,"docs":{},"}":{"df":0,"docs":{},"{":{"(":{"df":0,"docs":{},"η":{"2":{"df":0,"docs":{},"υ":{"df":0,"docs":{},"s":{"df":0,"docs":{},"​":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"h":{"2":{"df":0,"docs":{},"​":{")":{":":{"0":{"df":0,"docs":{},"≤":{"df":0,"docs":{},"s":{"<":{"df":0,"docs":{},"q":{"df":0,"docs":{},"}":{",":{"df":0,"docs":{},"{":{"(":{"df":0,"docs":{},"π":{"df":0,"docs":{},"k":{"df":0,"docs":{},"υ":{"df":0,"docs":{},"s":{"2":{"df":0,"docs":{},"k":{"df":0,"docs":{},"​":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"p":{"df":0,"docs":{},"k":{"df":0,"docs":{},"​":{")":{":":{"1":{"df":0,"docs":{},"≤":{"df":0,"docs":{},"k":{"<":{"df":0,"docs":{},"n":{",":{"0":{"df":0,"docs":{},"≤":{"df":0,"docs":{},"s":{"<":{"df":0,"docs":{},"q":{"df":0,"docs":{},"}":{",":{"df":0,"docs":{},"{":{"(":{"df":0,"docs":{},"τ":{"df":0,"docs":{},"j":{"df":0,"docs":{},"−":{"df":0,"docs":{},"υ":{"df":0,"docs":{},"s":{"df":0,"docs":{},"​":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"t":{"df":0,"docs":{},"j":{"df":0,"docs":{},"′":{"df":0,"docs":{},"​":{")":{":":{"0":{"df":0,"docs":{},"≤":{"df":0,"docs":{},"j":{"<":{"df":0,"docs":{},"m":{",":{"0":{"df":0,"docs":{},"≤":{"df":0,"docs":{},"s":{"<":{"df":0,"docs":{},"q":{"df":0,"docs":{},"}":{",":{"df":0,"docs":{},"{":{"(":{"df":0,"docs":{},"η":{"1":{"df":0,"docs":{},"−":{"df":0,"docs":{},"υ":{"df":0,"docs":{},"s":{"df":0,"docs":{},"​":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"h":{"1":{"df":0,"docs":{},"′":{"df":0,"docs":{},"​":{")":{":":{"0":{"df":0,"docs":{},"≤":{"df":0,"docs":{},"s":{"<":{"df":0,"docs":{},"q":{"df":0,"docs":{},"}":{"df":0,"docs":{},"{":{"(":{"df":0,"docs":{},"η":{"2":{"df":0,"docs":{},"−":{"df":0,"docs":{},"υ":{"df":0,"docs":{},"s":{"df":0,"docs":{},"​":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"h":{"2":{"df":0,"docs":{},"′":{"df":0,"docs":{},"​":{")":{":":{"0":{"df":0,"docs":{},"≤":{"df":0,"docs":{},"s":{"<":{"df":0,"docs":{},"q":{"df":0,"docs":{},"}":{",":{"df":0,"docs":{},"{":{"(":{"df":0,"docs":{},"π":{"df":0,"docs":{},"k":{"df":0,"docs":{},"−":{"df":0,"docs":{},"υ":{"df":0,"docs":{},"s":{"2":{"df":0,"docs":{},"k":{"df":0,"docs":{},"​":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"p":{"df":0,"docs":{},"k":{"df":0,"docs":{},"′":{"df":0,"docs":{},"​":{")":{":":{"1":{"df":0,"docs":{},"≤":{"df":0,"docs":{},"k":{"<":{"df":0,"docs":{},"n":{",":{"0":{"df":0,"docs":{},"≤":{"df":0,"docs":{},"s":{"<":{"df":0,"docs":{},"q":{"df":1,"docs":{"95":{"tf":1.0}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}}}},"df":0,"docs":{}}}}},"df":0,"docs":{}}}}}}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}}}},"df":0,"docs":{}}}}}}}}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}}}}},"df":0,"docs":{}}}}}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}}}}}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}}}}}},"df":0,"docs":{}}}}}},"]":{",":{"df":0,"docs":{},"t":{"df":0,"docs":{},"j":{"df":0,"docs":{},"​":{"(":{"df":0,"docs":{},"z":{")":{",":{"df":0,"docs":{},"t":{"df":0,"docs":{},"j":{"df":0,"docs":{},"​":{"(":{"df":0,"docs":{},"g":{"df":0,"docs":{},"z":{")":{":":{"0":{"df":0,"docs":{},"≤":{"df":0,"docs":{},"j":{"<":{"df":0,"docs":{},"m":{"df":0,"docs":{},"}":{",":{"[":{"df":0,"docs":{},"h":{"1":{"df":0,"docs":{},"​":{"]":{",":{"df":0,"docs":{},"h":{"1":{"df":0,"docs":{},"​":{"(":{"df":0,"docs":{},"z":{"2":{")":{",":{"[":{"df":0,"docs":{},"h":{"2":{"df":0,"docs":{},"​":{"]":{",":{"df":0,"docs":{},"h":{"2":{"df":0,"docs":{},"​":{"(":{"df":0,"docs":{},"z":{"2":{")":{",":{"df":0,"docs":{},"{":{"[":{"df":0,"docs":{},"p":{"df":0,"docs":{},"k":{"df":0,"docs":{},"​":{"]":{":":{"1":{"df":0,"docs":{},"≤":{"df":0,"docs":{},"k":{"<":{"df":0,"docs":{},"n":{"df":0,"docs":{},"}":{",":{"df":0,"docs":{},"p":{"df":0,"docs":{},"n":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"y":{",":{"df":0,"docs":{},"{":{"df":0,"docs":{},"o":{"df":0,"docs":{},"p":{"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"(":{"df":0,"docs":{},"t":{"df":0,"docs":{},"j":{"df":0,"docs":{},"​":{"(":{"d":{"df":0,"docs":{},"l":{"d":{"df":0,"docs":{},"e":{"df":0,"docs":{},"​":{")":{",":{"df":0,"docs":{},"υ":{"df":0,"docs":{},"s":{"df":0,"docs":{},"​":{")":{":":{"0":{"df":0,"docs":{},"≤":{"df":0,"docs":{},"j":{"<":{"df":0,"docs":{},"m":{",":{"0":{"df":0,"docs":{},"≤":{"df":0,"docs":{},"s":{"<":{"df":0,"docs":{},"q":{"df":0,"docs":{},"}":{",":{"df":0,"docs":{},"{":{"df":0,"docs":{},"o":{"df":0,"docs":{},"p":{"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"(":{"df":0,"docs":{},"h":{"1":{"df":0,"docs":{},"​":{"(":{"d":{"df":0,"docs":{},"l":{"d":{"df":0,"docs":{},"e":{"df":0,"docs":{},"​":{")":{",":{"df":0,"docs":{},"υ":{"df":0,"docs":{},"s":{"df":0,"docs":{},"​":{")":{":":{"0":{"df":0,"docs":{},"≤":{"df":0,"docs":{},"s":{"<":{"df":0,"docs":{},"q":{"df":0,"docs":{},"}":{",":{"df":0,"docs":{},"{":{"df":0,"docs":{},"o":{"df":0,"docs":{},"p":{"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"(":{"df":0,"docs":{},"h":{"2":{"df":0,"docs":{},"​":{"(":{"d":{"df":0,"docs":{},"l":{"d":{"df":0,"docs":{},"e":{"df":0,"docs":{},"​":{")":{",":{"df":0,"docs":{},"υ":{"df":0,"docs":{},"s":{"df":0,"docs":{},"​":{")":{":":{"0":{"df":0,"docs":{},"≤":{"df":0,"docs":{},"s":{"<":{"df":0,"docs":{},"q":{"df":0,"docs":{},"}":{",":{"df":0,"docs":{},"{":{"df":0,"docs":{},"o":{"df":0,"docs":{},"p":{"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"(":{"df":0,"docs":{},"p":{"df":0,"docs":{},"k":{"df":0,"docs":{},"​":{"(":{"d":{"df":0,"docs":{},"k":{"df":0,"docs":{},"​":{")":{",":{"df":0,"docs":{},"υ":{"df":0,"docs":{},"s":{"2":{"df":0,"docs":{},"k":{"df":0,"docs":{},"​":{")":{":":{"1":{"df":0,"docs":{},"≤":{"df":0,"docs":{},"k":{"<":{"df":0,"docs":{},"n":{",":{"0":{"df":0,"docs":{},"≤":{"df":0,"docs":{},"s":{"<":{"df":0,"docs":{},"q":{"df":0,"docs":{},"}":{",":{"df":0,"docs":{},"{":{"df":0,"docs":{},"o":{"df":0,"docs":{},"p":{"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"(":{"df":0,"docs":{},"t":{"df":0,"docs":{},"j":{"df":0,"docs":{},"​":{"(":{"d":{"df":0,"docs":{},"l":{"d":{"df":0,"docs":{},"e":{"df":0,"docs":{},"​":{")":{",":{"df":0,"docs":{},"−":{"df":0,"docs":{},"υ":{"df":0,"docs":{},"s":{"df":0,"docs":{},"​":{")":{":":{"0":{"df":0,"docs":{},"≤":{"df":0,"docs":{},"j":{"<":{"df":0,"docs":{},"m":{",":{"0":{"df":0,"docs":{},"≤":{"df":0,"docs":{},"s":{"<":{"df":0,"docs":{},"q":{"df":0,"docs":{},"}":{",":{"df":0,"docs":{},"{":{"df":0,"docs":{},"o":{"df":0,"docs":{},"p":{"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"(":{"df":0,"docs":{},"h":{"1":{"df":0,"docs":{},"​":{"(":{"d":{"df":0,"docs":{},"l":{"d":{"df":0,"docs":{},"e":{"df":0,"docs":{},"​":{")":{",":{"df":0,"docs":{},"−":{"df":0,"docs":{},"υ":{"df":0,"docs":{},"s":{"df":0,"docs":{},"​":{")":{":":{"0":{"df":0,"docs":{},"≤":{"df":0,"docs":{},"s":{"<":{"df":0,"docs":{},"q":{"df":0,"docs":{},"}":{",":{"df":0,"docs":{},"{":{"df":0,"docs":{},"o":{"df":0,"docs":{},"p":{"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"(":{"df":0,"docs":{},"h":{"2":{"df":0,"docs":{},"​":{"(":{"d":{"df":0,"docs":{},"l":{"d":{"df":0,"docs":{},"e":{"df":0,"docs":{},"​":{")":{",":{"df":0,"docs":{},"−":{"df":0,"docs":{},"υ":{"df":0,"docs":{},"s":{"df":0,"docs":{},"​":{")":{":":{"0":{"df":0,"docs":{},"≤":{"df":0,"docs":{},"s":{"<":{"df":0,"docs":{},"q":{"df":0,"docs":{},"}":{",":{"df":0,"docs":{},"{":{"df":0,"docs":{},"o":{"df":0,"docs":{},"p":{"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"(":{"df":0,"docs":{},"p":{"df":0,"docs":{},"k":{"df":0,"docs":{},"​":{"(":{"d":{"df":0,"docs":{},"k":{"df":0,"docs":{},"​":{")":{",":{"df":0,"docs":{},"−":{"df":0,"docs":{},"υ":{"df":0,"docs":{},"s":{"2":{"df":0,"docs":{},"k":{"df":0,"docs":{},"​":{")":{":":{"1":{"df":0,"docs":{},"≤":{"df":0,"docs":{},"k":{"<":{"df":0,"docs":{},"n":{",":{"0":{"df":0,"docs":{},"≤":{"df":0,"docs":{},"s":{"<":{"df":0,"docs":{},"q":{"df":1,"docs":{"94":{"tf":1.0}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}}}}}}},"df":0,"docs":{}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}}}},"df":0,"docs":{}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}}}},"df":0,"docs":{}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}}}}}}},"df":0,"docs":{}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}}}}}}},"df":0,"docs":{}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}}}},"df":0,"docs":{}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}}}},"df":0,"docs":{}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}}}}}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}},"df":0,"docs":{}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}}},":":{"=":{"c":{"df":0,"docs":{},"o":{"df":0,"docs":{},"m":{"df":0,"docs":{},"m":{"df":0,"docs":{},"i":{"df":0,"docs":{},"t":{"(":{"df":0,"docs":{},"t":{"df":0,"docs":{},"j":{"df":0,"docs":{},"​":{"(":{"d":{"df":0,"docs":{},"l":{"df":1,"docs":{"94":{"tf":1.4142135623730951}}}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}}}}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"l":{"df":0,"docs":{},"o":{"df":0,"docs":{},"​":{"]":{"1":{"df":0,"docs":{},"​":{",":{"[":{"df":0,"docs":{},"t":{"df":0,"docs":{},"m":{"df":0,"docs":{},"i":{"d":{"df":0,"docs":{},"​":{"]":{"1":{"df":0,"docs":{},"​":{",":{"[":{"df":0,"docs":{},"t":{"df":0,"docs":{},"h":{"df":0,"docs":{},"i":{"df":0,"docs":{},"​":{"]":{"1":{"df":2,"docs":{"26":{"tf":1.0},"32":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}}}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{},"t":{"df":0,"docs":{},"m":{"df":0,"docs":{},"i":{"d":{"df":0,"docs":{},"​":{"df":0,"docs":{},"t":{"df":0,"docs":{},"h":{"df":0,"docs":{},"i":{"df":0,"docs":{},"​":{"df":0,"docs":{},"​":{"=":{"df":0,"docs":{},"t":{"df":0,"docs":{},"l":{"df":0,"docs":{},"o":{"df":0,"docs":{},"′":{"df":0,"docs":{},"​":{"+":{"b":{"1":{"0":{"df":0,"docs":{},"​":{"df":0,"docs":{},"x":{"df":0,"docs":{},"n":{"+":{"2":{"=":{"df":0,"docs":{},"t":{"df":0,"docs":{},"m":{"df":0,"docs":{},"i":{"d":{"df":0,"docs":{},"′":{"df":0,"docs":{},"​":{"df":0,"docs":{},"−":{"b":{"1":{"0":{"df":0,"docs":{},"​":{"+":{"b":{"1":{"1":{"df":0,"docs":{},"​":{"df":0,"docs":{},"x":{"df":0,"docs":{},"n":{"+":{"2":{"=":{"df":0,"docs":{},"t":{"df":0,"docs":{},"h":{"df":0,"docs":{},"i":{"df":0,"docs":{},"′":{"df":0,"docs":{},"​":{"df":0,"docs":{},"−":{"b":{"1":{"1":{"df":1,"docs":{"26":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}}}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}}}}},"df":0,"docs":{}}}}}}}},"df":0,"docs":{}}}}},"′":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"t":{"df":0,"docs":{},"m":{"df":0,"docs":{},"i":{"d":{"df":1,"docs":{"26":{"tf":1.0}}},"df":0,"docs":{}}}}},"df":0,"docs":{}}}}},"m":{"df":0,"docs":{},"p":{"0":{"df":1,"docs":{"136":{"tf":1.0}}},"1":{"df":1,"docs":{"136":{"tf":1.0}}},"df":0,"docs":{}},"′":{"+":{"1":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"…":{",":{"df":0,"docs":{},"t":{"df":0,"docs":{},"m":{"df":0,"docs":{},"′":{"+":{"df":0,"docs":{},"m":{"df":1,"docs":{"94":{"tf":1.0}}}},"df":0,"docs":{}}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"o":{"df":0,"docs":{},"g":{"df":0,"docs":{},"e":{"df":0,"docs":{},"t":{"df":0,"docs":{},"h":{"df":1,"docs":{"147":{"tf":1.0}}}}}},"o":{"df":0,"docs":{},"l":{"df":5,"docs":{"19":{"tf":1.0},"50":{"tf":1.0},"70":{"tf":1.0},"8":{"tf":1.0},"9":{"tf":1.0}}}},"p":{"df":1,"docs":{"151":{"tf":1.0}}},"t":{"a":{"df":0,"docs":{},"l":{"df":6,"docs":{"133":{"tf":1.0},"135":{"tf":1.4142135623730951},"144":{"tf":1.0},"147":{"tf":1.0},"73":{"tf":1.0},"91":{"tf":1.0}}}},"df":0,"docs":{}},"w":{"a":{"df":0,"docs":{},"r":{"d":{"df":1,"docs":{"50":{"tf":1.0}}},"df":0,"docs":{}}},"df":0,"docs":{}},"y":{"df":3,"docs":{"34":{"tf":1.0},"7":{"tf":2.449489742783178},"9":{"tf":1.0}}}},"p":{"a":{"df":0,"docs":{},"r":{"df":0,"docs":{},"t":{"df":0,"docs":{},"i":{"a":{"df":0,"docs":{},"l":{"df":0,"docs":{},"​":{"(":{"df":0,"docs":{},"ζ":{")":{"=":{"df":0,"docs":{},"t":{"df":1,"docs":{"28":{"tf":1.0}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"+":{"df":0,"docs":{},"υ":{"df":0,"docs":{},"p":{"df":0,"docs":{},"n":{"c":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"υ":{"2":{"a":{"+":{"df":0,"docs":{},"υ":{"3":{"b":{"+":{"df":0,"docs":{},"υ":{"4":{"c":{"+":{"df":0,"docs":{},"υ":{"5":{"df":0,"docs":{},"s":{"df":0,"docs":{},"σ":{"1":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"υ":{"6":{"df":0,"docs":{},"s":{"df":0,"docs":{},"σ":{"2":{"df":1,"docs":{"28":{"tf":1.0}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}},"]":{"1":{"df":1,"docs":{"32":{"tf":1.0}},"​":{"=":{"[":{"df":0,"docs":{},"t":{"df":0,"docs":{},"l":{"df":0,"docs":{},"o":{"df":0,"docs":{},"​":{"]":{"1":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"ζ":{"df":0,"docs":{},"n":{"+":{"2":{"[":{"df":0,"docs":{},"t":{"df":0,"docs":{},"m":{"df":0,"docs":{},"i":{"d":{"df":0,"docs":{},"​":{"]":{"1":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"ζ":{"2":{"(":{"df":0,"docs":{},"n":{"+":{"2":{")":{"[":{"df":0,"docs":{},"t":{"df":0,"docs":{},"h":{"df":0,"docs":{},"i":{"df":0,"docs":{},"​":{"]":{"1":{"df":1,"docs":{"32":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}}}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"r":{"a":{"c":{"df":0,"docs":{},"e":{"'":{"df":2,"docs":{"68":{"tf":1.0},"70":{"tf":1.0}}},".":{"c":{"df":0,"docs":{},"l":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"df":1,"docs":{"108":{"tf":1.0}}}}}},"df":0,"docs":{}},"_":{"c":{"df":0,"docs":{},"o":{"df":0,"docs":{},"l":{"df":0,"docs":{},"u":{"df":0,"docs":{},"m":{"df":0,"docs":{},"n":{"df":1,"docs":{"109":{"tf":1.4142135623730951}}}}}}}},"df":0,"docs":{},"t":{"a":{"b":{"df":0,"docs":{},"l":{"df":0,"docs":{},"e":{".":{"df":0,"docs":{},"n":{"_":{"c":{"df":0,"docs":{},"o":{"df":0,"docs":{},"l":{"df":1,"docs":{"109":{"tf":1.0}}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":1,"docs":{"108":{"tf":1.4142135623730951}}}},"df":68,"docs":{"10":{"tf":1.7320508075688772},"104":{"tf":1.7320508075688772},"105":{"tf":1.4142135623730951},"107":{"tf":1.0},"108":{"tf":2.6457513110645907},"109":{"tf":2.449489742783178},"11":{"tf":1.0},"110":{"tf":1.0},"112":{"tf":1.7320508075688772},"113":{"tf":1.7320508075688772},"114":{"tf":1.4142135623730951},"117":{"tf":1.0},"119":{"tf":1.7320508075688772},"12":{"tf":1.0},"123":{"tf":1.4142135623730951},"125":{"tf":2.0},"127":{"tf":2.8284271247461903},"128":{"tf":1.4142135623730951},"129":{"tf":1.7320508075688772},"13":{"tf":1.0},"130":{"tf":2.0},"131":{"tf":1.4142135623730951},"132":{"tf":1.0},"133":{"tf":2.0},"134":{"tf":1.4142135623730951},"135":{"tf":1.0},"136":{"tf":1.0},"137":{"tf":2.0},"138":{"tf":1.0},"139":{"tf":1.0},"14":{"tf":1.0},"141":{"tf":1.7320508075688772},"142":{"tf":2.0},"143":{"tf":1.7320508075688772},"144":{"tf":2.23606797749979},"145":{"tf":2.0},"146":{"tf":3.3166247903554},"147":{"tf":1.0},"149":{"tf":2.0},"16":{"tf":1.0},"20":{"tf":1.4142135623730951},"23":{"tf":1.0},"34":{"tf":1.0},"35":{"tf":1.0},"48":{"tf":1.4142135623730951},"49":{"tf":1.7320508075688772},"50":{"tf":1.7320508075688772},"51":{"tf":1.4142135623730951},"52":{"tf":3.1622776601683795},"53":{"tf":1.0},"54":{"tf":1.4142135623730951},"58":{"tf":1.7320508075688772},"59":{"tf":1.4142135623730951},"60":{"tf":2.0},"62":{"tf":1.4142135623730951},"63":{"tf":1.4142135623730951},"65":{"tf":2.23606797749979},"66":{"tf":1.7320508075688772},"68":{"tf":1.4142135623730951},"69":{"tf":1.7320508075688772},"70":{"tf":2.8284271247461903},"8":{"tf":1.0},"83":{"tf":2.6457513110645907},"84":{"tf":2.23606797749979},"9":{"tf":2.23606797749979},"91":{"tf":2.23606797749979},"94":{"tf":2.0},"95":{"tf":1.0}},"t":{"df":1,"docs":{"108":{"tf":2.449489742783178}}}}},"d":{"df":0,"docs":{},"i":{"df":0,"docs":{},"t":{"df":1,"docs":{"149":{"tf":1.0}}}},"u":{"c":{"df":1,"docs":{"14":{"tf":1.0}}},"df":0,"docs":{}}},"df":0,"docs":{},"i":{"df":0,"docs":{},"t":{"df":1,"docs":{"105":{"tf":1.0}}}},"n":{"df":0,"docs":{},"s":{"c":{"df":0,"docs":{},"r":{"df":0,"docs":{},"i":{"df":0,"docs":{},"p":{"df":0,"docs":{},"t":{"df":16,"docs":{"113":{"tf":1.0},"118":{"tf":2.0},"147":{"tf":1.0},"24":{"tf":1.4142135623730951},"25":{"tf":1.4142135623730951},"26":{"tf":1.4142135623730951},"27":{"tf":1.4142135623730951},"28":{"tf":1.0},"31":{"tf":1.7320508075688772},"32":{"tf":2.0},"40":{"tf":2.449489742783178},"42":{"tf":1.0},"88":{"tf":1.0},"89":{"tf":1.7320508075688772},"94":{"tf":4.123105625617661},"95":{"tf":3.872983346207417}}}}}}},"df":0,"docs":{},"f":{"df":0,"docs":{},"o":{"df":0,"docs":{},"r":{"df":0,"docs":{},"m":{"df":7,"docs":{"123":{"tf":1.0},"3":{"tf":1.0},"4":{"tf":1.0},"69":{"tf":1.0},"74":{"tf":1.4142135623730951},"79":{"tf":1.4142135623730951},"99":{"tf":1.0}}}}}},"i":{"df":0,"docs":{},"t":{"df":20,"docs":{"105":{"tf":1.4142135623730951},"107":{"tf":2.23606797749979},"109":{"tf":2.23606797749979},"112":{"tf":1.0},"113":{"tf":1.0},"114":{"tf":2.0},"125":{"tf":1.0},"146":{"tf":1.0},"149":{"tf":1.0},"49":{"tf":1.0},"50":{"tf":1.0},"53":{"tf":1.0},"55":{"tf":2.23606797749979},"56":{"tf":1.0},"58":{"tf":1.0},"62":{"tf":1.0},"66":{"tf":2.23606797749979},"70":{"tf":2.0},"84":{"tf":1.7320508075688772},"91":{"tf":1.4142135623730951}},"i":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"_":{"d":{"df":0,"docs":{},"e":{"df":0,"docs":{},"g":{"df":0,"docs":{},"r":{"df":0,"docs":{},"e":{"df":1,"docs":{"109":{"tf":1.4142135623730951}}}}}},"i":{"df":0,"docs":{},"v":{"df":0,"docs":{},"i":{"df":0,"docs":{},"s":{"df":0,"docs":{},"o":{"df":0,"docs":{},"r":{"df":1,"docs":{"114":{"tf":1.0}}}}}}}}},"df":0,"docs":{},"e":{"df":0,"docs":{},"x":{"df":0,"docs":{},"e":{"df":0,"docs":{},"m":{"df":0,"docs":{},"p":{"df":0,"docs":{},"t":{"df":2,"docs":{"109":{"tf":1.4142135623730951},"114":{"tf":1.0}}}}}}}},"o":{"df":0,"docs":{},"f":{"df":0,"docs":{},"f":{"df":0,"docs":{},"s":{"df":0,"docs":{},"e":{"df":0,"docs":{},"t":{"df":1,"docs":{"109":{"tf":1.4142135623730951}}}}}}}}},"df":0,"docs":{}}}}}},"l":{"a":{"df":0,"docs":{},"t":{"df":1,"docs":{"52":{"tf":1.0}}}},"df":0,"docs":{}},"p":{"a":{"df":0,"docs":{},"r":{"df":1,"docs":{"81":{"tf":1.0}}}},"df":0,"docs":{}}}},"v":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":0,"docs":{},"s":{"df":1,"docs":{"80":{"tf":1.0}}}}}}},"df":0,"docs":{},"e":{"a":{"df":0,"docs":{},"t":{"df":3,"docs":{"151":{"tf":1.7320508075688772},"57":{"tf":1.0},"68":{"tf":1.0}}}},"df":0,"docs":{},"e":{"'":{"df":1,"docs":{"72":{"tf":1.0}}},"df":6,"docs":{"101":{"tf":1.7320508075688772},"72":{"tf":1.4142135623730951},"76":{"tf":1.0},"80":{"tf":2.449489742783178},"91":{"tf":1.0},"92":{"tf":2.23606797749979}}}},"i":{"c":{"df":0,"docs":{},"k":{"df":8,"docs":{"115":{"tf":1.0},"123":{"tf":1.0},"130":{"tf":1.0},"14":{"tf":1.7320508075688772},"146":{"tf":1.0},"18":{"tf":1.4142135623730951},"21":{"tf":1.7320508075688772},"98":{"tf":1.0}}}},"df":5,"docs":{"111":{"tf":1.0},"14":{"tf":1.0},"16":{"tf":1.0},"79":{"tf":1.0},"80":{"tf":1.0}},"v":{"df":0,"docs":{},"i":{"a":{"df":0,"docs":{},"l":{"df":2,"docs":{"14":{"tf":1.0},"146":{"tf":1.4142135623730951}}}},"df":0,"docs":{}}}},"u":{"df":0,"docs":{},"e":{"df":6,"docs":{"10":{"tf":1.0},"104":{"tf":1.0},"14":{"tf":1.4142135623730951},"146":{"tf":1.0},"21":{"tf":1.0},"84":{"tf":1.0}}}}},"u":{"df":0,"docs":{},"p":{"df":0,"docs":{},"l":{"df":2,"docs":{"14":{"tf":1.7320508075688772},"7":{"tf":1.4142135623730951}}}},"r":{"df":0,"docs":{},"n":{"df":4,"docs":{"14":{"tf":1.0},"65":{"tf":1.0},"70":{"tf":1.0},"79":{"tf":1.0}}}}},"w":{"df":0,"docs":{},"i":{"d":{"d":{"df":0,"docs":{},"l":{"df":1,"docs":{"3":{"tf":1.0}}}},"df":0,"docs":{}},"df":0,"docs":{}},"o":{"df":35,"docs":{"101":{"tf":1.0},"104":{"tf":1.0},"105":{"tf":1.0},"107":{"tf":1.0},"108":{"tf":1.0},"109":{"tf":1.4142135623730951},"116":{"tf":1.0},"121":{"tf":1.0},"123":{"tf":1.0},"128":{"tf":1.0},"13":{"tf":2.23606797749979},"133":{"tf":1.0},"14":{"tf":3.0},"142":{"tf":1.0},"145":{"tf":1.0},"146":{"tf":2.23606797749979},"16":{"tf":1.0},"32":{"tf":1.0},"36":{"tf":1.0},"38":{"tf":1.4142135623730951},"40":{"tf":1.0},"44":{"tf":1.4142135623730951},"48":{"tf":1.0},"49":{"tf":1.7320508075688772},"52":{"tf":2.0},"59":{"tf":1.0},"68":{"tf":1.4142135623730951},"71":{"tf":1.0},"73":{"tf":1.0},"74":{"tf":1.0},"75":{"tf":1.0},"8":{"tf":1.0},"84":{"tf":1.4142135623730951},"9":{"tf":1.4142135623730951},"90":{"tf":1.0}}}},"y":{"df":0,"docs":{},"p":{"df":0,"docs":{},"e":{"df":3,"docs":{"10":{"tf":1.0},"52":{"tf":1.0},"70":{"tf":1.0}}},"i":{"c":{"df":1,"docs":{"106":{"tf":1.4142135623730951}}},"df":0,"docs":{}}}},"ˉ":{"=":{"df":0,"docs":{},"t":{"df":1,"docs":{"28":{"tf":1.0}}}},"df":0,"docs":{}},"∈":{"df":0,"docs":{},"f":{"2":{"df":0,"docs":{},"n":{"df":0,"docs":{},"×":{"3":{"3":{"df":1,"docs":{"142":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}}},"u":{"=":{"6":{"df":1,"docs":{"9":{"tf":1.0}}},"df":0,"docs":{}},"df":2,"docs":{"11":{"tf":1.0},"9":{"tf":1.4142135623730951}},"l":{"df":0,"docs":{},"t":{"df":0,"docs":{},"i":{"df":0,"docs":{},"m":{"df":1,"docs":{"50":{"tf":1.0}}}}}},"n":{"b":{"a":{"df":0,"docs":{},"l":{"df":1,"docs":{"146":{"tf":1.4142135623730951}}}},"df":0,"docs":{}},"c":{"df":0,"docs":{},"h":{"a":{"df":0,"docs":{},"n":{"df":0,"docs":{},"g":{"df":1,"docs":{"20":{"tf":1.0}}}}},"df":0,"docs":{}}},"d":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":16,"docs":{"111":{"tf":1.7320508075688772},"112":{"tf":1.0},"113":{"tf":1.0},"114":{"tf":1.4142135623730951},"115":{"tf":1.0},"116":{"tf":1.0},"117":{"tf":1.0},"118":{"tf":1.0},"119":{"tf":1.0},"120":{"tf":1.0},"121":{"tf":1.0},"122":{"tf":1.0},"123":{"tf":1.0},"124":{"tf":1.0},"125":{"tf":1.0},"64":{"tf":1.0}},"l":{"df":0,"docs":{},"i":{"df":2,"docs":{"40":{"tf":1.0},"90":{"tf":1.0}}}},"s":{"df":0,"docs":{},"t":{"a":{"df":0,"docs":{},"n":{"d":{"df":7,"docs":{"111":{"tf":1.0},"14":{"tf":1.0},"151":{"tf":1.0},"46":{"tf":1.0},"50":{"tf":1.0},"68":{"tf":1.0},"8":{"tf":1.0}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}}},"df":0,"docs":{},"i":{"df":0,"docs":{},"q":{"df":0,"docs":{},"u":{"df":4,"docs":{"14":{"tf":1.4142135623730951},"20":{"tf":1.0},"73":{"tf":1.7320508075688772},"75":{"tf":1.0}}}},"t":{"df":0,"docs":{},"i":{"df":20,"docs":{"112":{"tf":1.0},"114":{"tf":1.4142135623730951},"123":{"tf":2.449489742783178},"124":{"tf":3.4641016151377544},"125":{"tf":2.23606797749979},"14":{"tf":3.0},"16":{"tf":1.0},"23":{"tf":1.0},"34":{"tf":1.0},"35":{"tf":1.0},"36":{"tf":1.0},"38":{"tf":1.0},"5":{"tf":1.0},"52":{"tf":1.7320508075688772},"62":{"tf":1.0},"68":{"tf":3.0},"73":{"tf":1.0},"75":{"tf":1.0},"82":{"tf":1.4142135623730951},"91":{"tf":1.0}}}},"v":{"a":{"df":0,"docs":{},"r":{"df":0,"docs":{},"i":{"df":4,"docs":{"71":{"tf":1.0},"75":{"tf":1.0},"83":{"tf":1.0},"84":{"tf":1.4142135623730951}}}}},"df":0,"docs":{}}},"l":{"df":0,"docs":{},"i":{"df":0,"docs":{},"k":{"df":1,"docs":{"79":{"tf":1.0}}}}},"o":{"df":0,"docs":{},"r":{"d":{"df":1,"docs":{"146":{"tf":1.0}}},"df":0,"docs":{}}},"p":{"a":{"c":{"df":0,"docs":{},"k":{"df":1,"docs":{"142":{"tf":1.0}}}},"df":0,"docs":{}},"df":0,"docs":{},"r":{"df":0,"docs":{},"e":{"d":{"df":0,"docs":{},"i":{"c":{"df":0,"docs":{},"t":{"df":2,"docs":{"56":{"tf":1.0},"79":{"tf":1.0}}}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"t":{"df":0,"docs":{},"i":{"df":0,"docs":{},"l":{"df":7,"docs":{"121":{"tf":1.0},"130":{"tf":1.0},"134":{"tf":1.0},"135":{"tf":1.0},"142":{"tf":1.0},"146":{"tf":1.0},"94":{"tf":1.0}}}},"r":{"df":0,"docs":{},"u":{"df":0,"docs":{},"s":{"df":0,"docs":{},"t":{"df":2,"docs":{"4":{"tf":1.0},"7":{"tf":1.0}}}}}}},"u":{"df":0,"docs":{},"s":{"df":4,"docs":{"130":{"tf":2.23606797749979},"134":{"tf":2.449489742783178},"139":{"tf":1.0},"146":{"tf":1.7320508075688772}}}}},"p":{"d":{"a":{"df":0,"docs":{},"t":{"df":5,"docs":{"11":{"tf":1.0},"13":{"tf":1.4142135623730951},"136":{"tf":1.7320508075688772},"146":{"tf":1.4142135623730951},"40":{"tf":1.4142135623730951}}}},"df":0,"docs":{}},"df":8,"docs":{"10":{"tf":1.0},"12":{"tf":1.0},"121":{"tf":1.0},"13":{"tf":1.4142135623730951},"130":{"tf":1.0},"16":{"tf":1.7320508075688772},"71":{"tf":1.0},"84":{"tf":1.0}},"p":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":1,"docs":{"73":{"tf":1.0}}}}}},"s":{"a":{"df":0,"docs":{},"g":{"df":1,"docs":{"35":{"tf":1.4142135623730951}}}},"df":65,"docs":{"101":{"tf":1.4142135623730951},"102":{"tf":1.4142135623730951},"104":{"tf":1.0},"106":{"tf":1.0},"107":{"tf":1.0},"108":{"tf":1.0},"109":{"tf":2.0},"110":{"tf":1.0},"111":{"tf":1.4142135623730951},"113":{"tf":1.0},"114":{"tf":1.7320508075688772},"116":{"tf":1.0},"118":{"tf":2.0},"119":{"tf":1.0},"121":{"tf":1.0},"123":{"tf":2.6457513110645907},"124":{"tf":1.4142135623730951},"125":{"tf":2.23606797749979},"128":{"tf":1.0},"13":{"tf":1.0},"130":{"tf":1.0},"134":{"tf":1.4142135623730951},"136":{"tf":1.4142135623730951},"14":{"tf":2.8284271247461903},"142":{"tf":1.0},"144":{"tf":1.4142135623730951},"146":{"tf":4.0},"147":{"tf":1.0},"149":{"tf":1.4142135623730951},"150":{"tf":1.0},"151":{"tf":2.23606797749979},"19":{"tf":1.0},"21":{"tf":1.0},"33":{"tf":1.0},"34":{"tf":1.4142135623730951},"35":{"tf":1.0},"36":{"tf":1.0},"38":{"tf":1.0},"4":{"tf":1.0},"40":{"tf":1.7320508075688772},"41":{"tf":1.0},"44":{"tf":1.4142135623730951},"45":{"tf":2.0},"48":{"tf":1.4142135623730951},"49":{"tf":1.4142135623730951},"5":{"tf":1.4142135623730951},"50":{"tf":2.23606797749979},"57":{"tf":1.0},"59":{"tf":1.0},"60":{"tf":1.0},"63":{"tf":1.0},"68":{"tf":1.0},"7":{"tf":2.23606797749979},"71":{"tf":1.0},"74":{"tf":2.0},"75":{"tf":1.0},"8":{"tf":1.7320508075688772},"87":{"tf":1.0},"89":{"tf":1.0},"9":{"tf":1.0},"91":{"tf":1.4142135623730951},"92":{"tf":1.0},"94":{"tf":1.0},"95":{"tf":1.4142135623730951},"97":{"tf":1.0}},"e":{"df":0,"docs":{},"r":{"df":1,"docs":{"50":{"tf":1.0}}}},"i":{"df":0,"docs":{},"z":{"df":1,"docs":{"35":{"tf":1.0}}}},"u":{"a":{"df":0,"docs":{},"l":{"df":9,"docs":{"123":{"tf":1.0},"133":{"tf":1.0},"14":{"tf":1.0},"151":{"tf":1.0},"5":{"tf":1.0},"53":{"tf":1.0},"70":{"tf":1.0},"74":{"tf":1.0},"75":{"tf":1.0}}}},"df":0,"docs":{}}},"t":{"df":0,"docs":{},"i":{"df":0,"docs":{},"l":{"df":1,"docs":{"99":{"tf":1.0}}}}}},"v":{"=":{"9":{"df":1,"docs":{"9":{"tf":1.0}}},"df":0,"docs":{}},"a":{"df":0,"docs":{},"l":{"df":0,"docs":{},"i":{"d":{"df":23,"docs":{"10":{"tf":1.0},"101":{"tf":1.0},"105":{"tf":1.0},"11":{"tf":1.4142135623730951},"125":{"tf":1.0},"13":{"tf":1.0},"14":{"tf":1.0},"146":{"tf":1.4142135623730951},"16":{"tf":1.7320508075688772},"21":{"tf":1.0},"33":{"tf":1.0},"34":{"tf":1.0},"48":{"tf":1.0},"49":{"tf":1.0},"50":{"tf":1.0},"52":{"tf":1.4142135623730951},"69":{"tf":1.4142135623730951},"70":{"tf":1.0},"77":{"tf":1.0},"80":{"tf":1.0},"84":{"tf":1.7320508075688772},"86":{"tf":1.0},"9":{"tf":1.4142135623730951}}},"df":0,"docs":{}},"u":{"a":{"b":{"df":0,"docs":{},"l":{"df":1,"docs":{"103":{"tf":1.0}}}},"df":0,"docs":{}},"df":58,"docs":{"101":{"tf":2.23606797749979},"102":{"tf":2.0},"107":{"tf":1.7320508075688772},"108":{"tf":1.4142135623730951},"114":{"tf":1.4142135623730951},"118":{"tf":2.0},"119":{"tf":1.0},"128":{"tf":1.0},"13":{"tf":2.23606797749979},"130":{"tf":3.0},"131":{"tf":1.7320508075688772},"133":{"tf":3.3166247903554},"134":{"tf":1.7320508075688772},"135":{"tf":2.0},"136":{"tf":1.7320508075688772},"137":{"tf":1.0},"139":{"tf":1.0},"14":{"tf":3.3166247903554},"142":{"tf":2.449489742783178},"146":{"tf":5.196152422706632},"147":{"tf":2.23606797749979},"151":{"tf":2.449489742783178},"16":{"tf":1.4142135623730951},"19":{"tf":1.0},"20":{"tf":2.23606797749979},"21":{"tf":2.8284271247461903},"22":{"tf":1.0},"25":{"tf":1.4142135623730951},"3":{"tf":1.0},"32":{"tf":2.23606797749979},"33":{"tf":1.4142135623730951},"34":{"tf":1.7320508075688772},"35":{"tf":3.0},"36":{"tf":1.7320508075688772},"42":{"tf":1.0},"49":{"tf":1.4142135623730951},"5":{"tf":1.0},"52":{"tf":1.4142135623730951},"54":{"tf":1.0},"58":{"tf":1.0},"60":{"tf":1.0},"66":{"tf":1.0},"69":{"tf":1.0},"7":{"tf":2.23606797749979},"70":{"tf":1.4142135623730951},"72":{"tf":1.7320508075688772},"73":{"tf":1.0},"75":{"tf":2.0},"77":{"tf":1.4142135623730951},"79":{"tf":1.0},"80":{"tf":1.7320508075688772},"83":{"tf":1.0},"87":{"tf":1.0},"89":{"tf":1.0},"9":{"tf":2.23606797749979},"91":{"tf":2.23606797749979},"94":{"tf":2.0},"95":{"tf":2.8284271247461903}}}},"n":{"df":0,"docs":{},"i":{"df":0,"docs":{},"s":{"df":0,"docs":{},"h":{"df":5,"docs":{"123":{"tf":2.6457513110645907},"125":{"tf":1.7320508075688772},"20":{"tf":1.0},"55":{"tf":1.0},"84":{"tf":1.0}}}}}},"r":{"df":0,"docs":{},"i":{"a":{"b":{"df":0,"docs":{},"l":{"df":11,"docs":{"11":{"tf":1.0},"13":{"tf":1.4142135623730951},"14":{"tf":1.0},"151":{"tf":1.0},"36":{"tf":1.0},"44":{"tf":1.7320508075688772},"45":{"tf":1.7320508075688772},"5":{"tf":1.0},"70":{"tf":1.7320508075688772},"9":{"tf":1.4142135623730951},"95":{"tf":1.0}}}},"df":0,"docs":{},"n":{"df":0,"docs":{},"t":{"df":5,"docs":{"74":{"tf":2.0},"77":{"tf":1.0},"80":{"tf":1.0},"96":{"tf":1.4142135623730951},"97":{"tf":2.23606797749979}}}}},"df":0,"docs":{}}}},"df":13,"docs":{"11":{"tf":2.0},"12":{"tf":1.0},"13":{"tf":1.4142135623730951},"14":{"tf":2.23606797749979},"141":{"tf":1.0},"142":{"tf":2.0},"146":{"tf":2.449489742783178},"147":{"tf":1.7320508075688772},"16":{"tf":1.4142135623730951},"34":{"tf":1.4142135623730951},"36":{"tf":1.7320508075688772},"8":{"tf":1.0},"9":{"tf":2.0}},"e":{"c":{"!":{"[":{"0":{"df":1,"docs":{"109":{"tf":1.0}}},"1":{"df":2,"docs":{"108":{"tf":1.0},"109":{"tf":1.0}}},"2":{"df":1,"docs":{"109":{"tf":1.0}}},"df":0,"docs":{},"t":{"df":0,"docs":{},"h":{"df":0,"docs":{},"i":{"df":0,"docs":{},"r":{"d":{"_":{"df":0,"docs":{},"r":{"df":0,"docs":{},"o":{"df":0,"docs":{},"w":{"[":{"0":{"df":2,"docs":{"107":{"tf":1.0},"114":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"x":{".":{"c":{"df":0,"docs":{},"l":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"df":1,"docs":{"35":{"tf":1.4142135623730951}}}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"<":{"df":0,"docs":{},"f":{"df":0,"docs":{},"i":{"df":0,"docs":{},"e":{"df":0,"docs":{},"l":{"d":{"df":0,"docs":{},"e":{"df":0,"docs":{},"l":{"df":0,"docs":{},"e":{"df":0,"docs":{},"m":{"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"df":0,"docs":{},"t":{"<":{"df":0,"docs":{},"f":{"df":1,"docs":{"35":{"tf":2.6457513110645907}}},"s":{"df":0,"docs":{},"e":{"df":0,"docs":{},"l":{"df":0,"docs":{},"f":{":":{":":{"df":0,"docs":{},"f":{"df":0,"docs":{},"i":{"df":0,"docs":{},"e":{"df":0,"docs":{},"l":{"d":{"df":2,"docs":{"107":{"tf":1.0},"114":{"tf":1.0}}},"df":0,"docs":{}}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}}}},"df":0,"docs":{}}}}}}}}},"df":0,"docs":{}}}}}},"df":3,"docs":{"108":{"tf":1.0},"109":{"tf":1.0},"116":{"tf":1.0}},"t":{"df":0,"docs":{},"o":{"df":0,"docs":{},"r":{"'":{"df":1,"docs":{"72":{"tf":1.0}}},"df":18,"docs":{"101":{"tf":1.0},"107":{"tf":1.0},"141":{"tf":1.0},"142":{"tf":1.7320508075688772},"147":{"tf":2.449489742783178},"71":{"tf":1.0},"72":{"tf":2.0},"73":{"tf":3.1622776601683795},"74":{"tf":2.449489742783178},"75":{"tf":1.0},"76":{"tf":1.4142135623730951},"78":{"tf":1.4142135623730951},"79":{"tf":2.449489742783178},"80":{"tf":2.23606797749979},"82":{"tf":1.0},"90":{"tf":1.4142135623730951},"91":{"tf":1.0},"92":{"tf":2.449489742783178}}}}}},"df":0,"docs":{},"r":{"df":0,"docs":{},"i":{"df":10,"docs":{"118":{"tf":1.0},"123":{"tf":1.0},"14":{"tf":1.7320508075688772},"20":{"tf":1.0},"49":{"tf":1.0},"50":{"tf":1.0},"69":{"tf":1.0},"73":{"tf":1.0},"79":{"tf":1.0},"98":{"tf":1.0}},"f":{"df":3,"docs":{"30":{"tf":1.4142135623730951},"4":{"tf":1.0},"7":{"tf":1.4142135623730951}},"i":{"c":{"a":{"df":0,"docs":{},"t":{"df":0,"docs":{},"i":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"df":0,"docs":{},"k":{"df":0,"docs":{},"e":{"df":0,"docs":{},"y":{"<":{"df":0,"docs":{},"g":{"1":{"df":0,"docs":{},"p":{"df":0,"docs":{},"o":{"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"df":0,"docs":{},"t":{"df":1,"docs":{"35":{"tf":1.0}}}}}}}},"df":0,"docs":{}}},"df":1,"docs":{"35":{"tf":1.0}}}}}}}}}},"df":0,"docs":{}},"df":59,"docs":{"102":{"tf":2.0},"104":{"tf":2.0},"109":{"tf":1.0},"110":{"tf":1.4142135623730951},"111":{"tf":1.4142135623730951},"112":{"tf":1.0},"113":{"tf":2.0},"115":{"tf":1.7320508075688772},"117":{"tf":1.4142135623730951},"118":{"tf":2.23606797749979},"119":{"tf":1.4142135623730951},"127":{"tf":1.0},"13":{"tf":1.4142135623730951},"137":{"tf":1.0},"144":{"tf":1.0},"145":{"tf":1.0},"146":{"tf":2.449489742783178},"147":{"tf":1.4142135623730951},"151":{"tf":1.0},"20":{"tf":1.0},"21":{"tf":3.3166247903554},"32":{"tf":1.0},"33":{"tf":1.7320508075688772},"34":{"tf":1.0},"35":{"tf":2.23606797749979},"4":{"tf":1.0},"44":{"tf":2.0},"48":{"tf":1.4142135623730951},"50":{"tf":1.0},"53":{"tf":1.0},"55":{"tf":1.0},"56":{"tf":1.0},"57":{"tf":1.7320508075688772},"58":{"tf":1.0},"59":{"tf":1.4142135623730951},"60":{"tf":1.4142135623730951},"61":{"tf":1.0},"62":{"tf":1.7320508075688772},"63":{"tf":1.7320508075688772},"69":{"tf":1.7320508075688772},"7":{"tf":2.0},"70":{"tf":1.4142135623730951},"71":{"tf":1.0},"72":{"tf":1.7320508075688772},"73":{"tf":1.4142135623730951},"74":{"tf":1.7320508075688772},"75":{"tf":2.449489742783178},"77":{"tf":2.23606797749979},"79":{"tf":1.4142135623730951},"8":{"tf":1.0},"80":{"tf":2.6457513110645907},"82":{"tf":1.4142135623730951},"83":{"tf":1.7320508075688772},"84":{"tf":1.4142135623730951},"85":{"tf":1.7320508075688772},"86":{"tf":1.0},"91":{"tf":1.7320508075688772},"94":{"tf":1.0},"95":{"tf":3.0}},"e":{"df":0,"docs":{},"r":{"'":{"df":2,"docs":{"35":{"tf":1.0},"95":{"tf":1.0}}},":":{":":{"df":0,"docs":{},"n":{"df":0,"docs":{},"e":{"df":0,"docs":{},"w":{"(":{"df":0,"docs":{},"k":{"df":0,"docs":{},"z":{"df":0,"docs":{},"g":{"df":2,"docs":{"35":{"tf":1.4142135623730951},"44":{"tf":1.0}}}}}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"<":{"df":0,"docs":{},"f":{"df":1,"docs":{"35":{"tf":1.0}}}},"df":0,"docs":{}}}},"y":{"(":{"(":{"df":0,"docs":{},"υ":{"df":0,"docs":{},"s":{"2":{"df":0,"docs":{},"k":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"x":{")":{",":{"df":0,"docs":{},"p":{"df":0,"docs":{},"k":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"p":{"df":0,"docs":{},"k":{"df":1,"docs":{"102":{"tf":1.0}}}}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}},"π":{"df":0,"docs":{},"k":{"df":0,"docs":{},"υ":{"df":0,"docs":{},"s":{"2":{"df":0,"docs":{},"k":{"df":0,"docs":{},"​":{"df":0,"docs":{},"​":{")":{",":{"df":0,"docs":{},"p":{"df":0,"docs":{},"k":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"p":{"df":0,"docs":{},"k":{"df":1,"docs":{"102":{"tf":1.0}}}}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}}},"−":{"df":0,"docs":{},"υ":{"df":0,"docs":{},"s":{"2":{"df":0,"docs":{},"k":{"df":0,"docs":{},"​":{"df":0,"docs":{},"​":{")":{",":{"df":0,"docs":{},"p":{"df":0,"docs":{},"k":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"p":{"df":0,"docs":{},"k":{"df":1,"docs":{"95":{"tf":1.0}}}}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}}}}}}},"df":0,"docs":{}}}},"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"η":{"1":{"df":0,"docs":{},"υ":{"df":0,"docs":{},"s":{"df":0,"docs":{},"​":{"df":0,"docs":{},"​":{")":{",":{"df":0,"docs":{},"h":{"1":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"h":{"1":{"df":1,"docs":{"95":{"tf":1.0}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}}}},"2":{"df":0,"docs":{},"υ":{"df":0,"docs":{},"s":{"df":0,"docs":{},"​":{"df":0,"docs":{},"​":{")":{",":{"df":0,"docs":{},"h":{"2":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"h":{"2":{"df":1,"docs":{"95":{"tf":1.0}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}}}},"df":0,"docs":{}},"τ":{"df":0,"docs":{},"j":{"df":0,"docs":{},"υ":{"df":0,"docs":{},"s":{"df":0,"docs":{},"​":{"df":0,"docs":{},"​":{")":{",":{"df":0,"docs":{},"t":{"df":0,"docs":{},"j":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"t":{"df":0,"docs":{},"j":{"df":1,"docs":{"95":{"tf":1.0}}}}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}}}}}},"df":0,"docs":{}}}},"−":{"df":0,"docs":{},"υ":{"df":0,"docs":{},"s":{"2":{"df":0,"docs":{},"k":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"π":{"df":0,"docs":{},"k":{"df":0,"docs":{},"−":{"df":0,"docs":{},"υ":{"df":0,"docs":{},"s":{"2":{"df":0,"docs":{},"k":{"df":0,"docs":{},"​":{"df":0,"docs":{},"​":{")":{",":{"df":0,"docs":{},"p":{"df":0,"docs":{},"k":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"p":{"df":0,"docs":{},"k":{"df":1,"docs":{"95":{"tf":1.0}}}}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}}}}}}},"df":0,"docs":{}}}},"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"η":{"1":{"df":0,"docs":{},"υ":{"df":0,"docs":{},"s":{"df":0,"docs":{},"​":{"df":0,"docs":{},"​":{")":{",":{"df":0,"docs":{},"h":{"1":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"h":{"1":{"df":1,"docs":{"95":{"tf":1.0}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}}}},"2":{"df":0,"docs":{},"υ":{"df":0,"docs":{},"s":{"df":0,"docs":{},"​":{"df":0,"docs":{},"​":{")":{",":{"df":0,"docs":{},"h":{"2":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"h":{"2":{"df":1,"docs":{"95":{"tf":1.0}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}}}},"df":0,"docs":{}},"τ":{"df":0,"docs":{},"j":{"df":0,"docs":{},"υ":{"df":0,"docs":{},"s":{"df":0,"docs":{},"​":{"df":0,"docs":{},"​":{")":{",":{"df":0,"docs":{},"t":{"df":0,"docs":{},"j":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"t":{"df":0,"docs":{},"j":{"df":1,"docs":{"95":{"tf":1.0}}}}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}}}}}},"df":0,"docs":{}}}}}},"[":{"df":0,"docs":{},"f":{"]":{"1":{"df":1,"docs":{"19":{"tf":1.0}}},"df":0,"docs":{}},"b":{"a":{"df":0,"docs":{},"t":{"c":{"df":0,"docs":{},"h":{"df":0,"docs":{},"​":{"]":{"1":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"π":{"b":{"a":{"df":0,"docs":{},"t":{"c":{"df":0,"docs":{},"h":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"ζ":{",":{"df":0,"docs":{},"f":{"b":{"a":{"df":0,"docs":{},"t":{"c":{"df":0,"docs":{},"h":{"df":1,"docs":{"33":{"tf":1.0}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"z":{"]":{"1":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"π":{"df":0,"docs":{},"s":{"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"df":0,"docs":{},"g":{"df":0,"docs":{},"l":{"df":0,"docs":{},"e":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"ζ":{"df":0,"docs":{},"ω":{",":{"df":0,"docs":{},"z":{"df":1,"docs":{"33":{"tf":1.0}}}},"df":0,"docs":{}}}},"df":0,"docs":{}}}}}}}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"a":{"b":{"df":0,"docs":{},"i":{",":{"df":0,"docs":{},"y":{",":{"df":0,"docs":{},"r":{",":{"df":1,"docs":{"92":{"tf":1.4142135623730951}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{},"i":{",":{"df":0,"docs":{},"y":{",":{"df":0,"docs":{},"r":{",":{"df":1,"docs":{"92":{"tf":1.4142135623730951}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"df":0,"docs":{},"g":{"_":{"df":0,"docs":{},"k":{"df":0,"docs":{},"e":{"df":0,"docs":{},"y":{"df":1,"docs":{"35":{"tf":2.449489742783178}}}}}},"df":0,"docs":{}}}}}}},"s":{"df":0,"docs":{},"i":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"df":1,"docs":{"13":{"tf":1.0}}}}}}}},"i":{",":{"df":0,"docs":{},"j":{"df":0,"docs":{},"​":{"=":{"df":0,"docs":{},"v":{"df":0,"docs":{},"k":{",":{"df":0,"docs":{},"l":{"df":4,"docs":{"11":{"tf":1.0},"13":{"tf":1.0},"14":{"tf":1.0},"16":{"tf":1.0}}}},"df":0,"docs":{}}}},"df":0,"docs":{}}}},"a":{"df":1,"docs":{"3":{"tf":1.0}}},"df":0,"docs":{},"e":{"df":0,"docs":{},"w":{"df":1,"docs":{"95":{"tf":1.0}}}},"o":{"df":0,"docs":{},"l":{"a":{"df":0,"docs":{},"t":{"df":1,"docs":{"20":{"tf":1.0}}}},"df":0,"docs":{}}},"r":{"df":0,"docs":{},"t":{"df":0,"docs":{},"u":{"a":{"df":0,"docs":{},"l":{"df":11,"docs":{"128":{"tf":2.8284271247461903},"130":{"tf":1.0},"131":{"tf":1.4142135623730951},"133":{"tf":2.0},"134":{"tf":1.0},"136":{"tf":1.0},"139":{"tf":1.0},"148":{"tf":1.7320508075688772},"149":{"tf":2.6457513110645907},"150":{"tf":2.449489742783178},"50":{"tf":1.4142135623730951}}}},"df":0,"docs":{}}}},"s":{"df":0,"docs":{},"u":{"a":{"df":0,"docs":{},"l":{"df":3,"docs":{"128":{"tf":1.0},"133":{"tf":1.0},"137":{"tf":1.0}}}},"df":0,"docs":{}}}},"k":{"df":1,"docs":{"44":{"tf":1.7320508075688772}}},"m":{"'":{"df":2,"docs":{"144":{"tf":1.0},"146":{"tf":1.0}}},"df":7,"docs":{"127":{"tf":1.4142135623730951},"133":{"tf":1.0},"144":{"tf":1.7320508075688772},"146":{"tf":1.0},"149":{"tf":1.0},"151":{"tf":2.8284271247461903},"50":{"tf":1.7320508075688772}}},"u":{"df":0,"docs":{},"l":{"df":0,"docs":{},"n":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":1,"docs":{"42":{"tf":1.0}}}}}}}},"w":{"2":{"=":{"df":0,"docs":{},"g":{"df":1,"docs":{"125":{"tf":1.0}}}},"df":1,"docs":{"124":{"tf":1.0}},"k":{"df":1,"docs":{"124":{"tf":1.0}}}},"=":{"8":{"df":1,"docs":{"9":{"tf":1.0}}},"df":0,"docs":{}},"_":{"df":0,"docs":{},"z":{"df":0,"docs":{},"e":{"df":0,"docs":{},"t":{"a":{"_":{"1":{"df":1,"docs":{"35":{"tf":1.0}}},"df":0,"docs":{},"o":{"df":0,"docs":{},"m":{"df":0,"docs":{},"e":{"df":0,"docs":{},"g":{"a":{"_":{"1":{"df":1,"docs":{"35":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"a":{"df":0,"docs":{},"l":{"df":0,"docs":{},"k":{"df":0,"docs":{},"t":{"df":0,"docs":{},"h":{"df":0,"docs":{},"r":{"df":0,"docs":{},"o":{"df":0,"docs":{},"u":{"df":0,"docs":{},"g":{"df":0,"docs":{},"h":{"df":2,"docs":{"51":{"tf":1.4142135623730951},"64":{"tf":1.0}}}}}}}}}}},"n":{"df":0,"docs":{},"t":{"df":10,"docs":{"103":{"tf":1.0},"123":{"tf":1.4142135623730951},"125":{"tf":1.0},"14":{"tf":1.4142135623730951},"150":{"tf":1.4142135623730951},"151":{"tf":1.0},"16":{"tf":1.0},"44":{"tf":1.0},"5":{"tf":1.0},"50":{"tf":1.0}}}},"y":{"df":22,"docs":{"10":{"tf":1.0},"101":{"tf":1.4142135623730951},"107":{"tf":1.0},"12":{"tf":1.0},"130":{"tf":1.0},"131":{"tf":1.0},"14":{"tf":1.4142135623730951},"146":{"tf":1.0},"149":{"tf":1.0},"151":{"tf":1.0},"31":{"tf":1.0},"50":{"tf":1.0},"52":{"tf":1.0},"54":{"tf":1.0},"66":{"tf":1.4142135623730951},"67":{"tf":1.0},"7":{"tf":1.0},"70":{"tf":1.0},"79":{"tf":1.0},"83":{"tf":1.0},"84":{"tf":1.0},"9":{"tf":1.0}}}},"df":3,"docs":{"11":{"tf":1.0},"124":{"tf":1.4142135623730951},"9":{"tf":1.0}},"e":{"'":{"d":{"df":1,"docs":{"125":{"tf":1.0}}},"df":0,"docs":{},"l":{"df":0,"docs":{},"l":{"df":9,"docs":{"10":{"tf":1.0},"19":{"tf":1.0},"43":{"tf":1.0},"51":{"tf":1.0},"53":{"tf":1.0},"64":{"tf":1.0},"7":{"tf":1.4142135623730951},"80":{"tf":1.0},"9":{"tf":1.7320508075688772}}}},"r":{"df":1,"docs":{"50":{"tf":1.0}}},"v":{"df":1,"docs":{"105":{"tf":1.0}}}},"a":{"df":0,"docs":{},"k":{"df":1,"docs":{"42":{"tf":1.4142135623730951}}}},"df":0,"docs":{},"l":{"df":0,"docs":{},"l":{"df":6,"docs":{"113":{"tf":1.0},"146":{"tf":1.4142135623730951},"34":{"tf":1.0},"36":{"tf":1.0},"4":{"tf":1.0},"98":{"tf":1.0}}}},"r":{"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"'":{"df":0,"docs":{},"t":{"df":2,"docs":{"151":{"tf":1.0},"3":{"tf":1.0}}}},"df":0,"docs":{}}}}},"h":{"a":{"df":0,"docs":{},"t":{"'":{"df":2,"docs":{"15":{"tf":1.0},"69":{"tf":1.0}}},"df":0,"docs":{}}},"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"df":0,"docs":{},"e":{"df":0,"docs":{},"v":{"df":2,"docs":{"118":{"tf":1.0},"123":{"tf":1.0}}}}},"t":{"df":0,"docs":{},"h":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":3,"docs":{"14":{"tf":1.7320508075688772},"84":{"tf":1.0},"92":{"tf":1.4142135623730951}}}}}}},"i":{"df":0,"docs":{},"t":{"df":0,"docs":{},"e":{"df":0,"docs":{},"p":{"a":{"df":0,"docs":{},"p":{"df":5,"docs":{"130":{"tf":1.0},"131":{"tf":1.0},"138":{"tf":1.0},"139":{"tf":1.0},"146":{"tf":1.0}}}},"df":0,"docs":{}}}}},"o":{"df":0,"docs":{},"l":{"df":0,"docs":{},"e":{"df":4,"docs":{"0":{"tf":1.0},"16":{"tf":1.4142135623730951},"21":{"tf":1.0},"80":{"tf":1.0}}}},"s":{"df":0,"docs":{},"e":{"df":5,"docs":{"101":{"tf":1.0},"114":{"tf":1.0},"123":{"tf":1.0},"149":{"tf":1.0},"54":{"tf":1.0}}}}}},"i":{"df":0,"docs":{},"k":{"df":0,"docs":{},"i":{"df":0,"docs":{},"p":{"df":0,"docs":{},"e":{"d":{"df":0,"docs":{},"i":{"a":{"df":1,"docs":{"101":{"tf":1.0}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}},"n":{"df":0,"docs":{},"t":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":0,"docs":{},"f":{"df":0,"docs":{},"e":{"df":0,"docs":{},"l":{"df":1,"docs":{"103":{"tf":1.0}}}}}}}}},"r":{"df":0,"docs":{},"e":{"df":2,"docs":{"11":{"tf":1.0},"35":{"tf":1.7320508075688772}}}},"s":{"df":0,"docs":{},"h":{"df":2,"docs":{"20":{"tf":1.0},"80":{"tf":1.0}}}},"t":{"df":2,"docs":{"35":{"tf":2.0},"44":{"tf":1.4142135623730951}},"h":{"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"df":1,"docs":{"4":{"tf":1.0}}}},"o":{"df":0,"docs":{},"u":{"df":0,"docs":{},"t":{"df":5,"docs":{"103":{"tf":1.0},"125":{"tf":1.0},"146":{"tf":1.7320508075688772},"19":{"tf":1.0},"7":{"tf":1.0}}}}}},"n":{"df":0,"docs":{},"e":{"df":0,"docs":{},"s":{"df":0,"docs":{},"s":{":":{":":{"df":0,"docs":{},"n":{"df":0,"docs":{},"e":{"df":0,"docs":{},"w":{"(":{"a":{"df":0,"docs":{},"s":{"df":0,"docs":{},"s":{"df":0,"docs":{},"i":{"df":0,"docs":{},"g":{"df":0,"docs":{},"n":{"df":1,"docs":{"44":{"tf":1.0}}}}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"<":{"df":0,"docs":{},"f":{"df":1,"docs":{"35":{"tf":1.0}}}},"df":0,"docs":{}}}}}}},"o":{"df":0,"docs":{},"n":{"'":{"df":0,"docs":{},"t":{"df":9,"docs":{"109":{"tf":1.0},"118":{"tf":1.0},"146":{"tf":1.4142135623730951},"149":{"tf":1.0},"151":{"tf":1.0},"67":{"tf":1.0},"72":{"tf":1.0},"83":{"tf":1.0},"9":{"tf":1.0}}}},"d":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":1,"docs":{"16":{"tf":1.0}}}}},"df":0,"docs":{}},"r":{"d":{"df":2,"docs":{"133":{"tf":1.4142135623730951},"146":{"tf":1.0}}},"df":0,"docs":{},"k":{"df":21,"docs":{"0":{"tf":1.0},"111":{"tf":1.4142135623730951},"119":{"tf":1.0},"125":{"tf":1.0},"14":{"tf":2.23606797749979},"146":{"tf":1.4142135623730951},"149":{"tf":1.0},"151":{"tf":1.4142135623730951},"16":{"tf":1.0},"19":{"tf":1.0},"20":{"tf":1.0},"21":{"tf":1.0},"4":{"tf":1.0},"46":{"tf":1.0},"50":{"tf":1.0},"52":{"tf":1.0},"56":{"tf":1.0},"69":{"tf":1.0},"73":{"tf":1.0},"75":{"tf":1.4142135623730951},"88":{"tf":1.4142135623730951}}},"t":{"df":0,"docs":{},"h":{"df":2,"docs":{"3":{"tf":1.0},"37":{"tf":1.0}}}}},"u":{"df":0,"docs":{},"l":{"d":{"df":0,"docs":{},"n":{"'":{"df":0,"docs":{},"t":{"df":1,"docs":{"151":{"tf":1.0}}}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"r":{"a":{"df":0,"docs":{},"p":{"df":4,"docs":{"124":{"tf":1.0},"146":{"tf":1.0},"16":{"tf":1.7320508075688772},"45":{"tf":1.0}}}},"df":0,"docs":{},"i":{"df":0,"docs":{},"t":{"df":0,"docs":{},"e":{"df":6,"docs":{"14":{"tf":1.0},"146":{"tf":1.0},"26":{"tf":1.0},"45":{"tf":1.0},"50":{"tf":1.7320508075688772},"92":{"tf":1.4142135623730951}}},"t":{"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"df":1,"docs":{"7":{"tf":1.4142135623730951}}}}}}},"o":{"df":0,"docs":{},"n":{"df":0,"docs":{},"g":{"df":2,"docs":{"124":{"tf":1.0},"42":{"tf":1.0}}}}}}},"x":{",":{"0":{"df":1,"docs":{"7":{"tf":1.0}}},"df":0,"docs":{},"i":{"df":1,"docs":{"34":{"tf":1.0}}}},"0":{"df":1,"docs":{"114":{"tf":1.0}}},"2":{"df":0,"docs":{},"n":{"df":0,"docs":{},"−":{"1":{"df":1,"docs":{"123":{"tf":1.0}},"q":{"df":0,"docs":{},"k":{"df":0,"docs":{},"t":{"df":2,"docs":{"84":{"tf":1.4142135623730951},"85":{"tf":1.0}}}}}},"df":0,"docs":{}}}},":":{"=":{"df":0,"docs":{},"g":{"+":{"df":0,"docs":{},"ζ":{"df":0,"docs":{},"k":{"df":0,"docs":{},"​":{"df":0,"docs":{},"h":{"df":1,"docs":{"102":{"tf":1.0}}}}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"=":{"3":{"df":2,"docs":{"7":{"tf":1.4142135623730951},"9":{"tf":1.0}}},"df":0,"docs":{},"x":{"0":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"x":{"1":{"df":0,"docs":{},"​":{"df":0,"docs":{},"∗":{"2":{"1":{"6":{"+":{"df":0,"docs":{},"x":{"2":{"df":0,"docs":{},"​":{"df":0,"docs":{},"∗":{"2":{"3":{"2":{"+":{"df":0,"docs":{},"x":{"3":{"df":0,"docs":{},"​":{"df":0,"docs":{},"∗":{"2":{"4":{"8":{"+":{".":{".":{".":{"+":{"df":0,"docs":{},"x":{"7":{"df":0,"docs":{},"​":{"df":0,"docs":{},"∗":{"2":{"1":{"1":{"2":{"df":1,"docs":{"151":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"_":{"0":{"df":3,"docs":{"112":{"tf":1.0},"60":{"tf":1.4142135623730951},"62":{"tf":1.0}}},"df":0,"docs":{}},"df":18,"docs":{"102":{"tf":1.0},"106":{"tf":1.0},"11":{"tf":1.0},"124":{"tf":1.0},"14":{"tf":1.0},"142":{"tf":1.4142135623730951},"151":{"tf":1.7320508075688772},"28":{"tf":1.0},"34":{"tf":1.0},"35":{"tf":1.7320508075688772},"44":{"tf":2.23606797749979},"45":{"tf":1.4142135623730951},"5":{"tf":1.7320508075688772},"52":{"tf":1.0},"7":{"tf":2.23606797749979},"88":{"tf":1.4142135623730951},"9":{"tf":2.0},"94":{"tf":1.0}},"i":{"df":2,"docs":{"123":{"tf":1.0},"151":{"tf":1.4142135623730951}}},"n":{"=":{"1":{"df":1,"docs":{"124":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{},"−":{"1":{"df":2,"docs":{"124":{"tf":1.0},"14":{"tf":1.0}}},"df":0,"docs":{}}},"x":{"df":0,"docs":{},"x":{"df":0,"docs":{},"x":{"df":0,"docs":{},"x":{"df":0,"docs":{},"x":{"df":0,"docs":{},"x":{"df":0,"docs":{},"x":{"df":0,"docs":{},"x":{"df":0,"docs":{},"x":{"df":0,"docs":{},"x":{"df":0,"docs":{},"x":{"df":0,"docs":{},"x":{"df":0,"docs":{},"x":{"df":0,"docs":{},"x":{"df":0,"docs":{},"x":{"df":0,"docs":{},"|":{"df":0,"docs":{},"x":{"df":0,"docs":{},"|":{"df":0,"docs":{},"x":{"df":0,"docs":{},"x":{"df":0,"docs":{},"|":{"df":0,"docs":{},"x":{"df":0,"docs":{},"x":{"df":0,"docs":{},"x":{"df":0,"docs":{},"x":{"df":0,"docs":{},"|":{"df":0,"docs":{},"x":{"df":0,"docs":{},"x":{"df":0,"docs":{},"x":{"df":0,"docs":{},"x":{"df":0,"docs":{},"|":{"df":0,"docs":{},"x":{"df":0,"docs":{},"x":{"df":0,"docs":{},"x":{"df":0,"docs":{},"|":{"df":0,"docs":{},"x":{"df":0,"docs":{},"x":{"df":0,"docs":{},"x":{"df":2,"docs":{"142":{"tf":1.0},"146":{"tf":1.0}},"|":{"df":0,"docs":{},"x":{"df":0,"docs":{},"x":{"df":0,"docs":{},"x":{"df":0,"docs":{},"x":{"df":0,"docs":{},"|":{"df":0,"docs":{},"x":{"df":0,"docs":{},"x":{"df":0,"docs":{},"x":{"df":0,"docs":{},"x":{"df":0,"docs":{},"|":{"df":0,"docs":{},"x":{"df":0,"docs":{},"x":{"df":0,"docs":{},"x":{"df":0,"docs":{},"x":{"df":0,"docs":{},"|":{"df":0,"docs":{},"x":{"df":0,"docs":{},"x":{"df":0,"docs":{},"x":{"df":0,"docs":{},"|":{"df":0,"docs":{},"x":{"df":0,"docs":{},"x":{"df":0,"docs":{},"x":{"df":1,"docs":{"147":{"tf":1.0}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}},"−":{"a":{"df":1,"docs":{"100":{"tf":1.0}}},"df":0,"docs":{},"z":{"2":{"df":0,"docs":{},"h":{"1":{"df":0,"docs":{},"​":{"df":0,"docs":{},"−":{"df":0,"docs":{},"h":{"1":{"df":0,"docs":{},"​":{"(":{"df":0,"docs":{},"z":{"2":{")":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"γ":{"df":0,"docs":{},"′":{"df":0,"docs":{},"x":{"df":0,"docs":{},"−":{"df":0,"docs":{},"z":{"2":{"df":0,"docs":{},"h":{"2":{"df":0,"docs":{},"​":{"df":0,"docs":{},"−":{"df":0,"docs":{},"h":{"2":{"df":0,"docs":{},"​":{"(":{"df":0,"docs":{},"z":{"2":{")":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"j":{"df":0,"docs":{},"∑":{"df":0,"docs":{},"​":{"df":0,"docs":{},"γ":{"df":0,"docs":{},"j":{"df":0,"docs":{},"​":{"df":0,"docs":{},"x":{"df":0,"docs":{},"−":{"df":0,"docs":{},"z":{"df":0,"docs":{},"t":{"df":0,"docs":{},"j":{"df":0,"docs":{},"​":{"df":0,"docs":{},"−":{"df":0,"docs":{},"t":{"df":0,"docs":{},"j":{"df":0,"docs":{},"​":{"(":{"df":0,"docs":{},"z":{")":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"γ":{"df":0,"docs":{},"j":{"df":0,"docs":{},"′":{"df":0,"docs":{},"​":{"df":0,"docs":{},"x":{"df":0,"docs":{},"−":{"df":0,"docs":{},"g":{"df":0,"docs":{},"z":{"df":0,"docs":{},"t":{"df":0,"docs":{},"j":{"df":0,"docs":{},"​":{"df":0,"docs":{},"−":{"df":0,"docs":{},"t":{"df":0,"docs":{},"j":{"df":0,"docs":{},"​":{"(":{"df":0,"docs":{},"g":{"df":0,"docs":{},"z":{"df":1,"docs":{"94":{"tf":1.0}}}}},"df":0,"docs":{}}}}}}}}}}}}}}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}}}}}}}}}}}}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"y":{",":{"df":1,"docs":{"95":{"tf":1.0}}},"0":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"…":{",":{"df":0,"docs":{},"y":{"df":0,"docs":{},"m":{"df":1,"docs":{"73":{"tf":1.0}}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"1":{"df":0,"docs":{},"​":{"+":{"d":{"1":{"df":0,"docs":{},"−":{"1":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"…":{",":{"df":0,"docs":{},"y":{"df":0,"docs":{},"m":{"df":0,"docs":{},"​":{"+":{"d":{"df":0,"docs":{},"m":{"df":0,"docs":{},"−":{"1":{"df":1,"docs":{"74":{"tf":1.0}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"=":{"(":{"1":{",":{"1":{",":{"df":0,"docs":{},"…":{",":{"1":{"df":1,"docs":{"73":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{},"p":{"(":{"d":{"1":{"df":0,"docs":{},"​":{")":{",":{"df":0,"docs":{},"…":{",":{"df":0,"docs":{},"p":{"(":{"d":{"df":0,"docs":{},"m":{"df":1,"docs":{"73":{"tf":1.4142135623730951}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"y":{"0":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"…":{",":{"df":0,"docs":{},"y":{"df":0,"docs":{},"m":{"df":1,"docs":{"72":{"tf":1.0}}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"1":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"…":{",":{"df":0,"docs":{},"y":{"df":0,"docs":{},"m":{"df":3,"docs":{"73":{"tf":1.4142135623730951},"74":{"tf":1.0},"79":{"tf":1.0}}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{},"p":{"(":{"df":0,"docs":{},"z":{"df":1,"docs":{"75":{"tf":1.0}}}},"df":0,"docs":{}},"x":{"df":0,"docs":{},"e":{"+":{"5":{"df":1,"docs":{"34":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":15,"docs":{"19":{"tf":1.4142135623730951},"35":{"tf":2.23606797749979},"44":{"tf":2.23606797749979},"45":{"tf":1.4142135623730951},"72":{"tf":2.0},"73":{"tf":2.0},"74":{"tf":1.7320508075688772},"75":{"tf":1.4142135623730951},"77":{"tf":1.4142135623730951},"78":{"tf":1.0},"79":{"tf":1.4142135623730951},"88":{"tf":1.4142135623730951},"92":{"tf":1.7320508075688772},"94":{"tf":1.4142135623730951},"95":{"tf":1.4142135623730951}},"i":{"df":2,"docs":{"72":{"tf":1.0},"80":{"tf":1.0}},"​":{",":{"df":1,"docs":{"92":{"tf":1.0}}},"=":{"df":0,"docs":{},"p":{"df":0,"docs":{},"i":{"df":0,"docs":{},"​":{"(":{"df":0,"docs":{},"z":{"df":0,"docs":{},"i":{"df":1,"docs":{"80":{"tf":1.0}}}}},"df":0,"docs":{}}}}},"df":0,"docs":{},"−":{"df":0,"docs":{},"y":{")":{"/":{"(":{"d":{"df":0,"docs":{},"i":{"df":0,"docs":{},"​":{"df":0,"docs":{},"−":{"df":0,"docs":{},"z":{"df":1,"docs":{"79":{"tf":1.0}}}}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"j":{"(":{"1":{")":{"df":0,"docs":{},"​":{"df":0,"docs":{},"∣":{"df":0,"docs":{},"∣":{"df":0,"docs":{},"…":{"df":0,"docs":{},"∣":{"df":0,"docs":{},"∣":{"df":0,"docs":{},"y":{"df":0,"docs":{},"j":{"(":{"df":0,"docs":{},"k":{"df":1,"docs":{"92":{"tf":1.0}}}},"df":0,"docs":{}}}}}}}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"o":{"df":0,"docs":{},"u":{"'":{"df":0,"docs":{},"l":{"df":0,"docs":{},"l":{"df":2,"docs":{"114":{"tf":1.0},"16":{"tf":1.0}}}},"r":{"df":1,"docs":{"111":{"tf":1.0}}}},"df":0,"docs":{},"r":{"df":0,"docs":{},"s":{"df":0,"docs":{},"e":{"df":0,"docs":{},"l":{"df":0,"docs":{},"f":{"df":2,"docs":{"118":{"tf":1.0},"42":{"tf":1.0}}}}}}}}}},"z":{"(":{"d":{")":{"df":0,"docs":{},"f":{"(":{"d":{")":{"=":{"df":0,"docs":{},"g":{"(":{"d":{")":{"df":0,"docs":{},"z":{"(":{"df":0,"docs":{},"η":{"d":{"df":1,"docs":{"14":{"tf":1.0}}},"df":0,"docs":{}},"ω":{"d":{"df":1,"docs":{"14":{"tf":1.0}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{},"h":{")":{"df":0,"docs":{},"f":{"(":{"df":0,"docs":{},"h":{")":{"=":{"df":0,"docs":{},"g":{"(":{"df":0,"docs":{},"h":{")":{"df":0,"docs":{},"z":{"(":{"df":0,"docs":{},"ω":{"df":0,"docs":{},"h":{"df":1,"docs":{"14":{"tf":1.0}}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"x":{")":{"=":{"df":0,"docs":{},"∏":{"(":{"df":0,"docs":{},"x":{"df":0,"docs":{},"−":{"df":0,"docs":{},"e":{"df":0,"docs":{},"i":{"df":0,"docs":{},"​":{")":{"df":0,"docs":{},"x":{"2":{"df":0,"docs":{},"n":{"df":0,"docs":{},"−":{"1":{"df":1,"docs":{"123":{"tf":1.0}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"x":{"df":0,"docs":{},"i":{"df":1,"docs":{"123":{"tf":1.0}}}}}}},"df":0,"docs":{}}},"df":0,"docs":{},"f":{"(":{"df":0,"docs":{},"x":{")":{"=":{"df":0,"docs":{},"g":{"(":{"df":0,"docs":{},"x":{")":{"df":0,"docs":{},"z":{"(":{"df":0,"docs":{},"ω":{"df":0,"docs":{},"x":{"df":1,"docs":{"14":{"tf":1.7320508075688772}}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":2,"docs":{"35":{"tf":1.0},"5":{"tf":1.0}}},"η":{"0":{")":{"=":{"1":{"df":1,"docs":{"14":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"ω":{"0":{")":{"=":{"1":{"df":1,"docs":{"14":{"tf":2.0}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{},"k":{"df":0,"docs":{},"−":{"1":{")":{"=":{"df":0,"docs":{},"∏":{"df":0,"docs":{},"i":{"=":{"0":{"df":0,"docs":{},"k":{"df":0,"docs":{},"−":{"2":{"df":0,"docs":{},"​":{"(":{"a":{"df":0,"docs":{},"i":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"γ":{")":{"/":{"df":0,"docs":{},"∏":{"df":0,"docs":{},"i":{"=":{"0":{"df":0,"docs":{},"k":{"df":0,"docs":{},"−":{"2":{"df":0,"docs":{},"​":{"(":{"b":{"df":0,"docs":{},"i":{"df":1,"docs":{"14":{"tf":1.0}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{},"g":{"(":{"df":0,"docs":{},"ω":{"df":0,"docs":{},"k":{"df":0,"docs":{},"−":{"1":{")":{"df":0,"docs":{},"f":{"(":{"df":0,"docs":{},"ω":{"df":0,"docs":{},"k":{"df":0,"docs":{},"−":{"1":{")":{"df":0,"docs":{},"​":{"=":{"df":0,"docs":{},"z":{"(":{"df":0,"docs":{},"ω":{"df":0,"docs":{},"k":{")":{"=":{"df":0,"docs":{},"z":{"(":{"df":0,"docs":{},"w":{"0":{")":{"=":{"1":{"df":1,"docs":{"14":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"0":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"…":{",":{"df":0,"docs":{},"z":{"df":0,"docs":{},"n":{"df":0,"docs":{},"−":{"1":{"df":1,"docs":{"25":{"tf":1.0}}},"df":0,"docs":{}}}}},"df":0,"docs":{}}},"=":{"1":{"df":1,"docs":{"25":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}}},"1":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"…":{",":{"df":0,"docs":{},"z":{"df":0,"docs":{},"l":{"df":1,"docs":{"80":{"tf":1.0}}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"=":{"(":{"b":{"7":{"df":0,"docs":{},"​":{"df":0,"docs":{},"x":{"2":{"+":{"b":{"8":{"df":0,"docs":{},"​":{"df":0,"docs":{},"x":{"+":{"b":{"9":{"df":0,"docs":{},"​":{")":{"df":0,"docs":{},"z":{"df":0,"docs":{},"h":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"z":{"df":1,"docs":{"25":{"tf":1.0}}}},"df":0,"docs":{}}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"]":{"1":{"df":3,"docs":{"25":{"tf":1.0},"32":{"tf":1.0},"33":{"tf":1.0}}},"df":0,"docs":{}},"^":{"2":{"df":1,"docs":{"116":{"tf":1.0}}},"df":0,"docs":{}},"_":{"1":{"df":1,"docs":{"35":{"tf":1.0}}},"df":0,"docs":{},"z":{"df":0,"docs":{},"e":{"df":0,"docs":{},"t":{"a":{"_":{"df":0,"docs":{},"o":{"df":0,"docs":{},"m":{"df":0,"docs":{},"e":{"df":0,"docs":{},"g":{"a":{"df":1,"docs":{"35":{"tf":1.0}}},"df":0,"docs":{}}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"a":{"df":0,"docs":{},"v":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":0,"docs":{},"u":{"c":{"df":0,"docs":{},"h":{"a":{"df":3,"docs":{"19":{"tf":1.0},"35":{"tf":1.0},"38":{"tf":1.0}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}}},"df":22,"docs":{"112":{"tf":1.0},"14":{"tf":3.1622776601683795},"16":{"tf":1.0},"25":{"tf":1.0},"28":{"tf":1.0},"33":{"tf":1.4142135623730951},"35":{"tf":1.4142135623730951},"44":{"tf":1.4142135623730951},"45":{"tf":1.4142135623730951},"5":{"tf":1.0},"58":{"tf":1.4142135623730951},"59":{"tf":1.0},"60":{"tf":1.4142135623730951},"62":{"tf":1.0},"69":{"tf":1.4142135623730951},"75":{"tf":1.4142135623730951},"77":{"tf":1.0},"79":{"tf":1.7320508075688772},"85":{"tf":2.0},"86":{"tf":1.0},"94":{"tf":1.0},"95":{"tf":1.4142135623730951}},"e":{"df":0,"docs":{},"r":{"df":0,"docs":{},"o":{"df":14,"docs":{"10":{"tf":1.0},"107":{"tf":1.0},"121":{"tf":1.0},"125":{"tf":1.0},"13":{"tf":1.7320508075688772},"142":{"tf":1.4142135623730951},"146":{"tf":1.4142135623730951},"20":{"tf":1.0},"36":{"tf":1.0},"4":{"tf":1.0},"84":{"tf":1.0},"88":{"tf":1.0},"94":{"tf":1.0},"95":{"tf":1.0}},"f":{"df":0,"docs":{},"i":{"df":5,"docs":{"114":{"tf":1.7320508075688772},"123":{"tf":1.4142135623730951},"125":{"tf":2.0},"54":{"tf":1.0},"91":{"tf":1.4142135623730951}}}}}}},"f":{"df":0,"docs":{},"−":{"df":0,"docs":{},"g":{"df":0,"docs":{},"z":{"df":0,"docs":{},"′":{"=":{"df":0,"docs":{},"z":{"df":0,"docs":{},"h":{"df":0,"docs":{},"​":{"df":0,"docs":{},"t":{"2":{"df":1,"docs":{"16":{"tf":1.0}}},"df":0,"docs":{}}}}}},"df":0,"docs":{}}}}}},"h":{"df":2,"docs":{"14":{"tf":1.0},"20":{"tf":1.0}},"​":{"(":{"df":0,"docs":{},"ω":{"df":0,"docs":{},"i":{")":{"=":{"0":{"df":1,"docs":{"20":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}},":":{"=":{"df":0,"docs":{},"x":{"df":0,"docs":{},"n":{"df":0,"docs":{},"−":{"1":{"df":1,"docs":{"23":{"tf":1.0}}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"=":{"df":0,"docs":{},"x":{"df":0,"docs":{},"n":{"df":0,"docs":{},"−":{"1":{"df":2,"docs":{"16":{"tf":1.0},"20":{"tf":1.0}}},"df":0,"docs":{}}}}},"df":0,"docs":{},"t":{"=":{"df":0,"docs":{},"​":{"a":{"df":0,"docs":{},"q":{"df":0,"docs":{},"l":{"df":0,"docs":{},"​":{"+":{"b":{"df":0,"docs":{},"q":{"df":0,"docs":{},"r":{"df":0,"docs":{},"​":{"+":{"a":{"b":{"df":0,"docs":{},"q":{"df":0,"docs":{},"m":{"df":0,"docs":{},"​":{"+":{"c":{"df":0,"docs":{},"q":{"df":0,"docs":{},"o":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"q":{"c":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"p":{"df":0,"docs":{},"i":{"+":{"df":0,"docs":{},"α":{"(":{"df":0,"docs":{},"g":{"df":0,"docs":{},"z":{"df":0,"docs":{},"′":{"df":0,"docs":{},"−":{"df":0,"docs":{},"f":{"df":0,"docs":{},"z":{")":{"+":{"df":0,"docs":{},"α":{"2":{"(":{"df":0,"docs":{},"z":{"df":0,"docs":{},"−":{"1":{")":{"df":0,"docs":{},"l":{"1":{"df":1,"docs":{"16":{"tf":1.0}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}}}}}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"i":{"df":0,"docs":{},"p":{"df":0,"docs":{},"p":{"df":0,"docs":{},"e":{"df":0,"docs":{},"l":{"df":1,"docs":{"14":{"tf":1.4142135623730951}}}}}}},"j":{"b":{"df":1,"docs":{"91":{"tf":1.0}}},"df":0,"docs":{},"t":{"df":1,"docs":{"91":{"tf":1.0}}}},"k":{"+":{"1":{"df":0,"docs":{},"​":{"=":{"df":0,"docs":{},"z":{"df":0,"docs":{},"k":{"df":0,"docs":{},"​":{"(":{"a":{"df":0,"docs":{},"k":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"β":{"df":0,"docs":{},"s":{"df":0,"docs":{},"σ":{"1":{"df":0,"docs":{},"​":{"(":{"df":0,"docs":{},"ω":{"df":0,"docs":{},"k":{")":{"+":{"df":0,"docs":{},"γ":{")":{"(":{"b":{"df":0,"docs":{},"k":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"β":{"df":0,"docs":{},"s":{"df":0,"docs":{},"σ":{"2":{"df":0,"docs":{},"​":{"(":{"df":0,"docs":{},"ω":{"df":0,"docs":{},"k":{")":{"+":{"df":0,"docs":{},"γ":{")":{"(":{"c":{"df":0,"docs":{},"k":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"β":{"df":0,"docs":{},"s":{"df":0,"docs":{},"σ":{"3":{"df":0,"docs":{},"​":{"(":{"df":0,"docs":{},"ω":{"df":0,"docs":{},"k":{")":{"+":{"df":0,"docs":{},"γ":{")":{"(":{"a":{"df":0,"docs":{},"k":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"β":{"df":0,"docs":{},"ω":{"df":0,"docs":{},"k":{"+":{"df":0,"docs":{},"γ":{")":{"(":{"b":{"df":0,"docs":{},"k":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"β":{"df":0,"docs":{},"ω":{"df":0,"docs":{},"k":{"df":0,"docs":{},"k":{"1":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"γ":{")":{"(":{"c":{"df":0,"docs":{},"k":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"β":{"df":0,"docs":{},"ω":{"df":0,"docs":{},"k":{"df":0,"docs":{},"k":{"2":{"df":1,"docs":{"25":{"tf":1.0}}},"df":0,"docs":{}}}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"d":{"df":0,"docs":{},"o":{"c":{"df":1,"docs":{"42":{"tf":1.0}}},"df":0,"docs":{}}},"df":1,"docs":{"4":{"tf":1.0}}},"′":{"(":{"df":0,"docs":{},"x":{")":{"=":{"df":0,"docs":{},"z":{"(":{"df":0,"docs":{},"x":{"df":1,"docs":{"16":{"tf":1.0}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},":":{"=":{"df":0,"docs":{},"z":{"(":{"df":0,"docs":{},"ω":{"df":0,"docs":{},"x":{"df":1,"docs":{"5":{"tf":1.0}}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{},"∈":{"df":0,"docs":{},"f":{"df":1,"docs":{"147":{"tf":1.0}}}}},"∈":{"df":0,"docs":{},"f":{"df":2,"docs":{"147":{"tf":1.0},"85":{"tf":1.0}},"∖":{"d":{"df":0,"docs":{},"l":{"d":{"df":0,"docs":{},"e":{"df":1,"docs":{"94":{"tf":1.0}}}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"−":{"1":{")":{"df":0,"docs":{},"l":{"1":{"df":0,"docs":{},"​":{"=":{"df":0,"docs":{},"z":{"df":0,"docs":{},"h":{"df":0,"docs":{},"​":{"df":0,"docs":{},"t":{"3":{"df":1,"docs":{"16":{"tf":1.0}}},"df":0,"docs":{}}}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"title":{"root":{"0":{"df":1,"docs":{"130":{"tf":1.0}}},"1":{"df":3,"docs":{"131":{"tf":1.0},"24":{"tf":1.0},"83":{"tf":1.0}}},"2":{"df":3,"docs":{"132":{"tf":1.0},"25":{"tf":1.0},"84":{"tf":1.0}}},"3":{"df":3,"docs":{"133":{"tf":1.0},"26":{"tf":1.0},"85":{"tf":1.0}}},"4":{"df":3,"docs":{"135":{"tf":1.0},"27":{"tf":1.0},"86":{"tf":1.0}}},"5":{"df":2,"docs":{"136":{"tf":1.0},"28":{"tf":1.0}}},"6":{"df":1,"docs":{"138":{"tf":1.0}}},"7":{"df":1,"docs":{"139":{"tf":1.0}}},"a":{"df":0,"docs":{},"i":{"df":0,"docs":{},"r":{"df":2,"docs":{"105":{"tf":1.0},"109":{"tf":1.0}}}},"l":{"df":0,"docs":{},"g":{"df":0,"docs":{},"o":{"df":0,"docs":{},"r":{"df":0,"docs":{},"i":{"df":0,"docs":{},"t":{"df":0,"docs":{},"h":{"df":0,"docs":{},"m":{"df":2,"docs":{"23":{"tf":1.0},"30":{"tf":1.0}}}}}}}}}},"p":{"df":0,"docs":{},"i":{"df":2,"docs":{"104":{"tf":1.0},"43":{"tf":1.0}}}},"r":{"df":0,"docs":{},"i":{"df":0,"docs":{},"t":{"df":0,"docs":{},"h":{"df":0,"docs":{},"m":{"df":0,"docs":{},"e":{"df":0,"docs":{},"t":{"df":3,"docs":{"70":{"tf":1.0},"8":{"tf":1.0},"83":{"tf":1.0}}}}}}}}}},"b":{"a":{"df":0,"docs":{},"t":{"c":{"df":0,"docs":{},"h":{"df":3,"docs":{"80":{"tf":1.0},"86":{"tf":1.0},"98":{"tf":1.0}}}},"df":0,"docs":{}}},"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"c":{"df":0,"docs":{},"h":{"df":0,"docs":{},"m":{"a":{"df":0,"docs":{},"r":{"df":0,"docs":{},"k":{"df":1,"docs":{"2":{"tf":1.0}}}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"i":{"df":0,"docs":{},"t":{"df":1,"docs":{"101":{"tf":1.0}}}},"l":{"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"d":{"df":1,"docs":{"20":{"tf":1.0}}},"df":0,"docs":{}}}},"o":{"df":0,"docs":{},"u":{"df":0,"docs":{},"n":{"d":{"a":{"df":0,"docs":{},"r":{"df":0,"docs":{},"i":{"df":2,"docs":{"106":{"tf":1.0},"54":{"tf":1.0}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"u":{"df":0,"docs":{},"i":{"df":0,"docs":{},"l":{"d":{"df":1,"docs":{"45":{"tf":1.0}}},"df":0,"docs":{},"t":{"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"df":1,"docs":{"151":{"tf":1.0}}}}}}}}},"c":{"a":{"df":0,"docs":{},"i":{"df":0,"docs":{},"r":{"df":0,"docs":{},"o":{"df":3,"docs":{"140":{"tf":1.0},"143":{"tf":1.0},"50":{"tf":1.0}}}}}},"df":0,"docs":{},"e":{"df":0,"docs":{},"l":{"df":0,"docs":{},"l":{"df":1,"docs":{"134":{"tf":1.0}}}}},"h":{"a":{"df":0,"docs":{},"l":{"df":0,"docs":{},"l":{"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"df":0,"docs":{},"g":{"df":1,"docs":{"97":{"tf":1.0}}}}}}}},"df":0,"docs":{},"e":{"c":{"df":0,"docs":{},"k":{"df":4,"docs":{"138":{"tf":1.0},"33":{"tf":1.0},"58":{"tf":1.0},"60":{"tf":1.0}}}},"df":0,"docs":{}}},"i":{"df":0,"docs":{},"r":{"c":{"df":0,"docs":{},"u":{"df":0,"docs":{},"i":{"df":0,"docs":{},"t":{"df":2,"docs":{"43":{"tf":1.0},"9":{"tf":1.0}}}}}},"df":0,"docs":{}}},"l":{"df":0,"docs":{},"i":{"df":1,"docs":{"152":{"tf":1.0}}}},"o":{"df":0,"docs":{},"l":{"df":0,"docs":{},"u":{"df":0,"docs":{},"m":{"df":0,"docs":{},"n":{"df":15,"docs":{"128":{"tf":1.4142135623730951},"129":{"tf":1.0},"130":{"tf":1.0},"131":{"tf":1.0},"132":{"tf":1.0},"133":{"tf":1.0},"135":{"tf":1.0},"136":{"tf":1.0},"137":{"tf":1.0},"138":{"tf":1.0},"139":{"tf":1.4142135623730951},"147":{"tf":1.0},"148":{"tf":1.0},"149":{"tf":1.0},"65":{"tf":1.0}}}}}},"m":{"df":0,"docs":{},"m":{"df":0,"docs":{},"i":{"df":0,"docs":{},"t":{"df":9,"docs":{"19":{"tf":1.0},"32":{"tf":1.0},"38":{"tf":1.0},"57":{"tf":1.0},"71":{"tf":1.0},"72":{"tf":1.0},"75":{"tf":1.0},"76":{"tf":1.0},"83":{"tf":1.0}}}},"o":{"df":0,"docs":{},"n":{"df":1,"docs":{"15":{"tf":1.0}}}}},"p":{"a":{"df":0,"docs":{},"r":{"df":0,"docs":{},"i":{"df":0,"docs":{},"s":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"df":1,"docs":{"3":{"tf":1.0}}}}}}}},"df":0,"docs":{},"l":{"df":0,"docs":{},"e":{"df":0,"docs":{},"t":{"df":1,"docs":{"78":{"tf":1.0}}},"x":{"df":1,"docs":{"45":{"tf":1.0}}}}},"o":{"df":0,"docs":{},"n":{"df":1,"docs":{"6":{"tf":1.0}}},"s":{"df":0,"docs":{},"i":{"df":0,"docs":{},"t":{"df":4,"docs":{"53":{"tf":1.0},"59":{"tf":1.0},"67":{"tf":1.0},"84":{"tf":1.0}}}}}},"u":{"df":0,"docs":{},"t":{"df":1,"docs":{"48":{"tf":1.0}}}}}},"n":{"df":0,"docs":{},"s":{"df":0,"docs":{},"i":{"d":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":1,"docs":{"120":{"tf":1.0}}}}},"df":0,"docs":{},"s":{"df":0,"docs":{},"t":{"df":2,"docs":{"58":{"tf":1.0},"60":{"tf":1.0}}}}},"t":{"df":0,"docs":{},"r":{"a":{"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"df":0,"docs":{},"t":{"df":5,"docs":{"106":{"tf":1.0},"107":{"tf":1.0},"114":{"tf":1.0},"55":{"tf":1.0},"66":{"tf":1.0}}}}}},"df":0,"docs":{},"u":{"c":{"df":0,"docs":{},"t":{"df":5,"docs":{"142":{"tf":1.0},"145":{"tf":1.0},"146":{"tf":1.0},"56":{"tf":1.0},"84":{"tf":1.0}}}},"df":0,"docs":{}}}}},"t":{"df":0,"docs":{},"e":{"df":0,"docs":{},"x":{"df":0,"docs":{},"t":{"df":1,"docs":{"109":{"tf":1.0}}}}}}},"s":{"df":0,"docs":{},"e":{"df":0,"docs":{},"t":{"df":1,"docs":{"125":{"tf":1.0}}}}}},"r":{"a":{"df":0,"docs":{},"t":{"df":0,"docs":{},"e":{"df":1,"docs":{"1":{"tf":1.0}}}}},"df":0,"docs":{}},"u":{"df":0,"docs":{},"m":{"df":1,"docs":{"139":{"tf":1.0}},"u":{"df":0,"docs":{},"l":{"df":1,"docs":{"138":{"tf":1.0}}}}},"s":{"df":0,"docs":{},"t":{"df":0,"docs":{},"o":{"df":0,"docs":{},"m":{"df":1,"docs":{"12":{"tf":1.0}}}}}}}},"d":{"df":0,"docs":{},"e":{"c":{"df":0,"docs":{},"o":{"d":{"df":0,"docs":{},"e":{"/":{"df":0,"docs":{},"o":{"df":0,"docs":{},"p":{"c":{"df":0,"docs":{},"o":{"d":{"df":1,"docs":{"131":{"tf":1.0}}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{},"m":{"df":0,"docs":{},"p":{"df":0,"docs":{},"o":{"df":0,"docs":{},"s":{"df":0,"docs":{},"i":{"df":0,"docs":{},"t":{"df":2,"docs":{"116":{"tf":1.0},"67":{"tf":1.0}}}}}}}}}},"df":0,"docs":{},"e":{"df":0,"docs":{},"p":{"df":1,"docs":{"59":{"tf":1.0}}}},"f":{"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"df":0,"docs":{},"i":{"df":0,"docs":{},"t":{"df":1,"docs":{"91":{"tf":1.0}}}}}}},"g":{"df":0,"docs":{},"r":{"df":0,"docs":{},"e":{"df":1,"docs":{"68":{"tf":1.0}}}}},"s":{"c":{"df":0,"docs":{},"r":{"df":0,"docs":{},"i":{"df":0,"docs":{},"p":{"df":0,"docs":{},"t":{"df":1,"docs":{"82":{"tf":1.0}}}}}}},"df":0,"docs":{}},"t":{"a":{"df":0,"docs":{},"i":{"df":0,"docs":{},"l":{"df":3,"docs":{"145":{"tf":1.0},"18":{"tf":1.0},"37":{"tf":1.0}}}}},"df":0,"docs":{}}},"o":{"c":{"df":0,"docs":{},"u":{"df":0,"docs":{},"m":{"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"df":0,"docs":{},"t":{"df":1,"docs":{"126":{"tf":1.0}}}}}}}},"df":0,"docs":{},"m":{"a":{"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"df":1,"docs":{"117":{"tf":1.0}}}}},"df":0,"docs":{}}}},"df":0,"docs":{},"e":{"df":0,"docs":{},"l":{"df":0,"docs":{},"e":{"df":0,"docs":{},"m":{"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"df":0,"docs":{},"t":{"df":1,"docs":{"41":{"tf":1.0}}}}}}}},"v":{"a":{"df":0,"docs":{},"l":{"df":0,"docs":{},"u":{"df":2,"docs":{"121":{"tf":1.0},"85":{"tf":1.0}}}}},"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"/":{"df":0,"docs":{},"o":{"d":{"d":{"df":1,"docs":{"116":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"x":{"a":{"df":0,"docs":{},"m":{"df":0,"docs":{},"p":{"df":0,"docs":{},"l":{"df":3,"docs":{"104":{"tf":1.0},"44":{"tf":1.0},"7":{"tf":1.0}}}}}},"df":0,"docs":{},"e":{"c":{"df":0,"docs":{},"u":{"df":0,"docs":{},"t":{"df":5,"docs":{"110":{"tf":1.0},"142":{"tf":1.0},"143":{"tf":1.0},"83":{"tf":1.0},"9":{"tf":1.0}}}}},"df":0,"docs":{}},"t":{"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"d":{"df":1,"docs":{"147":{"tf":1.0}}},"df":0,"docs":{},"s":{"df":1,"docs":{"68":{"tf":1.0}}}}},"r":{"a":{"c":{"df":0,"docs":{},"t":{"df":1,"docs":{"32":{"tf":1.0}}}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"f":{"df":0,"docs":{},"f":{"df":0,"docs":{},"t":{"df":3,"docs":{"121":{"tf":1.0},"2":{"tf":1.0},"99":{"tf":1.0}}}},"i":{"a":{"df":0,"docs":{},"t":{"df":2,"docs":{"39":{"tf":1.0},"42":{"tf":1.0}}}},"b":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"a":{"c":{"c":{"df":0,"docs":{},"i":{"df":3,"docs":{"104":{"tf":1.0},"49":{"tf":1.0},"51":{"tf":1.0}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{},"e":{"df":0,"docs":{},"l":{"d":{"df":1,"docs":{"41":{"tf":1.0}}},"df":0,"docs":{}}}},"l":{"a":{"df":0,"docs":{},"g":{"df":1,"docs":{"131":{"tf":1.0}}}},"df":0,"docs":{}},"r":{"a":{"df":0,"docs":{},"m":{"df":0,"docs":{},"e":{"df":1,"docs":{"117":{"tf":1.0}}}}},"df":0,"docs":{},"i":{"df":2,"docs":{"68":{"tf":1.0},"73":{"tf":1.0}}}}},"g":{"a":{"df":0,"docs":{},"t":{"df":0,"docs":{},"e":{"df":1,"docs":{"12":{"tf":1.0}}}}},"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":1,"docs":{"90":{"tf":1.0}}}}}},"r":{"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"d":{"df":1,"docs":{"88":{"tf":1.0}}},"df":0,"docs":{}}}}},"h":{"df":4,"docs":{"116":{"tf":1.0},"56":{"tf":1.0},"57":{"tf":1.0},"84":{"tf":1.0}},"i":{"df":0,"docs":{},"g":{"df":0,"docs":{},"h":{"df":2,"docs":{"104":{"tf":1.0},"82":{"tf":1.0}}}}},"o":{"df":0,"docs":{},"o":{"d":{"df":1,"docs":{"111":{"tf":1.0}}},"df":0,"docs":{}}}},"i":{"d":{"df":0,"docs":{},"e":{"a":{"df":1,"docs":{"6":{"tf":1.0}}},"df":0,"docs":{}}},"df":0,"docs":{},"m":{"df":0,"docs":{},"p":{"df":0,"docs":{},"l":{"df":0,"docs":{},"e":{"df":0,"docs":{},"m":{"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"df":0,"docs":{},"t":{"df":3,"docs":{"103":{"tf":1.0},"34":{"tf":1.0},"37":{"tf":1.0}}}}}}}},"o":{"df":0,"docs":{},"r":{"df":0,"docs":{},"t":{"df":1,"docs":{"92":{"tf":1.0}}}}}}},"n":{"df":0,"docs":{},"i":{"df":0,"docs":{},"t":{"df":0,"docs":{},"i":{"df":1,"docs":{"31":{"tf":1.0}}}}},"p":{"df":0,"docs":{},"u":{"df":0,"docs":{},"t":{"df":2,"docs":{"13":{"tf":1.0},"15":{"tf":1.0}}}}},"t":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"a":{"c":{"df":0,"docs":{},"t":{"df":1,"docs":{"137":{"tf":1.0}}}},"df":0,"docs":{}},"df":0,"docs":{},"p":{"df":0,"docs":{},"o":{"df":0,"docs":{},"l":{"df":2,"docs":{"121":{"tf":1.0},"3":{"tf":1.0}}}}}}},"r":{"df":0,"docs":{},"o":{"d":{"df":0,"docs":{},"u":{"c":{"df":0,"docs":{},"t":{"df":1,"docs":{"0":{"tf":1.0}}}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"v":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":0,"docs":{},"s":{"df":1,"docs":{"98":{"tf":1.0}}}}}}}},"l":{"a":{"df":0,"docs":{},"m":{"b":{"d":{"a":{"df":0,"docs":{},"w":{"df":0,"docs":{},"o":{"df":0,"docs":{},"r":{"df":0,"docs":{},"k":{"df":1,"docs":{"103":{"tf":1.0}}}}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"y":{"df":0,"docs":{},"o":{"df":0,"docs":{},"u":{"df":0,"docs":{},"t":{"df":1,"docs":{"127":{"tf":1.0}}}}}}},"df":0,"docs":{},"e":{"a":{"df":0,"docs":{},"v":{"df":1,"docs":{"101":{"tf":1.0}}}},"df":0,"docs":{},"v":{"df":0,"docs":{},"e":{"df":0,"docs":{},"l":{"df":2,"docs":{"104":{"tf":1.0},"82":{"tf":1.0}}}}}},"i":{"df":0,"docs":{},"n":{"df":0,"docs":{},"e":{"a":{"df":0,"docs":{},"r":{"df":1,"docs":{"21":{"tf":1.0}}}},"df":0,"docs":{}}}},"o":{"df":0,"docs":{},"w":{"df":1,"docs":{"68":{"tf":1.0}}}}},"m":{"a":{"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"df":2,"docs":{"129":{"tf":1.0},"146":{"tf":1.0}}}},"t":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":0,"docs":{},"i":{"df":1,"docs":{"144":{"tf":1.0}}}}},"r":{"df":0,"docs":{},"i":{"c":{"df":1,"docs":{"14":{"tf":1.0}}},"df":0,"docs":{},"x":{"df":2,"docs":{"10":{"tf":1.0},"11":{"tf":1.0}}}}}}},"df":0,"docs":{},"e":{"df":0,"docs":{},"m":{"df":3,"docs":{"133":{"tf":1.0},"134":{"tf":1.0},"135":{"tf":1.0}}},"r":{"df":0,"docs":{},"k":{"df":0,"docs":{},"l":{"df":1,"docs":{"101":{"tf":1.0}}}}},"t":{"df":0,"docs":{},"h":{"df":0,"docs":{},"o":{"d":{"df":1,"docs":{"3":{"tf":1.0}}},"df":0,"docs":{}}}}},"u":{"df":0,"docs":{},"l":{"df":0,"docs":{},"t":{"df":0,"docs":{},"i":{"df":1,"docs":{"139":{"tf":1.0}},"p":{"df":0,"docs":{},"l":{"df":2,"docs":{"65":{"tf":1.0},"66":{"tf":1.0}}}}}}}}},"n":{"df":0,"docs":{},"o":{"df":0,"docs":{},"t":{"a":{"df":0,"docs":{},"t":{"df":3,"docs":{"5":{"tf":1.0},"90":{"tf":1.0},"92":{"tf":1.0}}}},"df":0,"docs":{},"e":{"df":1,"docs":{"96":{"tf":1.0}}}}},"u":{"df":0,"docs":{},"m":{"b":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":1,"docs":{"49":{"tf":1.0}}}}},"df":0,"docs":{}}}},"o":{"df":0,"docs":{},"m":{"df":0,"docs":{},"i":{"df":0,"docs":{},"s":{"df":0,"docs":{},"s":{"df":1,"docs":{"64":{"tf":1.0}}}}}},"p":{"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"df":2,"docs":{"77":{"tf":1.0},"86":{"tf":1.0}}},"r":{"df":1,"docs":{"92":{"tf":1.0}}}},"t":{"df":0,"docs":{},"i":{"df":0,"docs":{},"m":{"df":1,"docs":{"96":{"tf":1.0}}}}}},"r":{"d":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":1,"docs":{"101":{"tf":1.0}}}}},"df":0,"docs":{}},"u":{"df":0,"docs":{},"t":{"df":1,"docs":{"117":{"tf":1.0}}}},"v":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":0,"docs":{},"v":{"df":0,"docs":{},"i":{"df":0,"docs":{},"e":{"df":0,"docs":{},"w":{"df":1,"docs":{"69":{"tf":1.0}}}}}}}}}},"p":{"a":{"d":{"df":1,"docs":{"36":{"tf":1.0}}},"df":0,"docs":{}},"c":{"df":1,"docs":{"136":{"tf":1.0}}},"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":0,"docs":{},"m":{"df":0,"docs":{},"u":{"df":0,"docs":{},"t":{"df":1,"docs":{"139":{"tf":1.0}}}}}}},"l":{"a":{"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"df":1,"docs":{"127":{"tf":1.0}}}}},"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"df":0,"docs":{},"k":{"df":2,"docs":{"4":{"tf":1.0},"8":{"tf":1.0}}}}}},"o":{"df":0,"docs":{},"l":{"df":0,"docs":{},"y":{"df":0,"docs":{},"n":{"df":0,"docs":{},"o":{"df":0,"docs":{},"m":{"df":0,"docs":{},"i":{"df":15,"docs":{"114":{"tf":1.0},"14":{"tf":1.0},"19":{"tf":1.0},"3":{"tf":1.0},"48":{"tf":1.0},"52":{"tf":1.0},"53":{"tf":1.0},"54":{"tf":1.0},"55":{"tf":1.0},"59":{"tf":1.0},"67":{"tf":1.0},"71":{"tf":1.0},"75":{"tf":1.0},"84":{"tf":1.0},"85":{"tf":1.0}}}}}}}},"o":{"df":0,"docs":{},"l":{"df":4,"docs":{"130":{"tf":1.0},"133":{"tf":1.0},"134":{"tf":1.0},"135":{"tf":1.0}}}}},"r":{"df":0,"docs":{},"e":{"df":0,"docs":{},"p":{"df":0,"docs":{},"r":{"df":0,"docs":{},"o":{"c":{"df":0,"docs":{},"e":{"df":0,"docs":{},"s":{"df":0,"docs":{},"s":{"df":1,"docs":{"15":{"tf":1.0}}}}}},"df":0,"docs":{}}}}},"i":{"df":0,"docs":{},"m":{"df":0,"docs":{},"i":{"df":0,"docs":{},"t":{"df":1,"docs":{"124":{"tf":1.0}}}}}},"o":{"d":{"df":0,"docs":{},"u":{"c":{"df":0,"docs":{},"t":{"df":2,"docs":{"138":{"tf":1.0},"139":{"tf":1.0}}}},"df":0,"docs":{}}},"df":0,"docs":{},"g":{"df":0,"docs":{},"r":{"a":{"df":0,"docs":{},"m":{"df":1,"docs":{"7":{"tf":1.0}}}},"df":0,"docs":{}}},"o":{"df":0,"docs":{},"f":{"df":4,"docs":{"102":{"tf":1.0},"119":{"tf":1.0},"29":{"tf":1.0},"33":{"tf":1.0}}}},"t":{"df":0,"docs":{},"o":{"c":{"df":0,"docs":{},"o":{"df":0,"docs":{},"l":{"df":6,"docs":{"17":{"tf":1.0},"69":{"tf":1.0},"82":{"tf":1.0},"86":{"tf":1.0},"87":{"tf":1.0},"93":{"tf":1.0}}}}},"df":0,"docs":{}}},"v":{"df":0,"docs":{},"e":{"df":2,"docs":{"110":{"tf":1.0},"23":{"tf":1.0}},"r":{"df":7,"docs":{"103":{"tf":1.0},"112":{"tf":1.0},"126":{"tf":1.0},"127":{"tf":1.0},"46":{"tf":1.0},"62":{"tf":1.0},"94":{"tf":1.0}}}}}}},"u":{"b":{"df":0,"docs":{},"l":{"df":0,"docs":{},"i":{"c":{"df":1,"docs":{"13":{"tf":1.0}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"q":{"df":1,"docs":{"10":{"tf":1.0}}},"r":{"a":{"df":0,"docs":{},"n":{"df":0,"docs":{},"g":{"df":1,"docs":{"138":{"tf":1.0}}}},"w":{"df":1,"docs":{"144":{"tf":1.0}}}},"c":{"1":{"6":{"/":{"df":0,"docs":{},"s":{"df":0,"docs":{},"o":{"df":0,"docs":{},"r":{"df":0,"docs":{},"t":{"df":1,"docs":{"132":{"tf":1.0}}}}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":3,"docs":{"130":{"tf":1.0},"131":{"tf":1.0},"132":{"tf":1.0}}},"df":0,"docs":{},"e":{"c":{"a":{"df":0,"docs":{},"p":{"df":1,"docs":{"47":{"tf":1.0}}}},"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"df":0,"docs":{},"s":{"df":0,"docs":{},"t":{"df":0,"docs":{},"r":{"df":0,"docs":{},"u":{"c":{"df":0,"docs":{},"t":{"df":1,"docs":{"114":{"tf":1.0}}}},"df":0,"docs":{}}}}}}}},"d":{"df":0,"docs":{},"u":{"df":0,"docs":{},"n":{"d":{"df":1,"docs":{"102":{"tf":1.0}}},"df":0,"docs":{}}}},"df":0,"docs":{},"f":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":1,"docs":{"81":{"tf":1.0}}}}},"g":{"df":0,"docs":{},"i":{"df":0,"docs":{},"s":{"df":0,"docs":{},"t":{"df":1,"docs":{"136":{"tf":1.0}}}}}},"v":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":0,"docs":{},"s":{"df":1,"docs":{"101":{"tf":1.0}}}}}}},"o":{"df":0,"docs":{},"o":{"df":0,"docs":{},"t":{"df":3,"docs":{"123":{"tf":1.0},"124":{"tf":1.0},"68":{"tf":1.0}}}},"u":{"df":0,"docs":{},"n":{"d":{"df":9,"docs":{"24":{"tf":1.0},"25":{"tf":1.0},"26":{"tf":1.0},"27":{"tf":1.0},"28":{"tf":1.0},"83":{"tf":1.0},"84":{"tf":1.0},"85":{"tf":1.0},"86":{"tf":1.0}}},"df":0,"docs":{}}}},"u":{"df":0,"docs":{},"f":{"df":0,"docs":{},"f":{"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"df":0,"docs":{},"i":{"'":{"df":1,"docs":{"100":{"tf":1.0}}},"df":0,"docs":{}}}}}},"l":{"df":0,"docs":{},"e":{"df":1,"docs":{"100":{"tf":1.0}}}},"n":{"df":1,"docs":{"86":{"tf":1.0}}}}},"s":{"a":{"df":0,"docs":{},"m":{"df":0,"docs":{},"p":{"df":0,"docs":{},"l":{"df":1,"docs":{"97":{"tf":1.0}}}}}},"c":{"df":0,"docs":{},"h":{"df":0,"docs":{},"e":{"df":0,"docs":{},"m":{"df":0,"docs":{},"e":{"df":3,"docs":{"19":{"tf":1.0},"38":{"tf":1.0},"71":{"tf":1.0}}}}}}},"df":0,"docs":{},"e":{"df":0,"docs":{},"t":{"df":0,"docs":{},"u":{"df":0,"docs":{},"p":{"df":1,"docs":{"22":{"tf":1.0}}}}}},"h":{"a":{"df":0,"docs":{},"m":{"df":0,"docs":{},"i":{"df":0,"docs":{},"r":{"df":2,"docs":{"39":{"tf":1.0},"42":{"tf":1.0}}}}}},"df":0,"docs":{}},"i":{"d":{"df":0,"docs":{},"e":{"df":4,"docs":{"112":{"tf":1.0},"113":{"tf":1.0},"62":{"tf":1.0},"63":{"tf":1.0}}}},"df":0,"docs":{},"m":{"df":0,"docs":{},"p":{"df":0,"docs":{},"l":{"df":1,"docs":{"44":{"tf":1.0}},"i":{"df":0,"docs":{},"f":{"df":1,"docs":{"64":{"tf":1.0}}}}}}}},"o":{"df":0,"docs":{},"r":{"df":0,"docs":{},"t":{"df":2,"docs":{"132":{"tf":1.0},"135":{"tf":1.0}}}},"u":{"df":0,"docs":{},"n":{"d":{"df":1,"docs":{"79":{"tf":1.0}}},"df":0,"docs":{}}}},"p":{"df":0,"docs":{},"e":{"c":{"df":0,"docs":{},"i":{"a":{"df":0,"docs":{},"l":{"df":1,"docs":{"120":{"tf":1.0}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"t":{"a":{"df":0,"docs":{},"r":{"df":0,"docs":{},"k":{"df":5,"docs":{"103":{"tf":1.0},"46":{"tf":1.0},"47":{"tf":1.0},"74":{"tf":1.0},"87":{"tf":1.0}}}}},"df":0,"docs":{},"e":{"df":0,"docs":{},"p":{"df":1,"docs":{"51":{"tf":1.4142135623730951}}}},"o":{"df":0,"docs":{},"n":{"df":0,"docs":{},"e":{"df":2,"docs":{"126":{"tf":1.0},"127":{"tf":1.0}}}}},"r":{"a":{"df":0,"docs":{},"t":{"df":0,"docs":{},"e":{"df":0,"docs":{},"g":{"df":0,"docs":{},"i":{"df":1,"docs":{"40":{"tf":1.0}}}}}}},"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"df":0,"docs":{},"g":{"df":1,"docs":{"42":{"tf":1.0}}}}}}},"u":{"b":{"c":{"df":0,"docs":{},"o":{"df":0,"docs":{},"l":{"df":0,"docs":{},"u":{"df":0,"docs":{},"m":{"df":0,"docs":{},"n":{"df":2,"docs":{"148":{"tf":1.0},"150":{"tf":1.0}}}}}}}},"df":0,"docs":{}},"df":0,"docs":{},"m":{"df":0,"docs":{},"m":{"a":{"df":0,"docs":{},"r":{"df":0,"docs":{},"i":{"df":1,"docs":{"61":{"tf":1.0}}}}},"df":0,"docs":{}}}},"y":{"df":0,"docs":{},"s":{"df":0,"docs":{},"t":{"df":0,"docs":{},"e":{"df":0,"docs":{},"m":{"df":1,"docs":{"45":{"tf":1.0}}}}}}}},"t":{"df":1,"docs":{"142":{"tf":1.0}},"h":{"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"df":0,"docs":{},"g":{"df":1,"docs":{"16":{"tf":1.0}}}}},"r":{"df":0,"docs":{},"o":{"df":0,"docs":{},"u":{"df":0,"docs":{},"g":{"df":0,"docs":{},"h":{"df":1,"docs":{"48":{"tf":1.0}}}}}}}},"o":{"df":0,"docs":{},"y":{"df":1,"docs":{"7":{"tf":1.0}}}},"r":{"a":{"c":{"df":0,"docs":{},"e":{"df":11,"docs":{"127":{"tf":1.0},"129":{"tf":1.0},"137":{"tf":1.0},"141":{"tf":1.0},"142":{"tf":1.0},"143":{"tf":1.0},"146":{"tf":1.0},"52":{"tf":1.0},"65":{"tf":1.0},"83":{"tf":1.0},"9":{"tf":1.0}},"t":{"df":1,"docs":{"108":{"tf":1.0}}}}},"df":0,"docs":{},"n":{"df":0,"docs":{},"s":{"c":{"df":0,"docs":{},"r":{"df":0,"docs":{},"i":{"df":0,"docs":{},"p":{"df":0,"docs":{},"t":{"df":4,"docs":{"118":{"tf":1.0},"31":{"tf":1.0},"40":{"tf":1.0},"89":{"tf":1.0}}}}}}},"df":0,"docs":{},"i":{"df":0,"docs":{},"t":{"df":4,"docs":{"107":{"tf":1.0},"114":{"tf":1.0},"55":{"tf":1.0},"66":{"tf":1.0}}}}}}},"df":0,"docs":{},"e":{"df":0,"docs":{},"e":{"df":1,"docs":{"101":{"tf":1.0}}}},"i":{"c":{"df":0,"docs":{},"k":{"df":2,"docs":{"18":{"tf":1.0},"21":{"tf":1.0}}}},"df":0,"docs":{}}}},"u":{"df":0,"docs":{},"n":{"d":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":1,"docs":{"111":{"tf":1.0}}}}},"df":0,"docs":{},"i":{"df":0,"docs":{},"t":{"df":0,"docs":{},"i":{"df":3,"docs":{"123":{"tf":1.0},"124":{"tf":1.0},"68":{"tf":1.0}}}}},"u":{"df":0,"docs":{},"s":{"df":1,"docs":{"134":{"tf":1.0}}}}},"p":{"d":{"a":{"df":0,"docs":{},"t":{"df":1,"docs":{"136":{"tf":1.0}}}},"df":0,"docs":{}},"df":1,"docs":{"16":{"tf":1.0}}},"s":{"a":{"df":0,"docs":{},"g":{"df":1,"docs":{"35":{"tf":1.0}}}},"df":3,"docs":{"123":{"tf":1.0},"125":{"tf":1.0},"74":{"tf":1.0}}}},"v":{"a":{"df":0,"docs":{},"l":{"df":0,"docs":{},"u":{"df":2,"docs":{"102":{"tf":1.0},"32":{"tf":1.0}}}},"r":{"df":0,"docs":{},"i":{"a":{"df":0,"docs":{},"n":{"df":0,"docs":{},"t":{"df":3,"docs":{"74":{"tf":1.0},"96":{"tf":1.0},"97":{"tf":1.0}}}}},"df":0,"docs":{}}}},"df":1,"docs":{"11":{"tf":1.0}},"e":{"c":{"df":0,"docs":{},"t":{"df":0,"docs":{},"o":{"df":0,"docs":{},"r":{"df":1,"docs":{"72":{"tf":1.0}}}}}},"df":0,"docs":{},"r":{"df":0,"docs":{},"i":{"df":0,"docs":{},"f":{"df":1,"docs":{"30":{"tf":1.0}},"i":{"df":5,"docs":{"113":{"tf":1.0},"115":{"tf":1.0},"48":{"tf":1.0},"63":{"tf":1.0},"95":{"tf":1.0}}}}}}},"i":{"df":0,"docs":{},"r":{"df":0,"docs":{},"t":{"df":0,"docs":{},"u":{"a":{"df":0,"docs":{},"l":{"df":4,"docs":{"128":{"tf":1.0},"148":{"tf":1.0},"149":{"tf":1.0},"150":{"tf":1.0}}}},"df":0,"docs":{}}}}}},"w":{"a":{"df":0,"docs":{},"l":{"df":0,"docs":{},"k":{"df":0,"docs":{},"t":{"df":0,"docs":{},"h":{"df":0,"docs":{},"r":{"df":0,"docs":{},"o":{"df":0,"docs":{},"u":{"df":0,"docs":{},"g":{"df":0,"docs":{},"h":{"df":1,"docs":{"51":{"tf":1.0}}}}}}}}}}}},"df":0,"docs":{},"h":{"df":0,"docs":{},"o":{"df":0,"docs":{},"l":{"df":0,"docs":{},"e":{"df":1,"docs":{"16":{"tf":1.0}}}}}},"o":{"df":0,"docs":{},"r":{"df":0,"docs":{},"k":{"df":1,"docs":{"111":{"tf":1.0}}}}},"r":{"a":{"df":0,"docs":{},"p":{"df":1,"docs":{"16":{"tf":1.0}}}},"df":0,"docs":{}}},"z":{"df":1,"docs":{"85":{"tf":1.0}}}}}},"lang":"English","pipeline":["trimmer","stopWordFilter","stemmer"],"ref":"id","version":"0.9.5"},"results_options":{"limit_results":30,"teaser_word_count":30},"search_options":{"bool":"OR","expand":true,"fields":{"body":{"boost":1},"breadcrumbs":{"boost":1},"title":{"boost":2}}}}); \ No newline at end of file diff --git a/searchindex.json b/searchindex.json new file mode 100644 index 000000000..aab7e9dff --- /dev/null +++ b/searchindex.json @@ -0,0 +1 @@ +{"doc_urls":["introduction.html#introduction","introduction.html#crates","fft/benchmarks.html#fft-benchmarks","fft/benchmarks.html#polynomial-interpolation-methods-comparison","plonk/recap.html#plonk","plonk/recap.html#notation","plonk/recap.html#the-ideas-and-components","plonk/recap.html#programs-our-toy-example","plonk/recap.html#plonk-arithmetization","plonk/recap.html#circuits-and-execution-traces","plonk/recap.html#the-q-matrix","plonk/recap.html#the-v-matrix","plonk/recap.html#custom-gates","plonk/recap.html#public-inputs","plonk/recap.html#from-matrices-to-polynomials","plonk/recap.html#common-preprocessed-input","plonk/recap.html#wrapping-up-the-whole-thing","plonk/protocol.html#protocol","plonk/protocol.html#details-and-tricks","plonk/protocol.html#polynomial-commitment-scheme","plonk/protocol.html#blindings","plonk/protocol.html#linearization-trick","plonk/protocol.html#setup","plonk/protocol.html#proving-algorithm","plonk/protocol.html#round-1","plonk/protocol.html#round-2","plonk/protocol.html#round-3","plonk/protocol.html#round-4","plonk/protocol.html#round-5","plonk/protocol.html#proof","plonk/protocol.html#verification-algorithm","plonk/protocol.html#transcript-initialization","plonk/protocol.html#extraction-of-values-and-commitments","plonk/protocol.html#proof-check","plonk/implementation.html#implementation","plonk/implementation.html#usage","plonk/implementation.html#padding","plonk/implementation.html#implementation-details","plonk/implementation.html#commitment-scheme","plonk/implementation.html#fiat-shamir","plonk/implementation.html#transcript-strategy","plonk/implementation.html#field-elements","plonk/implementation.html#strong-fiat-shamir","plonk/constraint_system.html#circuit-api","plonk/constraint_system.html#simple-example","plonk/constraint_system.html#building-complex-systems","starks/starks.html#stark-prover","starks/recap.html#starks-recap","starks/recap.html#verifying-computation-through-polynomials","starks/recap.html#fibonacci-numbers","starks/recap.html#cairo","starks/recap.html#fibonacci-step-by-step-walkthrough","starks/recap.html#trace-polynomial","starks/recap.html#composition-polynomial","starks/recap.html#boundary-polynomial","starks/recap.html#transition-constraint-polynomial","starks/recap.html#constructing-h","starks/recap.html#commiting-to-h","starks/recap.html#consistency-check","starks/recap.html#deep-composition-polynomial","starks/recap.html#consistency-check","starks/recap.html#summary","starks/recap.html#prover-side","starks/recap.html#verifier-side","starks/recap.html#simplifications-and-omissions","starks/recap.html#multiple-trace-columns","starks/recap.html#multiple-transition-constraints","starks/recap.html#composition-polynomial-decomposition","starks/recap.html#fri-low-degree-extensions-and-roots-of-unity","starks/protocol_overview.html#protocol-overview","starks/protocol_overview.html#arithmetization","starks/protocol_overview.html#polynomial-commitment-scheme","starks/protocol_overview.html#vector-commitments","starks/protocol_overview.html#fri","starks/protocol_overview.html#variant-useful-for-starks","starks/protocol_overview.html#polynomial-commitments","starks/protocol_overview.html#commit","starks/protocol_overview.html#open","starks/protocol_overview.html#completeness","starks/protocol_overview.html#soundness","starks/protocol_overview.html#batch","starks/protocol_overview.html#references","starks/protocol_overview.html#high-level-description-of-the-protocol","starks/protocol_overview.html#round-1-arithmetization-and-commitment-of-the-execution-trace","starks/protocol_overview.html#round-2-construction-of-composition-polynomial-h","starks/protocol_overview.html#round-3-evaluation-of-polynomials-at-z","starks/protocol_overview.html#round-4-run-batch-open-protocol","starks/protocol.html#starks-protocol","starks/protocol.html#grinding","starks/protocol.html#transcript","starks/protocol.html#general-notation","starks/protocol.html#definitions","starks/protocol.html#notation-of-important-operations","starks/protocol.html#protocol","starks/protocol.html#prover","starks/protocol.html#verifier","starks/protocol.html#notes-on-optimizations-and-variants","starks/protocol.html#sampling-of-challenges-variant","starks/protocol.html#batch-inversion","starks/protocol.html#fft","starks/protocol.html#ruffinis-rule","starks/protocol.html#bit-reversal-ordering-of-merkle-tree-leaves","starks/protocol.html#redundant-values-in-the-proof","starks/implementation.html#starks-prover-lambdaworks-implementation","starks/api.html#high-level-api-fibonacci-example","starks/api.html#air","starks/api.html#boundary-constraints","starks/api.html#transition-constraints","starks/api.html#tracetable","starks/api.html#air-context","starks/api.html#proving-execution","starks/under_the_hood.html#how-this-works-under-the-hood","starks/under_the_hood.html#prover-side","starks/under_the_hood.html#verifier-side","starks/under_the_hood.html#reconstructing-the-transition-constraint-polynomials","starks/under_the_hood.html#verifier","starks/under_the_hood.html#evenodd-decomposition-for-h","starks/under_the_hood.html#out-of-domain-frame","starks/under_the_hood.html#transcript","starks/under_the_hood.html#proof","starks/under_the_hood.html#special-considerations","starks/under_the_hood.html#fft-evaluation-and-interpolation","starks/under_the_hood.html#other","starks/under_the_hood.html#why-use-roots-of-unity","starks/under_the_hood.html#what-is-a-primitive-root-of-unity","starks/under_the_hood.html#why-use-cosets","starks/stone_prover/introduction.html#stone-prover-documentation","starks/stone_prover/trace_plain_layout.html#stone-prover-trace---layout-plain","starks/stone_prover/trace_plain_layout.html#columns--virtual-columns","starks/stone_prover/trace_plain_layout.html#main-trace-columns","starks/stone_prover/trace_plain_layout.html#column-0---rc-pool","starks/stone_prover/trace_plain_layout.html#column-1---flags-decodeopcode-rc","starks/stone_prover/trace_plain_layout.html#column-2---sorted-rc-rc16sorted","starks/stone_prover/trace_plain_layout.html#column-3---mem-pool","starks/stone_prover/trace_plain_layout.html#unused-cells-in-mem-pool","starks/stone_prover/trace_plain_layout.html#column-4---sorted-mem-pool","starks/stone_prover/trace_plain_layout.html#column-5---registers--pc-update","starks/stone_prover/trace_plain_layout.html#interaction-trace-columns","starks/stone_prover/trace_plain_layout.html#column-6---range-check-cumulative-product","starks/stone_prover/trace_plain_layout.html#column-7---multi-column-permutation-cum-product","starks/cairo.html#cairo","starks/cairo_trace_succinct.html#trace","starks/cairo_trace_succinct.html#construction-of-execution-trace-t","starks/cairo_trace_descriptive.html#cairo-execution-trace","starks/cairo_trace_descriptive.html#raw-materials","starks/cairo_trace_descriptive.html#construction-details","starks/cairo_trace_descriptive.html#main-trace-construction","starks/cairo_rap.html#extended-columns","starks/virtual_cols.html#virtual-columns-and-subcolumns","starks/virtual_cols.html#virtual-columns","starks/virtual_cols.html#virtual-subcolumns","starks/builtins.html#builtins","starks/cairo_cli.html#cli"],"index":{"documentStore":{"docInfo":{"0":{"body":9,"breadcrumbs":2,"title":1},"1":{"body":6,"breadcrumbs":2,"title":1},"10":{"body":188,"breadcrumbs":4,"title":2},"100":{"body":18,"breadcrumbs":4,"title":2},"101":{"body":120,"breadcrumbs":8,"title":6},"102":{"body":60,"breadcrumbs":5,"title":3},"103":{"body":46,"breadcrumbs":6,"title":4},"104":{"body":98,"breadcrumbs":10,"title":5},"105":{"body":45,"breadcrumbs":6,"title":1},"106":{"body":74,"breadcrumbs":7,"title":2},"107":{"body":111,"breadcrumbs":7,"title":2},"108":{"body":73,"breadcrumbs":6,"title":1},"109":{"body":150,"breadcrumbs":7,"title":2},"11":{"body":106,"breadcrumbs":4,"title":2},"110":{"body":45,"breadcrumbs":7,"title":2},"111":{"body":36,"breadcrumbs":7,"title":3},"112":{"body":78,"breadcrumbs":6,"title":2},"113":{"body":91,"breadcrumbs":6,"title":2},"114":{"body":298,"breadcrumbs":8,"title":4},"115":{"body":12,"breadcrumbs":5,"title":1},"116":{"body":41,"breadcrumbs":7,"title":3},"117":{"body":52,"breadcrumbs":7,"title":3},"118":{"body":132,"breadcrumbs":5,"title":1},"119":{"body":55,"breadcrumbs":5,"title":1},"12":{"body":79,"breadcrumbs":4,"title":2},"120":{"body":0,"breadcrumbs":6,"title":2},"121":{"body":34,"breadcrumbs":7,"title":3},"122":{"body":0,"breadcrumbs":4,"title":0},"123":{"body":128,"breadcrumbs":7,"title":3},"124":{"body":129,"breadcrumbs":7,"title":3},"125":{"body":177,"breadcrumbs":6,"title":2},"126":{"body":8,"breadcrumbs":6,"title":3},"127":{"body":70,"breadcrumbs":12,"title":5},"128":{"body":65,"breadcrumbs":10,"title":3},"129":{"body":0,"breadcrumbs":10,"title":3},"13":{"body":260,"breadcrumbs":4,"title":2},"130":{"body":154,"breadcrumbs":11,"title":4},"131":{"body":79,"breadcrumbs":12,"title":5},"132":{"body":10,"breadcrumbs":12,"title":5},"133":{"body":222,"breadcrumbs":11,"title":4},"134":{"body":113,"breadcrumbs":11,"title":4},"135":{"body":70,"breadcrumbs":12,"title":5},"136":{"body":60,"breadcrumbs":12,"title":5},"137":{"body":14,"breadcrumbs":10,"title":3},"138":{"body":20,"breadcrumbs":13,"title":6},"139":{"body":33,"breadcrumbs":14,"title":7},"14":{"body":941,"breadcrumbs":4,"title":2},"140":{"body":0,"breadcrumbs":2,"title":1},"141":{"body":19,"breadcrumbs":5,"title":1},"142":{"body":226,"breadcrumbs":8,"title":4},"143":{"body":0,"breadcrumbs":7,"title":3},"144":{"body":89,"breadcrumbs":6,"title":2},"145":{"body":26,"breadcrumbs":6,"title":2},"146":{"body":820,"breadcrumbs":7,"title":3},"147":{"body":289,"breadcrumbs":4,"title":2},"148":{"body":0,"breadcrumbs":6,"title":3},"149":{"body":320,"breadcrumbs":5,"title":2},"15":{"body":11,"breadcrumbs":5,"title":3},"150":{"body":69,"breadcrumbs":5,"title":2},"151":{"body":271,"breadcrumbs":4,"title":1},"152":{"body":0,"breadcrumbs":3,"title":1},"16":{"body":207,"breadcrumbs":6,"title":4},"17":{"body":0,"breadcrumbs":3,"title":1},"18":{"body":0,"breadcrumbs":4,"title":2},"19":{"body":131,"breadcrumbs":5,"title":3},"2":{"body":0,"breadcrumbs":5,"title":2},"20":{"body":129,"breadcrumbs":3,"title":1},"21":{"body":166,"breadcrumbs":4,"title":2},"22":{"body":18,"breadcrumbs":3,"title":1},"23":{"body":42,"breadcrumbs":4,"title":2},"24":{"body":23,"breadcrumbs":4,"title":2},"25":{"body":25,"breadcrumbs":4,"title":2},"26":{"body":33,"breadcrumbs":4,"title":2},"27":{"body":6,"breadcrumbs":4,"title":2},"28":{"body":46,"breadcrumbs":4,"title":2},"29":{"body":2,"breadcrumbs":3,"title":1},"3":{"body":115,"breadcrumbs":7,"title":4},"30":{"body":0,"breadcrumbs":4,"title":2},"31":{"body":11,"breadcrumbs":4,"title":2},"32":{"body":115,"breadcrumbs":5,"title":3},"33":{"body":40,"breadcrumbs":4,"title":2},"34":{"body":100,"breadcrumbs":3,"title":1},"35":{"body":486,"breadcrumbs":3,"title":1},"36":{"body":74,"breadcrumbs":3,"title":1},"37":{"body":12,"breadcrumbs":4,"title":2},"38":{"body":40,"breadcrumbs":4,"title":2},"39":{"body":0,"breadcrumbs":4,"title":2},"4":{"body":64,"breadcrumbs":3,"title":1},"40":{"body":97,"breadcrumbs":4,"title":2},"41":{"body":37,"breadcrumbs":4,"title":2},"42":{"body":43,"breadcrumbs":5,"title":3},"43":{"body":10,"breadcrumbs":5,"title":2},"44":{"body":151,"breadcrumbs":5,"title":2},"45":{"body":120,"breadcrumbs":6,"title":3},"46":{"body":25,"breadcrumbs":3,"title":2},"47":{"body":0,"breadcrumbs":4,"title":2},"48":{"body":52,"breadcrumbs":6,"title":4},"49":{"body":113,"breadcrumbs":4,"title":2},"5":{"body":77,"breadcrumbs":3,"title":1},"50":{"body":147,"breadcrumbs":3,"title":1},"51":{"body":34,"breadcrumbs":6,"title":4},"52":{"body":135,"breadcrumbs":4,"title":2},"53":{"body":38,"breadcrumbs":4,"title":2},"54":{"body":71,"breadcrumbs":4,"title":2},"55":{"body":40,"breadcrumbs":5,"title":3},"56":{"body":52,"breadcrumbs":4,"title":2},"57":{"body":43,"breadcrumbs":4,"title":2},"58":{"body":66,"breadcrumbs":4,"title":2},"59":{"body":101,"breadcrumbs":5,"title":3},"6":{"body":0,"breadcrumbs":4,"title":2},"60":{"body":67,"breadcrumbs":4,"title":2},"61":{"body":9,"breadcrumbs":3,"title":1},"62":{"body":77,"breadcrumbs":4,"title":2},"63":{"body":52,"breadcrumbs":4,"title":2},"64":{"body":16,"breadcrumbs":4,"title":2},"65":{"body":55,"breadcrumbs":5,"title":3},"66":{"body":86,"breadcrumbs":5,"title":3},"67":{"body":30,"breadcrumbs":5,"title":3},"68":{"body":172,"breadcrumbs":8,"title":6},"69":{"body":124,"breadcrumbs":5,"title":2},"7":{"body":172,"breadcrumbs":5,"title":3},"70":{"body":192,"breadcrumbs":4,"title":1},"71":{"body":46,"breadcrumbs":6,"title":3},"72":{"body":57,"breadcrumbs":5,"title":2},"73":{"body":186,"breadcrumbs":4,"title":1},"74":{"body":90,"breadcrumbs":6,"title":3},"75":{"body":121,"breadcrumbs":5,"title":2},"76":{"body":16,"breadcrumbs":4,"title":1},"77":{"body":62,"breadcrumbs":4,"title":1},"78":{"body":30,"breadcrumbs":4,"title":1},"79":{"body":72,"breadcrumbs":4,"title":1},"8":{"body":47,"breadcrumbs":4,"title":2},"80":{"body":212,"breadcrumbs":4,"title":1},"81":{"body":21,"breadcrumbs":4,"title":1},"82":{"body":48,"breadcrumbs":7,"title":4},"83":{"body":93,"breadcrumbs":9,"title":6},"84":{"body":164,"breadcrumbs":9,"title":6},"85":{"body":47,"breadcrumbs":8,"title":5},"86":{"body":17,"breadcrumbs":9,"title":6},"87":{"body":16,"breadcrumbs":4,"title":2},"88":{"body":45,"breadcrumbs":3,"title":1},"89":{"body":14,"breadcrumbs":3,"title":1},"9":{"body":163,"breadcrumbs":5,"title":3},"90":{"body":44,"breadcrumbs":4,"title":2},"91":{"body":158,"breadcrumbs":3,"title":1},"92":{"body":107,"breadcrumbs":5,"title":3},"93":{"body":0,"breadcrumbs":3,"title":1},"94":{"body":261,"breadcrumbs":3,"title":1},"95":{"body":264,"breadcrumbs":3,"title":1},"96":{"body":0,"breadcrumbs":5,"title":3},"97":{"body":43,"breadcrumbs":5,"title":3},"98":{"body":20,"breadcrumbs":4,"title":2},"99":{"body":19,"breadcrumbs":3,"title":1}},"docs":{"0":{"body":"This site hosts the main documentation for Lambdaworks as a whole. It is still a work in progress.","breadcrumbs":"Introduction » Introduction","id":"0","title":"Introduction"},"1":{"body":"lambdaworks-math lambdaworks-crypto lambdaworks-gpu","breadcrumbs":"Introduction » Crates","id":"1","title":"Crates"},"10":{"body":"As we said, it only depends on the program itself and not on any particular evaluation of it. It has one row for each gate and its columns are called QL​,QR​,QO​,QM​,QC​. They encode the type of gate of the rows and are designed to satisfy the following. Claim: if columns L,R,O correspond to a valid evaluation of the circuit then for all i the following equality holds Ai​(QL​)i​+Bi​(QR​)i​+Ai​Bi​QM​+Ci​(QO​)i​+(QC​)i​=0 This is better seen with examples. A multiplication gate is represented by the row: QL​ QR​ QM​ QO​ QC​ 0 0 1 -1 0 And the row in the trace matrix that corresponds to the execution of that gate is A B C 2 3 6 The equation in the claim for that row is that 2×0+3×0+2×3×1+6×(−1)+0, which equals 0. The next is an addition gate. This is represented by the row QL​ QR​ QM​ QO​ QC​ 1 1 0 -1 0 The corresponding row in the trace matrix its A B C 6 3 9 And the equation of the claim is 6×1+3×1+2×3×0+9×(−1)+0, which adds up to 0. Our last row is the gate that adds a constant. Addition by constant C can be represented by the row QL​ QR​ QM​ QO​ QC​ 1 0 0 -1 C In our case C=−1. The corresponding row in the execution trace is A B C 9 - 8 And the equation of the claim is 9×1+0×0+9×0×0+8×(−1)+C. This is also zero. Putting it altogether, the full Q matrix is QL​ QR​ QM​ QO​ QC​ 0 0 1 -1 0 1 1 0 -1 0 1 0 0 -1 -1 And we saw that the claim is true for our particular execution: 2×0+3×0+2×3×1+6×(−1)+0=0 6×1+3×1+6×3×0+9×(−1)+0=0 9×1+0×0+9×0×0+8×(−1)+(−1)=0 Not important to our example, but multiplication by constant C can be represented by: QL​ QR​ QM​ QO​ QC​ C 0 0 -1 0 As you might have already noticed, there are several ways of representing the same gate in some cases. We'll exploit this in a moment.","breadcrumbs":"Plonk » Recap » The Q matrix","id":"10","title":"The Q matrix"},"100":{"body":"In specific scenarios, such as dividing by a polynomial of the form X−a, for example when building the deep composition polynomial, Ruffini's rule can be employed to further enhance performance.","breadcrumbs":"STARKs » Protocol » Ruffini's rule","id":"100","title":"Ruffini's rule"},"101":{"body":"As one can see from inspecting the protocol, there are multiple times where, for a polynomial p, the prover sends both openings Open(p(D),hωi) and Open(p(D),−hωi). This implies, a priori, sending two authentication paths. Domains can be indexed using bit-reverse ordering to reduce this to a single authentication path for both openings, as follows. The natural way of building a Merkle tree to commit to a vector (p(h),p(hω),p(hω2),…,p(hω2k−1)), is assigning the value p(hωi) to leaf i. If this is the case, the value p(hωi) is at position i and the value p(−hωi) is at position i+2k−1. This is because −1 equals ω2k−1 for the value ω used in the protocol. Instead of this naive approach, a better solution is to assign the value p(hωσ(i)) to leaf i, where σ is the bit-reversal permutation. This is the permutation that maps i to the index whose binary representation (padded to k bits), is the binary representation of i but in reverse order. For example, if k=3 and i=1, then its binary representation is 001, which reversed is 100. Therefore σ(1)=8. In the same way σ(0)=0 and σ(2)=4. Check out the wikipedia article. With this ordering of the leaves, if i is even, element p(hωσ(i)) is at index i and p(−hωσ(i)) is at index i+1. Which means that a single authentication path serves to validate both points simultaneously.","breadcrumbs":"STARKs » Protocol » Bit-reversal ordering of Merkle tree leaves","id":"101","title":"Bit-reversal ordering of Merkle tree leaves"},"102":{"body":"The prover opens the polynomials pk​ of the FRI layers at υs2k​ and −υs2k​ for all k>1. Later on, the verifier uses each of those pairs to reconstruct one of the values of the next layer, namely pk+1​(υ2k+1). So there's no need to add the value pk​(υ2k+1) to the proof, as the verifier reconstructs them. The prover only needs to send the authentication paths Pk​ for them. The protocol is only modified at Step 3 of the verifier as follows. Checking that Verify((υs2k​,πkυs2k​​),Pk​,Pk​) is skipped. After computing x:=G+ζk​H, the verifier uses it to check that Verify((υs2k​,x),Pk​,Pk​) is Accept , which proves that x is actually πkυs2k​​, and continues to the next iteration of the loop.","breadcrumbs":"STARKs » Protocol » Redundant values in the proof","id":"102","title":"Redundant values in the proof"},"103":{"body":"The goal of this section will be to go over the details of the implementation of the proving system. To this end, we will follow the flow the example in the recap chapter, diving deeper into the code when necessary and explaining how it fits into a more general case. This implementation couldn't be done without checking Facebook's Winterfell and Max Gillett's Giza . We want to thank everyone involved in them, along with Shahar Papini and Lior Goldberg from Starkware who also provided us valuable insight.","breadcrumbs":"STARKs » Implementation » STARKs Prover Lambdaworks Implementation","id":"103","title":"STARKs Prover Lambdaworks Implementation"},"104":{"body":"Let's go over the main test we use for our prover, where we compute a STARK proof for a fibonacci trace with 4 rows and then verify it. fn test_prove_fib() { let trace = simple_fibonacci::fibonacci_trace([FE::from(1), FE::from(1)], 8); let proof_options = ProofOptions::default_test_options(); let pub_inputs = FibonacciPublicInputs { a0: FE::one(), a1: FE::one(), }; let proof = prove::>(&trace, &pub_inputs, &proof_options).unwrap(); assert!(verify::>(&proof, &pub_inputs, &proof_options));\n} The proving system revolves around the prove function, that takes a trace, public inputs and proof options as inputs to generate a proof, and a verify function that takes the generated proof, the public inputs and the proof options as inputs, outputting true when the proof is verified correctly and false otherwise. Note that the public inputs and proof options should be the same for both. Public inputs should be shared by the Cairo runner to prover and verifier, and the proof options should have been agreed on beforehand by the two entities beforehand. Below we go over the main things involved in this code.","breadcrumbs":"STARKs » Implementation » High Level API » High level API: Fibonacci example","id":"104","title":"High level API: Fibonacci example"},"105":{"body":"To prove the integrity of a fibonacci trace, we first need to define what it means for a trace to be valid. As we've talked about in the recap, this involves defining an AIR for our computation where we specify both the boundary and transition constraints for a fibonacci sequence. In code, this is done through the AIR trait. Implementing AIR requires defining a couple methods, but the two most important ones are boundary_constraints and compute_transition, which encode the boundary and transition constraints of our computation.","breadcrumbs":"STARKs » Implementation » High Level API » AIR","id":"105","title":"AIR"},"106":{"body":"For our Fibonacci AIR, boundary constraints look like this: fn boundary_constraints( &self, _rap_challenges: &Self::RAPChallenges,\n) -> BoundaryConstraints { let a0 = BoundaryConstraint::new_simple(0, self.pub_inputs.a0.clone()); let a1 = BoundaryConstraint::new_simple(1, self.pub_inputs.a1.clone()); BoundaryConstraints::from_constraints(vec![a0, a1])\n} The BoundaryConstraint struct represents a specific boundary constraint, meaning \"column i at row j should be equal to x\". In this case, because we have only one column, we are using the new_simple method to simply say Row 0 should equal the public input a0, which in the typical fibonacci is set to 1. Row 1 should equal the public input a1, which in the typical fibonacci is set to 1. In the case of multiple columns, the new method exists so you can also specify column number. After instantiating each of these constraints, we return all of them through the struct BoundaryConstraints.","breadcrumbs":"STARKs » Implementation » High Level API » Boundary Constraints","id":"106","title":"Boundary Constraints"},"107":{"body":"The way we specify our fibonacci transition constraint looks like this: fn compute_transition( &self, frame: &air::frame::Frame, _rap_challenges: &Self::RAPChallenges,\n) -> Vec> { let first_row = frame.get_row(0); let second_row = frame.get_row(1); let third_row = frame.get_row(2); vec![third_row[0] - second_row[0] - first_row[0]]\n} It's not completely obvious why this is how we chose to express transition constraints, so let's talk a little about it. What we need to specify in this method is the relationship that has to hold between the current step of computation and the previous ones. For this, we get a Frame as an argument. This is a struct holding the current step (i.e. the current row of the trace) and all previous ones needed to encode our constraint. In our case, this is the current row and the two previous ones. To access rows we use the get_row method. The current step is always the last row (in our case 2), with the others coming before it. In our compute_transition method we get the three rows we need and return third_row[0] - second_row[0] - first_row[0] which is the value that needs to be zero for our constraint to hold. Because we support multiple transition constraints, we actually return a vector with one value per constraint, so the first element holds the first constraint value and so on.","breadcrumbs":"STARKs » Implementation » High Level API » Transition Constraints","id":"107","title":"Transition Constraints"},"108":{"body":"After defining our AIR, we create our specific trace to prove against it. let trace = fibonacci_trace([FE17::new(1), FE17::new(1)], 4); let trace_table = TraceTable { table: trace.clone(), num_cols: 1,\n}; TraceTable is the struct holding execution traces; the num_cols says how many columns the trace has, the table field is a vec holding the actual values of the trace in row-major form, meaning if the trace looks like this | 1 | 2 |\n| 3 | 4 |\n| 5 | 6 | then its corresponding TraceTable is let trace_table = TraceTable { table: vec![1, 2, 3, 4, 5, 6], num_cols: 2,\n}; In our example, fibonacci_trace is just a helper function we use to generate the fibonacci trace with 4 rows and [1, 1] as the first two values.","breadcrumbs":"STARKs » Implementation » High Level API » TraceTable","id":"108","title":"TraceTable"},"109":{"body":"After specifying our constraints and trace, the only thing left to do is provide a few parameters related to the STARK protocol and our AIR. These specify things such as the number of columns of the trace and proof configuration, among others. They are all encapsulated in the AirContext struct, which in our example we instantiate like this: let context = AirContext { options: ProofOptions { blowup_factor: 2, fri_number_of_queries: 1, coset_offset: 3, }, trace_columns: trace_table.n_cols, transition_degrees: vec![1], transition_exemptions: vec![2], transition_offsets: vec![0, 1, 2], num_transition_constraints: 1,\n}; Let's go over each of them: options requires a ProofOptions struct holding specific parameters related to the STARK protocol to be used when proving. They are: The blowup_factor used for the trace LDE extension, a parameter related to the security of the protocol. The number of queries performed by the verifier when doing FRI, also related to security. The offset used for the LDE coset. This depends on the field being used for the STARK proof. trace_columns are the number of columns of the trace, respectively. transition_degrees holds the degree of each transition constraint. transition_exemptions is a Vec which tells us, for each column, the number of rows the transition constraints should not apply, starting from the end of the trace. In the example, the transition constraints won't apply on the last two rows of the trace. transition_offsets holds the indexes that define a frame for our AIR. In our fibonacci case, these are [0, 1, 2] because we need the current row and the two previous one to define our transition constraint. num_transition_constraints simply says how many transition constraints our AIR has.","breadcrumbs":"STARKs » Implementation » High Level API » AIR Context","id":"109","title":"AIR Context"},"11":{"body":"The claim in the previous section is clearly not an \"if and only if\" statement because the following trace columns do satisfy the equations but do not correspond to a valid execution: A B C 2 3 6 0 0 0 20 - 19 The V matrix encodes the carry of the results from one gate to the right or left operand of a subsequent one. These are called wirings . Like the Q matrix, it's independent of the particular evaluation. It consists of indices for all input and intermediate variables. In this case that matrix is: L R O 0 1 2 2 1 3 3 - 4 Here 0 is the index of e, 1 is the index of x, 2 is the index of u, 3 is the index of v and 4 is the index of the output w. Now we can update the claim to have an \"if and only if\" statement. Claim: Let T be a matrix with columns A,B,C. It correspond to a valid evaluation of the circuit if and only if a) for all i the following equality holds Ai​(QL​)i​+Bi​(QR​)i​+Ai​Bi​QM​+Ci​(QO​)i​+(QC​)i​=0, b) for all i,j,k,l such that Vi,j​=Vk,l​ we have Ti,j​=Tk,l​. So now our malformed example does not pass the second check.","breadcrumbs":"Plonk » Recap » The V matrix","id":"11","title":"The V matrix"},"110":{"body":"Having defined all of the above, proving our fibonacci example amounts to instantiating the necessary structs and then calling prove passing the trace, public inputs and proof options. We use a simple implementation of a hasher called TestHasher to handle merkle proof building. let proof = prove(&trace_table, &pub_inputs, &proof_options); Verifying is then done by passing the proof of execution along with the same AIR to the verify function. assert!(verify(&proof, &pub_inputs, &proof_options));","breadcrumbs":"STARKs » Implementation » High Level API » Proving execution","id":"110","title":"Proving execution"},"111":{"body":"In this section we go over how a few things in the prove and verify functions are implemented. If you just need to use the prover, then you probably don't need to read this. If you're going through the code to try to understand it, read on. We will once again use the fibonacci example as an ilustration. Recall from the recap that the main steps for the prover and verifier are the following:","breadcrumbs":"STARKs » Implementation » Under the hood » How this works under the hood","id":"111","title":"How this works under the hood"},"112":{"body":"Compute the trace polynomial t by interpolating the trace column over a set of 2n-th roots of unity {gi:0≤i<2n}. Compute the boundary polynomial B. Compute the transition constraint polynomial C. Construct the composition polynomial H from B and C. Sample an out of domain point z and provide the evaluation H(z) and all the necessary trace evaluations to reconstruct it. In the fibonacci case, these are t(z), t(zg), and t(zg2). Sample a domain point x_0 and provide the evaluation H(x0​) and t(x0​). Construct the deep composition polynomial Deep(x) from H, t, and the evaluations from the item above. Do FRI on Deep(x) and provide the resulting FRI commitment to the verifier. Provide the merkle root of t and the merkle proof of t(x0​).","breadcrumbs":"STARKs » Implementation » Under the hood » Prover side","id":"112","title":"Prover side"},"113":{"body":"Take the evaluation H(z) along with the trace evaluations the prover provided. Reconstruct the evaluations B(z) and C(z) from the trace evaluations. Check that the claimed evaluation H(z) the prover gave us actually satisfies H(z)=β1​B(z)+β2​C(z) Take the evaluations H(x0​) and t(x0​). Check that the claimed evaluation Deep(x0​) the prover gave us actually satisfies Deep(x0​)=γ1​x0​−zH(x0​)−H(z)​+γ2​x0​−zt(x0​)−t(z)​+γ3​x0​−zgt(x0​)−t(zg)​+γ4​x0​−zg2t(x0​)−t(zg2)​ Take the provided FRI commitment and check that it verifies. Using the merkle root and the merkle proof the prover provided, check that t(x0​) belongs to the trace. Following along the code in the prove and verify functions, most of it maps pretty well to the steps above. The main things that are not immediately clear are: How we take the constraints defined in the AIR through the compute_transition method and map them to transition constraint polynomials. How we then construct H from them and the boundary constraint polynomials. What the composition polynomial even/odd decomposition is. What an ood frame is. What the transcript is.","breadcrumbs":"STARKs » Implementation » Under the hood » Verifier side","id":"113","title":"Verifier side"},"114":{"body":"This is possibly the most complex part of the code, so what follows is a long explanation for it. In our fibonacci example, after obtaining the trace polynomial t by interpolating, the transition constraint polynomial is C(x)=∏i=05​(x−gi)t(xg2)−t(xg)−t(x)​ On our prove code, if someone passes us a fibonacci AIR like the one we showed above used in one of our tests, we somehow need to construct C(x). However, what we are given is not a polynomial, but rather this method fn compute_transition( &self, frame: &air::frame::Frame, ) -> Vec> { let first_row = frame.get_row(0); let second_row = frame.get_row(1); let third_row = frame.get_row(2); vec![third_row[0] - second_row[0] - first_row[0]]\n} So how do we get to C(x) from this? The answer is interpolation. What the method above is doing is the following: if you pass it a frame that looks like this ​t(x0​)t(x0​g)t(x0​g2)​​ for any given point x0​, it will return the value t(x0​g2)−t(x0​g)−t(x0​) which is the numerator in C(x0​). Using the transition_exemptions field we defined in our AIR, we can also compute evaluations in the denominator, i.e. the zerofier evaluations. This is done under the hood by the transition_divisors() method. The above means that even though we don't explicitly have the polynomial C(x), we can evaluate it on points given an appropriate frame. If we can evaluate it on enough points, we can then interpolate them to recover C(x). This is exactly how we construct both transition constraint polynomials and subsequently the composition polynomial H. The job of evaluating H on enough points so we can then interpolate it is done by the ConstraintEvaluator struct. You'll notice prove does the following let constraint_evaluations = evaluator.evaluate( &lde_trace, &lde_roots_of_unity_coset, &alpha_and_beta_transition_coefficients, &alpha_and_beta_boundary_coefficients,\n); This function call will return the evaluations of the boundary terms βiB​Bi​(x) and constraint terms βiT​Ci​(x) for every i. The constraint_evaluations value returned is a ConstraintEvaluationTable struct, which is nothing more than a big list of evaluations of each polynomial required to construct H. With this in hand, we just call let composition_poly = constraint_evaluations.compute_composition_poly(& lde_roots_of_unity_coset); which simply interpolates the sum of all evaluations to obtain H. Let's go into more detail on how the evaluate method reconstructs C(x) in our fibonacci example. It receives the lde_trace as an argument, which is this: ​t(ω0)t(ω1)…t(ω15)​​ where ω is the primitive root of unity used for the LDE, that is, ω satisfies ω2=g. We need to recover C(x), a polynomial whose degree can't be more than t(x)'s. Because t was built by interpolating 8 points (the trace), we know we can recover C(x) by interpolating it on 16 points. We choose these points to be the LDE roots of unity {ω0,ω,ω2,…,ω15} Remember that to evaluate C(x) on these points, all we need are the evaluations of the polynomial t(xg2)−t(xg)−t(x) as the zerofier ones we can compute easily. These become: t(ω0g2)−t(ω0g)−t(ω0)t(ωg2)−t(ωg)−t(ω)t(ω2g2)−t(ω2g)−t(ω2)⋮t(ω15g2)−t(ω15g)−t(ω15) If we remember that ω2=g, this is t(ω4)−t(ω2)−t(ω0)t(ω5)−t(ω3)−t(ω)t(ω6)−t(ω4)−t(ω2)⋮t(ω3)−t(ω)−t(ω15) and we can compute each evaluation here by calling compute_transition on the appropriate frame built from the lde_trace. Specifically, for the first evaluation we can build the frame: ​t(ω0)t(ω2)t(ω4)​​ Calling compute_transition on this frame gives us the first evaluation. We can get the rest in a similar fashion, which is what this piece of code in the evaluate method does: for (i, d) in lde_domain.iter().enumerate() { let frame = Frame::read_from_trace( lde_trace, i, blowup_factor, &self.air.context().transition_offsets, ) let mut evaluations = self.air.compute_transition(&frame); ...\n} Each iteration builds a frame as above and computes one of the evaluations needed. The rest of the code just adds the zerofier evaluations, along with the alphas and betas. It then also computes boundary polynomial evaluations by explicitly constructing them.","breadcrumbs":"STARKs » Implementation » Under the hood » Reconstructing the transition constraint polynomials","id":"114","title":"Reconstructing the transition constraint polynomials"},"115":{"body":"The verifier employs the same trick to reconstruct the evaluations on the out of domain point Ci​(z) for the consistency check.","breadcrumbs":"STARKs » Implementation » Under the hood » Verifier","id":"115","title":"Verifier"},"116":{"body":"At the end of the recap we talked about how in our code we don't actually commit to H, but rather an even/odd decomposition for it. These are two polynomials H_1 and H_2 that satisfy H(x)=H1​(x2)+xH2​(x2) This all happens on this piece of code let composition_poly = constraint_evaluations.compute_composition_poly(&lde_roots_of_unity_coset); let (composition_poly_even, composition_poly_odd) = composition_poly.even_odd_decomposition(); // Evaluate H_1 and H_2 in z^2.\nlet composition_poly_evaluations = vec![ composition_poly_even.evaluate(&z_squared), composition_poly_odd.evaluate(&z_squared),\n]; After this, we don't really use H anymore, but rather H_1 and H_2. There's not that much to say other than that.","breadcrumbs":"STARKs » Implementation » Under the hood » Even/odd decomposition for H","id":"116","title":"Even/odd decomposition for H"},"117":{"body":"As part of the consistency check, the prover needs to provide evaluations of the trace polynomials in all the points needed by the verifier to check that H was constructed correctly. In the fibonacci example, these are t(z), t(zg), and t(zg2). In code, the prover passes these evaluations as a Frame, which we call the out of domain (ood) frame. The reason we do this is simple: with the frame in hand, the verifier can reconstruct the evaluations of the constraint polynomials Ci​(z) by calling the compute_transition method on the ood frame and then adding the alphas, betas, and so on, just like we explained in the section above.","breadcrumbs":"STARKs » Implementation » Under the hood » Out of Domain Frame","id":"117","title":"Out of Domain Frame"},"118":{"body":"Throughout the protocol, there are a number of times where the verifier randomly samples some values that the prover needs to use (think of the alphas and betas used when constructing H). Because we don't actually have an interaction between prover and verifier, we emulate it by using a hash function, which we assume is a source of randomness the prover can't control. The job of providing these samples for both prover and verifier is done by the Transcript struct, which you can think of as a stateful rng; whenever you call challenge() on a transcript you get a random value and the internal state gets mutated, so the next time you call challenge() you get a different one. You can also call append on it to mutate its internal state yourself. This is done a number of times throughout the protocol to keep the prover honest so it can't predict or manipulate the outcome of challenge(). Notice that to sample the same values, both prover and verifier need to call challenge and append in the same order (and with the same values in the case of append) and the same number of times. The idea explained above is called the Fiat-Shamir heuristic or just Fiat-Shamir, and is more generally used throughout proving systems to remove interaction between prover and verifier. Though the concept is very simple, getting it right so the prover can't cheat is not, but we won't go into that here.","breadcrumbs":"STARKs » Implementation » Under the hood » Transcript","id":"118","title":"Transcript"},"119":{"body":"The generated proof has got all the information needed for the verifier to verify it: Trace length: The number of rows of the trace table, needed to know the max degree of the polynomials that appear in the system. LDE trace commitments. DEEP composition polynomial out of domain even and odd evaluations. DEEP composition polynomial root. FRI layers merkle roots. FRI last layer value. Query list. DEEP composition poly openings. Nonce: Proof of work setting used to generate the proof.","breadcrumbs":"STARKs » Implementation » Under the hood » Proof","id":"119","title":"Proof"},"12":{"body":"Our matrices are fine now. But they can be optimized. Let's do that to showcase this flexibility of PLONK and also reduce the size of our example. PLONK has the flexibility to construct more sophisticated gates as combinations of the five columns. And therefore the same program can be expressed in multiple ways. In our case all three gates can actually be merged into a single custom gate. The Q matrix ends up being a single row. QL​ QR​ QM​ QO​ QC​ 0 1 1 -1 1 and also the V matrix L R O 0 1 2 The trace matrix for this representation is just A B C 2 3 8 And we check that it satisfies the equation 2×0+3×1+2×3×1+8×(−1)+(−1)=0 Of course, we can't always squash an entire program into a single gate.","breadcrumbs":"Plonk » Recap » Custom gates","id":"12","title":"Custom gates"},"120":{"body":"","breadcrumbs":"STARKs » Implementation » Under the hood » Special considerations","id":"120","title":"Special considerations"},"121":{"body":"When evaluating or interpolating a polynomial, if the input (be it coefficients or evaluations) size isn't a power of two then the FFT API will extend it with zero padding until this requirement is met. This is because the library currently only uses a radix-2 FFT algorithm. Also, right now FFT only supports inputs with a size up to 2264 elements.","breadcrumbs":"STARKs » Implementation » Under the hood » FFT evaluation and interpolation","id":"121","title":"FFT evaluation and interpolation"},"122":{"body":"","breadcrumbs":"STARKs » Implementation » Under the hood » Other","id":"122","title":"Other"},"123":{"body":"Whenever we interpolate or evaluate trace, boundary and constraint polynomials, we use some 2n-th roots of unity. There are a few reasons for this: Using roots of unity means we can use the Fast Fourier Transform and its inverse to evaluate and interpolate polynomials. This method is much faster than the naive Lagrange interpolation one. Since a huge part of the STARK protocol involves both evaluating and interpolating, this is a huge performance improvement. When computing boundary and constraint polynomials, we divide them by their zerofiers, polynomials that vanish on a few points (the trace elements where the constraints do not hold). These polynomials take the form Z(X)=∏(X−xi​) where the xi​ are the points where we want it to vanish. When implementing this, evaluating this polynomial can be very expensive as it involves a huge product. However, if we are using roots of unity, we can use the following trick. The vanishing polynomial for all the 2n roots of unity is X2n−1 Instead of expressing the zerofier as a product of the places where it should vanish, we express it as the vanishing polynomial above divided by the exemptions polynomial; the polynomial whose roots are the places where constraints don't need to hold. Z(X)=∏(X−ei​)X2n−1​ where the ei​ are now the points where we don't want it to vanish. This exemptions polynomial in the denominator is usually much smaller, and because the vanishing polynomial in the numerator is only two terms, evaluating it is really fast.","breadcrumbs":"STARKs » Implementation » Under the hood » Why use roots of unity?","id":"123","title":"Why use roots of unity?"},"124":{"body":"The n-th roots of unity are the numbers x that satisfy xn=1 There are n such numbers, because they are the roots of the polynomial Xn−1. The set of n-th roots of unity always has a generator, a root g that can be used to obtain every other root of unity by exponentiating. What this means is that the set of n-th roots of unity is {gi:0≤in. Polynomials enter now to squash most of these equations. We will traduce the set of all equations in conditions (a) and (b) to just a few equations on polynomials. Let ω be a primitive N-th root of unity and let H=ωi:0≤i1 and 0≤j≤J and I=∣Lj′′​∣. Extend M with additional L′′ rows to obtain a matrix M∈F(L+L′+L′′)×33 by appending copies of R′′ at the bottom. Pad M with copies of its last row until it has a power of two number of rows. As a result we obtain a matrix T∈F2n×33.","breadcrumbs":"Cairo » Trace Formal Description » Construction of execution trace T:","id":"142","title":"Construction of execution trace T:"},"143":{"body":"","breadcrumbs":"Cairo » Trace Detailed Description » Cairo execution trace","id":"143","title":"Cairo execution trace"},"144":{"body":"After the execution of a Cairo program in the Cairo VM, three files are generated that are the core components for the construction of the execution trace, needed for the proving system: trace file : Has the information on the state of the three Cairo VM registers ap, fp, and pc at every cycle of the execution of the program. To reduce ambiguity in terms, we should call these the register states of the Cairo VM, and leave the term trace to the final product that is passed to the prover to generate a proof. memory file : A file with the information of the VM's memory at the end of the program run, after the memory has been relocated. public inputs : A file with all the information that must be publicly available to the prover and verifier, such as the total number of execution steps, public memory, used builtins and their respective addresses range in memory. The next section will explain in detail how these elements are used to build the final execution trace.","breadcrumbs":"Cairo » Trace Detailed Description » Raw materials","id":"144","title":"Raw materials"},"145":{"body":"The execution trace is built in two stages. In the first one, the information on the files described in the previous section is aggregated to build a main trace table. In the second stage, there is an interaction with the verifier to add some extension columns to the main trace.","breadcrumbs":"Cairo » Trace Detailed Description » Construction details","id":"145","title":"Construction details"},"146":{"body":"The layout of the main execution trace is as follows: A. flags (16): Decoded instruction flags B. res (1): Res value C. pointers (2): Temporary memory pointers (ap and fp) D. mem_a (4): Memory addresses (pc, dst_addr, op0_addr, op1_addr) E. mem_v (4): Memory values (inst, dst, op0, op1) F. offsets (3) : (off_dst, off_op0, off_op1) G. derived (3) : (t0, t1, mul) A B C D E F G\n|xxxxxxxxxxxxxxxx|x|xx|xxxx|xxxx|xxx|xxx| Each letter from A to G represents some subsection of columns, and the number specifies how many columns correspond to that subsection. Cairo instructions It is important to have in mind the information that each executed Cairo instruction holds, since it is a key component of the construction of the execution trace. For a detailed explanation of how the building components of the instruction interact to change the VM state, refer to the Cairo whitepaper, sections 4.4 and 4.5. Structure of the 63-bit that forms the first word of each instruction: ┌─────────────────────────────────────────────────────────────────────────┐ │ off_dst (biased representation) │ ├─────────────────────────────────────────────────────────────────────────┤ │ off_op0 (biased representation) │ ├─────────────────────────────────────────────────────────────────────────┤ │ off_op1 (biased representation) │ ├─────┬─────┬───────┬───────┬───────────┬────────┬───────────────────┬────┤ │ dst │ op0 │ op1 │ res │ pc │ ap │ opcode │ 0 │ │ reg │ reg │ src │ logic │ update │ update │ │ │ ├─────┼─────┼───┬───┼───┬───┼───┬───┬───┼───┬────┼────┬────┬────┬────┼────┤ │ 0 │ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │ 8 │ 9 │ 10 │ 11 │ 12 │ 13 │ 14 │ 15 │ └─────┴─────┴───┴───┴───┴───┴───┴───┴───┴───┴────┴────┴────┴────┴────┴────┘ Columns The construction of the following columns corresponds to a colloquial explanation of what is done in the build_cairo_execution_trace function. Section A - Flags The flags section A corresponds to the 16 bits that represent the configuration of the dst_reg, op0_reg, op1_src, res_logic, pc_update, ap_update and opcode flags, as well as the zero flag. So there is one column for each bit of the flags decomposition. Section C - Temporary memory pointers The two columns in this section, as well as the pc column from section D , are the most trivial. For each step of the register states, the corresponding values are added to the columns, which are pointers to some memory cell in the VM's memory. Section D - Memory addresses As already mentioned, the first column of this section, pc, is trivially obtained from the register states for each cycle. Columns dst_addr, op0_addr, and op1_addr from section D are addresses constructed from pointers stored at ap or fp, and their respective offsets off_dst, off_op0 and off_op1. The exact way these are computed depends on the particular values of the flags for each instruction. Section E - Memory values The inst column, is obtained by fetching in memory the value stored at pointer pc, which corresponds to a 63-bit Cairo instruction. Columns dst, op0, and op1 are computed by fetching in memory by their respective addresses. Section F - Offsets/Range-checked values These columns represent integer values that are used to construct addresses dst_addr, op0_addr and op1_addr and are decoded directly from the instruction. These values have the property to be numbered in the range from 0 to 2^16. Section B - Res This column is computed depending on the decoded opcode and res_logic of every instruction. In some cases, res is unused in the instruction, and the value for (dst)^(-1) is used in that place as an optimization. Section G - Derived To have constraints of max degree two, some more columns are derived from the already calculated, t0, t1, and mul: t0 is the product of the values of DST and the PC_JNZ flag for each step. t1 is the product of t0 and res for each step. mul is the product of op0 and op1 for each step. Range check and Memory holes For the values constrained between ranges 0 and 216, the offsets, the prover uses a permutation argument to optimize enforcing this. In particular, it checks an ordered list with the offsets are the same as the original one, is continuous, the first value is rcmin​, and the last one is less than rcmax​. Since not all values are used, there may be unused values, and so the ordered offset may not be continuous. These unused values are called holes, and they need to be filled with the missing values, so the checks can be done. This is explained in section 9.9 of the Cairo Paper In the case of memory, something similar happens, where the values should be continuous, but if there are built-ins, this may not be the case. For example, the built-in may be using addresses in ranges higher than the ones used by the program. To fix this, holes in the memory cells are filled, just like the ones of the RC. It's something important to note that when filling the holes, we can't use dedicated columns like op0_addr, since this would break the constraints. For this to work, we either need new columns for holes, or make use of subcolumns, which are explained in their dedicated section. No matter which approach is used , either by subcolumns or columns, we will need cells where the constraints of the range check and memory are applied, but not the specific ones related to the instructions. Finally, using these columns, we can fill the holes without breaking the constraint system. Dummy memory accesses As part of proving the execution of a Cairo program, we need to prove that memory used by the program extends the public memory. This is important since public memory contains for example the bytecode that was executed and the outputs. The bytecode is something critical for the verifier to not only know that something was executed correctly but to know what was executed. To do this, we the permutation check, which that proves the memory is continuous and single-valued To do this, the permutation check of the memory is modified. Initially, we had M, V and M′, V′ pairs of memory addresses and values, where M and V are the values as used and without order, and M′, V′ the pairs ordered by address. For the public memory, we will add it directly to M′, V′. We also need to add dummy accesses to the pairs M V. These dummy accesses are just pairs of (M,V)=(0,0). This change makes the statement that (M′,V′) is a permutation of (M,V), which means that they are the same values in a different order, no longer true. This means that the two cumulative products used to check the statement are no longer equal, and their division Luckily, we can know the new expected value on the verifier, since we have access to the public memory. Even more, it's this fact that enables the verifier to check the memory is an extension of the public memory. The math is quite straightforward. When the memory was the same, we expected a final value pn−1​=1. This came from two cumulative products that should equal, one from the unordered pairs, and one from the ordered ones. Now, adding zeros to one side and the real values to the other unbalances the cumulative products, so the verifier will need to balance it by dividing pn−1​ by the extra factors that appeared with the addition of the public memory. Doing so will make the final value 1 again. Since they only depend on the public memory, the verifier has enough data to recalculate them and use them. Even more, if the prover lies, the equality that the verifier is expecting won't hold. In reality, instead of dividing and expecting the result to equal to 1, we can just check the equality against the new expected value, and avoid doing that inversion. All of this is explained in section 9.8 of the Cairo paper. Trace extension / Padding The last step is padding the trace to a power of two for efficiency. We may also need to pad the trace if for some reason some unbalance is given by the layout. For this, we will copy the last executed instruction until reaching the desired length. But there's a trick. If the last executed instruction is any instruction, and it's copied, the transition constraints won't be satisfied. To be able to do this, we need to use something called \"proof mode\". In proof mode, the main function of the program is wrapped in another one, which calls it and returns to an infinite loop. This loop is a jump relative 0. Since this loop can be executed many times without changing the validity of the trace, it can be copied as many times as needed, solving the issues mentioned before. Summary To construct the execution trace, we augment the RegisterStates with the information obtained from the Memory. This includes decoding instruction of each steps, and writing all the data needed to check the execution is valid. Additionally, memory holes have to be filled, public memory added, and a final pad using an infinite loop is needed for everything to work properly. Adding all of that, we create an execution trace that's ready for the prover to generate a proof.","breadcrumbs":"Cairo » Trace Detailed Description » Main trace construction","id":"146","title":"Main trace construction"},"147":{"body":"The verifier sends challenges α,z∈F (or the prover samples them from the transcript). Additional columns are added to incorporate the memory constraints. To define them the prover follows these steps: Stack the rows of the submatrix of T defined by the columns pc, dst_addr, op0_addr, op1_addr into a vector a of length 2n+2 (this means that the first entries of a are pc[0], dst_addr[0], op0_addr[0], op1_addr[0], pc[1], dst_addr[1],...). Stack the the rows of the submatrix defined by the columns inst, dst, op0, op1 into a vector v of length 2n+2. Define MMem​∈F2n+2×2 to be the matrix with columns a, v. Define MMemRepl​∈F2n+2×2 to be the matrix that's equal to MMem​ in the first 2n+2−Lpub​ rows, and its last Lpub​ entries are the addresses and values of the actual public memory (program code). Sort MMemRepl​ by the first column in increasing order. The result is a matrix MMemReplSorted​ of size 2n+2×2. Denote its columns by a′ and v′. Compute the vector p of size 2n+2 with entries pi​:=j=0∏i​z−(ai​+αvi​)z−(ai′​+αvi′​)​ Reshape the matrix MMemReplSorted​ into a 2n×8 in row-major. Reshape the vector p into a 2n×4 matrix in row-major. Concatenate these 12 rows. The result is a matrix MMemRAP2​ of size 2n×12 The verifier sends challenge z′∈F. Further columns are added to incorporate the range check constraints following these steps: Stack the rows of the submatrix of T defined by the columns in the group offsets into a vector b of length 3⋅2n. Sort the values of b in increasing order. Let b′ be the result. Compute the vector p′ of size 3⋅2n with entries pi′​:=j=0∏i​z′−bi​z′−bi′​​ Reshape b′ and p′ into matrices of size 2n×3 each and concatenate them into a matrix MRangeCheckRAP2​ of size 2n×6. Concatenate MMemRAP2​ and MRangeCheckRAP2​ into a matrix MRAP2​ of size 2n×18. Using the notation described at the beginning, m′=33, m′′=18 and m=52. They are respectively the columns of the first and second part of the rap, and the total number of columns. Putting all together, the final layout of the trace is the following A. flags (16) : Decoded instruction flags B. res (1) : Res value C. pointers (2) : Temporary memory pointers (ap and fp) D. mem_a (4) : Memory addresses (pc, dst_addr, op0_addr, op1_addr) E. mem_v (4) : Memory values (inst, dst, op0, op1) F. offsets (3) : (off_dst, off_op0, off_op1) G. derived (3) : (t0, t1, mul) H. mem_a' (4) : Sorted memory addresses I. mem_v' (4) : Sorted memory values J. mem_p (4) : Memory permutation argument columns K. offsets_b' (3) : Sorted offset columns L. offsets_p' (3) : Range check permutation argument columns A B C D E F G H I J K L\n|xxxxxxxxxxxxxxxx|x|xx|xxxx|xxxx|xxx|xxx|xxxx|xxxx|xxxx|xxx|xxx|","breadcrumbs":"Cairo » RAP » Extended columns","id":"147","title":"Extended columns"},"148":{"body":"","breadcrumbs":"Cairo » Virtual Columns » Virtual columns and Subcolumns","id":"148","title":"Virtual columns and Subcolumns"},"149":{"body":"In previous chapters, we have seen how the registers states and the memory are augmented to generate a provable trace. While we have shown a way of doing that, there isn't only one possible provable trace. In fact, there are multiple configurations possible. For example, in the Cairo VM, we have 15 flags. These flags include \"DstReg\", \"Op0Reg\", \"OpCode\" and others. For simplification, let's imagine we have 3 flags with letters from \"A\" to \"C\", where \"A\" is the first flag. Now, let's assume we have 4 steps in our trace. If we were to only use plain columns, the layout would look like this: FlagA FlagB FlagB A0 B0 C0 A1 B1 C1 A2 B2 C2 A3 B3 C3 But, we could also organize them like this Flags A0 B0 C0 A1 B1 C1 A2 B2 C2 A3 B3 C3 The only problem is that now the constraints for each transition of the rows are not the same. We will have to define then a concept called \"Virtual Column\". A Virtual Column is like a traditional column, which has its own set of constraints, but it exists interleaved with another one. In the previous example, each row is associated with a column, but in practice, we could have different ratios. We could have 3 rows corresponding to one Virtual Column, and the next one corresponding to another one. For the time being, let's focus on this simpler example. Each row corresponding to Flag A will have the constraints associated with its own Virtual Column, and the same will apply to Flag B and Flag C. Now, to do this, we will need to evaluate the multiple rows taking into account that they are part of the same step. For a real case, we will add a dummy flag D, whose purpose is to make the evaluation move in a number that is a power of 2. Let's see how it works. If we were evaluating the Frame where the constraints should give 0, the frame movement would look like this: + A0 | B0 | C0\n+ A1 | B1 | C1 A2 | B2 | C2 A3 | B3 | C3 A0 | B0 | C0\n+ A1 | B1 | C1\n+ A2 | B2 | C2 A3 | B3 | C3 A0 | B0 | C0 A1 | B1 | C1\n+ A2 | B2 | C2\n+ A3 | B3 | C3 In the second case, the evaluation would look like this: + A0 |\n+ B0 |\n+ C0 |\n+ D0 |\n+ A1 |\n+ B1 |\n+ C1 |\n+ D1 | A2 | B2 | C2 | D2 | A3 | B3 | C3 | D3 | A0 | B0 | C0 | D0 |\n+ A1 |\n+ B1 |\n+ C1 |\n+ D1 |\n+ A2 |\n+ B2 |\n+ C2 |\n+ D2 | A3 | B3 | C3 | D3 | A0 | B0 | C0 | D0 | A1 | B1 | C1 | D1 |\n+ A2 |\n+ B2 |\n+ C2 |\n+ D2 |\n+ A3 |\n+ B3 |\n+ C3 |\n+ D3 | When evaluating the composition polynomial, we will do it over the points on the LDE, where the constraints won't evaluate to 0, but we will use the same spacing. Assume we have three constraints for each flag, C0​, C1​, and C2​, and that they don't involve other trace cells. Let's call the index of the frame evaluation i, starting from 0. In the first case, the constraint C0​, C1​ and C2​ would be applied over the same rows, giving an equation that looks like this: ‘Ck​(wi,wi+1)‘ In the second case, the equations would look like: ‘C0​(wi∗4,wi∗4+4)‘ ‘C1​(wi∗4+1,wi∗4+5)‘ ‘C2​(wi∗4+2,wi∗4+6)‘","breadcrumbs":"Cairo » Virtual Columns » Virtual Columns","id":"149","title":"Virtual Columns"},"15":{"body":"We have arrived at the eight polynomials we mentioned at the beginning: qL​,qR​,qM​,qO​,qC​,Sσ1​,Sσ2​,Sσ3​. These are what's called the common preprocessed input .","breadcrumbs":"Plonk » Recap » Common preprocessed input","id":"15","title":"Common preprocessed input"},"150":{"body":"Assume now we have 3 columns that share some constraints. For example, let's have three flags that can be either 0 or 1. Each flag will also have its own set of dedicated constraints. Let's denote the shared constraint B, and the independent constraints Ci​. What we can do is define a Column for the flags, where the binary constraint B is enforced. Additionally, we will define a subcolumn for each flag, which will enforce each Ci​. In summary, if we have a set of shared constraints to apply, we will be using a Column. If we want to mix or interleave Columns, we will define them as Virtual Columns. And if we want to apply more constraints to a subset of a Column of Virtual Columns, or share constraints between columns, we will define Virtual Subcolumns.","breadcrumbs":"Cairo » Virtual Columns » Virtual Subcolumns","id":"150","title":"Virtual Subcolumns"},"151":{"body":"We can understand the built-in as a small machine, that we can use to efficiently prove a subprogram. For example, it may be able to prove a hash, like Poseidon or Keccak, verify a signature, or check that some variable is in a range, and the cost would be less than what we would have if using the Cairo VM instructions. For each subprogram we want to prove, we will have a machine, which will have its own set of constraints in the prover. Let's take for example the Range Check built-in. This builtin enforces that a value X is between 0 and 2128. The logic behind the built-in is pretty straightforward. We split X into 8 parts. So we will say that X=X0​+X1​∗216+X2​∗232+X3​∗248+...+X7​∗2112 Then we require that each is in the range 0n. Then we constructed polynomials qL​,qR​,qM​,qO​,qC​,Sσ1​,Sσ2​,Sσ3​, f, g off the matrices Q and V. They are the result of interpolating at a domain H={1,ω,ω2,…,ωN−1} for some N-th primitive root of unity and a few random values. And also constructed polynomials a,b,c,pi off the matrices T and PI. Loosely speaking, the above fact can be reformulated in terms of polynomial equations as follows. Fact: Let zH​=XN−1. Let T be a N×3 matrix with columns A,B,C and PI a N×1 matrix. They correspond to a valid execution instance with public input given by PI if and only if a) There is a polynomial t1​ such that the following equality holds aqL​+bqR​+abqM​+cqO​+qC​+pi=zH​t1​, b) There are polynomials t2​,t3​, z such that zf−gz′=zH​t2​ and (z−1)L1​=zH​t3​, where z′(X)=z(Xω) You might be wondering where the polynomials ti​ came from. Recall that for a polynomial F, we have F(h)=0 for all h∈H if and only if F=zH​t for some polynomial t. Finally both conditions (a) and (b) are equivalent to a single equation (c) if we let more randomness to come into play. This is: (c) Let α be a random field element. There is a polynomial t such that zH​t=​aqL​+bqR​+abqM​+cqO​+qC​+pi+α(gz′−fz)+α2(z−1)L1​​ This last step is not obvious. You can check the paper to see the proof. Anyway, this is the equation you'll recognize below in the description of the protocol. Randomness is a delicate matter and an important part of the protocol is where it comes from, who chooses it and when they choose it. Check out the protocol to see how it works.","breadcrumbs":"Plonk » Recap » Wrapping up the whole thing","id":"16","title":"Wrapping up the whole thing"},"17":{"body":"","breadcrumbs":"Plonk » Protocol » Protocol","id":"17","title":"Protocol"},"18":{"body":"","breadcrumbs":"Plonk » Protocol » Details and tricks","id":"18","title":"Details and tricks"},"19":{"body":"A polynomial commitment scheme (PCS) is a cryptographic tool that allows one party to commit to a polynomial, and later prove properties of that polynomial. This commitment polynomial hides the original polynomial's coefficients and can be publicly shared without revealing any information about the original polynomial. Later, the party can use the commitment to prove certain properties of the polynomial, such as that it satisfies certain constraints or that it evaluates to a certain value at a specific point. In the implementation section we'll explain the inner workings of the Kate-Zaverucha-Goldberg scheme, a popular PCS chosen in Lambdaworks for PLONK. For the moment we only need the following about it: It consists of a finite group G and the following algorithms: Commit(f) : This algorithm takes a polynomial f and produces an element of the group G. It is called the commitment of f and is denoted by [f]1​. It is homomorphic in the sense that [f+g]1​=[f]1​+[g]1​. The former sum being addition of polynomials. The latter is addition in the group G. Open(f, ζ ) : It takes a polynomial f and a field element ζ and produces an element π of the group G. This element is called an opening proof for f(ζ). It is the proof that f evaluated at ζ gives f(ζ). Verify([f]1​, π, ζ, y) : It takes group elements [f]1​ and π, and also field elements ζ and y. With overwhelming probability it outputs Accept if f(z)=y and Reject otherwise.","breadcrumbs":"Plonk » Protocol » Polynomial commitment scheme","id":"19","title":"Polynomial commitment scheme"},"2":{"body":"","breadcrumbs":"FFT Library » Benchmarks » FFT Benchmarks","id":"2","title":"FFT Benchmarks"},"20":{"body":"As you will see in the protocol, the prover reveals the value taken by a bunch of the polynomials at a random ζ. In order for the protocol to be Honest Verifier Zero Knowledge , these polynomials need to be blinded . This is a process that makes the values of these polynomials at ζ seemingly random by forcing them to be of certain degree. Here's how it works. Let's take for example the polynomial a the prover constructs. This is the interpolation of the first column of the trace matrix T at the domain H. This matrix has all of the left operands of all the gates. The prover wishes to keep them secret. Say the trace matrix T has N rows. H is {1,ω,ω2,…,ωN−1}. The invariant that the prover cannot violate is that ablinded​(ωi) must take the value T0,i​, for all i. This is what the interpolation polynomial a satisfies. And is the unique such polynomial of degree at most N−1 with such property. But for higher degrees, there are many such polynomials. The blinding process takes a and a desired degree M≥N, and produces a new polynomial ablinded​ of degree exactly M. This new polynomial satisfies that ablinded​(ωi)=a(ωi) for all i. But outside H differs from a. This may seem hard but it's actually very simple. Let zH​ be the polynomial zH​=XN−1. If M=N+k, with k≥0, then sample random values b0​,…,bk​ and define ablinded​:=(b0​+b1​X+⋯+bk​Xk)zH​+a The reason why this does the job is that zH​(ωi)=0 for all i. Therefore the added term vanishes at H and leaves the values of a at H unchanged.","breadcrumbs":"Plonk » Protocol » Blindings","id":"20","title":"Blindings"},"21":{"body":"This is an optimization in PLONK to reduce the number of checks of the verifier. One of the main checks in PLONK boils down to check that p(ζ)=zH​(ζ)t(ζ), with p some polynomial that looks like p=aqL​+bqR​+abqM​+⋯, and so on. In particular the verifier needs to get the value p(ζ) from somewhere. For the sake of simplicity, in this section assume p is exactly aqL​+bqR​. Secret to the prover here are only a,b. The polynomials qL​ and qR​ are known also to the verifier. The verifier will already have the commitments [a]1​,[b]1​,[qL​]1​ and [qR​]1​. So the prover could send just a(ζ), b(ζ) along with their opening proofs and let the verifier compute by himself qL​(ζ) and qR​(ζ). Then with all these values the verifier could compute p(ζ)=a(ζ)qL​(ζ)+b(ζ)qR​(ζ). And also use his commitments to validate the opening proofs of a(ζ) and b(ζ). This has the problem that computing qL​(ζ) and qR​(ζ) is expensive. The prover can instead save the verifier this by sending also qL​(ζ),qR​(ζ) along with opening proofs. Since the verifier will have the commitments [qL​]1​ and [qR​]1​ beforehand, he can check that the prover is not cheating and cheaply be convinced that the claimed values are actually qL​(ζ) and qR​(ζ). This is much better. It involves the check of four opening proofs and the computation of p(ζ) off the values received from the prover. But it can be further improved as follows. As before, the prover sends a(ζ),b(ζ) along with their opening proofs. She constructs the polynomial f=a(ζ)qL​+b(ζ)qR​. She sends the value f(ζ) along with an opening proof of it. Notice that the value of f(ζ) is exactly p(ζ). The verifier can compute by himself [f]1​ as a(ζ)[qL​]1​+b(ζ)[qR​]1​. The verifier has everything to check all three openings and get convinced that the claimed value f(ζ) is true. And this value is actually p(ζ). So this means no more work for the verifier. And the whole thing got reduced to three openings. This is called the linearization trick. The polynomial f is called the linearization of p.","breadcrumbs":"Plonk » Protocol » Linearization trick","id":"21","title":"Linearization trick"},"22":{"body":"There's a one time setup phase to compute some values common to any execution and proof of the particular circuit. Precisely, the following commitments are computed and published. [qL​]1​,[qR​]1​,[qM​]1​,[qO​]1​,[qC​]1​,[Sσ1​]1​,[Sσ2​]1​,[Sσ3​]1​","breadcrumbs":"Plonk » Protocol » Setup","id":"22","title":"Setup"},"23":{"body":"Next we describe the proving algorithm for a program of size N. That includes public inputs. Let ω be a primitive N-th root of unity. Let H={1,ω,ω2,…,ωN−1}. Define ZH​:=XN−1. Assume the eight polynomials of common preprocessed input are already given. The prover computes the trace matrix T as described in the first sections. That means, with the first rows corresponding to the public inputs. It should be a N×3 matrix.","breadcrumbs":"Plonk » Protocol » Proving algorithm","id":"23","title":"Proving algorithm"},"24":{"body":"Add to the transcript the following: [Sσ1​]1​,[Sσ2​]1​,[Sσ3]1​,[qL​]1​,[qR​]1​,[qM​]1​,[qO​]1​,[qC​]1​ Compute polynomials a′,b′,c′ as the interpolation polynomials of the columns of T at the domain H. Sample random b1​,b2​,b3​,b4​,b5​,b6​ Let a:=(b1​X+b2​)ZH​+a′ b:=(b3​X+b4​)ZH​+b′ c:=(b5​X+b6​)ZH​+c′ Compute [a]1​,[b]1​,[c]1​ and add them to the transcript.","breadcrumbs":"Plonk » Protocol » Round 1","id":"24","title":"Round 1"},"25":{"body":"Sample β,γ from the transcript. Let z0​=1 and define recursively for 0≤k30000 ms 28745 ms 2^22 >30000 ms >30000 ms 2^23 >30000 ms >30000 ms 2^24 >30000 ms >30000 ms","breadcrumbs":"FFT Library » Benchmarks » Polynomial interpolation methods comparison","id":"3","title":"Polynomial interpolation methods comparison"},"30":{"body":"","breadcrumbs":"Plonk » Protocol » Verification algorithm","id":"30","title":"Verification algorithm"},"31":{"body":"The first step is to initialize the transcript in the same way the prover did, adding to it the following elements. [Sσ1​]1​,[Sσ2​]1​,[Sσ3​]1​,[qL​]1​,[qR​]1​,[qM​]1​,[qO​]1​,[qC​]1​","breadcrumbs":"Plonk » Protocol » Transcript initialization","id":"31","title":"Transcript initialization"},"32":{"body":"Challenges Firstly, the verifier needs to compute all the challenges. For that, he follows these steps: Add [a]1​,[b]1​,[c]1​ to the transcript. Sample two challenges β,γ. Add [z]1​ to the transcript. Sample a challenge α. Add [tlo​]1​,[tmid​]1​,[thi​]1​ to the transcript. Sample a challenge ζ. Add aˉ,bˉ,cˉ,sˉσ1​,sˉσ2​,zˉω​ to the transcript. Sample a challenge υ. Compute p.i(ζ) Also he needs compute a few values off all these data. First, he computes the PI matrix with the public inputs and outputs. He needs to compute pi(ζ), where pi is the interpolation of PI at the domain H. But he doesn't need to compute pi. He can instead compute pi(ζ) as i=0∑n​Li​(ζ)(PI)i​, where n is the number of public inputs and Li​ is the Lagrange basis at the domain H. Compute claimed values of p(ζ) and t(ζ) He computes pˉ​c​:=pi(ζ)+αzˉω​(cˉ+γ)(aˉ+βsˉσ1​+γ)(bˉ+βsˉσ2​+γ)−α2L1​(ζ) This is the constant part of the linearization of p. So adding it to what the prover claims to be pˉ​nc​, he obtains p(ζ)=pˉ​c​+pˉ​nc​ With respect to t(ζ), this is actually already tˉ. Compute [tpartial​]1​ and [pnc​]1​ He computes these off the commitments in the proof as follows [tpartial​]1​=[tlo​]1​+ζN+2[tmid​]1​+ζ2(N+2)[thi​]1​ For [pnc​]1​, first compute [p^​nc1​]1​[p^​nc2​]1​[p^​nc3​]1​​=aˉ[qL​]1​+bˉ[qR​]1​+(aˉbˉ)[qM​]1​+cˉ[qO​]1​+[qC​]1​=(aˉ+βζ+γ)(bˉ+βk1​ζ+γ)(cˉ+βk2​ζ+γ)[z]1​−(aˉ+βsˉσ1​+γ)(bˉ+βsˉσ2​+γ)βzˉω​[Sσ3​]1​=L1​(ζ)[z]1​​ Then [pnc​]1​=[pnc1​]1​+[pnc2​]1​+[pnc3​]1​. Compute claimed value fbatch​(ζ) and [fbatch​]1​ Compute fbatch​(ζ) as fbatch​(ζ)=tˉ+υpˉ​nc​+υ2aˉ+υ3bˉ+υ4cˉ+υ5sˉσ1​+υ6sˉσ2​ Also, the commitment of the polynomial fbatch​ is [fbatch​]1​=[tpartial​]1​+υ[pnc​]1​+υ2[a]1​+υ3[b]1​+υ4[c]1​+υ5[Sσ1​]1​+υ6[Sσ2​]1​","breadcrumbs":"Plonk » Protocol » Extraction of values and commitments","id":"32","title":"Extraction of values and commitments"},"33":{"body":"Now the verifier has all the necessary values to proceed with the checks. Check that p(ζ) equals (ζN−1)t(ζ). Verify the opening of fbatch​ at ζ. That is, check that Verify([fbatch​]1​,πbatch​,ζ,fbatch​(ζ)) outputs Accept . Verify the opening of z at ζω. That is, check the validity of the proof πsingle​ using the commitment [z]1​ and the value zˉω​. That is, check that Verify([z]1​,πsingle​,ζω,zˉω​) outputs Accept . If all checks pass, he outputs Accept . Otherwise outputs Reject .","breadcrumbs":"Plonk » Protocol » Proof check","id":"33","title":"Proof check"},"34":{"body":"In this section we discuss the implementation details of the PLONK algorithm. We use the notation and terminology of the protocol and recap sections. At the moment our API supports the backend of PLONK, that is, all the setup, prove and verify algorithms. We temporarily rely on external sources for the definition of a circuit and the creation of the Q and V matrices, as well as the execution of it to obtain the trace matrix T. We mainly use gnark temporarily for that purpose. To generate proofs and validate them, we need to feed the algorithms with precomputed values of the Q, V and T matrices, and the primitive root of unity ω. Let's see our API on a test circuit that provides all these values. The program in this case is the one that takes an input x, a private input e and computes y=xe+5. As in the toy example of the recap, the output of the program is added to the public inputs and the circuit actually asserts that the output is the claimed value. So more precisely, the prover will generate a proof for the statement ASSERT(x*e+5==y), where both x,y are public inputs.","breadcrumbs":"Plonk » Implementation » Implementation","id":"34","title":"Implementation"},"35":{"body":"Here is the happy path. // This is the common preprocessed input for\n// the test circuit ( ASSERT(x * e + 5 == y) )\nlet common_preprocessed_input = test_common_preprocessed_input_2(); // Input\nlet x = FieldElement::from(2_u64); // Private input\nlet e = FieldElement::from(3_u64); let y, witness = test_witness_2(x, e); let srs = test_srs(common_preprocessed_input.n);\nlet kzg = KZG::new(srs); let verifying_key = setup(&common_preprocessed_input, &kzg); let random_generator = TestRandomFieldGenerator {};\nlet prover = Prover::new(kzg.clone(), random_generator); let public_input = vec![x.clone(), y]; let proof = prover.prove( &witness, &public_input, &common_preprocessed_input, &verifying_key,\n); let verifier = Verifier::new(kzg);\nassert!(verifier.verify( &proof, &public_input, &common_preprocessed_input, &verifying_key\n)); Let's brake it down. The helper function test_common_preprocessed_input_2() returns an instance of the following struct for the particular test circuit: pub struct CommonPreprocessedInput { pub n: usize, pub domain: Vec>, pub omega: FieldElement, pub k1: FieldElement, pub ql: Polynomial>, pub qr: Polynomial>, pub qo: Polynomial>, pub qm: Polynomial>, pub qc: Polynomial>, pub s1: Polynomial>, pub s2: Polynomial>, pub s3: Polynomial>, pub s1_lagrange: Vec>, pub s2_lagrange: Vec>, pub s3_lagrange: Vec>,\n} Apart from the eight polynomials in the canonical basis, we store also here the number of constraints n, the domain H, the primitive n-th of unity ω and the element k1​. The element k2​ will be k12​. For convenience, we also store the polynomials Sσi​ in Lagrange form. The following lines define the particular values of the program input x and the private input e. // Input\nlet x = FieldElement::from(2_u64); // Private input\nlet e = FieldElement::from(3_u64);\nlet y, witness = test_witness_2(x, e); The function test_witness_2(x, e) returns an instance of the following struct, that holds the polynomials that interpolate the columns A,B,C of the trace matrix T. pub struct Witness { pub a: Vec>, pub b: Vec>, pub c: Vec>,\n} Next the commitment scheme KZG (Kate-Zaverucha-Goldberg) is instantiated. let srs = test_srs(common_preprocessed_input.n);\nlet kzg = KZG::new(srs); The setup function performs the setup phase. It only needs the common preprocessed input and the commitment scheme. let verifying_key = setup(&common_preprocessed_input, &kzg); It outputs an instance of the struct VerificationKey: pub struct VerificationKey { pub qm_1: G1Point, pub ql_1: G1Point, pub qr_1: G1Point, pub qo_1: G1Point, pub qc_1: G1Point, pub s1_1: G1Point, pub s2_1: G1Point, pub s3_1: G1Point,\n} It stores the commitments of the eight polynomials of the common preprocessed input. The suffix _1 means it is a commitment. It comes from the notation [f]1​, where f is a polynomial. Then a prover is instantiated let random_generator = TestRandomFieldGenerator {};\nlet prover = Prover::new(kzg.clone(), random_generator); The prover is an instance of the struct Prover: pub struct Prover\nwhere F: IsField, CS: IsCommitmentScheme, R: IsRandomFieldElementGenerator { commitment_scheme: CS, random_generator: R, phantom: PhantomData,\n} It stores an instance of a commitment scheme and a random field element generator needed for blinding polynomials. Then the public input is defined. As we mentioned in the recap, the public input contains the output of the program. let public_input = vec![x.clone(), y]; We then generate a proof using the prover's method prove let proof = prover.prove( &witness, &public_input, &common_preprocessed_input, &verifying_key,\n); The output is an instance of the struct Proof. pub struct Proof> { // Round 1. /// Commitment to the wire polynomial `a(x)` pub a_1: CS::Commitment, /// Commitment to the wire polynomial `b(x)` pub b_1: CS::Commitment, /// Commitment to the wire polynomial `c(x)` pub c_1: CS::Commitment, // Round 2. /// Commitment to the copy constraints polynomial `z(x)` pub z_1: CS::Commitment, // Round 3. /// Commitment to the low part of the quotient polynomial t(X) pub t_lo_1: CS::Commitment, /// Commitment to the middle part of the quotient polynomial t(X) pub t_mid_1: CS::Commitment, /// Commitment to the high part of the quotient polynomial t(X) pub t_hi_1: CS::Commitment, // Round 4. /// Value of `a(ζ)`. pub a_zeta: FieldElement, /// Value of `b(ζ)`. pub b_zeta: FieldElement, /// Value of `c(ζ)`. pub c_zeta: FieldElement, /// Value of `S_σ1(ζ)`. pub s1_zeta: FieldElement, /// Value of `S_σ2(ζ)`. pub s2_zeta: FieldElement, /// Value of `z(ζω)`. pub z_zeta_omega: FieldElement, // Round 5 /// Value of `p_non_constant(ζ)`. pub p_non_constant_zeta: FieldElement, /// Value of `t(ζ)`. pub t_zeta: FieldElement, /// Batch opening proof for all the evaluations at ζ pub w_zeta_1: CS::Commitment, /// Single opening proof for `z(ζω)`. pub w_zeta_omega_1: CS::Commitment,\n} Finally, we instantiate a verifier. let verifier = Verifier::new(kzg); It's an instance of Verifier: struct Verifier> { commitment_scheme: CS, phantom: PhantomData,\n} Finally, we call the verifier's method verify that outputs a bool. assert!(verifier.verify( &proof, &public_input, &common_preprocessed_input, &verifying_key\n));","breadcrumbs":"Plonk » Implementation » Usage","id":"35","title":"Usage"},"36":{"body":"All the matrices Q,V,T,PI are padded with dummy rows so that their length is a power of two. To be able to interpolate their columns, we need a primitive root of unity ω of that order. Given the particular field used in our implementation, that means that the maximum possible size for a circuit is 232. The entries of the dummy rows are filled in with zeroes in the Q, V and PI matrices. The T matrix needs to be consistent with the V matrix. Therefore it is filled with the value of the variable with index 0. Some other rows in the V matrix have also dummy values. These are the rows corresponding to the B and C columns of the public input rows. In the recap we denoted them with the empty - symbol. They are filled in with the same logic as the padding rows, as well as the corresponding values in the T matrix.","breadcrumbs":"Plonk » Implementation » Padding","id":"36","title":"Padding"},"37":{"body":"The implementation pretty much follows the rounds as are described in the protocol section. There are a few details that are worth mentioning.","breadcrumbs":"Plonk » Implementation » Implementation details","id":"37","title":"Implementation details"},"38":{"body":"The commitment scheme we use is the Kate-Zaverucha-Goldberg scheme with the BLS 12 381 curve and the ate pairing. It can be found in the commitments module of the lambdaworks_crypto package. The order r of the cyclic subgroup is 0x73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001 The maximum power of two that divides r−1 is 232. Therefore, that is the maximum possible order for a primitive root of unity in Fr​ with order a power of two.","breadcrumbs":"Plonk » Implementation » Commitment Scheme","id":"38","title":"Commitment Scheme"},"39":{"body":"","breadcrumbs":"Plonk » Implementation » Fiat-Shamir","id":"39","title":"Fiat-Shamir"},"4":{"body":"PLONK is a popular cryptographic proving system within the Zero Knowledge (ZK) community due to its efficiency and flexibility. It enables the verification of complex computations executed by untrusted parties through the transformation of programs into circuit representations. The system relies on a process called arithmetization, which converts logical circuits into polynomial representations. The main idea behind arithmetization is to express the computation as a set of polynomial equations. The solutions to these equations correspond to the outputs of the circuit. In this section, we will delve into the mechanics of how arithmetization works in PLONK, as well as the protocol used to generate and verify proofs. The paper can be found here","breadcrumbs":"Plonk » Recap » PLONK","id":"4","title":"PLONK"},"40":{"body":"Here we describe our implementation of the transcript used for the Fiat-Shamir heuristic. A Transcript exposes two methods: append and challenge. The method append adds a message to the transcript by updating the internal state of the hasher with the raw bytes of the message. The method challenge returns the result of the hasher using the current internal state of the hasher. It subsequently resets the hasher and updates the internal state with the last result. Here is an example of this process: Start a fresh transcript. Call append and pass message_1. Call append and pass message_2. The internal state of the hasher at this point is message_2 || message_1. Call challenge. The output is Hash(message_2 || message_1). Call append and pass message_3. Call challenge. The output is Hash(message_3 || Hash(message_2 || message_1)). Call append and pass message_4. The internal state of the hasher at the end of this exercise is message_4 || Hash(message_3 || Hash(message_2 || message_1)) The underlying hasher function we use is h=sha3.","breadcrumbs":"Plonk » Implementation » Transcript strategy","id":"40","title":"Transcript strategy"},"41":{"body":"The result of every challenge is a 256-bit string, which is interpreted as an integer in big-endian order. A field element is constructed out of it by taking modulo the field order. The prime field used in this implementation has a 255-bit order. Therefore some field elements are more probable to occur than others because they have more representatives as 256-bit integers.","breadcrumbs":"Plonk » Implementation » Field elements","id":"41","title":"Field elements"},"42":{"body":"The first messages added to the transcript are all commitments of the polynomials of the common preprocessed input and the values of the public inputs. This prevents a known vulnerability called \"weak Fiat-Shamir\". Check out the following resources to learn more about it. What can go wrong (zkdocs) How not to Prove Yourself: Pitfalls of the Fiat-Shamir Heuristic and Applications to Helios Weak Fiat-Shamir Attacks on Modern Proof Systems","breadcrumbs":"Plonk » Implementation » Strong Fiat-Shamir","id":"42","title":"Strong Fiat-Shamir"},"43":{"body":"In this section, we'll discuss how to build your own constraint system to prove the execution of a particular program.","breadcrumbs":"Plonk » Circuit API » Circuit API","id":"43","title":"Circuit API"},"44":{"body":"Let's take the following simple program as an example. We have two public inputs: x and y. We want to prove to a verifier that we know a private input e such that x * e = y. You can achieve this by building the following constraint system: use lambdaworks_plonk::constraint_system::ConstraintSystem;\nuse lambdaworks_math::elliptic_curve::short_weierstrass::curves::bls12_381::default_types::FrField; fn main() { let system = &mut ConstraintSystem::::new(); let x = system.new_public_input(); let y = system.new_public_input(); let e = system.new_variable(); let z = system.mul(&x, &e); // This constraint system asserts that x * e == y system.assert_eq(&y, &z);\n} This code creates a constraint system over the field of the BLS12381 curve. Then, it creates three variables: two public inputs x and y, and a private variable e. Note that every variable is private except for the public inputs. Finally, it adds the constraints that represent a multiplication and an assertion. Before generating proofs for this system, we need to run a setup and obtain a verifying key: let common = CommonPreprocessedInput::from_constraint_system(&system, &ORDER_R_MINUS_1_ROOT_UNITY);\nlet srs = test_srs(common.n);\nlet kzg = KZG::new(srs); // The commitment scheme for plonk.\nlet vk = setup(&common, &kzg); Now we can generate proofs for our system. We just need to specify the public inputs and obtain a witness that is a solution for our constraint system: let inputs = HashMap::from([(x, FieldElement::from(4)), (e, FieldElement::from(3))]);\nlet assignments = system.solve(inputs).unwrap();\nlet witness = Witness::new(assignments, &system); Once you have all these ingredients, you can call the prover: let public_inputs = system.public_input_values(&assignments);\nlet prover = Prover::new(kzg.clone(), TestRandomFieldGenerator {});\nlet proof = prover.prove(&witness, &public_inputs, &common, &vk); and verify: let verifier = Verifier::new(kzg);\nassert!(verifier.verify(&proof, &public_inputs, &common, &vk));","breadcrumbs":"Plonk » Circuit API » Simple Example","id":"44","title":"Simple Example"},"45":{"body":"Some operations are common, and it makes sense to wrap the set of constraints that do these operations in a function and use it several times. Lambdaworks comes with a collection of functions to help you build your own constraint systems, such as conditionals, inverses, and hash functions. However, if you have an operation that does not come with Lambdaworks, you can easily extend Lambdaworks functionality. Suppose that the exponentiation operation is something common in your program. You can write the square and multiply algorithm and put it inside a function: pub fn pow( system: &mut ConstraintSystem, base: Variable, exponent: Variable,\n) -> Variable { let exponent_bits = system.new_u32(&exponent); let mut result = system.new_constant(FieldElement::one()); for i in 0..32 { if i != 0 { result = system.mul(&result, &result); } let result_times_base = system.mul(&result, &base); result = system.if_else(&exponent_bits[i], &result_times_base, &result); } result\n} This function can then be used to modify our simple program from the previous section. The following circuit checks that the prover knows e such that pow(x, e) = y: use lambdaworks_plonk::constraint_system::ConstraintSystem;\nuse lambdaworks_math::elliptic_curve::short_weierstrass::curves::bls12_381::default_types::FrField; fn main() { let system = &mut ConstraintSystem::::new(); let x = system.new_public_input(); let y = system.new_public_input(); let e = system.new_variable(); let z = pow(system, &x, &e); system.assert_eq(&y, &z);\n} You can keep composing these functions in order to create more complex systems.","breadcrumbs":"Plonk » Circuit API » Building Complex Systems","id":"45","title":"Building Complex Systems"},"46":{"body":"The goal of this document is to give a good a understanding of our stark prover code. To this end, in the first section we go through a recap of how the proving system works at a high level mathematically; then we dive into how that's actually implemented in our code.","breadcrumbs":"STARKs » STARK Prover","id":"46","title":"STARK Prover"},"47":{"body":"","breadcrumbs":"STARKs » Recap » STARKs Recap","id":"47","title":"STARKs Recap"},"48":{"body":"In general, we express computation in our proving system by providing an execution trace satisfying certain constraints . The execution trace is a table containing the state of the system at every step of computation. This computation needs to follow certain rules to be valid; these rules are our constraints . The constraints for our computation are expressed using an Algebraic Intermediate Representation or AIR. This representation uses polynomials to encode constraints, which is why sometimes they are called polynomial constraints. To make all this less abstract, let's go through two examples.","breadcrumbs":"STARKs » Recap » Verifying Computation through Polynomials","id":"48","title":"Verifying Computation through Polynomials"},"49":{"body":"Throughout this section and the following we will use this example extensively to have a concrete example. Even though it's a bit contrived (no one cares about computing fibonacci numbers), it's simple enough to be useful. STARKs and proving systems in general are very abstract things; having an example in mind is essential to not get lost. Let's say our computation consists of calculating the k-th number in the fibonacci sequence. This is just the sequence of numbers \\(a_n\\) satisfying \\[ a_0 = 1 \\] \\[ a_1 = 1 \\] \\[ a_{n+2} = a_{n + 1} + a_n \\] An execution trace for this just consists of a table with one column, where each row is the i-th number in the sequence: a_i 1 1 2 3 5 8 13 21 A valid trace for this computation is a table satisfying two things: The first two rows are 1. The value on any other row is the sum of the two preceding ones. The first item is called a boundary constraint, it just enforces specific values on the trace at certain points. The second one is a transition constraint; it tells you how to go from one step of computation to the next.","breadcrumbs":"STARKs » Recap » Fibonacci numbers","id":"49","title":"Fibonacci numbers"},"5":{"body":"We use the following notation. The symbol F denotes a finite field. It is fixed all along. The symbol ω denotes a primitive root of unity in F. All polynomials have coefficients in F and the variable is usually denoted by X. We denote polynomials by single letters like p,a,b,z. We only denote them as z(X) when we want to emphasize the fact that it is a polynomial in X, or we need that to explicitly define a polynomial from another one. For example when composing a polynomial z with the polynomial ωX, the result being denoted by z′:=z(ωX). The symbol ′ is not used to denote derivatives. When interpolating at a domain H={h0​,…,hn​}⊂F, the symbols Li​ denote the Lagrange basis. That is Li​ is the polynomial such that Li​(hj​)=0 for all j=i, and that Li​(hi​)=1. If M is a matrix, then Mi,j​ denotes the value at the row i and column j.","breadcrumbs":"Plonk » Recap » Notation","id":"5","title":"Notation"},"50":{"body":"The example above is extremely useful to have a mental model, but it's not really useful for anything else. The problem is it just works for the very narrow example of computing fibonacci numbers. If we wanted to prove execution of something else, we would have to write an AIR for it. What we're actually aiming for is an AIR for an entire general purpose Virtual Machine. This way, we can provide proofs of execution for any computation using just one AIR. This is what cairo as a programming language does. Cairo code compiles to the bytecode of a virtual machine with an already defined AIR. The general flow when using cairo is the following: User writes a cairo program. The program is compiled into Cairo's VM bytecode. The VM executes said code and provides an execution trace for it. The trace is passed on to a STARK prover, which creates a proof of correct execution according to Cairo's AIR. The proof is passed to a verifier, who checks that the proof is valid. Ultimately, our goal is to give the tools to write a STARK prover for the cairo VM and do so. However, this is not a good example to start out as it's incredibly complex. The execution trace of a cairo program has around 30 columns, some for general purpose registers, some for other reasons. Cairo's AIR contains a lot of different transition constraints, encoding all the different possible instructions (arithmetic operations, jumps, etc). Use the fibonacci example as your go-to for understanding all the moving parts; keep the Cairo example in mind as the thing we are actually building towards.","breadcrumbs":"STARKs » Recap » Cairo","id":"50","title":"Cairo"},"51":{"body":"Below we go through a step by step explanation of a STARK prover. We will assume the trace of the fibonacci sequence mentioned above; it consists of only one column of length \\(2^n\\). In this case, we'll take n=3. The trace looks like this a_i a_0 a_1 a_2 a_3 a_4 a_5 a_6 a_7","breadcrumbs":"STARKs » Recap » Fibonacci step by step walkthrough","id":"51","title":"Fibonacci step by step walkthrough"},"52":{"body":"The first step is to interpolate these values to generate the trace polynomial. This will be a polynomial encoding all the information about the trace. The way we do it is the following: in the finite field we are working in, we take an 8-th primitive root of unity, let's call it g. It being a primitive root means two things: g is an 8-th root of unity, i.e., \\(g^8 = 1\\). Every 8-th root of unity is of the form \\(g^i\\) for some \\(0 \\leq i \\leq 7\\). With g in hand, we take the trace polynomial t to be the one satisfying t(gi)=ai​ From here onwards, we will talk about the validity of the trace in terms of properties that this polynomial must satisfy. We will also implicitly identify a certain power of \\(g\\) with its corresponding trace element, so for example we sometimes think of \\(g^5\\) as \\(a_5\\), the fifth row in the trace, even though technically it's \\(t\\) evaluated in \\(g^5\\) that equals \\(a_5\\). We talked about two different types of constraints the trace must satisfy to be valid. They were: The first two rows are 1. The value on any other row is the sum of the two preceding ones. In terms of t, this translates to \\(t(g^0) = 1\\) and \\(t(g) = 1\\). \\(t(x g^2) - t(xg) - t(x) = 0\\) for all \\(x \\in {g^0, g^1, g^2, g^3, g^4, g^5}\\). This is because multiplying by g is the same as advancing a row in the trace.","breadcrumbs":"STARKs » Recap » Trace polynomial","id":"52","title":"Trace polynomial"},"53":{"body":"To convince the verifier that the trace polynomial satisfies the relationships above, the prover will construct another polynomial that shows that both the boundary and transition constraints are satisfied and commit to it. We call this polynomial the composition polynomial, and usually denote it with \\(H\\). Constructing it involves a lot of different things, so we'll go step by step introducing all the moving parts required.","breadcrumbs":"STARKs » Recap » Composition Polynomial","id":"53","title":"Composition Polynomial"},"54":{"body":"To show that the boundary constraints are satisfied, we construct the boundary polynomial. Recall that our boundary constraints are \\(t(g^0) = t(g) = 1\\). Let's call \\(P\\) the polynomial that interpolates these constraints, that is, \\(P\\) satisfies: P(1)=1P(g)=1 The boundary polynomial \\(B\\) is defined as follows: B(x)=(x−1)(x−g)t(x)−P(x)​ The denominator here is called the boundary zerofier, and it's the polynomial whose roots are the elements of the trace where the boundary constraints must hold. How does \\(B\\) encode the boundary constraints? The idea is that, if the trace satisfies said constraints, then t(1)−P(1)=1−1=0 t(g)−P(g)=1−1=0 so \\(t(x) - P(x)\\) has \\(1\\) and \\(g\\) as roots. Showing these values are roots is the same as showing that \\(B(x)\\) is a polynomial instead of a rational function, and that's why we construct \\(B\\) this way.","breadcrumbs":"STARKs » Recap » Boundary polynomial","id":"54","title":"Boundary polynomial"},"55":{"body":"To convince the verifier that the transition constraints are satisfied, we construct the transition constraint polynomial and call it \\(C(x)\\). It's defined as follows: C(x)=∏i=05​(x−gi)t(xg2)−t(xg)−t(x)​ How does \\(C\\) encode the transition constraints? We mentioned above that these are satisfied if the polynomial in the numerator vanishes in the elements \\({g^0, g^1, g^2, g^3, g^4, g^5}\\). As with \\(B\\), this is the same as showing that \\(C(x)\\) is a polynomial instead of a rational function.","breadcrumbs":"STARKs » Recap » Transition constraint polynomial","id":"55","title":"Transition constraint polynomial"},"56":{"body":"With the boundary and transition constraint polynomials in hand, we build the composition polynomial \\(H\\) as follows: The verifier will sample four numbers \\(\\beta_1, \\beta_2\\) and \\(H\\) will be H(x)=β1​B(x)+β2​C(x) Why not just take \\(H(x) = B(x) + C(x)\\)? The reason for the betas is to make the resulting \\(H\\) be always different and unpredictable for the prover, so they can't precompute stuff beforehand. With what we discussed above, showing that the constraints are satisfied is equivalent to saying that H is a polynomial and not a rational function (we are simplifying things a bit here, but it works for our purposes).","breadcrumbs":"STARKs » Recap » Constructing \\(H\\)","id":"56","title":"Constructing \\(H\\)"},"57":{"body":"To show \\(H\\) is a polynomial we are going to use the FRI protocol, which we treat as a black box. For all we care, a FRI proof will verify if what we committed to is indeed a polynomial. Thus, the prover will provide a FRI commitment to H, and if it passes, the verifier will be convinced that the constraints are satisfied. There is one catch here though: how does the verifier know that FRI was applied to H and not any other polynomial? For this we need to add an additional step to the protocol.","breadcrumbs":"STARKs » Recap » Commiting to \\(H\\)","id":"57","title":"Commiting to \\(H\\)"},"58":{"body":"After commiting to H, the prover needs to show that H was constructed correctly according to the formula above. To do this, it will ask the prover to provide an evaluation of H on some random point z and evaluations of the trace at the points \\(t(z), t(zg)\\) and \\(t(zg^2)\\). Because the boundary and transition constraints are a public part of the protocol, the verifier knows them, and thus the only thing it needs to compute the evaluation \\((z)\\) by itself are the three trace evaluations mentioned above. Because it asked the prover for them, it can check both sides of the equation: H(z)=β1​B(z)+β2​C(z) and be convinced that \\(H\\) was constructed correctly. We are still not done, however, as the prover could have now cheated on the values of the trace or composition polynomial evaluations.","breadcrumbs":"STARKs » Recap » Consistency check","id":"58","title":"Consistency check"},"59":{"body":"There are two things left the prover needs to show to complete the proof: That \\(H\\) effectively is a polynomial, i.e., that the constraints are satisfied. That the evaluations the prover provided on the consistency check were indeed evaluations of the trace polynomial and composition polynomial on the out of domain point z. Earlier we said we would use the FRI protocol to commit to H and show the first item in the list. However, we can slightly modify the polynomial we do FRI on to show both the first and second items at the same time. This new modified polynomial is called the DEEP composition polynomial. We define it as follows: Deep(x)=γ1​x−zH(x)−H(z)​+γ2​x−zt(x)−t(z)​+γ3​x−zgt(x)−t(zg)​+γ4​x−zg2t(x)−t(zg2)​ where the numbers \\(\\gamma_i\\) are randomly sampled by the verifier. The high level idea is the following: If we apply FRI to this polynomial and it verifies, we are simultaneously showing that \\(H\\) is a polynomial and the prover indeed provided H(z) as one of the out of domain evaluations. This is the first summand in Deep(x). The trace evaluations provided by the prover were the correct ones, i.e., they were \\(t(z)\\), \\(t(zg)\\), and \\(t(zg^2)\\). These are the remaining summands of the Deep(x).","breadcrumbs":"STARKs » Recap » Deep Composition Polynomial","id":"59","title":"Deep Composition Polynomial"},"6":{"body":"","breadcrumbs":"Plonk » Recap » The ideas and components","id":"6","title":"The ideas and components"},"60":{"body":"The prover needs to show that Deep was constructed correctly according to the formula above. To do this, the verifier will ask the prover to provide: An evaluation of H on z and x_0 Evaluations of the trace at the points \\(t(z)\\), \\(t(zg)\\), \\(t(zg^2)\\) and \\(t(x_0)\\) Where z is the same random, out of domain point used in the consistency check of the composition polynomial, and x_0 is a random point that belongs to the trace domain. With the values provided by the prover, the verifier can check both sides of the equation: Deep(x0​)=γ1​x0​−zH(x0​)−H(z)​+γ2​x0​−zt(x0​)−t(z)​+γ3​x0​−zgt(x0​)−t(zg)​+γ4​x0​−zg2t(x0​)−t(zg2)​ The prover also needs to show that the trace evaluation \\(t(x_0)\\) belongs to the trace. To achieve this, it needs to commit the merkle roots of t and the merkle proof of \\(t(x_0)\\).","breadcrumbs":"STARKs » Recap » Consistency check","id":"60","title":"Consistency check"},"61":{"body":"We summarize below the steps required in a STARK proof for both prover and verifier.","breadcrumbs":"STARKs » Recap » Summary","id":"61","title":"Summary"},"62":{"body":"Compute the trace polynomial t by interpolating the trace column over a set of \\(2^n\\)-th roots of unity \\({g^i : 0 \\leq i < 2^n}\\). Compute the boundary polynomial B. Compute the transition constraint polynomial C. Construct the composition polynomial H from B and C. Sample an out of domain point z and provide the evaluations \\(H(z)\\), \\(t(z)\\), \\(t(zg)\\), and \\(t(zg^2)\\) to the verifier. Sample a domain point x_0 and provide the evaluations \\(H(x_0)\\) and \\(t(x_0)\\) to the verifier. Construct the deep composition polynomial Deep(x) from H, t, and the evaluations from the item above. Do FRI on Deep(x) and provide the resulting FRI commitment to the verifier. Provide the merkle root of t and the merkle proof of \\(t(x_0)\\).","breadcrumbs":"STARKs » Recap » Prover side","id":"62","title":"Prover side"},"63":{"body":"Take the evaluations \\(H(z)\\), \\(H(x_0)\\), \\(t(z)\\), \\(t(zg)\\), \\(t(zg^2)\\) and \\(t(x_0)\\) the prover provided. Reconstruct the evaluations \\(B(z)\\) and \\(C(z)\\) from the trace evaluations we were given. Check that the claimed evaluation \\(H(z)\\) the prover gave us actually satisfies H(z)=β1​B(z)+β2​C(z) Check that the claimed evaluation \\(Deep(x_0)\\) the prover gave us actually satisfies Deep(x0​)=γ1​x0​−zH(x0​)−H(z)​+γ2​x0​−zt(x0​)−t(z)​+γ3​x0​−zgt(x0​)−t(zg)​+γ4​x0​−zg2t(x0​)−t(zg2)​ Using the merkle root and the merkle proof the prover provided, check that \\(t(x_0)\\) belongs to the trace. Take the provided FRI commitment and check that it verifies.","breadcrumbs":"STARKs » Recap » Verifier side","id":"63","title":"Verifier side"},"64":{"body":"The walkthrough above was for the fibonacci example which, because of its simplicity, allowed us to sweep under the rug a few more complexities that we'll have to tackle on the implementation side. They are:","breadcrumbs":"STARKs » Recap » Simplifications and Omissions","id":"64","title":"Simplifications and Omissions"},"65":{"body":"Our trace contained only one column, but in the general setting there can be multiple (the Cairo AIR has around 30). This means there isn't just one trace polynomial, but several; one for each column. This also means there are multiple boundary constraint polynomials. The general idea, however, remains the same. The deep composition polynomial H is now the sum of several terms containing the boundary constraint polynomials \\(B_1(x), \\dots, B_k(x)\\) (one per column), and each \\(B_i\\) is in turn constructed from the \\(i\\)-th trace polynomial \\(t_i(x)\\).","breadcrumbs":"STARKs » Recap » Multiple trace columns","id":"65","title":"Multiple trace columns"},"66":{"body":"Much in the same way, our fibonacci AIR had only one transition constraint, but there could be several. We will therefore have multiple transition constraint polynomials \\(C_1(x), \\dots, C_n(x)\\), each of which encodes a different relationship between rows that must be satisfied. Also, because there are multiple trace columns, a transition constraint can mix different trace polynomials. One such constraint could be C1​(x)=t1​(gx)−t2​(x) which means \"The first column on the next row has to be equal to the second column in the current row\". Again, even though this seems way more complex, the ideas remain the same. The composition polynomial H will now include a term for every \\(C_i(x)\\), and for each one the prover will have to provide out of domain evaluations of the trace polynomials at the appropriate values. In our example above, to perform the consistency check on \\(C_1(x)\\) the prover will have to provide the evaluations \\(t_1(zg)\\) and \\(t_2(z)\\).","breadcrumbs":"STARKs » Recap » Multiple transition constraints","id":"66","title":"Multiple transition constraints"},"67":{"body":"In the actual implementation, we won't commit to \\(H\\), but rather to a decomposition of \\(H\\) into an even term \\(H_1(x)\\) and an odd term \\(H_2(x)\\), which satisfy H(x)=H1​(x2)+xH2​(x2) This way, we don't commit to \\(H\\) but to \\(H_1\\) and \\(H_2\\). This is just an optimization at the code level; once again, the ideas remain exactly the same.","breadcrumbs":"STARKs » Recap » Composition polynomial decomposition","id":"67","title":"Composition polynomial decomposition"},"68":{"body":"We treated FRI as a black box entirely. However, there is one thing we do need to understand about it: low degree extensions. When applying FRI to a polynomial of degree \\(n\\), we need to provide evaluations of it over a domain with more than \\(n\\) points. In our case, the DEEP composition polynomial's degree is around the same as the trace's, which is, at most, \\(2^n - 1\\) (because it interpolates the trace containing \\(2^n\\) points). The domain we are going to choose to evaluate our DEEP polynomial on will be a set of higher roots of unity. In our fibonacci example, we will take a primitive \\(16\\)-th root of unity \\(\\omega\\). As a reminder, this means: \\(\\omega\\) is an \\(16\\)-th root of unity, i.e., \\(\\omega^{16} = 1\\). Every \\(16\\)-th root of unity is of the form \\(\\omega^i\\) for some \\(0 \\leq i \\leq 15\\). Additionally, we also take it so that \\(\\omega\\) satisfies \\(\\omega^2 = g\\) (\\(g\\) being the \\(8\\)-th primitive root of unity we used to construct t). The evaluation of \\(t\\) on the set \\({\\omega^i : 0 \\leq i \\leq 15}\\) is called a low degree extension (LDE) of \\(t\\). Notice this is not a new polynomial, they're evaluations of \\(t\\) on some set of points. Also note that, because \\(\\omega^2 = g\\), the LDE contains all the evaluations of \\(t\\) on the set of powers of \\(g\\). In fact, {t(ω2i):0≤i≤15}={t(gi):0≤i≤7} This will be extremely important when we get to implementation. For our LDE, we chose \\(16\\)-th roots of unity, but we could have chosen any other power of two greater than \\(8\\). In general, this choice is called the blowup factor, so that if the trace has \\(2^n\\) elements, a blowup factor of \\(b\\) means our LDE evaluates over the \\(2^{n} * b\\) roots of unity (\\(b\\) needs to be a power of two). The blowup factor is a parameter of the protocol related to its security.","breadcrumbs":"STARKs » Recap » FRI, low degree extensions and roots of unity","id":"68","title":"FRI, low degree extensions and roots of unity"},"69":{"body":"In this section, we start diving deeper before showing the formal protocol. If you haven't done so, we recommend reading the \"Recap\" section first. At a high level, the protocol works as follows. The starting point is a matrix T that encodes the trace of a valid execution of the program. This matrix needs to be in a particular format so that its correctness is equivalent to checking a finite number of polynomial equations on its rows. Transforming the execution to this matrix is what's called the arithmetization process. Then a single polynomial F is constructed that encodes the set of all the polynomial constraints. The satisfiability of all these constraints is equivalent to F being divisible by some public polynomial G. So the prover constructs H as the quotient F/G called the composition polynomial. Then the verifier chooses a random point z and challenges the prover to reveal the values F(z) and H(z). Then the verifier checks that H(z)=F(z)/G(z), which convinces him that the same relation holds at a level of polynomials and, in consequence, convinces the verifier that the private trace T of the prover is valid. In summary, at a very high level, the STARK protocol can be organized into three major parts: Arithmetization and commitment of execution trace. Construction and commitment of composition polynomial H. Opening of polynomials at random z.","breadcrumbs":"STARKs » Protocol overview » Protocol Overview","id":"69","title":"Protocol Overview"},"7":{"body":"For better clarity, we'll be using the following toy program throughout this recap. INPUT: x PRIVATE INPUT: e OUTPUT: e * x + x - 1 The observer would have noticed that this program could also be written as (e+1)∗x−1, which is more sensible. But the way it is written now serves us to better explain the arithmetization of PLONK. So we'll stick to it. The idea here is that the verifier holds some value x, say x=3. He gives it to the prover. She executes the program using her own chosen value e, and sends the output value, say 8, along with a proof π demonstrating correct execution of the program and obtaining the correct output. In the context of PLONK, both the inputs and outputs of the program are considered public inputs . This may sound odd, but it is because these are the inputs to the verification algorithm. This is the algorithm that takes, in this case, the tuple (3,8,π) and outputs Accept if the toy program was executed with input x=3, some private value e not revealed to the verifier, and out came 8. Otherwise it outputs Reject . PLONK can be used to delegate program executions to untrusted parties, but it can also be used as a proof of knowledge. Our program could be used by a prover to demostrate that she knows the multiplicative inverse of some value x in the finite field without revealing it. She would do it by sending the verifier the tuple (x,0,π), where π is the proof of the execution of our toy program. In our toy example this is pointless because inverting field elements is easily performed by any verifier. But change our program to the following and you get proofs of knowledge of the preimage of SHA256 digests. PRIVATE INPUT: e OUTPUT: SHA256(e) Here there's no input aside from the prover's private input. As we mentioned, the output h of the program is then part of the inputs to the verification algorithm. Which in this case just takes (h,π).","breadcrumbs":"Plonk » Recap » Programs. Our toy example","id":"7","title":"Programs. Our toy example"},"70":{"body":"As the Recap mentions, the trace is a table containing the system's state at every step. In this section, we will denote the trace as T. A trace can have several columns to store different aspects or features of a particular state at a specific moment. We will refer to the j-th column as Tj​. You can think of a trace as a matrix T where the entry Tij​ is the j-th element of the i-th state. Most proving systems' primary tool is polynomials over a finite field F. Each column Tj​ of the trace T will be interpreted as evaluations of such a polynomial tj​. Consequently, any information about the states must be encoded somehow as an element in F. To ease notation, we will assume here and in the protocol that the constraints encoding transition rules depend only on a state and the previous one. Everything can be easily generalized to transitions that depend on many preceding states. Then, constraints can be expressed as multivariate polynomials in 2m variables PkT​(X1​,…,Xm​,Y1​,…,Ym​) A transition from state i to state i+1 will be valid if and only if when we plug row i of T in the first m variables and row i+1 in the second m variables of PkT​, we get 0 for all k. In mathematical notation, this is PkT​(Ti,0​,…,Ti,m​,Ti+1,0​,…,Ti+1,m​)=0 for all k These are called transition constraints and check the trace's local properties, where local means relative to specific rows. There is another type of constraint, called boundary constraint , and denoted PjB​. These enforce parts of the trace to take particular values. It is helpful, for example, to verify the initial states. So far, these constraints can only express the local properties of the trace. There are situations where the global properties of the trace need to be checked for consistency. For example, a column may need to take all values in a range but not in any predefined way. Several methods exist to express these global properties as local by adding redundant columns. Usually, they need to involve randomness from the verifier to make sense, and they turn into an interactive protocol called Randomized AIR with Preprocessing .","breadcrumbs":"STARKs » Protocol overview » Arithmetization","id":"70","title":"Arithmetization"},"71":{"body":"To make interactions possible, a crucial cryptographic primitive is the Polynomial Commitment Scheme. This prevents the prover from changing the polynomials to adjust them to what the verifier expects. Such a scheme consists of the commit and the open protocols. STARK uses a univariate polynomial commitment scheme that internally combines a vector commitment scheme and a protocol called FRI. Let's begin with these two components and see how they build up the polynomial commitment scheme.","breadcrumbs":"STARKs » Protocol overview » Polynomial commitment scheme","id":"71","title":"Polynomial commitment scheme"},"72":{"body":"Given a vector Y=(y0​,…,yM​), commiting to Y means the following. The prover builds a Merkle tree out of it and sends its root to the verifier. The verifier can then ask the prover to reveal, or open , the value of the vector Y at some index i. The prover won't have any choice except to send the correct value. The verifier will expect the corresponding value yi​ and the authentication path to the tree's root to check its authenticity. The authentication path also encodes the vector's position i and its length M. The root of the Merkle tree is said to be the commitment of Y, and we denote it here by [Y].","breadcrumbs":"STARKs » Protocol overview » Vector commitments","id":"72","title":"Vector commitments"},"73":{"body":"In STARKs, all commited vectors are of the form Y=(p(d1​),…,p(dM​)) for some polynomial p and some fixed domain D=(d1​,…,dM​). The domain is always known to the prover and the verifier. It can be proved, as long as M is less than the total number of field elements, that every vector (y0​,…,yM​) is equal to (p(d1​),…,p(dM​)) for a unique polynomial p of degree at most M−1. This is called the Lagrange interpolation theorem. It means, there is a unique polynomial of degree at most M−1 such that p(di​)=yi​ for all i. And M−1 is an upper bound to the degree of p. It could be less. For example, the vector of all ones Y=(1,1,…,1) is the evaluation of the constant polynomial p=1, which has degree 0. Suppose the vector Y=(y1​,…,yM​) is the vector of evaluations of a polynomial p of degree strictly less than M−1. Suppose one party holds the vector Y and another party holds only the commitment [Y] of it. The FRI protocol is an efficient interactive protocol with which the former can convince the latter that the commitment they hold corresponds to the vector of evaluations of a polynomial p of degree strictly less than M. More precisely, the protocol depends on the following parameters Powers of two N=2n and M=2m with n":{":":{":":{"df":0,"docs":{},"n":{"df":0,"docs":{},"e":{"df":0,"docs":{},"w":{"df":2,"docs":{"44":{"tf":1.0},"45":{"tf":1.0}}}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}}}}}},"df":0,"docs":{}},"df":0,"docs":{}},"<":{"df":0,"docs":{},"f":{"df":0,"docs":{},"r":{"df":0,"docs":{},"f":{"df":0,"docs":{},"i":{"df":0,"docs":{},"e":{"df":0,"docs":{},"l":{"d":{"df":1,"docs":{"45":{"tf":1.0}}},"df":0,"docs":{}}}}}}}},"df":0,"docs":{}}}}}}}}}}},"df":0,"docs":{},"u":{"c":{"df":0,"docs":{},"t":{"df":30,"docs":{"112":{"tf":1.4142135623730951},"113":{"tf":1.0},"114":{"tf":2.0},"117":{"tf":1.0},"118":{"tf":1.0},"12":{"tf":1.0},"125":{"tf":1.0},"14":{"tf":1.4142135623730951},"142":{"tf":1.4142135623730951},"144":{"tf":1.0},"145":{"tf":1.0},"146":{"tf":2.449489742783178},"16":{"tf":1.4142135623730951},"20":{"tf":1.0},"21":{"tf":1.0},"41":{"tf":1.0},"53":{"tf":1.4142135623730951},"54":{"tf":1.4142135623730951},"55":{"tf":1.0},"56":{"tf":1.0},"58":{"tf":1.4142135623730951},"60":{"tf":1.0},"62":{"tf":1.4142135623730951},"65":{"tf":1.0},"68":{"tf":1.0},"69":{"tf":1.7320508075688772},"84":{"tf":1.4142135623730951},"85":{"tf":1.4142135623730951},"91":{"tf":1.0},"94":{"tf":1.0}}}},"df":0,"docs":{}}}}},"t":{"a":{"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"df":8,"docs":{"13":{"tf":1.0},"146":{"tf":1.0},"35":{"tf":1.0},"48":{"tf":1.0},"50":{"tf":1.0},"65":{"tf":1.4142135623730951},"68":{"tf":1.4142135623730951},"70":{"tf":1.0}}}}},"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"df":0,"docs":{},"t":{"df":1,"docs":{"133":{"tf":1.0}}}},"x":{"df":0,"docs":{},"t":{"df":5,"docs":{"109":{"tf":1.4142135623730951},"14":{"tf":1.0},"7":{"tf":1.0},"80":{"tf":1.0},"84":{"tf":1.0}}}}},"i":{"df":0,"docs":{},"n":{"df":0,"docs":{},"u":{"df":4,"docs":{"102":{"tf":1.0},"130":{"tf":1.0},"134":{"tf":1.0},"146":{"tf":2.0}}}}},"r":{"df":0,"docs":{},"i":{"df":0,"docs":{},"v":{"df":1,"docs":{"49":{"tf":1.0}}}},"o":{"df":0,"docs":{},"l":{"df":1,"docs":{"118":{"tf":1.0}}}}}},"v":{"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"df":0,"docs":{},"i":{"df":2,"docs":{"35":{"tf":1.0},"92":{"tf":1.0}}},"t":{"df":1,"docs":{"95":{"tf":1.0}}}},"r":{"df":0,"docs":{},"s":{"df":1,"docs":{"14":{"tf":1.4142135623730951}}},"t":{"df":2,"docs":{"14":{"tf":1.0},"4":{"tf":1.0}}}}},"i":{"df":0,"docs":{},"n":{"c":{"df":9,"docs":{"21":{"tf":1.4142135623730951},"53":{"tf":1.0},"55":{"tf":1.0},"57":{"tf":1.0},"58":{"tf":1.0},"69":{"tf":1.4142135623730951},"73":{"tf":1.0},"75":{"tf":1.0},"80":{"tf":1.0}}},"df":0,"docs":{}}}}},"o":{"df":0,"docs":{},"r":{"d":{"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"df":4,"docs":{"14":{"tf":1.4142135623730951},"73":{"tf":1.0},"79":{"tf":1.0},"92":{"tf":1.0}}}}},"df":0,"docs":{}}},"p":{"df":0,"docs":{},"i":{"df":3,"docs":{"142":{"tf":1.7320508075688772},"146":{"tf":1.7320508075688772},"35":{"tf":1.0}}},"r":{"df":0,"docs":{},"i":{"df":0,"docs":{},"m":{"df":1,"docs":{"124":{"tf":1.0}}}}}},"r":{"df":0,"docs":{},"e":{"df":1,"docs":{"144":{"tf":1.0}}},"r":{"df":0,"docs":{},"e":{"c":{"df":0,"docs":{},"t":{"df":6,"docs":{"151":{"tf":1.0},"50":{"tf":1.0},"59":{"tf":1.0},"69":{"tf":1.0},"7":{"tf":1.4142135623730951},"72":{"tf":1.0}},"l":{"df":0,"docs":{},"i":{"df":7,"docs":{"104":{"tf":1.0},"117":{"tf":1.0},"146":{"tf":1.0},"151":{"tf":1.0},"58":{"tf":1.4142135623730951},"60":{"tf":1.0},"84":{"tf":1.0}}}}}},"df":0,"docs":{},"s":{"df":0,"docs":{},"p":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"d":{"df":18,"docs":{"10":{"tf":2.0},"108":{"tf":1.0},"11":{"tf":1.4142135623730951},"13":{"tf":1.0},"134":{"tf":1.0},"136":{"tf":1.0},"14":{"tf":1.0},"142":{"tf":1.4142135623730951},"146":{"tf":2.23606797749979},"149":{"tf":1.7320508075688772},"16":{"tf":1.4142135623730951},"23":{"tf":1.0},"36":{"tf":1.4142135623730951},"4":{"tf":1.0},"52":{"tf":1.0},"72":{"tf":1.0},"73":{"tf":1.0},"75":{"tf":1.0}}},"df":0,"docs":{}}}}}}}},"s":{"df":0,"docs":{},"e":{"df":0,"docs":{},"t":{"_":{"df":0,"docs":{},"o":{"df":0,"docs":{},"f":{"df":0,"docs":{},"f":{"df":0,"docs":{},"s":{"df":0,"docs":{},"e":{"df":0,"docs":{},"t":{"df":1,"docs":{"109":{"tf":1.0}}}}}}}}},"df":3,"docs":{"109":{"tf":1.0},"125":{"tf":2.23606797749979},"91":{"tf":1.0}}}},"t":{"df":1,"docs":{"151":{"tf":1.0}},"l":{"df":0,"docs":{},"i":{"df":1,"docs":{"80":{"tf":1.0}}}}}},"u":{"df":0,"docs":{},"l":{"d":{"df":0,"docs":{},"n":{"'":{"df":0,"docs":{},"t":{"df":1,"docs":{"103":{"tf":1.0}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"p":{"df":0,"docs":{},"l":{"df":1,"docs":{"105":{"tf":1.0}}}},"r":{"df":0,"docs":{},"s":{"df":3,"docs":{"12":{"tf":1.0},"125":{"tf":1.0},"14":{"tf":1.0}}}}}},"p":{"df":0,"docs":{},"u":{"_":{"a":{"df":0,"docs":{},"i":{"df":0,"docs":{},"r":{"_":{"d":{"df":0,"docs":{},"e":{"df":0,"docs":{},"f":{"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"df":0,"docs":{},"i":{"df":0,"docs":{},"t":{"df":0,"docs":{},"i":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"1":{"0":{".":{"df":0,"docs":{},"h":{"df":1,"docs":{"127":{"tf":1.0}}},"i":{"df":0,"docs":{},"n":{"df":0,"docs":{},"l":{"df":1,"docs":{"127":{"tf":1.0}}}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}}}}}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":1,"docs":{"3":{"tf":2.0}}}},"r":{"a":{"df":0,"docs":{},"f":{"df":0,"docs":{},"t":{"df":1,"docs":{"79":{"tf":1.0}}}},"t":{"df":0,"docs":{},"e":{"df":1,"docs":{"1":{"tf":1.0}}}}},"df":0,"docs":{},"e":{"a":{"df":0,"docs":{},"t":{"df":5,"docs":{"108":{"tf":1.0},"146":{"tf":1.0},"44":{"tf":1.4142135623730951},"45":{"tf":1.0},"50":{"tf":1.0}},"i":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"df":1,"docs":{"34":{"tf":1.0}}}}}}},"df":0,"docs":{}},"i":{"df":0,"docs":{},"t":{"df":0,"docs":{},"i":{"c":{"df":1,"docs":{"146":{"tf":1.0}}},"df":0,"docs":{}}}},"u":{"c":{"df":0,"docs":{},"i":{"a":{"df":0,"docs":{},"l":{"df":1,"docs":{"71":{"tf":1.0}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"y":{"df":0,"docs":{},"p":{"df":0,"docs":{},"t":{"df":0,"docs":{},"o":{"df":1,"docs":{"1":{"tf":1.0}},"g":{"df":0,"docs":{},"r":{"a":{"df":0,"docs":{},"p":{"df":0,"docs":{},"h":{"df":3,"docs":{"19":{"tf":1.0},"4":{"tf":1.0},"71":{"tf":1.0}}}}},"df":0,"docs":{}}}}}}}},"s":{":":{":":{"c":{"df":0,"docs":{},"o":{"df":0,"docs":{},"m":{"df":0,"docs":{},"m":{"df":0,"docs":{},"i":{"df":0,"docs":{},"t":{"df":1,"docs":{"35":{"tf":3.0}}}}}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":1,"docs":{"35":{"tf":2.449489742783178}}},"u":{"df":0,"docs":{},"m":{"df":1,"docs":{"139":{"tf":1.0}},"u":{"df":0,"docs":{},"l":{"df":3,"docs":{"138":{"tf":1.4142135623730951},"139":{"tf":1.0},"146":{"tf":1.7320508075688772}}}}},"r":{"df":0,"docs":{},"r":{"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"df":0,"docs":{},"t":{"df":7,"docs":{"107":{"tf":2.23606797749979},"109":{"tf":1.0},"121":{"tf":1.0},"133":{"tf":1.0},"40":{"tf":1.0},"66":{"tf":1.0},"84":{"tf":1.0}}}}}},"v":{"df":2,"docs":{"38":{"tf":1.0},"44":{"tf":1.0}}}},"s":{"df":0,"docs":{},"t":{"df":0,"docs":{},"o":{"df":0,"docs":{},"m":{"df":1,"docs":{"12":{"tf":1.4142135623730951}}}}}}},"y":{"c":{"df":0,"docs":{},"l":{"df":4,"docs":{"127":{"tf":1.0},"128":{"tf":1.0},"144":{"tf":1.0},"146":{"tf":1.0}},"i":{"c":{"df":1,"docs":{"38":{"tf":1.0}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"d":{"0":{"df":1,"docs":{"149":{"tf":1.7320508075688772}},"​":{":":{"=":{"d":{"df":0,"docs":{},"l":{"d":{"df":0,"docs":{},"e":{"df":1,"docs":{"94":{"tf":1.0}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"1":{"df":1,"docs":{"149":{"tf":1.7320508075688772}}},"2":{"df":1,"docs":{"149":{"tf":1.7320508075688772}}},"3":{"df":1,"docs":{"149":{"tf":1.7320508075688772}}},"=":{"(":{"d":{"1":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"…":{",":{"d":{"df":0,"docs":{},"m":{"df":3,"docs":{"73":{"tf":1.4142135623730951},"75":{"tf":1.0},"80":{"tf":1.0}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{},"y":{"1":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"…":{",":{"df":0,"docs":{},"y":{"df":0,"docs":{},"l":{"df":1,"docs":{"90":{"tf":1.0}}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{},"{":{"1":{",":{"df":0,"docs":{},"η":{",":{"df":0,"docs":{},"η":{"2":{",":{"df":0,"docs":{},"…":{",":{"df":0,"docs":{},"η":{"3":{"df":0,"docs":{},"n":{"df":0,"docs":{},"−":{"1":{"df":1,"docs":{"14":{"tf":1.0}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{},"g":{"df":0,"docs":{},"i":{"df":0,"docs":{},"​":{"df":0,"docs":{},"}":{"df":0,"docs":{},"i":{"=":{"0":{"2":{"df":0,"docs":{},"n":{"df":0,"docs":{},"−":{"1":{"df":1,"docs":{"84":{"tf":1.0}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}}}}}},"a":{"df":0,"docs":{},"t":{"a":{"df":2,"docs":{"146":{"tf":1.4142135623730951},"32":{"tf":1.0}}},"df":0,"docs":{}}},"df":11,"docs":{"114":{"tf":1.0},"14":{"tf":1.0},"142":{"tf":1.4142135623730951},"146":{"tf":2.23606797749979},"147":{"tf":1.4142135623730951},"149":{"tf":1.0},"3":{"tf":1.4142135623730951},"73":{"tf":1.0},"76":{"tf":1.0},"82":{"tf":1.0},"84":{"tf":1.0}},"e":{"c":{"df":0,"docs":{},"e":{"df":0,"docs":{},"i":{"df":0,"docs":{},"v":{"df":1,"docs":{"83":{"tf":1.0}}}}},"o":{"d":{"df":3,"docs":{"142":{"tf":1.0},"146":{"tf":2.0},"147":{"tf":1.0}},"e":{"/":{"df":0,"docs":{},"o":{"df":0,"docs":{},"p":{"c":{"df":0,"docs":{},"o":{"d":{"df":1,"docs":{"131":{"tf":1.0}}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{},"m":{"df":0,"docs":{},"p":{"df":0,"docs":{},"o":{"df":0,"docs":{},"s":{"df":2,"docs":{"151":{"tf":1.0},"94":{"tf":1.4142135623730951}},"i":{"df":0,"docs":{},"t":{"df":4,"docs":{"113":{"tf":1.0},"116":{"tf":1.4142135623730951},"146":{"tf":1.0},"67":{"tf":1.4142135623730951}}}}}}}}}},"d":{"df":0,"docs":{},"i":{"c":{"df":2,"docs":{"146":{"tf":1.4142135623730951},"150":{"tf":1.0}}},"df":0,"docs":{}}},"df":0,"docs":{},"e":{"df":0,"docs":{},"p":{"(":{"df":0,"docs":{},"x":{")":{"=":{"df":0,"docs":{},"γ":{"1":{"df":0,"docs":{},"​":{"df":0,"docs":{},"x":{"df":0,"docs":{},"−":{"df":0,"docs":{},"z":{"df":0,"docs":{},"h":{"(":{"df":0,"docs":{},"x":{")":{"df":0,"docs":{},"−":{"df":0,"docs":{},"h":{"(":{"df":0,"docs":{},"z":{")":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"γ":{"2":{"df":0,"docs":{},"​":{"df":0,"docs":{},"x":{"df":0,"docs":{},"−":{"df":0,"docs":{},"z":{"df":0,"docs":{},"t":{"(":{"df":0,"docs":{},"x":{")":{"df":0,"docs":{},"−":{"df":0,"docs":{},"t":{"(":{"df":0,"docs":{},"z":{")":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"γ":{"3":{"df":0,"docs":{},"​":{"df":0,"docs":{},"x":{"df":0,"docs":{},"−":{"df":0,"docs":{},"z":{"df":0,"docs":{},"g":{"df":0,"docs":{},"t":{"(":{"df":0,"docs":{},"x":{")":{"df":0,"docs":{},"−":{"df":0,"docs":{},"t":{"(":{"df":0,"docs":{},"z":{"df":0,"docs":{},"g":{")":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"γ":{"4":{"df":0,"docs":{},"​":{"df":0,"docs":{},"x":{"df":0,"docs":{},"−":{"df":0,"docs":{},"z":{"df":0,"docs":{},"g":{"2":{"df":0,"docs":{},"t":{"(":{"df":0,"docs":{},"x":{")":{"df":0,"docs":{},"−":{"df":0,"docs":{},"t":{"(":{"df":0,"docs":{},"z":{"df":0,"docs":{},"g":{"2":{"df":1,"docs":{"59":{"tf":1.0}}},"df":0,"docs":{}}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"0":{"df":1,"docs":{"113":{"tf":1.0}},"​":{")":{"=":{"df":0,"docs":{},"γ":{"1":{"df":0,"docs":{},"​":{"df":0,"docs":{},"x":{"0":{"df":0,"docs":{},"​":{"df":0,"docs":{},"−":{"df":0,"docs":{},"z":{"df":0,"docs":{},"h":{"(":{"df":0,"docs":{},"x":{"0":{"df":0,"docs":{},"​":{")":{"df":0,"docs":{},"−":{"df":0,"docs":{},"h":{"(":{"df":0,"docs":{},"z":{")":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"γ":{"2":{"df":0,"docs":{},"​":{"df":0,"docs":{},"x":{"0":{"df":0,"docs":{},"​":{"df":0,"docs":{},"−":{"df":0,"docs":{},"z":{"df":0,"docs":{},"t":{"(":{"df":0,"docs":{},"x":{"0":{"df":0,"docs":{},"​":{")":{"df":0,"docs":{},"−":{"df":0,"docs":{},"t":{"(":{"df":0,"docs":{},"z":{")":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"γ":{"3":{"df":0,"docs":{},"​":{"df":0,"docs":{},"x":{"0":{"df":0,"docs":{},"​":{"df":0,"docs":{},"−":{"df":0,"docs":{},"z":{"df":0,"docs":{},"g":{"df":0,"docs":{},"t":{"(":{"df":0,"docs":{},"x":{"0":{"df":0,"docs":{},"​":{")":{"df":0,"docs":{},"−":{"df":0,"docs":{},"t":{"(":{"df":0,"docs":{},"z":{"df":0,"docs":{},"g":{")":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"γ":{"4":{"df":0,"docs":{},"​":{"df":0,"docs":{},"x":{"0":{"df":0,"docs":{},"​":{"df":0,"docs":{},"−":{"df":0,"docs":{},"z":{"df":0,"docs":{},"g":{"2":{"df":0,"docs":{},"t":{"(":{"df":0,"docs":{},"x":{"0":{"df":0,"docs":{},"​":{")":{"df":0,"docs":{},"−":{"df":0,"docs":{},"t":{"(":{"df":0,"docs":{},"z":{"df":0,"docs":{},"g":{"2":{"df":3,"docs":{"113":{"tf":1.0},"60":{"tf":1.0},"63":{"tf":1.0}}},"df":0,"docs":{}}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"_":{"0":{"df":1,"docs":{"63":{"tf":1.0}}},"df":0,"docs":{}},"df":3,"docs":{"112":{"tf":1.4142135623730951},"59":{"tf":1.4142135623730951},"62":{"tf":1.4142135623730951}}}},"df":13,"docs":{"100":{"tf":1.0},"112":{"tf":1.0},"119":{"tf":1.7320508075688772},"125":{"tf":1.0},"59":{"tf":1.4142135623730951},"60":{"tf":1.0},"62":{"tf":1.0},"65":{"tf":1.0},"68":{"tf":1.4142135623730951},"80":{"tf":1.0},"81":{"tf":1.4142135623730951},"95":{"tf":1.0},"97":{"tf":1.0}},"e":{"df":0,"docs":{},"r":{"df":2,"docs":{"103":{"tf":1.0},"69":{"tf":1.0}}}}}},"f":{"a":{"df":0,"docs":{},"u":{"df":0,"docs":{},"l":{"df":0,"docs":{},"t":{"df":1,"docs":{"9":{"tf":1.0}}}}}},"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"df":29,"docs":{"105":{"tf":1.7320508075688772},"108":{"tf":1.0},"109":{"tf":1.4142135623730951},"110":{"tf":1.0},"113":{"tf":1.0},"114":{"tf":1.0},"128":{"tf":1.0},"131":{"tf":1.0},"14":{"tf":1.7320508075688772},"142":{"tf":1.7320508075688772},"147":{"tf":2.449489742783178},"149":{"tf":1.0},"150":{"tf":2.0},"20":{"tf":1.0},"23":{"tf":1.0},"25":{"tf":1.0},"26":{"tf":1.4142135623730951},"28":{"tf":1.4142135623730951},"35":{"tf":1.4142135623730951},"5":{"tf":1.0},"50":{"tf":1.0},"54":{"tf":1.0},"55":{"tf":1.0},"59":{"tf":1.0},"8":{"tf":1.0},"90":{"tf":1.0},"94":{"tf":1.4142135623730951},"95":{"tf":1.0},"97":{"tf":1.7320508075688772}},"i":{"df":0,"docs":{},"t":{"df":3,"docs":{"14":{"tf":1.4142135623730951},"34":{"tf":1.0},"91":{"tf":1.0}}}}}}},"g":{"df":0,"docs":{},"r":{"df":0,"docs":{},"e":{"df":18,"docs":{"109":{"tf":1.0},"114":{"tf":1.0},"119":{"tf":1.0},"125":{"tf":1.0},"14":{"tf":1.4142135623730951},"146":{"tf":1.0},"20":{"tf":2.23606797749979},"26":{"tf":1.0},"3":{"tf":1.4142135623730951},"68":{"tf":2.23606797749979},"73":{"tf":3.0},"74":{"tf":1.4142135623730951},"75":{"tf":1.7320508075688772},"78":{"tf":2.0},"79":{"tf":1.7320508075688772},"80":{"tf":1.0},"81":{"tf":1.0},"91":{"tf":1.4142135623730951}}}}},"l":{"df":0,"docs":{},"e":{"df":0,"docs":{},"g":{"df":2,"docs":{"151":{"tf":1.0},"7":{"tf":1.0}}}},"i":{"c":{"df":1,"docs":{"16":{"tf":1.0}}},"df":0,"docs":{}},"v":{"df":1,"docs":{"4":{"tf":1.0}}}},"m":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"df":0,"docs":{},"s":{"df":0,"docs":{},"t":{"df":0,"docs":{},"r":{"df":2,"docs":{"13":{"tf":1.0},"7":{"tf":1.0}}}}}},"s":{"df":0,"docs":{},"t":{"df":0,"docs":{},"r":{"df":1,"docs":{"7":{"tf":1.0}}}}}}},"n":{"df":0,"docs":{},"o":{"df":0,"docs":{},"m":{"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"df":4,"docs":{"114":{"tf":1.0},"123":{"tf":1.0},"125":{"tf":1.4142135623730951},"54":{"tf":1.0}}}}},"t":{"df":18,"docs":{"14":{"tf":1.0},"147":{"tf":1.0},"150":{"tf":1.0},"19":{"tf":1.0},"36":{"tf":1.0},"5":{"tf":3.0},"53":{"tf":1.0},"70":{"tf":1.4142135623730951},"72":{"tf":1.0},"75":{"tf":1.0},"77":{"tf":1.0},"82":{"tf":1.0},"83":{"tf":1.0},"84":{"tf":1.0},"88":{"tf":1.0},"90":{"tf":2.0},"91":{"tf":1.4142135623730951},"95":{"tf":1.4142135623730951}}}}},"p":{"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"d":{"df":9,"docs":{"10":{"tf":1.0},"109":{"tf":1.0},"13":{"tf":1.4142135623730951},"146":{"tf":1.7320508075688772},"70":{"tf":1.4142135623730951},"73":{"tf":1.4142135623730951},"8":{"tf":1.0},"84":{"tf":1.4142135623730951},"92":{"tf":1.0}}},"df":0,"docs":{}}}},"r":{"df":0,"docs":{},"i":{"df":0,"docs":{},"v":{"df":5,"docs":{"142":{"tf":1.0},"146":{"tf":1.7320508075688772},"147":{"tf":1.0},"5":{"tf":1.0},"91":{"tf":1.0}}}}},"s":{"c":{"df":0,"docs":{},"r":{"df":0,"docs":{},"i":{"b":{"df":12,"docs":{"142":{"tf":1.0},"145":{"tf":1.0},"147":{"tf":1.0},"23":{"tf":1.4142135623730951},"37":{"tf":1.0},"40":{"tf":1.0},"74":{"tf":1.0},"77":{"tf":1.0},"80":{"tf":1.0},"86":{"tf":1.0},"87":{"tf":1.0},"95":{"tf":1.0}}},"df":0,"docs":{},"p":{"df":0,"docs":{},"t":{"df":3,"docs":{"142":{"tf":1.0},"16":{"tf":1.0},"82":{"tf":1.0}}}}}}},"df":0,"docs":{},"i":{"df":0,"docs":{},"g":{"df":0,"docs":{},"n":{"df":2,"docs":{"10":{"tf":1.0},"151":{"tf":1.0}}}},"r":{"df":2,"docs":{"146":{"tf":1.0},"20":{"tf":1.0}}}}},"t":{"a":{"df":0,"docs":{},"i":{"df":0,"docs":{},"l":{"df":15,"docs":{"103":{"tf":1.0},"114":{"tf":1.0},"127":{"tf":1.0},"133":{"tf":1.0},"134":{"tf":1.0},"138":{"tf":1.0},"139":{"tf":1.4142135623730951},"144":{"tf":1.0},"145":{"tf":1.0},"146":{"tf":1.0},"151":{"tf":1.0},"18":{"tf":1.0},"34":{"tf":1.0},"37":{"tf":1.4142135623730951},"75":{"tf":1.0}}}}},"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":0,"docs":{},"m":{"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"df":1,"docs":{"91":{"tf":1.0}},"i":{"df":0,"docs":{},"s":{"df":0,"docs":{},"t":{"df":1,"docs":{"137":{"tf":1.0}}}}}}}}}}},"v":{"df":0,"docs":{},"i":{"c":{"df":1,"docs":{"151":{"tf":1.4142135623730951}}},"df":0,"docs":{}}}},"i":{"a":{"df":0,"docs":{},"g":{"df":0,"docs":{},"r":{"a":{"df":0,"docs":{},"m":{"df":1,"docs":{"133":{"tf":1.0}}}},"df":0,"docs":{}}}},"d":{"df":0,"docs":{},"n":{"'":{"df":0,"docs":{},"t":{"df":1,"docs":{"134":{"tf":1.0}}}},"df":0,"docs":{}}},"df":0,"docs":{},"f":{"df":0,"docs":{},"f":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":15,"docs":{"118":{"tf":1.0},"14":{"tf":1.7320508075688772},"146":{"tf":1.0},"149":{"tf":1.0},"20":{"tf":1.0},"3":{"tf":1.0},"50":{"tf":1.4142135623730951},"52":{"tf":1.0},"53":{"tf":1.0},"56":{"tf":1.0},"66":{"tf":1.4142135623730951},"70":{"tf":1.0},"73":{"tf":1.0},"80":{"tf":1.0},"9":{"tf":1.0}}}}}},"g":{"df":0,"docs":{},"e":{"df":0,"docs":{},"s":{"df":0,"docs":{},"t":{"df":1,"docs":{"7":{"tf":1.0}}}}}},"r":{"df":0,"docs":{},"e":{"c":{"df":0,"docs":{},"t":{"df":0,"docs":{},"l":{"df":0,"docs":{},"i":{"df":3,"docs":{"127":{"tf":1.0},"14":{"tf":1.0},"146":{"tf":1.4142135623730951}}}}}},"df":0,"docs":{}}},"s":{"c":{"df":0,"docs":{},"u":{"df":0,"docs":{},"s":{"df":0,"docs":{},"s":{"df":3,"docs":{"34":{"tf":1.0},"43":{"tf":1.0},"56":{"tf":1.0}}}}}},"df":0,"docs":{},"j":{"df":0,"docs":{},"o":{"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"df":0,"docs":{},"t":{"df":1,"docs":{"90":{"tf":1.0}}}}}}},"t":{"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"df":0,"docs":{},"g":{"df":0,"docs":{},"u":{"df":0,"docs":{},"i":{"df":0,"docs":{},"s":{"df":0,"docs":{},"h":{"df":1,"docs":{"9":{"tf":1.0}}}}}}}}}}},"v":{"df":0,"docs":{},"e":{"df":4,"docs":{"103":{"tf":1.0},"46":{"tf":1.0},"69":{"tf":1.0},"81":{"tf":1.0}}},"i":{"d":{"df":7,"docs":{"100":{"tf":1.0},"123":{"tf":1.4142135623730951},"125":{"tf":1.7320508075688772},"146":{"tf":1.4142135623730951},"38":{"tf":1.0},"79":{"tf":1.0},"99":{"tf":1.0}}},"df":0,"docs":{},"s":{"df":4,"docs":{"125":{"tf":1.4142135623730951},"146":{"tf":1.0},"69":{"tf":1.0},"99":{"tf":1.0}}}}},"​":{"=":{"df":0,"docs":{},"h":{"df":0,"docs":{},"ω":{"df":0,"docs":{},"i":{"df":2,"docs":{"73":{"tf":1.0},"75":{"tf":1.0}}}}}},"df":0,"docs":{},"∈":{"d":{"df":1,"docs":{"73":{"tf":1.0}}},"df":0,"docs":{}},"−":{"df":0,"docs":{},"z":{"df":1,"docs":{"79":{"tf":1.0}}}}}},"k":{"df":0,"docs":{},"​":{":":{"=":{"(":{"d":{"0":{"2":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"…":{",":{"d":{"df":0,"docs":{},"l":{"df":0,"docs":{},"−":{"1":{"2":{"df":1,"docs":{"94":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"−":{"1":{"df":0,"docs":{},"​":{"=":{"(":{"d":{"0":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"…":{",":{"d":{"2":{"df":0,"docs":{},"l":{"df":0,"docs":{},"−":{"1":{"df":1,"docs":{"94":{"tf":1.0}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"l":{"d":{"df":0,"docs":{},"e":{"df":1,"docs":{"83":{"tf":1.0}},"​":{"=":{"(":{"df":0,"docs":{},"h":{",":{"df":0,"docs":{},"h":{"df":0,"docs":{},"ω":{",":{"df":0,"docs":{},"h":{"df":0,"docs":{},"ω":{"2":{",":{"df":0,"docs":{},"…":{",":{"df":0,"docs":{},"h":{"df":0,"docs":{},"ω":{"2":{"df":0,"docs":{},"n":{"+":{"df":0,"docs":{},"l":{"df":1,"docs":{"82":{"tf":1.0}},"−":{"1":{"df":1,"docs":{"91":{"tf":1.0}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}},"o":{"c":{"df":0,"docs":{},"u":{"df":0,"docs":{},"m":{"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"df":0,"docs":{},"t":{"df":3,"docs":{"0":{"tf":1.0},"126":{"tf":1.0},"46":{"tf":1.0}}}}}}}},"df":5,"docs":{"109":{"tf":1.0},"114":{"tf":1.0},"125":{"tf":1.7320508075688772},"146":{"tf":1.4142135623730951},"149":{"tf":1.0}},"e":{"df":0,"docs":{},"s":{"df":0,"docs":{},"n":{"'":{"df":0,"docs":{},"t":{"df":4,"docs":{"125":{"tf":1.0},"130":{"tf":1.0},"14":{"tf":1.0},"32":{"tf":1.0}}}},"df":0,"docs":{}}}},"m":{"a":{"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"df":24,"docs":{"101":{"tf":1.0},"112":{"tf":1.4142135623730951},"115":{"tf":1.0},"117":{"tf":1.4142135623730951},"119":{"tf":1.0},"125":{"tf":1.4142135623730951},"14":{"tf":2.0},"16":{"tf":1.0},"20":{"tf":1.0},"24":{"tf":1.0},"25":{"tf":1.0},"26":{"tf":1.0},"32":{"tf":1.4142135623730951},"35":{"tf":1.4142135623730951},"5":{"tf":1.0},"59":{"tf":1.4142135623730951},"60":{"tf":1.4142135623730951},"62":{"tf":1.4142135623730951},"66":{"tf":1.0},"68":{"tf":1.4142135623730951},"73":{"tf":1.7320508075688772},"84":{"tf":1.0},"91":{"tf":1.0},"94":{"tf":1.4142135623730951}}}}},"df":0,"docs":{}},"n":{"'":{"df":0,"docs":{},"t":{"df":9,"docs":{"111":{"tf":1.0},"114":{"tf":1.0},"116":{"tf":1.4142135623730951},"118":{"tf":1.0},"123":{"tf":1.4142135623730951},"125":{"tf":1.7320508075688772},"149":{"tf":1.0},"151":{"tf":1.0},"67":{"tf":1.0}}}},"df":0,"docs":{},"e":{"df":12,"docs":{"103":{"tf":1.0},"105":{"tf":1.0},"110":{"tf":1.0},"114":{"tf":1.4142135623730951},"118":{"tf":1.4142135623730951},"130":{"tf":1.0},"146":{"tf":1.4142135623730951},"151":{"tf":1.4142135623730951},"58":{"tf":1.0},"69":{"tf":1.0},"83":{"tf":1.0},"84":{"tf":1.0}}}},"t":{"df":2,"docs":{"65":{"tf":1.0},"66":{"tf":1.0}}},"w":{"df":0,"docs":{},"n":{"df":4,"docs":{"14":{"tf":1.4142135623730951},"16":{"tf":1.0},"21":{"tf":1.0},"35":{"tf":1.0}}}}},"s":{"df":1,"docs":{"94":{"tf":1.4142135623730951}},"t":{"_":{"a":{"d":{"d":{"df":0,"docs":{},"r":{"[":{"0":{"df":1,"docs":{"147":{"tf":1.0}}},"1":{"df":1,"docs":{"147":{"tf":1.0}}},"df":0,"docs":{}},"df":3,"docs":{"142":{"tf":1.0},"146":{"tf":1.7320508075688772},"147":{"tf":1.4142135623730951}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{},"r":{"df":0,"docs":{},"e":{"df":0,"docs":{},"g":{"df":1,"docs":{"146":{"tf":1.0}}}}}},"df":4,"docs":{"133":{"tf":2.0},"142":{"tf":1.0},"146":{"tf":2.23606797749979},"147":{"tf":1.4142135623730951}},"r":{"df":0,"docs":{},"e":{"df":0,"docs":{},"g":{"df":1,"docs":{"149":{"tf":1.0}}}}}},"​":{"=":{"(":{"1":{",":{"df":0,"docs":{},"g":{",":{"df":0,"docs":{},"…":{",":{"df":0,"docs":{},"g":{"2":{"df":0,"docs":{},"n":{"df":0,"docs":{},"−":{"1":{"df":1,"docs":{"91":{"tf":1.0}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{},"{":{"df":0,"docs":{},"g":{"df":0,"docs":{},"i":{"df":0,"docs":{},"}":{"df":0,"docs":{},"i":{"=":{"0":{"2":{"df":0,"docs":{},"n":{"df":0,"docs":{},"−":{"1":{"df":0,"docs":{},"​":{"df":0,"docs":{},"⊆":{"df":0,"docs":{},"f":{"df":1,"docs":{"82":{"tf":1.0}}}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}}}}},"df":0,"docs":{}}},"u":{"df":0,"docs":{},"e":{"df":2,"docs":{"13":{"tf":1.0},"4":{"tf":1.0}}},"m":{"df":0,"docs":{},"m":{"df":0,"docs":{},"i":{"df":5,"docs":{"133":{"tf":1.7320508075688772},"135":{"tf":1.7320508075688772},"146":{"tf":1.7320508075688772},"149":{"tf":1.0},"36":{"tf":1.7320508075688772}}}}},"r":{"df":0,"docs":{},"e":{"df":2,"docs":{"14":{"tf":1.0},"80":{"tf":1.0}}}}}},"df":0,"docs":{},"e":{"+":{"1":{")":{"df":0,"docs":{},"∗":{"df":0,"docs":{},"x":{"df":0,"docs":{},"−":{"1":{"df":1,"docs":{"7":{"tf":1.0}}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}},"=":{"2":{"df":1,"docs":{"9":{"tf":1.0}}},"df":0,"docs":{}},"a":{"c":{"df":0,"docs":{},"h":{"df":30,"docs":{"10":{"tf":1.0},"102":{"tf":1.0},"106":{"tf":1.0},"109":{"tf":1.7320508075688772},"114":{"tf":1.7320508075688772},"125":{"tf":1.0},"128":{"tf":1.0},"130":{"tf":1.4142135623730951},"131":{"tf":2.0},"133":{"tf":1.4142135623730951},"136":{"tf":1.0},"14":{"tf":1.0},"142":{"tf":1.4142135623730951},"146":{"tf":3.3166247903554},"147":{"tf":1.0},"149":{"tf":2.0},"150":{"tf":1.7320508075688772},"151":{"tf":2.23606797749979},"3":{"tf":1.0},"49":{"tf":1.0},"65":{"tf":1.4142135623730951},"66":{"tf":1.4142135623730951},"70":{"tf":1.0},"80":{"tf":1.7320508075688772},"82":{"tf":1.4142135623730951},"83":{"tf":1.0},"84":{"tf":1.0},"94":{"tf":1.4142135623730951},"95":{"tf":1.0},"99":{"tf":1.0}}}},"df":0,"docs":{},"r":{"df":0,"docs":{},"l":{"df":0,"docs":{},"i":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":2,"docs":{"59":{"tf":1.0},"80":{"tf":1.0}}}}}}},"s":{"df":1,"docs":{"70":{"tf":1.0}},"i":{"df":1,"docs":{"14":{"tf":1.0}},"l":{"df":0,"docs":{},"i":{"df":5,"docs":{"114":{"tf":1.0},"133":{"tf":1.0},"45":{"tf":1.0},"7":{"tf":1.0},"70":{"tf":1.0}}}}}}},"df":10,"docs":{"11":{"tf":1.0},"142":{"tf":1.4142135623730951},"146":{"tf":1.7320508075688772},"147":{"tf":1.4142135623730951},"34":{"tf":1.0},"35":{"tf":2.6457513110645907},"44":{"tf":2.6457513110645907},"45":{"tf":2.0},"7":{"tf":2.23606797749979},"9":{"tf":1.7320508075688772}},"f":{"df":0,"docs":{},"f":{"df":0,"docs":{},"e":{"c":{"df":0,"docs":{},"t":{"df":1,"docs":{"59":{"tf":1.0}}}},"df":0,"docs":{}},"i":{"c":{"df":0,"docs":{},"i":{"df":4,"docs":{"146":{"tf":1.0},"151":{"tf":1.0},"4":{"tf":1.0},"73":{"tf":1.0}}}},"df":0,"docs":{}}}},"i":{"df":1,"docs":{"123":{"tf":1.0}},"g":{"df":0,"docs":{},"h":{"df":0,"docs":{},"t":{"df":5,"docs":{"15":{"tf":1.0},"151":{"tf":1.0},"23":{"tf":1.0},"35":{"tf":1.4142135623730951},"8":{"tf":1.0}}}}}},"l":{"df":0,"docs":{},"e":{"df":0,"docs":{},"m":{"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"df":0,"docs":{},"t":{"df":30,"docs":{"101":{"tf":1.0},"107":{"tf":1.0},"121":{"tf":1.0},"123":{"tf":1.0},"125":{"tf":2.0},"128":{"tf":1.7320508075688772},"14":{"tf":5.477225575051661},"144":{"tf":1.0},"16":{"tf":1.0},"19":{"tf":2.449489742783178},"31":{"tf":1.0},"35":{"tf":1.7320508075688772},"41":{"tf":1.7320508075688772},"52":{"tf":1.0},"54":{"tf":1.0},"55":{"tf":1.0},"68":{"tf":1.0},"7":{"tf":1.0},"70":{"tf":1.4142135623730951},"73":{"tf":1.0},"74":{"tf":1.4142135623730951},"77":{"tf":1.0},"79":{"tf":1.0},"82":{"tf":1.0},"84":{"tf":1.0},"91":{"tf":1.0},"92":{"tf":2.0},"95":{"tf":1.4142135623730951},"98":{"tf":1.4142135623730951},"99":{"tf":1.0}}}}}}}},"m":{"b":{"df":0,"docs":{},"e":{"d":{"df":1,"docs":{"130":{"tf":1.0}}},"df":0,"docs":{}}},"df":0,"docs":{},"p":{"df":0,"docs":{},"h":{"a":{"df":0,"docs":{},"s":{"df":1,"docs":{"5":{"tf":1.0}}}},"df":0,"docs":{}},"l":{"df":0,"docs":{},"o":{"df":0,"docs":{},"y":{"df":2,"docs":{"100":{"tf":1.0},"115":{"tf":1.0}}}}},"t":{"df":0,"docs":{},"i":{"df":2,"docs":{"134":{"tf":1.0},"36":{"tf":1.0}}}}},"u":{"df":0,"docs":{},"l":{"df":1,"docs":{"118":{"tf":1.0}}}}},"n":{"a":{"b":{"df":0,"docs":{},"l":{"df":2,"docs":{"146":{"tf":1.0},"4":{"tf":1.0}}}},"df":0,"docs":{}},"c":{"a":{"df":0,"docs":{},"p":{"df":0,"docs":{},"s":{"df":0,"docs":{},"u":{"df":0,"docs":{},"l":{"df":1,"docs":{"109":{"tf":1.0}}}}}}},"df":0,"docs":{},"o":{"d":{"df":15,"docs":{"10":{"tf":1.0},"105":{"tf":1.0},"107":{"tf":1.0},"11":{"tf":1.0},"133":{"tf":1.0},"48":{"tf":1.0},"50":{"tf":1.0},"52":{"tf":1.0},"54":{"tf":1.0},"55":{"tf":1.0},"66":{"tf":1.0},"69":{"tf":1.4142135623730951},"70":{"tf":1.4142135623730951},"72":{"tf":1.0},"84":{"tf":1.0}}},"df":0,"docs":{}}},"d":{"df":12,"docs":{"103":{"tf":1.0},"109":{"tf":1.0},"116":{"tf":1.0},"12":{"tf":1.0},"13":{"tf":1.0},"130":{"tf":1.0},"134":{"tf":1.0},"144":{"tf":1.0},"40":{"tf":1.0},"46":{"tf":1.0},"8":{"tf":1.0},"95":{"tf":1.0}},"i":{"a":{"df":0,"docs":{},"n":{"df":1,"docs":{"41":{"tf":1.0}}}},"df":0,"docs":{}}},"df":0,"docs":{},"f":{"df":0,"docs":{},"o":{"df":0,"docs":{},"r":{"c":{"df":5,"docs":{"146":{"tf":1.0},"150":{"tf":1.4142135623730951},"151":{"tf":2.0},"49":{"tf":1.0},"70":{"tf":1.0}}},"df":0,"docs":{}}}},"g":{"a":{"df":0,"docs":{},"g":{"df":3,"docs":{"75":{"tf":1.0},"77":{"tf":1.0},"86":{"tf":1.0}}}},"df":0,"docs":{}},"h":{"a":{"df":0,"docs":{},"n":{"c":{"df":1,"docs":{"100":{"tf":1.0}}},"df":0,"docs":{}}},"df":0,"docs":{}},"o":{"df":0,"docs":{},"u":{"df":0,"docs":{},"g":{"df":0,"docs":{},"h":{"df":3,"docs":{"114":{"tf":1.4142135623730951},"146":{"tf":1.0},"49":{"tf":1.0}}}}}},"s":{"df":0,"docs":{},"u":{"df":0,"docs":{},"r":{"df":1,"docs":{"83":{"tf":1.0}}}}},"t":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":1,"docs":{"14":{"tf":1.0}}}},"i":{"df":0,"docs":{},"r":{"df":4,"docs":{"12":{"tf":1.0},"14":{"tf":1.0},"50":{"tf":1.0},"68":{"tf":1.0}}},"t":{"df":0,"docs":{},"i":{"df":1,"docs":{"104":{"tf":1.0}}}}},"r":{"df":0,"docs":{},"i":{"df":7,"docs":{"134":{"tf":1.0},"141":{"tf":1.0},"142":{"tf":2.0},"147":{"tf":2.0},"36":{"tf":1.0},"70":{"tf":1.0},"91":{"tf":1.0}}}}}},"q":{"df":0,"docs":{},"u":{"a":{"df":0,"docs":{},"l":{"df":16,"docs":{"10":{"tf":1.4142135623730951},"101":{"tf":1.0},"106":{"tf":1.7320508075688772},"11":{"tf":1.0},"13":{"tf":1.0},"14":{"tf":5.830951894845301},"142":{"tf":1.4142135623730951},"146":{"tf":2.23606797749979},"147":{"tf":1.0},"16":{"tf":1.4142135623730951},"33":{"tf":1.0},"52":{"tf":1.0},"66":{"tf":1.0},"73":{"tf":1.0},"78":{"tf":1.4142135623730951},"95":{"tf":1.4142135623730951}}},"t":{"df":12,"docs":{"10":{"tf":1.7320508075688772},"11":{"tf":1.0},"12":{"tf":1.0},"13":{"tf":1.0},"14":{"tf":3.0},"149":{"tf":1.4142135623730951},"16":{"tf":1.7320508075688772},"4":{"tf":1.4142135623730951},"58":{"tf":1.0},"60":{"tf":1.0},"69":{"tf":1.0},"95":{"tf":1.0}}}},"df":0,"docs":{},"i":{"df":0,"docs":{},"v":{"a":{"df":0,"docs":{},"l":{"df":6,"docs":{"14":{"tf":2.8284271247461903},"16":{"tf":1.0},"56":{"tf":1.0},"69":{"tf":1.4142135623730951},"80":{"tf":1.0},"84":{"tf":1.0}}}},"df":0,"docs":{}}}}},"s":{"df":0,"docs":{},"s":{"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"c":{"df":1,"docs":{"133":{"tf":1.0}}},"df":0,"docs":{},"t":{"df":0,"docs":{},"i":{"df":1,"docs":{"49":{"tf":1.0}}}}}}}},"t":{"c":{"df":2,"docs":{"134":{"tf":1.0},"50":{"tf":1.0}}},"df":0,"docs":{}},"v":{"a":{"df":0,"docs":{},"l":{"df":0,"docs":{},"u":{"a":{"df":0,"docs":{},"t":{"df":0,"docs":{},"o":{"df":0,"docs":{},"r":{".":{"df":0,"docs":{},"e":{"df":0,"docs":{},"v":{"a":{"df":0,"docs":{},"l":{"df":0,"docs":{},"u":{"df":1,"docs":{"114":{"tf":1.0}}}}},"df":0,"docs":{}}}},"df":0,"docs":{}}}}},"df":39,"docs":{"10":{"tf":1.4142135623730951},"11":{"tf":1.4142135623730951},"112":{"tf":2.0},"113":{"tf":2.6457513110645907},"114":{"tf":4.358898943540674},"115":{"tf":1.0},"116":{"tf":1.0},"117":{"tf":1.7320508075688772},"119":{"tf":1.0},"121":{"tf":1.7320508075688772},"123":{"tf":2.23606797749979},"125":{"tf":2.23606797749979},"13":{"tf":1.7320508075688772},"14":{"tf":1.4142135623730951},"149":{"tf":2.6457513110645907},"19":{"tf":1.4142135623730951},"28":{"tf":1.0},"35":{"tf":1.0},"52":{"tf":1.0},"58":{"tf":2.23606797749979},"59":{"tf":2.0},"60":{"tf":1.7320508075688772},"62":{"tf":1.7320508075688772},"63":{"tf":2.23606797749979},"66":{"tf":1.4142135623730951},"68":{"tf":2.449489742783178},"70":{"tf":1.0},"73":{"tf":1.7320508075688772},"74":{"tf":1.4142135623730951},"75":{"tf":1.0},"76":{"tf":1.0},"77":{"tf":1.4142135623730951},"78":{"tf":1.0},"79":{"tf":1.0},"80":{"tf":2.0},"85":{"tf":1.4142135623730951},"86":{"tf":1.0},"94":{"tf":1.0},"95":{"tf":2.0}}}}},"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"/":{"df":0,"docs":{},"o":{"d":{"d":{"df":2,"docs":{"113":{"tf":1.0},"116":{"tf":1.4142135623730951}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":10,"docs":{"101":{"tf":1.0},"114":{"tf":1.0},"119":{"tf":1.0},"146":{"tf":1.4142135623730951},"49":{"tf":1.0},"52":{"tf":1.0},"66":{"tf":1.0},"67":{"tf":1.0},"73":{"tf":1.0},"94":{"tf":1.0}}},"r":{"df":0,"docs":{},"y":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"df":1,"docs":{"103":{"tf":1.0}}}},"t":{"df":0,"docs":{},"h":{"df":4,"docs":{"146":{"tf":1.0},"16":{"tf":1.0},"21":{"tf":1.0},"70":{"tf":1.0}}}}}}},"i":{"d":{"df":1,"docs":{"14":{"tf":1.0}}},"df":0,"docs":{}},"o":{"df":0,"docs":{},"l":{"df":0,"docs":{},"u":{"df":0,"docs":{},"t":{"df":1,"docs":{"141":{"tf":1.0}}}}}}},"x":{"a":{"c":{"df":0,"docs":{},"t":{"df":2,"docs":{"146":{"tf":1.0},"95":{"tf":1.0}},"l":{"df":0,"docs":{},"i":{"df":4,"docs":{"114":{"tf":1.0},"20":{"tf":1.0},"21":{"tf":1.4142135623730951},"67":{"tf":1.0}}}}}},"df":0,"docs":{},"m":{"df":0,"docs":{},"p":{"df":0,"docs":{},"l":{"df":42,"docs":{"10":{"tf":1.4142135623730951},"100":{"tf":1.0},"101":{"tf":1.0},"103":{"tf":1.0},"104":{"tf":1.0},"108":{"tf":1.0},"109":{"tf":1.4142135623730951},"11":{"tf":1.0},"110":{"tf":1.0},"111":{"tf":1.0},"114":{"tf":1.4142135623730951},"117":{"tf":1.0},"12":{"tf":1.0},"124":{"tf":1.0},"13":{"tf":1.0},"130":{"tf":1.0},"134":{"tf":1.0},"135":{"tf":1.0},"14":{"tf":2.0},"146":{"tf":1.4142135623730951},"149":{"tf":1.7320508075688772},"150":{"tf":1.0},"151":{"tf":1.4142135623730951},"20":{"tf":1.0},"34":{"tf":1.0},"40":{"tf":1.0},"44":{"tf":1.4142135623730951},"48":{"tf":1.0},"49":{"tf":1.7320508075688772},"5":{"tf":1.0},"50":{"tf":2.23606797749979},"52":{"tf":1.0},"64":{"tf":1.0},"66":{"tf":1.0},"68":{"tf":1.0},"7":{"tf":1.4142135623730951},"70":{"tf":1.4142135623730951},"73":{"tf":1.0},"74":{"tf":1.4142135623730951},"84":{"tf":1.0},"9":{"tf":1.0},"95":{"tf":2.0}}}}}},"c":{"df":0,"docs":{},"e":{"df":0,"docs":{},"p":{"df":0,"docs":{},"t":{"df":3,"docs":{"142":{"tf":1.4142135623730951},"44":{"tf":1.0},"72":{"tf":1.0}}}}}},"df":0,"docs":{},"e":{"c":{"df":0,"docs":{},"u":{"df":0,"docs":{},"t":{"df":30,"docs":{"10":{"tf":1.7320508075688772},"108":{"tf":1.0},"11":{"tf":1.0},"110":{"tf":1.4142135623730951},"127":{"tf":1.0},"13":{"tf":2.0},"131":{"tf":1.4142135623730951},"133":{"tf":1.0},"14":{"tf":1.0},"141":{"tf":1.0},"142":{"tf":1.4142135623730951},"143":{"tf":1.0},"144":{"tf":2.23606797749979},"145":{"tf":1.0},"146":{"tf":3.605551275463989},"16":{"tf":1.7320508075688772},"22":{"tf":1.0},"34":{"tf":1.0},"4":{"tf":1.0},"43":{"tf":1.0},"48":{"tf":1.4142135623730951},"49":{"tf":1.0},"50":{"tf":2.449489742783178},"69":{"tf":1.7320508075688772},"7":{"tf":2.23606797749979},"8":{"tf":2.0},"83":{"tf":1.0},"9":{"tf":2.23606797749979},"91":{"tf":1.4142135623730951},"94":{"tf":1.4142135623730951}}}}},"df":0,"docs":{},"m":{"df":0,"docs":{},"p":{"df":0,"docs":{},"t":{"df":1,"docs":{"123":{"tf":1.4142135623730951}}}}},"r":{"c":{"df":0,"docs":{},"i":{"df":0,"docs":{},"s":{"df":1,"docs":{"40":{"tf":1.0}}}}},"df":0,"docs":{}}},"i":{"df":0,"docs":{},"s":{"df":0,"docs":{},"t":{"df":4,"docs":{"106":{"tf":1.0},"14":{"tf":2.6457513110645907},"149":{"tf":1.0},"70":{"tf":1.0}}}}},"p":{"a":{"df":0,"docs":{},"n":{"d":{"df":1,"docs":{"14":{"tf":1.0}}},"df":0,"docs":{}}},"df":0,"docs":{},"e":{"c":{"df":0,"docs":{},"t":{"df":7,"docs":{"146":{"tf":2.23606797749979},"151":{"tf":1.0},"71":{"tf":1.0},"72":{"tf":1.0},"75":{"tf":1.0},"79":{"tf":1.0},"85":{"tf":1.0}}}},"df":0,"docs":{},"n":{"df":0,"docs":{},"s":{"df":2,"docs":{"123":{"tf":1.0},"21":{"tf":1.0}}}}},"l":{"a":{"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"df":7,"docs":{"103":{"tf":1.0},"117":{"tf":1.0},"118":{"tf":1.0},"144":{"tf":1.0},"146":{"tf":1.7320508075688772},"19":{"tf":1.0},"7":{"tf":1.0}}}},"n":{"df":4,"docs":{"114":{"tf":1.0},"128":{"tf":1.0},"146":{"tf":1.4142135623730951},"51":{"tf":1.0}}}},"df":0,"docs":{},"i":{"c":{"df":0,"docs":{},"i":{"df":0,"docs":{},"t":{"df":1,"docs":{"95":{"tf":1.0}},"l":{"df":0,"docs":{},"i":{"df":2,"docs":{"114":{"tf":1.4142135623730951},"5":{"tf":1.0}}}}}}},"df":0,"docs":{}},"o":{"df":0,"docs":{},"i":{"df":0,"docs":{},"t":{"df":1,"docs":{"10":{"tf":1.0}}}}}},"o":{"df":0,"docs":{},"n":{"df":1,"docs":{"45":{"tf":1.0}},"e":{"df":0,"docs":{},"n":{"df":0,"docs":{},"t":{"_":{"b":{"df":0,"docs":{},"i":{"df":0,"docs":{},"t":{"df":1,"docs":{"45":{"tf":1.0}}}}},"df":0,"docs":{}},"df":0,"docs":{},"i":{"df":2,"docs":{"124":{"tf":1.0},"45":{"tf":1.0}}}}}}},"s":{"df":1,"docs":{"40":{"tf":1.0}}}},"r":{"df":0,"docs":{},"e":{"df":0,"docs":{},"s":{"df":0,"docs":{},"s":{"df":7,"docs":{"107":{"tf":1.0},"12":{"tf":1.0},"123":{"tf":1.4142135623730951},"4":{"tf":1.0},"48":{"tf":1.4142135623730951},"70":{"tf":1.7320508075688772},"83":{"tf":1.0}}}}}}},"t":{"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"d":{"df":9,"docs":{"121":{"tf":1.0},"14":{"tf":1.0},"142":{"tf":1.7320508075688772},"146":{"tf":1.0},"147":{"tf":1.0},"45":{"tf":1.0},"83":{"tf":1.4142135623730951},"91":{"tf":1.0},"94":{"tf":1.0}}},"df":0,"docs":{},"s":{"df":8,"docs":{"109":{"tf":1.0},"125":{"tf":1.0},"145":{"tf":1.0},"146":{"tf":1.4142135623730951},"49":{"tf":1.0},"68":{"tf":1.7320508075688772},"84":{"tf":1.0},"91":{"tf":1.0}}}},"r":{"df":0,"docs":{},"n":{"df":2,"docs":{"151":{"tf":1.0},"34":{"tf":1.0}}}}},"r":{"a":{"c":{"df":0,"docs":{},"t":{"df":1,"docs":{"32":{"tf":1.0}}}},"df":2,"docs":{"13":{"tf":1.7320508075688772},"146":{"tf":1.0}}},"df":0,"docs":{},"e":{"df":0,"docs":{},"m":{"df":3,"docs":{"50":{"tf":1.0},"68":{"tf":1.0},"79":{"tf":1.0}}}}}}}},"f":{"(":{"a":{")":{":":{"=":{"df":0,"docs":{},"p":{"(":{"a":{")":{"df":0,"docs":{},"q":{"(":{"a":{")":{"df":0,"docs":{},"−":{"1":{"df":1,"docs":{"90":{"tf":1.0}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":1,"docs":{"90":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},",":{"b":{")":{"=":{"(":{"a":{"df":0,"docs":{},"−":{"df":0,"docs":{},"y":{")":{"/":{"(":{"b":{"df":0,"docs":{},"−":{"df":0,"docs":{},"z":{"df":1,"docs":{"77":{"tf":1.0}}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}},"a":{"+":{"b":{"df":0,"docs":{},"−":{"1":{"df":1,"docs":{"74":{"tf":1.0}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"1":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"…":{",":{"a":{"df":0,"docs":{},"l":{"df":0,"docs":{},"​":{",":{"b":{")":{"=":{"df":0,"docs":{},"i":{"=":{"1":{"df":0,"docs":{},"∑":{"df":0,"docs":{},"l":{"df":0,"docs":{},"​":{"df":0,"docs":{},"γ":{"df":0,"docs":{},"i":{"df":0,"docs":{},"​":{"(":{"a":{"df":0,"docs":{},"i":{"df":0,"docs":{},"​":{"df":0,"docs":{},"−":{"df":0,"docs":{},"y":{"df":0,"docs":{},"i":{"df":0,"docs":{},"​":{")":{"/":{"(":{"b":{"df":0,"docs":{},"−":{"df":0,"docs":{},"z":{"df":0,"docs":{},"i":{"df":1,"docs":{"80":{"tf":1.0}}}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}}}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"d":{"df":1,"docs":{"90":{"tf":1.0}}},"df":0,"docs":{},"h":{")":{"=":{"0":{"df":1,"docs":{"16":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"p":{"(":{"d":{"1":{"df":0,"docs":{},"​":{")":{",":{"d":{"1":{"df":0,"docs":{},"​":{")":{",":{"df":0,"docs":{},"…":{",":{"df":0,"docs":{},"f":{"(":{"df":0,"docs":{},"p":{"(":{"d":{"df":0,"docs":{},"m":{"df":0,"docs":{},"​":{")":{",":{"d":{"df":0,"docs":{},"m":{"df":1,"docs":{"78":{"tf":1.0}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"1":{"df":0,"docs":{},"​":{"(":{"d":{"1":{"df":0,"docs":{},"​":{")":{",":{"df":0,"docs":{},"…":{",":{"df":0,"docs":{},"p":{"df":0,"docs":{},"l":{"df":0,"docs":{},"​":{"(":{"d":{"1":{"df":0,"docs":{},"​":{")":{",":{"d":{"1":{"df":0,"docs":{},"​":{")":{",":{"df":0,"docs":{},"…":{",":{"df":0,"docs":{},"f":{"(":{"df":0,"docs":{},"p":{"1":{"df":0,"docs":{},"​":{"(":{"d":{"df":0,"docs":{},"m":{"df":0,"docs":{},"​":{")":{",":{"df":0,"docs":{},"…":{",":{"df":0,"docs":{},"p":{"df":0,"docs":{},"l":{"df":0,"docs":{},"​":{"(":{"d":{"df":0,"docs":{},"m":{"df":0,"docs":{},"​":{")":{",":{"d":{"df":0,"docs":{},"m":{"df":1,"docs":{"80":{"tf":1.0}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"y":{"1":{"df":0,"docs":{},"​":{")":{",":{"df":0,"docs":{},"…":{",":{"df":0,"docs":{},"f":{"(":{"df":0,"docs":{},"y":{"df":0,"docs":{},"l":{"df":1,"docs":{"90":{"tf":1.0}}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},",":{"d":{"1":{"df":0,"docs":{},"​":{")":{",":{"df":0,"docs":{},"…":{",":{"df":0,"docs":{},"f":{"(":{"df":0,"docs":{},"y":{"df":0,"docs":{},"m":{"df":0,"docs":{},"​":{",":{"d":{"df":0,"docs":{},"m":{"df":1,"docs":{"74":{"tf":1.0}}}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"z":{")":{"=":{"df":0,"docs":{},"i":{"df":1,"docs":{"19":{"tf":1.0}}}},"df":0,"docs":{}},"df":1,"docs":{"69":{"tf":1.0}}}},"+":{"df":0,"docs":{},"g":{"]":{"1":{"df":0,"docs":{},"​":{"=":{"[":{"df":0,"docs":{},"f":{"]":{"1":{"df":0,"docs":{},"​":{"+":{"[":{"df":0,"docs":{},"g":{"]":{"1":{"df":1,"docs":{"19":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},",":{"df":0,"docs":{},"g":{"df":1,"docs":{"14":{"tf":1.0}}}},"/":{"df":0,"docs":{},"g":{"df":1,"docs":{"69":{"tf":1.0}}}},"0":{"df":1,"docs":{"131":{"tf":1.0}}},"1":{"5":{"df":0,"docs":{},"​":{"=":{"0":{"df":1,"docs":{"131":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{},"~":{"df":0,"docs":{},"​":{"=":{"0":{"df":1,"docs":{"131":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}},":":{"a":{"df":0,"docs":{},"→":{"df":0,"docs":{},"f":{"df":1,"docs":{"90":{"tf":1.4142135623730951}}}}},"df":0,"docs":{},"f":{"df":0,"docs":{},"×":{"d":{"df":0,"docs":{},"→":{"df":0,"docs":{},"f":{"df":1,"docs":{"74":{"tf":1.0}}}}},"df":0,"docs":{}}},"s":{"df":0,"docs":{},"e":{"df":0,"docs":{},"t":{"(":{"d":{")":{"df":0,"docs":{},"→":{"df":0,"docs":{},"f":{"df":1,"docs":{"90":{"tf":1.0}}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"=":{"a":{"(":{"df":0,"docs":{},"ζ":{")":{"df":0,"docs":{},"q":{"df":0,"docs":{},"l":{"df":0,"docs":{},"​":{"+":{"b":{"(":{"df":0,"docs":{},"ζ":{")":{"df":0,"docs":{},"q":{"df":0,"docs":{},"r":{"df":1,"docs":{"21":{"tf":1.0}}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{},"z":{"df":0,"docs":{},"h":{"df":0,"docs":{},"​":{"df":0,"docs":{},"t":{"df":1,"docs":{"16":{"tf":1.0}}}}}}},"]":{"1":{"df":3,"docs":{"19":{"tf":1.4142135623730951},"21":{"tf":1.0},"35":{"tf":1.0}}},"df":0,"docs":{}},"a":{"c":{"df":0,"docs":{},"e":{"b":{"df":0,"docs":{},"o":{"df":0,"docs":{},"o":{"df":0,"docs":{},"k":{"'":{"df":1,"docs":{"103":{"tf":1.0}}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"t":{"df":9,"docs":{"13":{"tf":1.0},"131":{"tf":1.0},"139":{"tf":1.0},"14":{"tf":2.8284271247461903},"146":{"tf":1.0},"149":{"tf":1.0},"16":{"tf":1.7320508075688772},"5":{"tf":1.0},"68":{"tf":1.0}},"o":{"df":0,"docs":{},"r":{"df":8,"docs":{"125":{"tf":1.0},"14":{"tf":1.7320508075688772},"146":{"tf":1.0},"28":{"tf":1.0},"3":{"tf":1.0},"68":{"tf":1.7320508075688772},"73":{"tf":1.0},"91":{"tf":2.0}}}}}},"df":0,"docs":{},"l":{"df":0,"docs":{},"s":{"df":1,"docs":{"104":{"tf":1.0}}}},"r":{"df":2,"docs":{"16":{"tf":1.0},"70":{"tf":1.0}}},"s":{"df":0,"docs":{},"h":{"df":0,"docs":{},"i":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"df":2,"docs":{"114":{"tf":1.0},"151":{"tf":1.0}}}}}},"t":{"df":2,"docs":{"123":{"tf":1.4142135623730951},"99":{"tf":1.0}},"e":{"df":0,"docs":{},"r":{"df":1,"docs":{"123":{"tf":1.0}}}}}}},"b":{"a":{"df":0,"docs":{},"t":{"c":{"df":0,"docs":{},"h":{"df":3,"docs":{"28":{"tf":1.0},"32":{"tf":1.7320508075688772},"33":{"tf":1.0}},"​":{"(":{"df":0,"docs":{},"ζ":{")":{"=":{"df":0,"docs":{},"t":{"df":0,"docs":{},"ˉ":{"+":{"df":0,"docs":{},"υ":{"df":0,"docs":{},"p":{"df":0,"docs":{},"ˉ":{"df":0,"docs":{},"​":{"df":0,"docs":{},"n":{"c":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"υ":{"2":{"a":{"df":0,"docs":{},"ˉ":{"+":{"df":0,"docs":{},"υ":{"3":{"b":{"df":0,"docs":{},"ˉ":{"+":{"df":0,"docs":{},"υ":{"4":{"c":{"df":0,"docs":{},"ˉ":{"+":{"df":0,"docs":{},"υ":{"5":{"df":0,"docs":{},"s":{"df":0,"docs":{},"ˉ":{"df":0,"docs":{},"σ":{"1":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"υ":{"6":{"df":0,"docs":{},"s":{"df":0,"docs":{},"ˉ":{"df":0,"docs":{},"σ":{"2":{"df":1,"docs":{"32":{"tf":1.0}}},"df":0,"docs":{}}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"]":{"1":{"df":1,"docs":{"32":{"tf":1.0}},"​":{"=":{"[":{"df":0,"docs":{},"t":{"df":0,"docs":{},"p":{"a":{"df":0,"docs":{},"r":{"df":0,"docs":{},"t":{"df":0,"docs":{},"i":{"a":{"df":0,"docs":{},"l":{"df":0,"docs":{},"​":{"]":{"1":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"υ":{"[":{"df":0,"docs":{},"p":{"df":0,"docs":{},"n":{"c":{"df":0,"docs":{},"​":{"]":{"1":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"υ":{"2":{"[":{"a":{"]":{"1":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"υ":{"3":{"[":{"b":{"]":{"1":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"υ":{"4":{"[":{"c":{"]":{"1":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"υ":{"5":{"[":{"df":0,"docs":{},"s":{"df":0,"docs":{},"σ":{"1":{"df":0,"docs":{},"​":{"]":{"1":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"υ":{"6":{"[":{"df":0,"docs":{},"s":{"df":0,"docs":{},"σ":{"2":{"df":0,"docs":{},"​":{"]":{"1":{"df":1,"docs":{"32":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":21,"docs":{"14":{"tf":2.6457513110645907},"141":{"tf":1.0},"142":{"tf":1.4142135623730951},"146":{"tf":1.7320508075688772},"147":{"tf":1.4142135623730951},"16":{"tf":1.4142135623730951},"19":{"tf":2.449489742783178},"21":{"tf":2.0},"35":{"tf":1.4142135623730951},"5":{"tf":1.7320508075688772},"69":{"tf":1.4142135623730951},"70":{"tf":1.4142135623730951},"73":{"tf":1.0},"74":{"tf":1.4142135623730951},"75":{"tf":1.0},"80":{"tf":1.0},"82":{"tf":1.0},"90":{"tf":1.7320508075688772},"91":{"tf":1.4142135623730951},"94":{"tf":2.0},"95":{"tf":1.4142135623730951}},"e":{"1":{"7":{":":{":":{"df":0,"docs":{},"n":{"df":0,"docs":{},"e":{"df":0,"docs":{},"w":{"(":{"1":{"df":1,"docs":{"108":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},":":{":":{"df":0,"docs":{},"f":{"df":0,"docs":{},"r":{"df":0,"docs":{},"o":{"df":0,"docs":{},"m":{"(":{"1":{"df":1,"docs":{"104":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"o":{"df":0,"docs":{},"n":{"df":1,"docs":{"104":{"tf":1.4142135623730951}}}}},"df":0,"docs":{}},"a":{"df":0,"docs":{},"t":{"df":0,"docs":{},"u":{"df":0,"docs":{},"r":{"df":1,"docs":{"70":{"tf":1.0}}}}}},"df":0,"docs":{},"e":{"d":{"df":1,"docs":{"34":{"tf":1.0}}},"df":0,"docs":{}},"t":{"c":{"df":0,"docs":{},"h":{"df":1,"docs":{"146":{"tf":1.4142135623730951}}}},"df":0,"docs":{}},"w":{"df":10,"docs":{"109":{"tf":1.0},"111":{"tf":1.0},"123":{"tf":1.4142135623730951},"124":{"tf":1.0},"13":{"tf":1.0},"14":{"tf":1.0},"16":{"tf":1.0},"32":{"tf":1.0},"37":{"tf":1.0},"64":{"tf":1.0}}}},"f":{"df":0,"docs":{},"t":{"df":4,"docs":{"121":{"tf":2.0},"2":{"tf":1.0},"3":{"tf":1.4142135623730951},"99":{"tf":1.4142135623730951}}}},"i":{"a":{"df":0,"docs":{},"t":{"df":7,"docs":{"118":{"tf":1.4142135623730951},"39":{"tf":1.0},"40":{"tf":1.0},"42":{"tf":2.0},"89":{"tf":1.0},"94":{"tf":1.0},"95":{"tf":1.0}}}},"b":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"a":{"c":{"c":{"df":0,"docs":{},"i":{"_":{"df":0,"docs":{},"t":{"df":0,"docs":{},"r":{"a":{"c":{"df":1,"docs":{"108":{"tf":1.0}},"e":{"(":{"[":{"df":0,"docs":{},"f":{"df":0,"docs":{},"e":{"1":{"7":{":":{":":{"df":0,"docs":{},"n":{"df":0,"docs":{},"e":{"df":0,"docs":{},"w":{"(":{"1":{"df":1,"docs":{"108":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"a":{"df":0,"docs":{},"i":{"df":0,"docs":{},"r":{"<":{"df":0,"docs":{},"f":{">":{">":{"(":{"&":{"df":0,"docs":{},"p":{"df":0,"docs":{},"r":{"df":0,"docs":{},"o":{"df":0,"docs":{},"o":{"df":0,"docs":{},"f":{"df":1,"docs":{"104":{"tf":1.0}}}}}}},"t":{"df":0,"docs":{},"r":{"a":{"c":{"df":1,"docs":{"104":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":18,"docs":{"104":{"tf":1.4142135623730951},"105":{"tf":1.4142135623730951},"106":{"tf":1.7320508075688772},"107":{"tf":1.0},"108":{"tf":1.0},"109":{"tf":1.0},"110":{"tf":1.0},"111":{"tf":1.0},"112":{"tf":1.0},"114":{"tf":1.7320508075688772},"117":{"tf":1.0},"49":{"tf":1.7320508075688772},"50":{"tf":1.4142135623730951},"51":{"tf":1.4142135623730951},"64":{"tf":1.0},"66":{"tf":1.0},"68":{"tf":1.0},"84":{"tf":1.0}},"p":{"df":0,"docs":{},"u":{"b":{"df":0,"docs":{},"l":{"df":0,"docs":{},"i":{"c":{"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"df":0,"docs":{},"p":{"df":0,"docs":{},"u":{"df":0,"docs":{},"t":{"df":1,"docs":{"104":{"tf":1.0}}}}}}}},"df":0,"docs":{}}}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":1,"docs":{"131":{"tf":1.0}},"e":{"df":0,"docs":{},"l":{"d":{"df":22,"docs":{"108":{"tf":1.0},"109":{"tf":1.0},"114":{"tf":1.0},"14":{"tf":4.58257569495584},"16":{"tf":1.0},"19":{"tf":1.4142135623730951},"35":{"tf":1.0},"36":{"tf":1.0},"41":{"tf":2.23606797749979},"44":{"tf":1.0},"5":{"tf":1.0},"52":{"tf":1.0},"7":{"tf":1.4142135623730951},"70":{"tf":1.0},"73":{"tf":1.0},"74":{"tf":1.4142135623730951},"82":{"tf":1.0},"90":{"tf":1.0},"91":{"tf":1.0},"95":{"tf":1.0},"98":{"tf":1.0},"99":{"tf":1.0}},"e":{"df":0,"docs":{},"l":{"df":0,"docs":{},"e":{"df":0,"docs":{},"m":{"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"df":0,"docs":{},"t":{":":{":":{"df":0,"docs":{},"f":{"df":0,"docs":{},"r":{"df":0,"docs":{},"o":{"df":0,"docs":{},"m":{"(":{"2":{"_":{"df":0,"docs":{},"u":{"6":{"4":{"df":1,"docs":{"35":{"tf":1.4142135623730951}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"3":{"_":{"df":0,"docs":{},"u":{"6":{"4":{"df":1,"docs":{"35":{"tf":1.4142135623730951}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":1,"docs":{"44":{"tf":1.0}}},"4":{"df":1,"docs":{"44":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}}}}}},"df":0,"docs":{}},"<":{"df":0,"docs":{},"f":{"df":1,"docs":{"35":{"tf":3.1622776601683795}}}},"df":0,"docs":{}}}}}}}}},"df":0,"docs":{}}},"f":{"df":0,"docs":{},"t":{"df":0,"docs":{},"h":{"df":1,"docs":{"52":{"tf":1.0}}}}},"l":{"df":0,"docs":{},"e":{"df":2,"docs":{"144":{"tf":2.23606797749979},"145":{"tf":1.0}}},"l":{"df":6,"docs":{"13":{"tf":1.0},"130":{"tf":2.449489742783178},"134":{"tf":2.0},"139":{"tf":1.0},"146":{"tf":2.23606797749979},"36":{"tf":1.7320508075688772}}}},"n":{"a":{"df":0,"docs":{},"l":{"df":8,"docs":{"13":{"tf":1.0},"135":{"tf":1.0},"144":{"tf":1.4142135623730951},"146":{"tf":2.0},"147":{"tf":1.0},"16":{"tf":1.0},"35":{"tf":1.4142135623730951},"44":{"tf":1.0}}}},"d":{"df":3,"docs":{"128":{"tf":1.4142135623730951},"3":{"tf":1.4142135623730951},"88":{"tf":1.0}}},"df":0,"docs":{},"e":{"df":1,"docs":{"12":{"tf":1.0}}},"i":{"df":0,"docs":{},"t":{"df":10,"docs":{"14":{"tf":1.0},"19":{"tf":1.0},"5":{"tf":1.0},"52":{"tf":1.0},"69":{"tf":1.0},"7":{"tf":1.0},"70":{"tf":1.0},"90":{"tf":1.0},"91":{"tf":1.0},"98":{"tf":1.0}}}}},"r":{"df":0,"docs":{},"s":{"df":0,"docs":{},"t":{"_":{"df":0,"docs":{},"r":{"df":0,"docs":{},"o":{"df":0,"docs":{},"w":{"[":{"0":{"df":2,"docs":{"107":{"tf":1.4142135623730951},"114":{"tf":1.0}}},"df":0,"docs":{}},"df":2,"docs":{"107":{"tf":1.0},"114":{"tf":1.0}}}}}},"df":29,"docs":{"105":{"tf":1.0},"107":{"tf":1.4142135623730951},"108":{"tf":1.0},"114":{"tf":1.4142135623730951},"128":{"tf":1.0},"13":{"tf":1.7320508075688772},"130":{"tf":1.0},"134":{"tf":1.0},"135":{"tf":1.0},"14":{"tf":1.4142135623730951},"145":{"tf":1.0},"146":{"tf":1.7320508075688772},"147":{"tf":2.0},"149":{"tf":1.4142135623730951},"20":{"tf":1.0},"23":{"tf":1.4142135623730951},"31":{"tf":1.0},"32":{"tf":1.4142135623730951},"42":{"tf":1.0},"46":{"tf":1.0},"49":{"tf":1.4142135623730951},"52":{"tf":1.4142135623730951},"59":{"tf":1.7320508075688772},"66":{"tf":1.0},"69":{"tf":1.0},"70":{"tf":1.0},"8":{"tf":1.0},"83":{"tf":1.0},"9":{"tf":1.0}},"l":{"df":0,"docs":{},"i":{"df":1,"docs":{"32":{"tf":1.0}}}}}}},"t":{"df":2,"docs":{"103":{"tf":1.0},"133":{"tf":1.0}}},"v":{"df":0,"docs":{},"e":{"df":1,"docs":{"12":{"tf":1.0}}}},"x":{"df":5,"docs":{"146":{"tf":1.0},"5":{"tf":1.0},"73":{"tf":1.0},"88":{"tf":1.0},"91":{"tf":1.0}}},"​":{"df":0,"docs":{},"~":{"df":0,"docs":{},"​":{"=":{"df":0,"docs":{},"∑":{"df":0,"docs":{},"j":{"=":{"df":0,"docs":{},"i":{"1":{"4":{"df":0,"docs":{},"​":{"2":{"df":0,"docs":{},"j":{"df":0,"docs":{},"−":{"df":0,"docs":{},"i":{"df":0,"docs":{},"f":{"df":0,"docs":{},"i":{"df":1,"docs":{"131":{"tf":1.0}}}}}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{},"−":{"2":{"df":0,"docs":{},"f":{"df":0,"docs":{},"~":{"df":0,"docs":{},"​":{"df":0,"docs":{},"i":{"+":{"1":{"df":0,"docs":{},"​":{")":{"(":{"df":0,"docs":{},"f":{"df":0,"docs":{},"i":{"df":0,"docs":{},"​":{"df":0,"docs":{},"~":{"df":0,"docs":{},"​":{"df":0,"docs":{},"−":{"2":{"df":0,"docs":{},"f":{"df":0,"docs":{},"~":{"df":0,"docs":{},"​":{"df":0,"docs":{},"i":{"+":{"1":{"df":0,"docs":{},"​":{"df":0,"docs":{},"−":{"1":{")":{"=":{"0":{"df":1,"docs":{"131":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}}}}},"df":0,"docs":{}}}}}}}},"df":0,"docs":{}},"=":{"df":0,"docs":{},"f":{"df":0,"docs":{},"i":{"df":1,"docs":{"131":{"tf":1.0}}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}}}},"df":0,"docs":{}}}},"∗":{"(":{"df":0,"docs":{},"f":{"df":0,"docs":{},"i":{"df":0,"docs":{},"​":{"df":0,"docs":{},"−":{"1":{")":{"=":{"0":{"df":1,"docs":{"131":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}}}},"df":0,"docs":{}}}},"l":{"a":{"df":0,"docs":{},"g":{"a":{"df":1,"docs":{"149":{"tf":1.0}}},"b":{"df":1,"docs":{"149":{"tf":1.4142135623730951}}},"df":6,"docs":{"131":{"tf":2.0},"142":{"tf":1.4142135623730951},"146":{"tf":3.0},"147":{"tf":1.4142135623730951},"149":{"tf":3.1622776601683795},"150":{"tf":2.0}}}},"df":0,"docs":{},"e":{"df":0,"docs":{},"x":{"df":0,"docs":{},"i":{"b":{"df":0,"docs":{},"l":{"df":2,"docs":{"12":{"tf":1.4142135623730951},"4":{"tf":1.0}}}},"df":0,"docs":{}}}},"o":{"df":0,"docs":{},"w":{"df":2,"docs":{"103":{"tf":1.0},"50":{"tf":1.0}}}}},"n":{"df":6,"docs":{"104":{"tf":1.0},"106":{"tf":1.0},"107":{"tf":1.0},"114":{"tf":1.0},"44":{"tf":1.0},"45":{"tf":1.4142135623730951}}},"o":{"c":{"df":0,"docs":{},"u":{"df":1,"docs":{"149":{"tf":1.0}}}},"df":0,"docs":{},"l":{"df":0,"docs":{},"l":{"df":0,"docs":{},"o":{"df":0,"docs":{},"w":{"df":49,"docs":{"10":{"tf":1.4142135623730951},"101":{"tf":1.0},"102":{"tf":1.0},"103":{"tf":1.0},"11":{"tf":1.4142135623730951},"111":{"tf":1.0},"113":{"tf":1.0},"114":{"tf":1.7320508075688772},"123":{"tf":1.0},"128":{"tf":1.0},"13":{"tf":1.4142135623730951},"137":{"tf":1.0},"14":{"tf":3.7416573867739413},"142":{"tf":1.4142135623730951},"146":{"tf":1.4142135623730951},"147":{"tf":1.7320508075688772},"151":{"tf":1.0},"16":{"tf":2.0},"19":{"tf":1.4142135623730951},"21":{"tf":1.0},"22":{"tf":1.0},"24":{"tf":1.0},"31":{"tf":1.0},"32":{"tf":1.4142135623730951},"35":{"tf":1.7320508075688772},"37":{"tf":1.0},"42":{"tf":1.0},"44":{"tf":1.4142135623730951},"45":{"tf":1.0},"48":{"tf":1.0},"49":{"tf":1.0},"5":{"tf":1.0},"50":{"tf":1.0},"52":{"tf":1.0},"54":{"tf":1.0},"55":{"tf":1.0},"56":{"tf":1.0},"59":{"tf":1.4142135623730951},"69":{"tf":1.0},"7":{"tf":1.4142135623730951},"72":{"tf":1.0},"73":{"tf":1.0},"75":{"tf":1.0},"84":{"tf":1.0},"88":{"tf":1.0},"91":{"tf":1.0},"92":{"tf":1.0},"94":{"tf":1.7320508075688772},"95":{"tf":2.23606797749979}}}}}},"r":{"c":{"df":2,"docs":{"13":{"tf":1.4142135623730951},"20":{"tf":1.0}}},"df":0,"docs":{},"m":{"a":{"df":0,"docs":{},"l":{"df":1,"docs":{"69":{"tf":1.0}}},"t":{"df":1,"docs":{"69":{"tf":1.0}}}},"df":12,"docs":{"100":{"tf":1.0},"108":{"tf":1.0},"123":{"tf":1.0},"14":{"tf":2.23606797749979},"146":{"tf":1.0},"35":{"tf":1.0},"52":{"tf":1.0},"68":{"tf":1.0},"73":{"tf":1.0},"9":{"tf":1.0},"92":{"tf":1.0},"99":{"tf":1.0}},"e":{"df":0,"docs":{},"r":{"df":2,"docs":{"19":{"tf":1.0},"73":{"tf":1.0}}}},"u":{"df":0,"docs":{},"l":{"a":{"df":2,"docs":{"58":{"tf":1.0},"60":{"tf":1.0}}},"df":0,"docs":{}}}}},"u":{"df":0,"docs":{},"n":{"d":{"df":8,"docs":{"127":{"tf":1.0},"130":{"tf":1.4142135623730951},"133":{"tf":1.0},"134":{"tf":1.4142135623730951},"137":{"tf":1.0},"138":{"tf":1.0},"38":{"tf":1.0},"4":{"tf":1.0}}},"df":0,"docs":{}},"r":{"df":2,"docs":{"21":{"tf":1.0},"56":{"tf":1.0}},"i":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":3,"docs":{"123":{"tf":1.0},"3":{"tf":1.4142135623730951},"99":{"tf":1.0}}}}}}}},"p":{"df":6,"docs":{"136":{"tf":1.4142135623730951},"141":{"tf":1.0},"142":{"tf":1.0},"144":{"tf":1.0},"146":{"tf":1.4142135623730951},"147":{"tf":1.0}}},"r":{"a":{"df":0,"docs":{},"m":{"df":0,"docs":{},"e":{".":{"df":0,"docs":{},"g":{"df":0,"docs":{},"e":{"df":0,"docs":{},"t":{"_":{"df":0,"docs":{},"r":{"df":0,"docs":{},"o":{"df":0,"docs":{},"w":{"(":{"0":{"df":2,"docs":{"107":{"tf":1.0},"114":{"tf":1.0}}},"1":{"df":2,"docs":{"107":{"tf":1.0},"114":{"tf":1.0}}},"2":{"df":2,"docs":{"107":{"tf":1.0},"114":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}}}}},":":{":":{"df":0,"docs":{},"r":{"df":0,"docs":{},"e":{"a":{"d":{"_":{"df":0,"docs":{},"f":{"df":0,"docs":{},"r":{"df":0,"docs":{},"o":{"df":0,"docs":{},"m":{"_":{"df":0,"docs":{},"t":{"df":0,"docs":{},"r":{"a":{"c":{"df":1,"docs":{"114":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":7,"docs":{"107":{"tf":1.4142135623730951},"109":{"tf":1.0},"113":{"tf":1.0},"114":{"tf":2.8284271247461903},"117":{"tf":2.23606797749979},"149":{"tf":1.7320508075688772},"84":{"tf":1.0}}}}},"df":1,"docs":{"38":{"tf":1.0}},"e":{"df":0,"docs":{},"s":{"df":0,"docs":{},"h":{"df":1,"docs":{"40":{"tf":1.0}}}}},"i":{"_":{"df":0,"docs":{},"n":{"df":0,"docs":{},"u":{"df":0,"docs":{},"m":{"b":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"_":{"df":0,"docs":{},"o":{"df":0,"docs":{},"f":{"_":{"df":0,"docs":{},"q":{"df":0,"docs":{},"u":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":0,"docs":{},"i":{"df":1,"docs":{"109":{"tf":1.0}}}}}}}},"df":0,"docs":{}}}},"df":0,"docs":{}}}},"df":0,"docs":{}}}}},"d":{"a":{"df":0,"docs":{},"y":{"df":1,"docs":{"81":{"tf":1.0}}}},"df":0,"docs":{}},"df":22,"docs":{"102":{"tf":1.0},"109":{"tf":1.0},"112":{"tf":1.4142135623730951},"113":{"tf":1.0},"119":{"tf":1.4142135623730951},"125":{"tf":1.7320508075688772},"57":{"tf":2.0},"59":{"tf":1.7320508075688772},"62":{"tf":1.4142135623730951},"63":{"tf":1.0},"68":{"tf":1.7320508075688772},"71":{"tf":1.0},"73":{"tf":2.0},"74":{"tf":2.0},"75":{"tf":1.0},"77":{"tf":1.4142135623730951},"78":{"tf":1.0},"80":{"tf":1.7320508075688772},"81":{"tf":1.7320508075688772},"91":{"tf":1.0},"94":{"tf":1.4142135623730951},"95":{"tf":1.0}},"e":{"df":0,"docs":{},"n":{"d":{"df":0,"docs":{},"l":{"df":0,"docs":{},"i":{"df":1,"docs":{"83":{"tf":1.0}}}}},"df":0,"docs":{}}}}},"u":{"df":0,"docs":{},"l":{"df":0,"docs":{},"l":{"df":2,"docs":{"10":{"tf":1.0},"131":{"tf":1.0}}}},"n":{"c":{"df":0,"docs":{},"t":{"df":0,"docs":{},"i":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"df":24,"docs":{"104":{"tf":1.4142135623730951},"108":{"tf":1.0},"110":{"tf":1.0},"111":{"tf":1.0},"113":{"tf":1.0},"114":{"tf":1.0},"118":{"tf":1.0},"142":{"tf":1.0},"146":{"tf":1.4142135623730951},"35":{"tf":1.7320508075688772},"40":{"tf":1.0},"45":{"tf":2.6457513110645907},"54":{"tf":1.0},"55":{"tf":1.0},"56":{"tf":1.0},"74":{"tf":1.7320508075688772},"77":{"tf":1.0},"80":{"tf":1.0},"84":{"tf":1.4142135623730951},"85":{"tf":1.0},"88":{"tf":1.0},"90":{"tf":1.7320508075688772},"91":{"tf":1.4142135623730951},"95":{"tf":1.0}}}}}}},"df":0,"docs":{}},"r":{"df":0,"docs":{},"t":{"df":0,"docs":{},"h":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":4,"docs":{"100":{"tf":1.0},"147":{"tf":1.0},"21":{"tf":1.0},"74":{"tf":1.0}},"m":{"df":0,"docs":{},"o":{"df":0,"docs":{},"r":{"df":1,"docs":{"13":{"tf":1.0}}}}}}}}}}}},"g":{"+":{"df":0,"docs":{},"ζ":{"df":0,"docs":{},"k":{"df":0,"docs":{},"​":{"df":0,"docs":{},"h":{"df":1,"docs":{"95":{"tf":1.4142135623730951}}}}}}},",":{"df":0,"docs":{},"h":{"df":1,"docs":{"95":{"tf":1.0}}}},"1":{"df":0,"docs":{},"p":{"df":0,"docs":{},"o":{"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"df":0,"docs":{},"t":{"df":1,"docs":{"35":{"tf":2.8284271247461903}}}}}}}},"3":{"df":1,"docs":{"124":{"tf":1.0}}},"=":{"df":0,"docs":{},"ω":{"b":{"df":1,"docs":{"91":{"tf":1.0}}},"df":0,"docs":{}}},"^":{"0":{"df":2,"docs":{"52":{"tf":1.0},"55":{"tf":1.0}}},"1":{"df":2,"docs":{"52":{"tf":1.0},"55":{"tf":1.0}}},"2":{"df":2,"docs":{"52":{"tf":1.4142135623730951},"55":{"tf":1.0}}},"3":{"df":2,"docs":{"52":{"tf":1.0},"55":{"tf":1.0}}},"4":{"df":2,"docs":{"52":{"tf":1.0},"55":{"tf":1.0}}},"5":{"df":2,"docs":{"52":{"tf":1.7320508075688772},"55":{"tf":1.0}}},"8":{"df":1,"docs":{"52":{"tf":1.0}}},"df":0,"docs":{},"i":{"df":2,"docs":{"52":{"tf":1.0},"62":{"tf":1.0}}}},"a":{"df":0,"docs":{},"m":{"df":0,"docs":{},"m":{"a":{"_":{"df":0,"docs":{},"i":{"df":1,"docs":{"59":{"tf":1.0}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"p":{"df":1,"docs":{"134":{"tf":1.0}}},"t":{"df":0,"docs":{},"e":{"df":8,"docs":{"10":{"tf":2.6457513110645907},"11":{"tf":1.0},"12":{"tf":2.23606797749979},"13":{"tf":1.0},"14":{"tf":1.0},"16":{"tf":1.4142135623730951},"20":{"tf":1.0},"9":{"tf":3.3166247903554}}},"h":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":1,"docs":{"126":{"tf":1.0}}}}}},"v":{"df":0,"docs":{},"e":{"df":2,"docs":{"113":{"tf":1.4142135623730951},"63":{"tf":1.4142135623730951}}}}},"df":13,"docs":{"124":{"tf":2.23606797749979},"125":{"tf":1.4142135623730951},"14":{"tf":2.449489742783178},"142":{"tf":1.4142135623730951},"146":{"tf":2.0},"147":{"tf":1.4142135623730951},"16":{"tf":1.0},"19":{"tf":2.0},"52":{"tf":2.23606797749979},"54":{"tf":1.0},"68":{"tf":2.0},"69":{"tf":1.0},"82":{"tf":1.0}},"e":{"df":0,"docs":{},"n":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":26,"docs":{"103":{"tf":1.0},"104":{"tf":1.4142135623730951},"108":{"tf":1.0},"118":{"tf":1.0},"119":{"tf":1.4142135623730951},"124":{"tf":2.0},"144":{"tf":1.4142135623730951},"146":{"tf":1.0},"149":{"tf":1.0},"34":{"tf":1.4142135623730951},"35":{"tf":1.4142135623730951},"4":{"tf":1.0},"44":{"tf":1.4142135623730951},"48":{"tf":1.0},"49":{"tf":1.0},"50":{"tf":1.7320508075688772},"52":{"tf":1.0},"65":{"tf":1.4142135623730951},"68":{"tf":1.0},"70":{"tf":1.0},"77":{"tf":1.0},"8":{"tf":1.0},"80":{"tf":1.4142135623730951},"82":{"tf":1.0},"84":{"tf":1.0},"90":{"tf":1.0}}}}},"t":{"_":{"df":0,"docs":{},"r":{"df":0,"docs":{},"o":{"df":0,"docs":{},"w":{"df":1,"docs":{"107":{"tf":1.0}}}}}},"df":4,"docs":{"118":{"tf":1.4142135623730951},"151":{"tf":1.0},"75":{"tf":1.0},"82":{"tf":1.0}}}},"i":{":":{"0":{"df":0,"docs":{},"≤":{"df":0,"docs":{},"i":{"<":{"2":{"df":0,"docs":{},"n":{"df":1,"docs":{"112":{"tf":1.0}}}},"df":0,"docs":{},"n":{"df":1,"docs":{"124":{"tf":1.0}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":1,"docs":{"125":{"tf":1.4142135623730951}},"l":{"df":0,"docs":{},"l":{"df":0,"docs":{},"e":{"df":0,"docs":{},"t":{"df":0,"docs":{},"t":{"'":{"df":1,"docs":{"103":{"tf":1.0}}},"df":0,"docs":{}}}}}},"v":{"df":0,"docs":{},"e":{"df":8,"docs":{"114":{"tf":1.0},"14":{"tf":1.0},"149":{"tf":1.4142135623730951},"151":{"tf":1.7320508075688772},"19":{"tf":1.0},"46":{"tf":1.0},"50":{"tf":1.0},"7":{"tf":1.0}},"n":{"df":14,"docs":{"114":{"tf":1.7320508075688772},"128":{"tf":1.0},"130":{"tf":1.0},"14":{"tf":1.0},"146":{"tf":1.0},"16":{"tf":1.4142135623730951},"23":{"tf":1.0},"36":{"tf":1.0},"63":{"tf":1.0},"72":{"tf":1.0},"75":{"tf":1.0},"76":{"tf":1.0},"90":{"tf":1.0},"92":{"tf":1.0}}}}},"z":{"a":{"df":1,"docs":{"103":{"tf":1.0}}},"df":0,"docs":{}}},"k":{"df":1,"docs":{"124":{"tf":1.0}}},"l":{"df":0,"docs":{},"o":{"b":{"a":{"df":0,"docs":{},"l":{"df":1,"docs":{"70":{"tf":1.4142135623730951}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"n":{"+":{"1":{"=":{"df":0,"docs":{},"g":{"df":0,"docs":{},"g":{"df":0,"docs":{},"n":{"+":{"2":{"=":{"df":0,"docs":{},"g":{"2":{"df":1,"docs":{"124":{"tf":1.0}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}},"=":{"1":{"df":1,"docs":{"124":{"tf":1.0}}},"df":0,"docs":{}},"a":{"df":0,"docs":{},"r":{"df":0,"docs":{},"k":{"df":1,"docs":{"34":{"tf":1.0}}}}},"df":0,"docs":{}},"o":{"a":{"df":0,"docs":{},"l":{"df":4,"docs":{"103":{"tf":1.0},"46":{"tf":1.0},"50":{"tf":1.0},"77":{"tf":1.0}}}},"df":17,"docs":{"103":{"tf":1.0},"104":{"tf":1.4142135623730951},"109":{"tf":1.0},"111":{"tf":1.4142135623730951},"114":{"tf":1.0},"118":{"tf":1.0},"124":{"tf":1.0},"14":{"tf":1.7320508075688772},"42":{"tf":1.0},"46":{"tf":1.0},"48":{"tf":1.0},"49":{"tf":1.0},"50":{"tf":1.0},"51":{"tf":1.0},"53":{"tf":1.0},"57":{"tf":1.0},"68":{"tf":1.0}},"l":{"d":{"b":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":0,"docs":{},"g":{"df":4,"docs":{"103":{"tf":1.0},"19":{"tf":1.0},"35":{"tf":1.0},"38":{"tf":1.0}}}}}},"df":0,"docs":{}},"df":0,"docs":{}},"o":{"d":{"df":4,"docs":{"14":{"tf":1.0},"46":{"tf":1.0},"50":{"tf":1.0},"81":{"tf":1.0}}},"df":0,"docs":{}},"t":{"df":0,"docs":{},"h":{"df":0,"docs":{},"i":{"c":{"df":1,"docs":{"95":{"tf":1.0}}},"df":0,"docs":{}}}}},"p":{"df":0,"docs":{},"u":{"df":1,"docs":{"1":{"tf":1.0}}}},"r":{"a":{"df":0,"docs":{},"n":{"df":0,"docs":{},"t":{"df":1,"docs":{"14":{"tf":1.0}}}}},"df":0,"docs":{},"e":{"a":{"df":0,"docs":{},"t":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":2,"docs":{"3":{"tf":1.0},"68":{"tf":1.0}}}}}},"df":0,"docs":{},"e":{"df":0,"docs":{},"k":{"df":1,"docs":{"95":{"tf":1.0}}}}},"i":{"df":0,"docs":{},"n":{"d":{"df":4,"docs":{"88":{"tf":1.0},"91":{"tf":1.0},"94":{"tf":1.0},"95":{"tf":1.0}},"i":{"df":0,"docs":{},"n":{"df":0,"docs":{},"g":{"_":{"df":0,"docs":{},"f":{"a":{"c":{"df":0,"docs":{},"t":{"df":0,"docs":{},"o":{"df":0,"docs":{},"r":{"df":1,"docs":{"88":{"tf":1.4142135623730951}}}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}}},"df":0,"docs":{}}},"o":{"df":0,"docs":{},"u":{"df":0,"docs":{},"p":{"df":3,"docs":{"142":{"tf":1.4142135623730951},"147":{"tf":1.0},"19":{"tf":2.23606797749979}}}}}}},"h":{"(":{"df":0,"docs":{},"h":{"(":{"df":0,"docs":{},"p":{"df":0,"docs":{},"r":{"df":0,"docs":{},"e":{"df":0,"docs":{},"f":{"df":0,"docs":{},"i":{"df":0,"docs":{},"x":{"df":1,"docs":{"88":{"tf":1.0}}}}}}}}},"df":0,"docs":{}},"x":{")":{"=":{"df":0,"docs":{},"h":{"1":{"df":0,"docs":{},"​":{"(":{"df":0,"docs":{},"x":{"2":{")":{"+":{"df":0,"docs":{},"x":{"df":0,"docs":{},"h":{"2":{"df":0,"docs":{},"​":{"(":{"df":0,"docs":{},"x":{"2":{"df":2,"docs":{"116":{"tf":1.0},"67":{"tf":1.0}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"β":{"1":{"df":0,"docs":{},"​":{"b":{"(":{"df":0,"docs":{},"x":{")":{"+":{"df":0,"docs":{},"β":{"2":{"df":0,"docs":{},"​":{"c":{"(":{"df":0,"docs":{},"x":{"df":1,"docs":{"56":{"tf":1.0}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"0":{"df":2,"docs":{"112":{"tf":1.0},"113":{"tf":1.0}}},"_":{"0":{"df":2,"docs":{"62":{"tf":1.0},"63":{"tf":1.0}}},"df":0,"docs":{}},"df":1,"docs":{"56":{"tf":1.0}}},"z":{")":{"=":{"df":0,"docs":{},"f":{"(":{"df":0,"docs":{},"z":{")":{"/":{"df":0,"docs":{},"g":{"(":{"df":0,"docs":{},"z":{"df":1,"docs":{"69":{"tf":1.0}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"β":{"1":{"df":0,"docs":{},"​":{"b":{"(":{"df":0,"docs":{},"z":{")":{"+":{"df":0,"docs":{},"β":{"2":{"df":0,"docs":{},"​":{"c":{"(":{"df":0,"docs":{},"z":{"df":3,"docs":{"113":{"tf":1.0},"58":{"tf":1.0},"63":{"tf":1.0}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":7,"docs":{"112":{"tf":1.0},"113":{"tf":1.4142135623730951},"59":{"tf":1.0},"62":{"tf":1.0},"63":{"tf":1.4142135623730951},"69":{"tf":1.0},"85":{"tf":1.0}}}},".":{"df":0,"docs":{},"t":{"df":0,"docs":{},"h":{"df":0,"docs":{},"i":{"df":1,"docs":{"14":{"tf":1.0}}}}}},"1":{"df":2,"docs":{"94":{"tf":1.4142135623730951},"95":{"tf":1.0}},"​":{"(":{"df":0,"docs":{},"z":{"2":{"df":1,"docs":{"94":{"tf":1.4142135623730951}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"2":{"df":2,"docs":{"94":{"tf":1.4142135623730951},"95":{"tf":1.0}},"​":{"(":{"df":0,"docs":{},"z":{"2":{"df":1,"docs":{"94":{"tf":1.4142135623730951}}},"df":0,"docs":{}}},"df":0,"docs":{}}},":":{"=":{"df":0,"docs":{},"k":{"df":0,"docs":{},"∑":{"df":0,"docs":{},"​":{"df":0,"docs":{},"β":{"df":0,"docs":{},"k":{"df":0,"docs":{},"t":{"df":0,"docs":{},"​":{"c":{"df":0,"docs":{},"k":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"j":{"df":0,"docs":{},"∑":{"df":0,"docs":{},"​":{"df":0,"docs":{},"β":{"df":0,"docs":{},"j":{"b":{"df":0,"docs":{},"​":{"b":{"df":0,"docs":{},"j":{"df":1,"docs":{"94":{"tf":1.0}}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}}}},"df":0,"docs":{}}}},"df":0,"docs":{}}}}}}}},"η":{"1":{"df":0,"docs":{},"z":{"2":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"z":{"df":0,"docs":{},"η":{"2":{"df":0,"docs":{},"z":{"2":{"df":1,"docs":{"95":{"tf":1.0}}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"=":{"df":0,"docs":{},"h":{"1":{"df":0,"docs":{},"​":{"(":{"df":0,"docs":{},"x":{"2":{")":{"+":{"df":0,"docs":{},"x":{"df":0,"docs":{},"h":{"2":{"df":0,"docs":{},"​":{"(":{"df":0,"docs":{},"x":{"2":{"df":1,"docs":{"94":{"tf":1.0}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"k":{"df":0,"docs":{},"∑":{"df":0,"docs":{},"​":{"df":0,"docs":{},"β":{"df":0,"docs":{},"k":{"df":0,"docs":{},"t":{"df":0,"docs":{},"​":{"c":{"df":0,"docs":{},"k":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"j":{"df":0,"docs":{},"∑":{"df":0,"docs":{},"​":{"df":0,"docs":{},"β":{"df":0,"docs":{},"j":{"b":{"df":0,"docs":{},"​":{"b":{"df":0,"docs":{},"j":{"df":1,"docs":{"95":{"tf":1.0}}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}}}},"df":0,"docs":{}}}},"df":0,"docs":{}}}}}}}},"s":{"df":0,"docs":{},"h":{"a":{"3":{"df":1,"docs":{"40":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}}},"{":{"1":{",":{"df":0,"docs":{},"ω":{",":{"df":0,"docs":{},"ω":{"2":{",":{"df":0,"docs":{},"…":{",":{"df":0,"docs":{},"ω":{"df":0,"docs":{},"k":{"df":0,"docs":{},"−":{"1":{"df":1,"docs":{"14":{"tf":1.4142135623730951}}},"df":0,"docs":{}}},"n":{"df":0,"docs":{},"−":{"1":{"df":3,"docs":{"14":{"tf":1.4142135623730951},"16":{"tf":1.0},"23":{"tf":1.0}}},"df":0,"docs":{}}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{},"h":{"0":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"…":{",":{"df":0,"docs":{},"h":{"df":0,"docs":{},"n":{"df":0,"docs":{},"​":{"df":0,"docs":{},"}":{"df":0,"docs":{},"⊂":{"df":0,"docs":{},"f":{"df":1,"docs":{"5":{"tf":1.0}}}}}}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"ω":{"df":0,"docs":{},"i":{":":{"0":{"df":0,"docs":{},"≤":{"df":0,"docs":{},"i":{"<":{"df":0,"docs":{},"n":{"df":1,"docs":{"14":{"tf":1.0}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"_":{"1":{"(":{"df":0,"docs":{},"x":{"df":1,"docs":{"67":{"tf":1.0}}}},"df":2,"docs":{"116":{"tf":1.7320508075688772},"67":{"tf":1.0}}},"2":{"(":{"df":0,"docs":{},"x":{"df":1,"docs":{"67":{"tf":1.0}}}},"df":2,"docs":{"116":{"tf":1.7320508075688772},"67":{"tf":1.0}}},"df":0,"docs":{}},"a":{"df":0,"docs":{},"l":{"df":0,"docs":{},"f":{"df":1,"docs":{"125":{"tf":1.0}}}},"n":{"d":{"c":{"df":0,"docs":{},"r":{"a":{"df":0,"docs":{},"f":{"df":0,"docs":{},"t":{"df":1,"docs":{"83":{"tf":1.0}}}}},"df":0,"docs":{}}},"df":4,"docs":{"114":{"tf":1.0},"117":{"tf":1.0},"52":{"tf":1.0},"56":{"tf":1.0}},"l":{"df":2,"docs":{"110":{"tf":1.0},"9":{"tf":1.0}}}},"df":0,"docs":{}},"p":{"df":0,"docs":{},"p":{"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"df":4,"docs":{"116":{"tf":1.0},"125":{"tf":1.0},"146":{"tf":1.0},"77":{"tf":1.0}}}},"i":{"df":1,"docs":{"35":{"tf":1.0}}}}},"r":{"d":{"df":2,"docs":{"14":{"tf":1.0},"20":{"tf":1.0}}},"df":0,"docs":{}},"s":{"df":0,"docs":{},"h":{"(":{"df":0,"docs":{},"m":{"df":0,"docs":{},"e":{"df":0,"docs":{},"s":{"df":0,"docs":{},"s":{"a":{"df":0,"docs":{},"g":{"df":0,"docs":{},"e":{"_":{"2":{"df":1,"docs":{"40":{"tf":1.7320508075688772}}},"3":{"df":1,"docs":{"40":{"tf":1.4142135623730951}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}}}}},"df":7,"docs":{"118":{"tf":1.0},"151":{"tf":1.0},"45":{"tf":1.0},"75":{"tf":1.7320508075688772},"88":{"tf":1.0},"91":{"tf":1.4142135623730951},"92":{"tf":1.0}},"e":{"df":0,"docs":{},"r":{"df":2,"docs":{"110":{"tf":1.0},"40":{"tf":2.6457513110645907}}}},"m":{"a":{"df":0,"docs":{},"p":{":":{":":{"df":0,"docs":{},"f":{"df":0,"docs":{},"r":{"df":0,"docs":{},"o":{"df":0,"docs":{},"m":{"(":{"[":{"(":{"df":0,"docs":{},"x":{"df":1,"docs":{"44":{"tf":1.0}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"v":{"df":0,"docs":{},"e":{"df":3,"docs":{"110":{"tf":1.0},"130":{"tf":1.0},"49":{"tf":1.0}},"n":{"'":{"df":0,"docs":{},"t":{"df":1,"docs":{"69":{"tf":1.0}}}},"df":0,"docs":{}}}}},"df":35,"docs":{"112":{"tf":1.4142135623730951},"113":{"tf":1.0},"114":{"tf":2.0},"116":{"tf":1.7320508075688772},"117":{"tf":1.0},"118":{"tf":1.0},"125":{"tf":2.23606797749979},"134":{"tf":1.0},"14":{"tf":2.8284271247461903},"147":{"tf":1.4142135623730951},"20":{"tf":2.23606797749979},"24":{"tf":1.0},"25":{"tf":1.0},"26":{"tf":1.0},"32":{"tf":1.4142135623730951},"35":{"tf":1.0},"53":{"tf":1.0},"56":{"tf":2.23606797749979},"57":{"tf":2.0},"58":{"tf":2.0},"59":{"tf":1.7320508075688772},"60":{"tf":1.0},"62":{"tf":1.4142135623730951},"65":{"tf":1.0},"66":{"tf":1.0},"67":{"tf":1.7320508075688772},"69":{"tf":1.4142135623730951},"7":{"tf":1.4142135623730951},"73":{"tf":1.0},"75":{"tf":1.0},"82":{"tf":1.0},"84":{"tf":2.449489742783178},"85":{"tf":2.0},"88":{"tf":1.0},"94":{"tf":1.4142135623730951}},"e":{"df":0,"docs":{},"l":{"df":0,"docs":{},"i":{"df":0,"docs":{},"o":{"df":1,"docs":{"42":{"tf":1.0}}}},"p":{"df":2,"docs":{"45":{"tf":1.0},"70":{"tf":1.0}},"e":{"df":0,"docs":{},"r":{"df":2,"docs":{"108":{"tf":1.0},"35":{"tf":1.0}}}}}},"n":{"c":{"df":2,"docs":{"125":{"tf":1.0},"130":{"tf":1.0}}},"df":0,"docs":{}},"r":{"df":0,"docs":{},"e":{"'":{"df":1,"docs":{"20":{"tf":1.0}}},"df":27,"docs":{"11":{"tf":1.0},"114":{"tf":1.0},"118":{"tf":1.0},"124":{"tf":1.0},"133":{"tf":1.4142135623730951},"135":{"tf":1.0},"137":{"tf":1.0},"14":{"tf":2.449489742783178},"142":{"tf":1.0},"151":{"tf":1.4142135623730951},"21":{"tf":1.0},"35":{"tf":1.4142135623730951},"4":{"tf":1.0},"40":{"tf":1.4142135623730951},"52":{"tf":1.0},"54":{"tf":1.0},"56":{"tf":1.0},"57":{"tf":1.0},"7":{"tf":1.4142135623730951},"70":{"tf":1.0},"72":{"tf":1.0},"75":{"tf":1.0},"88":{"tf":1.0},"9":{"tf":1.0},"90":{"tf":1.0},"94":{"tf":1.7320508075688772},"98":{"tf":1.0}}}},"u":{"df":0,"docs":{},"r":{"df":0,"docs":{},"i":{"df":0,"docs":{},"s":{"df":0,"docs":{},"t":{"df":4,"docs":{"118":{"tf":1.0},"40":{"tf":1.0},"42":{"tf":1.0},"89":{"tf":1.0}}}}}}}},"i":{"d":{"df":0,"docs":{},"e":{"df":1,"docs":{"19":{"tf":1.0}}}},"df":0,"docs":{},"g":{"df":0,"docs":{},"h":{"df":6,"docs":{"104":{"tf":1.0},"35":{"tf":1.0},"46":{"tf":1.0},"59":{"tf":1.0},"69":{"tf":1.4142135623730951},"82":{"tf":1.0}},"e":{"df":0,"docs":{},"r":{"df":4,"docs":{"125":{"tf":1.0},"146":{"tf":1.0},"20":{"tf":1.0},"68":{"tf":1.0}}}}}},"m":{"df":0,"docs":{},"s":{"df":0,"docs":{},"e":{"df":0,"docs":{},"l":{"df":0,"docs":{},"f":{"df":1,"docs":{"21":{"tf":1.4142135623730951}}}}}}}},"o":{"df":0,"docs":{},"l":{"d":{"df":20,"docs":{"10":{"tf":1.0},"107":{"tf":2.0},"108":{"tf":1.4142135623730951},"109":{"tf":1.7320508075688772},"11":{"tf":1.0},"123":{"tf":1.4142135623730951},"13":{"tf":1.0},"131":{"tf":1.0},"133":{"tf":1.0},"14":{"tf":2.23606797749979},"146":{"tf":1.4142135623730951},"16":{"tf":1.4142135623730951},"35":{"tf":1.0},"54":{"tf":1.0},"69":{"tf":1.0},"7":{"tf":1.0},"73":{"tf":2.23606797749979},"74":{"tf":2.0},"75":{"tf":1.0},"77":{"tf":1.0}}},"df":0,"docs":{},"e":{"df":3,"docs":{"130":{"tf":2.0},"134":{"tf":2.8284271247461903},"146":{"tf":2.6457513110645907}}}},"m":{"df":0,"docs":{},"o":{"df":0,"docs":{},"m":{"df":0,"docs":{},"o":{"df":0,"docs":{},"r":{"df":0,"docs":{},"p":{"df":0,"docs":{},"h":{"df":1,"docs":{"19":{"tf":1.0}}}}}}}}},"n":{"df":0,"docs":{},"e":{"df":0,"docs":{},"s":{"df":0,"docs":{},"t":{"df":4,"docs":{"118":{"tf":1.0},"20":{"tf":1.0},"77":{"tf":1.0},"78":{"tf":1.0}}}}}},"o":{"d":{"df":2,"docs":{"111":{"tf":1.0},"114":{"tf":1.0}}},"df":0,"docs":{}},"s":{"df":0,"docs":{},"t":{"df":1,"docs":{"0":{"tf":1.0}}}}},"u":{"df":0,"docs":{},"g":{"df":0,"docs":{},"e":{"df":2,"docs":{"123":{"tf":1.7320508075688772},"80":{"tf":1.0}}}}},"ω":{"df":0,"docs":{},"i":{":":{"0":{"df":0,"docs":{},"≤":{"df":0,"docs":{},"i":{"<":{"2":{"df":0,"docs":{},"n":{"df":1,"docs":{"125":{"tf":1.0}}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"∈":{"d":{"df":1,"docs":{"14":{"tf":1.4142135623730951}}},"df":0,"docs":{},"f":{"df":0,"docs":{},"∖":{"df":0,"docs":{},"{":{"df":0,"docs":{},"ω":{"df":0,"docs":{},"i":{"df":0,"docs":{},"}":{"df":0,"docs":{},"i":{"df":0,"docs":{},"≥":{"0":{"df":1,"docs":{"91":{"tf":1.0}}},"df":0,"docs":{}}}}}}}}},"h":{"df":2,"docs":{"14":{"tf":1.7320508075688772},"16":{"tf":1.0}}}}},"i":{"+":{"1":{"df":2,"docs":{"101":{"tf":1.0},"70":{"tf":1.4142135623730951}}},"2":{"df":0,"docs":{},"k":{"df":0,"docs":{},"−":{"1":{"df":1,"docs":{"101":{"tf":1.0}}},"df":0,"docs":{}}}},"df":0,"docs":{}},",":{"df":0,"docs":{},"j":{")":{",":{"df":0,"docs":{},"v":{"df":1,"docs":{"14":{"tf":1.4142135623730951}}}},"df":0,"docs":{},"∈":{"df":0,"docs":{},"i":{"df":1,"docs":{"14":{"tf":1.0}}}}},",":{"df":0,"docs":{},"k":{",":{"df":0,"docs":{},"l":{"df":4,"docs":{"11":{"tf":1.0},"13":{"tf":1.0},"14":{"tf":1.0},"16":{"tf":1.0}}}},"df":0,"docs":{}},"l":{"df":1,"docs":{"14":{"tf":1.0}}}},"df":1,"docs":{"14":{"tf":2.0}}}},".":{"df":6,"docs":{"107":{"tf":1.0},"114":{"tf":1.0},"125":{"tf":1.0},"52":{"tf":1.0},"59":{"tf":1.4142135623730951},"68":{"tf":1.0}}},":":{"0":{"df":0,"docs":{},"≤":{"df":0,"docs":{},"i":{"<":{"2":{"df":0,"docs":{},"n":{"+":{"1":{"df":1,"docs":{"125":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}},"=":{"0":{"df":0,"docs":{},"k":{"df":0,"docs":{},"−":{"1":{"df":0,"docs":{},"​":{"(":{"a":{"df":0,"docs":{},"i":{"df":1,"docs":{"14":{"tf":1.4142135623730951}},"​":{"+":{"df":0,"docs":{},"γ":{")":{"/":{"df":0,"docs":{},"∏":{"df":0,"docs":{},"i":{"=":{"0":{"df":0,"docs":{},"k":{"df":0,"docs":{},"−":{"1":{"df":0,"docs":{},"​":{"(":{"b":{"df":0,"docs":{},"i":{"df":1,"docs":{"14":{"tf":1.0}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"b":{"df":0,"docs":{},"i":{"df":1,"docs":{"14":{"tf":1.4142135623730951}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"∏":{"df":0,"docs":{},"k":{"df":0,"docs":{},"−":{"1":{"df":0,"docs":{},"​":{"(":{"a":{"df":0,"docs":{},"i":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"γ":{")":{"=":{"df":0,"docs":{},"i":{"=":{"0":{"df":0,"docs":{},"∏":{"df":0,"docs":{},"k":{"df":0,"docs":{},"−":{"1":{"df":0,"docs":{},"​":{"(":{"b":{"df":0,"docs":{},"i":{"df":1,"docs":{"14":{"tf":1.0}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"∑":{"df":0,"docs":{},"n":{"df":0,"docs":{},"​":{"df":0,"docs":{},"l":{"df":0,"docs":{},"i":{"df":0,"docs":{},"​":{"(":{"df":0,"docs":{},"ζ":{")":{"(":{"df":0,"docs":{},"p":{"df":0,"docs":{},"i":{")":{"df":0,"docs":{},"i":{"df":1,"docs":{"32":{"tf":1.0}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}}}}}},"1":{"df":1,"docs":{"101":{"tf":1.0}}},"df":0,"docs":{},"{":{"(":{"df":0,"docs":{},"i":{",":{"df":0,"docs":{},"j":{"df":1,"docs":{"14":{"tf":1.0}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"∣":{"df":0,"docs":{},"l":{"df":0,"docs":{},"j":{"df":1,"docs":{"142":{"tf":1.0}}}}}},">":{"df":0,"docs":{},"n":{"df":2,"docs":{"14":{"tf":1.0},"16":{"tf":1.0}}}},"b":{"df":0,"docs":{},"​":{"b":{"df":0,"docs":{},"i":{"df":0,"docs":{},"​":{"(":{"df":0,"docs":{},"x":{"df":1,"docs":{"114":{"tf":1.0}}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"d":{"df":0,"docs":{},"e":{"a":{"df":12,"docs":{"118":{"tf":1.0},"14":{"tf":1.0},"151":{"tf":1.0},"4":{"tf":1.0},"54":{"tf":1.0},"59":{"tf":1.0},"6":{"tf":1.0},"65":{"tf":1.0},"66":{"tf":1.0},"67":{"tf":1.0},"7":{"tf":1.0},"79":{"tf":1.0}}},"df":0,"docs":{},"n":{"df":0,"docs":{},"t":{"df":0,"docs":{},"i":{"df":0,"docs":{},"f":{"df":0,"docs":{},"i":{"df":1,"docs":{"52":{"tf":1.0}}}}}}}}},"df":0,"docs":{},"l":{"df":0,"docs":{},"u":{"df":0,"docs":{},"s":{"df":0,"docs":{},"t":{"df":0,"docs":{},"r":{"df":1,"docs":{"111":{"tf":1.0}}}}}}},"m":{"a":{"df":0,"docs":{},"g":{"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"df":1,"docs":{"149":{"tf":1.0}}}}}},"df":0,"docs":{},"m":{"df":0,"docs":{},"e":{"d":{"df":0,"docs":{},"i":{"df":1,"docs":{"113":{"tf":1.0}}}},"df":0,"docs":{}}},"p":{"df":0,"docs":{},"l":{"df":0,"docs":{},"e":{"df":0,"docs":{},"m":{"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"df":0,"docs":{},"t":{"df":18,"docs":{"103":{"tf":1.7320508075688772},"105":{"tf":1.0},"110":{"tf":1.0},"111":{"tf":1.0},"123":{"tf":1.0},"124":{"tf":1.0},"127":{"tf":1.0},"19":{"tf":1.0},"34":{"tf":1.4142135623730951},"36":{"tf":1.0},"37":{"tf":1.4142135623730951},"40":{"tf":1.0},"41":{"tf":1.0},"46":{"tf":1.0},"64":{"tf":1.0},"67":{"tf":1.0},"68":{"tf":1.0},"9":{"tf":1.0}}}}}}},"i":{"c":{"df":0,"docs":{},"i":{"df":0,"docs":{},"t":{"df":0,"docs":{},"l":{"df":0,"docs":{},"i":{"df":2,"docs":{"52":{"tf":1.0},"95":{"tf":1.0}}}}}}},"df":1,"docs":{"101":{"tf":1.0}}}},"o":{"df":0,"docs":{},"r":{"df":0,"docs":{},"t":{"df":10,"docs":{"10":{"tf":1.0},"105":{"tf":1.0},"124":{"tf":1.0},"134":{"tf":1.0},"14":{"tf":1.4142135623730951},"146":{"tf":1.7320508075688772},"16":{"tf":1.0},"68":{"tf":1.0},"73":{"tf":1.0},"92":{"tf":1.0}}}}},"r":{"df":0,"docs":{},"o":{"df":0,"docs":{},"v":{"df":3,"docs":{"123":{"tf":1.0},"151":{"tf":1.0},"21":{"tf":1.0}}}}}}},"n":{"c":{"df":0,"docs":{},"l":{"df":0,"docs":{},"u":{"d":{"df":7,"docs":{"125":{"tf":1.7320508075688772},"13":{"tf":1.0},"146":{"tf":1.0},"149":{"tf":1.0},"23":{"tf":1.0},"66":{"tf":1.0},"84":{"tf":1.0}}},"df":0,"docs":{}}},"o":{"df":0,"docs":{},"r":{"df":0,"docs":{},"p":{"df":0,"docs":{},"o":{"df":0,"docs":{},"r":{"df":2,"docs":{"13":{"tf":1.4142135623730951},"147":{"tf":1.4142135623730951}}}}}}},"r":{"df":0,"docs":{},"e":{"a":{"df":0,"docs":{},"s":{"df":3,"docs":{"147":{"tf":1.4142135623730951},"88":{"tf":1.0},"94":{"tf":1.4142135623730951}}}},"d":{"df":1,"docs":{"50":{"tf":1.0}}},"df":0,"docs":{},"m":{"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"df":0,"docs":{},"t":{"df":1,"docs":{"142":{"tf":1.0}}}}}}}}},"d":{"df":0,"docs":{},"e":{"df":2,"docs":{"57":{"tf":1.0},"59":{"tf":1.4142135623730951}},"p":{"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"d":{"df":4,"docs":{"11":{"tf":1.0},"13":{"tf":1.4142135623730951},"150":{"tf":1.0},"80":{"tf":1.0}}},"df":0,"docs":{}}}},"x":{"df":11,"docs":{"101":{"tf":2.0},"109":{"tf":1.0},"11":{"tf":2.23606797749979},"13":{"tf":1.4142135623730951},"14":{"tf":1.4142135623730951},"149":{"tf":1.0},"36":{"tf":1.0},"72":{"tf":1.0},"80":{"tf":1.0},"94":{"tf":1.0},"95":{"tf":1.4142135623730951}}}},"i":{"c":{"df":4,"docs":{"11":{"tf":1.0},"14":{"tf":1.0},"28":{"tf":1.0},"95":{"tf":1.0}}},"df":0,"docs":{}},"u":{"c":{"df":2,"docs":{"14":{"tf":1.0},"90":{"tf":1.4142135623730951}}},"df":0,"docs":{}}},"df":1,"docs":{"146":{"tf":1.0}},"e":{"df":0,"docs":{},"f":{"df":0,"docs":{},"f":{"df":0,"docs":{},"i":{"c":{"df":0,"docs":{},"i":{"df":1,"docs":{"84":{"tf":1.0}}}},"df":0,"docs":{}}}}},"f":{"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"df":0,"docs":{},"i":{"df":0,"docs":{},"t":{"df":1,"docs":{"146":{"tf":1.4142135623730951}}}}}},"o":{"df":0,"docs":{},"r":{"df":0,"docs":{},"m":{"df":10,"docs":{"119":{"tf":1.0},"126":{"tf":1.0},"142":{"tf":1.0},"144":{"tf":1.7320508075688772},"145":{"tf":1.0},"146":{"tf":1.4142135623730951},"19":{"tf":1.0},"52":{"tf":1.0},"70":{"tf":1.0},"9":{"tf":1.0}}}}}},"g":{"df":0,"docs":{},"r":{"df":0,"docs":{},"e":{"d":{"df":0,"docs":{},"i":{"df":2,"docs":{"151":{"tf":1.0},"44":{"tf":1.0}}}},"df":0,"docs":{}}}},"i":{"df":0,"docs":{},"t":{"df":0,"docs":{},"i":{"df":5,"docs":{"146":{"tf":1.0},"151":{"tf":1.0},"31":{"tf":1.4142135623730951},"70":{"tf":1.0},"94":{"tf":1.0}}}}},"n":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":2,"docs":{"19":{"tf":1.0},"73":{"tf":1.0}}}}},"p":{"df":0,"docs":{},"u":{"df":0,"docs":{},"t":{"df":24,"docs":{"104":{"tf":2.449489742783178},"106":{"tf":1.4142135623730951},"11":{"tf":1.0},"110":{"tf":1.0},"121":{"tf":1.4142135623730951},"13":{"tf":2.449489742783178},"14":{"tf":1.4142135623730951},"142":{"tf":1.0},"144":{"tf":1.0},"15":{"tf":1.4142135623730951},"151":{"tf":1.0},"16":{"tf":1.7320508075688772},"23":{"tf":1.7320508075688772},"26":{"tf":1.0},"3":{"tf":1.7320508075688772},"32":{"tf":1.4142135623730951},"34":{"tf":2.0},"35":{"tf":3.3166247903554},"36":{"tf":1.0},"42":{"tf":1.4142135623730951},"44":{"tf":2.449489742783178},"7":{"tf":3.1622776601683795},"9":{"tf":1.0},"95":{"tf":1.0}}}}},"s":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":0,"docs":{},"t":{"df":3,"docs":{"130":{"tf":1.0},"133":{"tf":1.4142135623730951},"134":{"tf":1.7320508075688772}}}}},"i":{"d":{"df":1,"docs":{"45":{"tf":1.0}}},"df":0,"docs":{},"g":{"df":0,"docs":{},"h":{"df":0,"docs":{},"t":{"df":1,"docs":{"103":{"tf":1.0}}}}}},"p":{"df":0,"docs":{},"e":{"c":{"df":0,"docs":{},"t":{"df":2,"docs":{"101":{"tf":1.0},"14":{"tf":1.0}}}},"df":0,"docs":{}}},"t":{"a":{"df":0,"docs":{},"n":{"c":{"df":5,"docs":{"13":{"tf":1.0},"14":{"tf":1.0},"16":{"tf":1.4142135623730951},"35":{"tf":2.6457513110645907},"8":{"tf":1.0}}},"df":0,"docs":{},"t":{"df":0,"docs":{},"i":{"df":4,"docs":{"106":{"tf":1.0},"109":{"tf":1.0},"110":{"tf":1.0},"35":{"tf":1.7320508075688772}}}}}},"df":3,"docs":{"142":{"tf":1.0},"146":{"tf":1.4142135623730951},"147":{"tf":1.4142135623730951}},"e":{"a":{"d":{"df":13,"docs":{"101":{"tf":1.0},"123":{"tf":1.0},"131":{"tf":1.0},"14":{"tf":1.0},"146":{"tf":1.0},"21":{"tf":1.0},"32":{"tf":1.0},"54":{"tf":1.0},"55":{"tf":1.0},"80":{"tf":1.4142135623730951},"84":{"tf":1.0},"9":{"tf":1.0},"92":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}},"r":{"df":0,"docs":{},"u":{"c":{"df":0,"docs":{},"t":{"df":8,"docs":{"130":{"tf":1.0},"131":{"tf":1.4142135623730951},"133":{"tf":2.449489742783178},"142":{"tf":1.4142135623730951},"146":{"tf":3.872983346207417},"147":{"tf":1.0},"151":{"tf":1.4142135623730951},"50":{"tf":1.0}}}},"df":0,"docs":{}}}}},"t":{"df":0,"docs":{},"e":{"df":0,"docs":{},"g":{"df":3,"docs":{"142":{"tf":1.0},"146":{"tf":1.0},"41":{"tf":1.4142135623730951}},"r":{"df":2,"docs":{"105":{"tf":1.0},"84":{"tf":1.0}}}},"n":{"df":0,"docs":{},"s":{"df":1,"docs":{"99":{"tf":1.0}}}},"r":{"a":{"c":{"df":0,"docs":{},"t":{"df":14,"docs":{"118":{"tf":1.4142135623730951},"127":{"tf":1.0},"128":{"tf":1.0},"137":{"tf":1.7320508075688772},"145":{"tf":1.0},"146":{"tf":1.0},"70":{"tf":1.0},"71":{"tf":1.0},"73":{"tf":1.0},"75":{"tf":1.0},"77":{"tf":1.0},"82":{"tf":1.0},"91":{"tf":1.0},"95":{"tf":1.0}}}},"df":0,"docs":{}},"df":0,"docs":{},"e":{"df":0,"docs":{},"s":{"df":0,"docs":{},"t":{"df":1,"docs":{"14":{"tf":1.0}}}}},"l":{"df":0,"docs":{},"e":{"a":{"df":0,"docs":{},"v":{"df":2,"docs":{"149":{"tf":1.0},"150":{"tf":1.0}}}},"df":0,"docs":{}}},"m":{"df":0,"docs":{},"e":{"d":{"df":0,"docs":{},"i":{"df":2,"docs":{"11":{"tf":1.0},"48":{"tf":1.0}}}},"df":0,"docs":{}}},"n":{"df":4,"docs":{"118":{"tf":1.4142135623730951},"40":{"tf":2.23606797749979},"71":{"tf":1.0},"94":{"tf":1.0}}},"p":{"df":0,"docs":{},"o":{"df":0,"docs":{},"l":{"df":26,"docs":{"112":{"tf":1.0},"114":{"tf":2.6457513110645907},"121":{"tf":1.4142135623730951},"123":{"tf":2.0},"125":{"tf":1.0},"14":{"tf":3.0},"16":{"tf":1.0},"20":{"tf":1.4142135623730951},"24":{"tf":1.0},"25":{"tf":1.0},"26":{"tf":1.0},"3":{"tf":1.7320508075688772},"32":{"tf":1.0},"35":{"tf":1.0},"36":{"tf":1.0},"5":{"tf":1.0},"52":{"tf":1.0},"54":{"tf":1.0},"62":{"tf":1.0},"68":{"tf":1.0},"73":{"tf":1.0},"82":{"tf":1.0},"83":{"tf":1.0},"84":{"tf":1.0},"91":{"tf":1.0},"94":{"tf":1.4142135623730951}}}},"r":{"df":0,"docs":{},"e":{"df":0,"docs":{},"t":{"df":2,"docs":{"41":{"tf":1.0},"70":{"tf":1.0}}}}}}}},"r":{"df":0,"docs":{},"o":{"d":{"df":0,"docs":{},"u":{"c":{"df":3,"docs":{"125":{"tf":1.0},"14":{"tf":1.0},"53":{"tf":1.0}},"t":{"df":1,"docs":{"0":{"tf":1.0}}}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"v":{"a":{"df":0,"docs":{},"l":{"df":0,"docs":{},"i":{"d":{"df":2,"docs":{"79":{"tf":1.0},"9":{"tf":1.0}}},"df":0,"docs":{}}},"r":{"df":0,"docs":{},"i":{"df":1,"docs":{"20":{"tf":1.0}}}}},"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":0,"docs":{},"s":{"df":5,"docs":{"123":{"tf":1.0},"146":{"tf":1.0},"45":{"tf":1.0},"7":{"tf":1.0},"98":{"tf":1.7320508075688772}}},"t":{"df":2,"docs":{"7":{"tf":1.0},"98":{"tf":1.0}}}}},"o":{"df":0,"docs":{},"l":{"df":0,"docs":{},"v":{"df":8,"docs":{"103":{"tf":1.0},"104":{"tf":1.0},"105":{"tf":1.0},"123":{"tf":1.4142135623730951},"149":{"tf":1.0},"21":{"tf":1.0},"53":{"tf":1.0},"70":{"tf":1.0}}}}}}},"o":{"df":1,"docs":{"151":{"tf":1.0}}},"s":{"c":{"df":0,"docs":{},"o":{"df":0,"docs":{},"m":{"df":0,"docs":{},"m":{"df":0,"docs":{},"i":{"df":0,"docs":{},"t":{"df":0,"docs":{},"m":{"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"df":0,"docs":{},"t":{"df":0,"docs":{},"s":{"c":{"df":0,"docs":{},"h":{"df":0,"docs":{},"e":{"df":0,"docs":{},"m":{"df":0,"docs":{},"e":{"<":{"df":0,"docs":{},"f":{"df":1,"docs":{"35":{"tf":1.7320508075688772}}}},"df":0,"docs":{}}}}}},"df":0,"docs":{}}}}}}}}}}}},"df":0,"docs":{},"f":{"df":0,"docs":{},"i":{"df":0,"docs":{},"e":{"df":0,"docs":{},"l":{"d":{"df":1,"docs":{"35":{"tf":2.23606797749979}}},"df":0,"docs":{}}}}},"n":{"'":{"df":0,"docs":{},"t":{"df":3,"docs":{"121":{"tf":1.0},"149":{"tf":1.0},"65":{"tf":1.0}}}},"df":0,"docs":{}},"r":{"a":{"df":0,"docs":{},"n":{"d":{"df":0,"docs":{},"o":{"df":0,"docs":{},"m":{"df":0,"docs":{},"f":{"df":0,"docs":{},"i":{"df":0,"docs":{},"e":{"df":0,"docs":{},"l":{"d":{"df":0,"docs":{},"e":{"df":0,"docs":{},"l":{"df":0,"docs":{},"e":{"df":0,"docs":{},"m":{"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"df":0,"docs":{},"t":{"df":0,"docs":{},"g":{"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"a":{"df":0,"docs":{},"t":{"df":0,"docs":{},"o":{"df":0,"docs":{},"r":{"<":{"df":0,"docs":{},"f":{"df":1,"docs":{"35":{"tf":1.0}}}},"df":0,"docs":{}}}}},"df":0,"docs":{}}}}}}}}}}}}}},"df":0,"docs":{}}}}}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"s":{"df":0,"docs":{},"u":{"df":2,"docs":{"146":{"tf":1.0},"151":{"tf":1.0}}}}},"t":{"'":{"df":15,"docs":{"107":{"tf":1.0},"11":{"tf":1.0},"124":{"tf":1.0},"125":{"tf":1.0},"14":{"tf":1.0},"146":{"tf":1.7320508075688772},"151":{"tf":1.0},"20":{"tf":1.0},"35":{"tf":1.0},"49":{"tf":1.4142135623730951},"50":{"tf":1.4142135623730951},"52":{"tf":1.0},"54":{"tf":1.0},"55":{"tf":1.0},"81":{"tf":1.0}}},"df":0,"docs":{},"e":{"df":0,"docs":{},"m":{"df":4,"docs":{"112":{"tf":1.0},"49":{"tf":1.0},"59":{"tf":1.4142135623730951},"62":{"tf":1.0}}},"r":{"df":2,"docs":{"102":{"tf":1.0},"114":{"tf":1.0}}}},"s":{"df":0,"docs":{},"e":{"df":0,"docs":{},"l":{"df":0,"docs":{},"f":{"df":3,"docs":{"10":{"tf":1.0},"14":{"tf":1.0},"58":{"tf":1.0}}}}}},"​":{"c":{"df":0,"docs":{},"i":{"df":0,"docs":{},"​":{"(":{"df":0,"docs":{},"x":{"df":1,"docs":{"114":{"tf":1.0}}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"∈":{"[":{"0":{",":{"2":{"df":0,"docs":{},"n":{"+":{"df":0,"docs":{},"k":{"df":1,"docs":{"92":{"tf":1.0}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"":{"=":{"df":0,"docs":{},"ω":{"df":0,"docs":{},"j":{"df":0,"docs":{},"k":{"1":{"df":0,"docs":{},"​":{"df":0,"docs":{},"":{"=":{"df":0,"docs":{},"ω":{"df":0,"docs":{},"l":{"df":0,"docs":{},"k":{"2":{"df":1,"docs":{"14":{"tf":1.0}}},"df":0,"docs":{}}}}},"df":0,"docs":{}}}},"df":0,"docs":{}}}}},"df":0,"docs":{}}},"j":{"=":{"1":{",":{"df":0,"docs":{},"…":{",":{"df":0,"docs":{},"m":{"df":4,"docs":{"91":{"tf":1.4142135623730951},"94":{"tf":1.4142135623730951},"95":{"tf":1.0},"97":{"tf":2.0}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{},"m":{"df":0,"docs":{},"′":{"+":{"1":{",":{"df":0,"docs":{},"…":{",":{"df":0,"docs":{},"m":{"df":0,"docs":{},"′":{"+":{"df":0,"docs":{},"m":{"df":2,"docs":{"94":{"tf":1.4142135623730951},"95":{"tf":1.0}}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}},"b":{"df":1,"docs":{"97":{"tf":1.4142135623730951}},"​":{":":{"=":{"df":0,"docs":{},"β":{"df":0,"docs":{},"j":{"+":{"df":0,"docs":{},"n":{"df":0,"docs":{},"t":{"df":0,"docs":{},"​":{"df":0,"docs":{},"−":{"1":{"df":1,"docs":{"97":{"tf":1.0}}},"df":0,"docs":{}}}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":9,"docs":{"106":{"tf":1.0},"14":{"tf":1.0},"147":{"tf":1.4142135623730951},"5":{"tf":1.0},"70":{"tf":1.4142135623730951},"83":{"tf":1.0},"85":{"tf":1.0},"92":{"tf":1.4142135623730951},"94":{"tf":1.4142135623730951}},"g":{"df":0,"docs":{},"z":{"df":1,"docs":{"95":{"tf":1.4142135623730951}}}},"o":{"b":{"df":3,"docs":{"114":{"tf":1.0},"118":{"tf":1.0},"20":{"tf":1.0}}},"df":0,"docs":{}},"u":{"df":0,"docs":{},"m":{"df":0,"docs":{},"p":{"df":2,"docs":{"146":{"tf":1.0},"50":{"tf":1.0}}}}},"z":{"df":1,"docs":{"95":{"tf":1.4142135623730951}}},"υ":{"df":0,"docs":{},"s":{"df":0,"docs":{},"​":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"t":{"df":0,"docs":{},"j":{"df":1,"docs":{"95":{"tf":1.0}}}}},"df":0,"docs":{}}}}},"​":{",":{"df":0,"docs":{},"γ":{"df":0,"docs":{},"j":{"df":1,"docs":{"97":{"tf":1.0}}}}},":":{"=":{"df":0,"docs":{},"α":{"df":0,"docs":{},"j":{"df":1,"docs":{"97":{"tf":1.0}}}}},"df":0,"docs":{}},"df":0,"docs":{}},"′":{"df":0,"docs":{},"​":{":":{"=":{"df":0,"docs":{},"α":{"df":0,"docs":{},"j":{"+":{"df":0,"docs":{},"m":{"df":0,"docs":{},"−":{"1":{"df":1,"docs":{"97":{"tf":1.0}}},"df":0,"docs":{}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"":{"=":{"df":0,"docs":{},"i":{"df":1,"docs":{"5":{"tf":1.0}}}},"df":0,"docs":{}}},"k":{"+":{"1":{"df":0,"docs":{},"υ":{"df":0,"docs":{},"s":{"2":{"df":0,"docs":{},"k":{"+":{"1":{"df":1,"docs":{"95":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"1":{"2":{"df":1,"docs":{"35":{"tf":1.0}}},"df":2,"docs":{"14":{"tf":1.0},"35":{"tf":1.4142135623730951}}},"2":{"df":2,"docs":{"14":{"tf":1.0},"35":{"tf":1.0}}},"<":{"df":0,"docs":{},"n":{"df":2,"docs":{"94":{"tf":1.0},"95":{"tf":1.0}},"−":{"1":{"df":1,"docs":{"95":{"tf":1.0}}},"df":0,"docs":{}}}},"=":{"0":{",":{"df":0,"docs":{},"…":{",":{"df":0,"docs":{},"n":{"df":0,"docs":{},"−":{"1":{"df":1,"docs":{"95":{"tf":1.0}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"1":{",":{"df":0,"docs":{},"…":{",":{"df":0,"docs":{},"n":{"df":2,"docs":{"94":{"tf":1.0},"95":{"tf":1.0}},"t":{"df":2,"docs":{"91":{"tf":1.4142135623730951},"97":{"tf":1.4142135623730951}}},"−":{"1":{"df":1,"docs":{"94":{"tf":1.0}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"3":{"df":1,"docs":{"101":{"tf":1.0}}},"df":0,"docs":{},"n":{"df":1,"docs":{"95":{"tf":1.0}}},"ω":{"0":{"df":1,"docs":{"14":{"tf":1.0}}},"df":0,"docs":{}}},">":{"1":{"df":1,"docs":{"102":{"tf":1.0}}},"df":0,"docs":{}},"a":{"df":0,"docs":{},"t":{"df":0,"docs":{},"e":{"df":3,"docs":{"19":{"tf":1.0},"35":{"tf":1.0},"38":{"tf":1.0}}}}},"df":6,"docs":{"101":{"tf":1.0},"124":{"tf":1.0},"14":{"tf":2.0},"147":{"tf":1.4142135623730951},"49":{"tf":1.0},"70":{"tf":1.4142135623730951}},"e":{"c":{"c":{"a":{"df":0,"docs":{},"k":{"2":{"5":{"6":{"(":{"df":0,"docs":{},"x":{"df":0,"docs":{},"∣":{"df":0,"docs":{},"∣":{"df":0,"docs":{},"i":{"df":2,"docs":{"94":{"tf":1.0},"95":{"tf":1.0}}}}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":1,"docs":{"151":{"tf":1.0}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{},"e":{"df":0,"docs":{},"p":{"df":5,"docs":{"118":{"tf":1.0},"124":{"tf":1.0},"20":{"tf":1.0},"45":{"tf":1.0},"50":{"tf":1.0}}}},"y":{"df":3,"docs":{"146":{"tf":1.0},"151":{"tf":1.0},"44":{"tf":1.0}}}},"i":{"df":0,"docs":{},"n":{"d":{"df":1,"docs":{"74":{"tf":1.0}}},"df":0,"docs":{}}},"n":{"df":0,"docs":{},"o":{"df":0,"docs":{},"w":{"df":14,"docs":{"114":{"tf":1.0},"119":{"tf":1.0},"146":{"tf":1.7320508075688772},"151":{"tf":1.0},"44":{"tf":1.0},"45":{"tf":1.0},"57":{"tf":1.0},"58":{"tf":1.0},"7":{"tf":1.0},"74":{"tf":1.0},"75":{"tf":1.0},"79":{"tf":1.0},"80":{"tf":1.0},"83":{"tf":1.0}},"l":{"df":0,"docs":{},"e":{"d":{"df":0,"docs":{},"g":{"df":3,"docs":{"20":{"tf":1.0},"4":{"tf":1.0},"7":{"tf":1.4142135623730951}}}},"df":0,"docs":{}}},"n":{"df":6,"docs":{"151":{"tf":1.0},"21":{"tf":1.0},"42":{"tf":1.0},"73":{"tf":1.0},"91":{"tf":1.0},"98":{"tf":1.0}}}}}},"t":{"df":1,"docs":{"97":{"tf":1.4142135623730951}},"​":{":":{"=":{"df":0,"docs":{},"β":{"df":0,"docs":{},"k":{"df":0,"docs":{},"−":{"1":{"df":1,"docs":{"97":{"tf":1.0}}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"z":{"df":0,"docs":{},"g":{":":{":":{"df":0,"docs":{},"n":{"df":0,"docs":{},"e":{"df":0,"docs":{},"w":{"(":{"df":0,"docs":{},"s":{"df":0,"docs":{},"r":{"df":2,"docs":{"35":{"tf":1.4142135623730951},"44":{"tf":1.0}}}}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":2,"docs":{"35":{"tf":2.23606797749979},"44":{"tf":1.4142135623730951}}}},"υ":{"df":0,"docs":{},"s":{"2":{"df":0,"docs":{},"k":{"df":1,"docs":{"102":{"tf":1.0}},"​":{"df":0,"docs":{},"​":{"df":0,"docs":{},"π":{"df":0,"docs":{},"k":{"df":0,"docs":{},"−":{"df":0,"docs":{},"υ":{"df":0,"docs":{},"s":{"2":{"df":0,"docs":{},"k":{"df":0,"docs":{},"​":{"df":0,"docs":{},"​":{"df":0,"docs":{},"​":{"=":{"df":0,"docs":{},"g":{"+":{"df":0,"docs":{},"υ":{"df":0,"docs":{},"s":{"2":{"df":0,"docs":{},"k":{"df":0,"docs":{},"​":{"df":0,"docs":{},"h":{"=":{"df":0,"docs":{},"g":{"df":0,"docs":{},"−":{"df":0,"docs":{},"υ":{"df":0,"docs":{},"s":{"2":{"df":0,"docs":{},"k":{"df":0,"docs":{},"​":{"df":0,"docs":{},"h":{"df":1,"docs":{"95":{"tf":1.0}}}}}},"df":0,"docs":{}}}}}},"df":0,"docs":{}}}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}}},"df":0,"docs":{}}}}}}}}}},"df":0,"docs":{}}},"−":{"1":{"df":3,"docs":{"14":{"tf":1.0},"94":{"tf":1.0},"95":{"tf":1.0}}},"df":0,"docs":{}},"≥":{"0":{"df":1,"docs":{"20":{"tf":1.0}}},"df":0,"docs":{}}},"l":{",":{"df":0,"docs":{},"r":{",":{"df":0,"docs":{},"o":{"df":2,"docs":{"10":{"tf":1.0},"9":{"tf":1.0}}}},"df":0,"docs":{}}},"=":{"df":0,"docs":{},"∣":{"d":{"df":0,"docs":{},"k":{"df":0,"docs":{},"−":{"1":{"df":0,"docs":{},"​":{"df":0,"docs":{},"∣":{"/":{"2":{"df":1,"docs":{"94":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"a":{"df":0,"docs":{},"g":{"df":0,"docs":{},"r":{"a":{"df":0,"docs":{},"n":{"df":0,"docs":{},"g":{"df":7,"docs":{"123":{"tf":1.0},"3":{"tf":1.7320508075688772},"32":{"tf":1.0},"35":{"tf":1.0},"5":{"tf":1.0},"73":{"tf":1.0},"99":{"tf":1.0}}}}},"df":0,"docs":{}}},"m":{"b":{"d":{"a":{"df":0,"docs":{},"w":{"df":0,"docs":{},"o":{"df":0,"docs":{},"r":{"df":0,"docs":{},"k":{"df":6,"docs":{"0":{"tf":1.0},"1":{"tf":1.7320508075688772},"103":{"tf":1.0},"19":{"tf":1.0},"45":{"tf":1.7320508075688772},"87":{"tf":1.0}},"s":{"_":{"c":{"df":0,"docs":{},"r":{"df":0,"docs":{},"y":{"df":0,"docs":{},"p":{"df":0,"docs":{},"t":{"df":0,"docs":{},"o":{"df":1,"docs":{"38":{"tf":1.0}}}}}}}},"df":0,"docs":{},"m":{"a":{"df":0,"docs":{},"t":{"df":0,"docs":{},"h":{":":{":":{"df":0,"docs":{},"e":{"df":0,"docs":{},"l":{"df":0,"docs":{},"l":{"df":0,"docs":{},"i":{"df":0,"docs":{},"p":{"df":0,"docs":{},"t":{"df":0,"docs":{},"i":{"c":{"_":{"c":{"df":0,"docs":{},"u":{"df":0,"docs":{},"r":{"df":0,"docs":{},"v":{"df":0,"docs":{},"e":{":":{":":{"df":0,"docs":{},"s":{"df":0,"docs":{},"h":{"df":0,"docs":{},"o":{"df":0,"docs":{},"r":{"df":0,"docs":{},"t":{"_":{"df":0,"docs":{},"w":{"df":0,"docs":{},"e":{"df":0,"docs":{},"i":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":0,"docs":{},"s":{"df":0,"docs":{},"t":{"df":0,"docs":{},"r":{"a":{"df":0,"docs":{},"s":{"df":0,"docs":{},"s":{":":{":":{"c":{"df":0,"docs":{},"u":{"df":0,"docs":{},"r":{"df":0,"docs":{},"v":{"df":0,"docs":{},"e":{"df":0,"docs":{},"s":{":":{":":{"b":{"df":0,"docs":{},"l":{"df":0,"docs":{},"s":{"1":{"2":{"_":{"3":{"8":{"1":{":":{":":{"d":{"df":0,"docs":{},"e":{"df":0,"docs":{},"f":{"a":{"df":0,"docs":{},"u":{"df":0,"docs":{},"l":{"df":0,"docs":{},"t":{"_":{"df":0,"docs":{},"t":{"df":0,"docs":{},"y":{"df":0,"docs":{},"p":{"df":0,"docs":{},"e":{"df":0,"docs":{},"s":{":":{":":{"df":0,"docs":{},"f":{"df":0,"docs":{},"r":{"df":0,"docs":{},"f":{"df":0,"docs":{},"i":{"df":0,"docs":{},"e":{"df":0,"docs":{},"l":{"d":{"df":2,"docs":{"44":{"tf":1.0},"45":{"tf":1.0}}},"df":0,"docs":{}}}}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}}}}},"df":0,"docs":{}}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}}}}}}}}},"df":0,"docs":{}}}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}}}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}},"p":{"df":0,"docs":{},"l":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"df":0,"docs":{},"k":{":":{":":{"c":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"df":0,"docs":{},"s":{"df":0,"docs":{},"t":{"df":0,"docs":{},"r":{"a":{"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"df":0,"docs":{},"t":{"_":{"df":0,"docs":{},"s":{"df":0,"docs":{},"y":{"df":0,"docs":{},"s":{"df":0,"docs":{},"t":{"df":0,"docs":{},"e":{"df":0,"docs":{},"m":{":":{":":{"c":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"df":0,"docs":{},"s":{"df":0,"docs":{},"t":{"df":0,"docs":{},"r":{"a":{"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"df":0,"docs":{},"t":{"df":0,"docs":{},"s":{"df":0,"docs":{},"y":{"df":0,"docs":{},"s":{"df":0,"docs":{},"t":{"df":0,"docs":{},"e":{"df":0,"docs":{},"m":{"df":2,"docs":{"44":{"tf":1.0},"45":{"tf":1.0}}}}}}}}}}}},"df":0,"docs":{}}}}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}}}}}},"df":0,"docs":{}}}}},"df":0,"docs":{}}}}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}}}}},"df":0,"docs":{}}}}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"n":{"d":{"df":1,"docs":{"14":{"tf":1.4142135623730951}}},"df":0,"docs":{},"g":{"df":0,"docs":{},"u":{"a":{"df":0,"docs":{},"g":{"df":1,"docs":{"50":{"tf":1.0}}}},"df":0,"docs":{}}}},"s":{"df":0,"docs":{},"t":{"df":14,"docs":{"10":{"tf":1.0},"107":{"tf":1.0},"109":{"tf":1.0},"119":{"tf":1.0},"13":{"tf":1.4142135623730951},"134":{"tf":1.0},"14":{"tf":2.0},"142":{"tf":1.4142135623730951},"146":{"tf":2.0},"147":{"tf":1.0},"151":{"tf":1.0},"16":{"tf":1.0},"40":{"tf":1.0},"9":{"tf":1.0}}}},"t":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":3,"docs":{"102":{"tf":1.0},"19":{"tf":1.4142135623730951},"87":{"tf":1.0}}}},"t":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":2,"docs":{"19":{"tf":1.0},"73":{"tf":1.0}}}}}},"y":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":2,"docs":{"102":{"tf":1.4142135623730951},"119":{"tf":1.4142135623730951}}}},"o":{"df":0,"docs":{},"u":{"df":0,"docs":{},"t":{"df":6,"docs":{"127":{"tf":1.7320508075688772},"142":{"tf":1.0},"146":{"tf":1.4142135623730951},"147":{"tf":1.0},"149":{"tf":1.0},"151":{"tf":1.0}}}}}}},"d":{"df":0,"docs":{},"e":{"_":{"d":{"df":0,"docs":{},"o":{"df":0,"docs":{},"m":{"a":{"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{".":{"df":0,"docs":{},"i":{"df":0,"docs":{},"t":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"(":{")":{".":{"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"df":0,"docs":{},"u":{"df":0,"docs":{},"m":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":1,"docs":{"114":{"tf":1.0}}}}}}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}}}},"df":0,"docs":{}}}},"df":0,"docs":{}}}},"df":0,"docs":{},"r":{"df":0,"docs":{},"o":{"df":0,"docs":{},"o":{"df":0,"docs":{},"t":{"df":0,"docs":{},"s":{"_":{"df":0,"docs":{},"o":{"df":0,"docs":{},"f":{"_":{"df":0,"docs":{},"u":{"df":0,"docs":{},"n":{"df":0,"docs":{},"i":{"df":0,"docs":{},"t":{"df":0,"docs":{},"y":{"_":{"c":{"df":0,"docs":{},"o":{"df":0,"docs":{},"s":{"df":0,"docs":{},"e":{"df":0,"docs":{},"t":{"df":1,"docs":{"114":{"tf":1.4142135623730951}}}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}}}}},"df":0,"docs":{}}}},"df":0,"docs":{}}}}}},"t":{"df":0,"docs":{},"r":{"a":{"c":{"df":1,"docs":{"114":{"tf":2.0}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":6,"docs":{"109":{"tf":1.4142135623730951},"114":{"tf":1.4142135623730951},"119":{"tf":1.0},"125":{"tf":1.7320508075688772},"149":{"tf":1.0},"68":{"tf":2.0}}}},"df":7,"docs":{"11":{"tf":1.0},"12":{"tf":1.0},"13":{"tf":1.0},"14":{"tf":1.0},"142":{"tf":1.0},"147":{"tf":1.4142135623730951},"80":{"tf":1.4142135623730951}},"e":{"a":{"d":{"df":3,"docs":{"151":{"tf":1.0},"94":{"tf":1.0},"95":{"tf":1.0}}},"df":0,"docs":{},"f":{"df":2,"docs":{"101":{"tf":1.4142135623730951},"92":{"tf":1.0}}},"r":{"df":0,"docs":{},"n":{"df":1,"docs":{"42":{"tf":1.0}}}},"v":{"df":7,"docs":{"101":{"tf":1.4142135623730951},"14":{"tf":1.0},"144":{"tf":1.0},"16":{"tf":1.0},"20":{"tf":1.0},"80":{"tf":1.0},"92":{"tf":1.0}}}},"df":0,"docs":{},"f":{"df":0,"docs":{},"t":{"df":8,"docs":{"109":{"tf":1.0},"11":{"tf":1.0},"134":{"tf":1.0},"16":{"tf":1.0},"20":{"tf":1.0},"59":{"tf":1.0},"88":{"tf":1.0},"9":{"tf":2.23606797749979}}}},"m":{"df":0,"docs":{},"m":{"a":{"df":1,"docs":{"14":{"tf":1.4142135623730951}}},"df":0,"docs":{}}},"n":{"df":0,"docs":{},"g":{"df":0,"docs":{},"h":{"df":0,"docs":{},"t":{"df":1,"docs":{"131":{"tf":1.0}}}},"t":{"df":0,"docs":{},"h":{"df":7,"docs":{"119":{"tf":1.0},"142":{"tf":1.4142135623730951},"146":{"tf":1.0},"147":{"tf":1.7320508075688772},"36":{"tf":1.0},"51":{"tf":1.0},"72":{"tf":1.0}}}}}},"q":{"df":3,"docs":{"52":{"tf":1.4142135623730951},"62":{"tf":1.0},"68":{"tf":2.0}}},"s":{"df":0,"docs":{},"s":{"df":6,"docs":{"14":{"tf":1.0},"146":{"tf":1.0},"151":{"tf":1.4142135623730951},"48":{"tf":1.0},"73":{"tf":2.6457513110645907},"82":{"tf":1.0}}}},"t":{"'":{"df":24,"docs":{"104":{"tf":1.0},"107":{"tf":1.0},"109":{"tf":1.0},"114":{"tf":1.0},"12":{"tf":1.0},"125":{"tf":1.0},"14":{"tf":2.23606797749979},"149":{"tf":2.23606797749979},"150":{"tf":1.4142135623730951},"151":{"tf":1.7320508075688772},"16":{"tf":1.0},"20":{"tf":1.0},"34":{"tf":1.0},"35":{"tf":1.0},"44":{"tf":1.0},"48":{"tf":1.0},"49":{"tf":1.0},"52":{"tf":1.0},"54":{"tf":1.0},"71":{"tf":1.0},"75":{"tf":1.0},"77":{"tf":1.0},"79":{"tf":1.0},"9":{"tf":1.0}}},"df":0,"docs":{},"t":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":4,"docs":{"146":{"tf":1.0},"149":{"tf":1.0},"5":{"tf":1.0},"95":{"tf":1.7320508075688772}}}}}},"v":{"df":0,"docs":{},"e":{"df":0,"docs":{},"l":{"df":6,"docs":{"104":{"tf":1.0},"46":{"tf":1.0},"59":{"tf":1.0},"67":{"tf":1.0},"69":{"tf":1.7320508075688772},"82":{"tf":1.0}}},"r":{"a":{"df":0,"docs":{},"g":{"df":1,"docs":{"83":{"tf":1.0}}}},"df":0,"docs":{}}}}},"i":{",":{"df":0,"docs":{},"j":{"df":0,"docs":{},"′":{"df":0,"docs":{},"′":{"df":0,"docs":{},"​":{"<":{"df":0,"docs":{},"m":{"a":{"d":{"d":{"df":0,"docs":{},"r":{",":{"df":0,"docs":{},"i":{"+":{"1":{",":{"df":0,"docs":{},"j":{"df":1,"docs":{"142":{"tf":1.0}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}}}},"b":{"df":0,"docs":{},"r":{"a":{"df":0,"docs":{},"r":{"df":0,"docs":{},"i":{"df":1,"docs":{"121":{"tf":1.0}}}}},"df":0,"docs":{}}},"df":3,"docs":{"146":{"tf":1.0},"32":{"tf":1.0},"5":{"tf":1.4142135623730951}},"m":{"b":{"df":1,"docs":{"151":{"tf":1.4142135623730951}}},"df":0,"docs":{}},"n":{"df":0,"docs":{},"e":{"a":{"df":0,"docs":{},"r":{"df":6,"docs":{"14":{"tf":1.0},"21":{"tf":1.7320508075688772},"28":{"tf":1.0},"32":{"tf":1.0},"84":{"tf":1.0},"85":{"tf":1.4142135623730951}}}},"df":1,"docs":{"35":{"tf":1.0}}},"k":{"df":1,"docs":{"151":{"tf":1.0}}}},"o":{"df":0,"docs":{},"r":{"df":1,"docs":{"103":{"tf":1.0}}}},"s":{"df":0,"docs":{},"t":{"df":4,"docs":{"114":{"tf":1.0},"119":{"tf":1.0},"146":{"tf":1.0},"59":{"tf":1.0}}}},"t":{"df":0,"docs":{},"t":{"df":0,"docs":{},"l":{"df":2,"docs":{"107":{"tf":1.0},"14":{"tf":1.0}}}}},"​":{"(":{"df":0,"docs":{},"h":{"df":0,"docs":{},"i":{"df":0,"docs":{},"​":{")":{"=":{"1":{"df":1,"docs":{"5":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"j":{"df":0,"docs":{},"​":{")":{"=":{"0":{"df":1,"docs":{"5":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}}},"o":{"c":{"a":{"df":0,"docs":{},"l":{"df":2,"docs":{"70":{"tf":2.0},"83":{"tf":1.0}}}},"df":0,"docs":{}},"df":0,"docs":{},"g":{"df":0,"docs":{},"i":{"c":{"df":5,"docs":{"146":{"tf":1.0},"151":{"tf":1.0},"16":{"tf":1.0},"36":{"tf":1.0},"4":{"tf":1.0}}},"df":0,"docs":{}}},"n":{"df":0,"docs":{},"g":{"df":2,"docs":{"114":{"tf":1.0},"73":{"tf":1.0}},"e":{"df":0,"docs":{},"r":{"df":2,"docs":{"13":{"tf":1.0},"146":{"tf":1.4142135623730951}}}}}},"o":{"df":0,"docs":{},"k":{"df":8,"docs":{"106":{"tf":1.0},"107":{"tf":1.0},"108":{"tf":1.0},"114":{"tf":1.0},"14":{"tf":1.0},"149":{"tf":2.23606797749979},"21":{"tf":1.0},"51":{"tf":1.0}}},"p":{"df":2,"docs":{"102":{"tf":1.0},"146":{"tf":2.0}}},"s":{"df":1,"docs":{"16":{"tf":1.0}}}},"s":{"df":0,"docs":{},"t":{"df":1,"docs":{"49":{"tf":1.0}}}},"t":{"df":2,"docs":{"50":{"tf":1.0},"53":{"tf":1.0}}},"w":{"df":5,"docs":{"125":{"tf":1.0},"35":{"tf":1.0},"68":{"tf":1.7320508075688772},"81":{"tf":1.0},"91":{"tf":1.0}},"e":{"df":0,"docs":{},"s":{"df":0,"docs":{},"t":{"df":1,"docs":{"3":{"tf":1.0}}}}}}},"p":{"df":0,"docs":{},"u":{"b":{"df":2,"docs":{"142":{"tf":1.0},"147":{"tf":1.0}}},"df":0,"docs":{}}},"u":{"c":{"df":0,"docs":{},"k":{"df":0,"docs":{},"i":{"df":0,"docs":{},"l":{"df":0,"docs":{},"i":{"df":1,"docs":{"146":{"tf":1.0}}}}}}},"df":0,"docs":{}},"×":{"3":{"df":1,"docs":{"141":{"tf":1.0}}},"df":0,"docs":{}},"′":{":":{"=":{"df":0,"docs":{},"⌈":{"df":0,"docs":{},"l":{"df":0,"docs":{},"p":{"df":0,"docs":{},"u":{"b":{"df":0,"docs":{},"​":{"/":{"4":{"df":1,"docs":{"142":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}}}},"df":0,"docs":{}},"df":0,"docs":{},"′":{"=":{"(":{"df":0,"docs":{},"l":{"0":{"df":0,"docs":{},"′":{"df":0,"docs":{},"′":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"l":{"1":{"df":0,"docs":{},"′":{"df":0,"docs":{},"′":{"df":0,"docs":{},"​":{",":{".":{".":{".":{",":{"df":0,"docs":{},"l":{"df":0,"docs":{},"j":{"df":0,"docs":{},"′":{"df":0,"docs":{},"′":{"df":0,"docs":{},"​":{")":{"df":0,"docs":{},"t":{"df":1,"docs":{"142":{"tf":1.0}}}},"df":0,"docs":{}}}}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"m":{",":{"df":0,"docs":{},"v":{")":{"=":{"(":{"0":{",":{"0":{"df":1,"docs":{"146":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":1,"docs":{"146":{"tf":1.0}}}},"/":{"2":{"df":1,"docs":{"133":{"tf":1.0}}},"df":0,"docs":{}},"0":{"df":0,"docs":{},"​":{"=":{"df":0,"docs":{},"r":{"0":{"df":1,"docs":{"151":{"tf":1.0}}},"df":0,"docs":{}}},"df":0,"docs":{}}},":":{"=":{"df":0,"docs":{},"m":{"df":0,"docs":{},"′":{"+":{"df":0,"docs":{},"m":{"df":1,"docs":{"91":{"tf":1.0}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"=":{"2":{"df":0,"docs":{},"m":{"df":3,"docs":{"73":{"tf":1.0},"75":{"tf":1.0},"80":{"tf":1.0}}}},"5":{"2":{"df":1,"docs":{"147":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{},"n":{"+":{"df":0,"docs":{},"k":{"df":1,"docs":{"20":{"tf":1.0}}}},"df":0,"docs":{}}},"^":{"df":0,"docs":{},"j":{"df":1,"docs":{"94":{"tf":1.0}}}},"a":{"c":{"df":0,"docs":{},"h":{"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"df":2,"docs":{"151":{"tf":1.7320508075688772},"50":{"tf":1.4142135623730951}}}}}},"d":{"d":{"df":0,"docs":{},"r":{",":{"df":0,"docs":{},"i":{"+":{"1":{",":{"df":0,"docs":{},"j":{"df":0,"docs":{},"​":{"df":0,"docs":{},"−":{"df":0,"docs":{},"m":{"a":{"d":{"d":{"df":0,"docs":{},"r":{",":{"df":0,"docs":{},"i":{",":{"df":0,"docs":{},"j":{"df":0,"docs":{},"​":{">":{"1":{"df":1,"docs":{"142":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}}}},"df":0,"docs":{}},"df":0,"docs":{}},",":{"df":0,"docs":{},"j":{"df":0,"docs":{},"​":{"<":{"df":0,"docs":{},"l":{"0":{",":{"df":0,"docs":{},"j":{"df":1,"docs":{"142":{"tf":1.0}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":1,"docs":{"142":{"tf":1.0}}}},"df":0,"docs":{},"e":{"df":1,"docs":{"130":{"tf":1.0}}}},"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"df":14,"docs":{"0":{"tf":1.0},"104":{"tf":1.4142135623730951},"111":{"tf":1.0},"113":{"tf":1.0},"128":{"tf":1.0},"129":{"tf":1.0},"133":{"tf":1.0},"145":{"tf":1.4142135623730951},"146":{"tf":1.7320508075688772},"21":{"tf":1.0},"4":{"tf":1.0},"44":{"tf":1.0},"45":{"tf":1.0},"94":{"tf":1.0}},"l":{"df":0,"docs":{},"i":{"df":1,"docs":{"34":{"tf":1.0}}}},"t":{"a":{"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"df":1,"docs":{"13":{"tf":1.0}}}}},"df":0,"docs":{}}}},"j":{"df":0,"docs":{},"o":{"df":0,"docs":{},"r":{"df":3,"docs":{"108":{"tf":1.0},"147":{"tf":1.4142135623730951},"69":{"tf":1.0}}}}},"k":{"df":0,"docs":{},"e":{"df":15,"docs":{"125":{"tf":1.0},"146":{"tf":1.7320508075688772},"149":{"tf":1.0},"151":{"tf":1.0},"20":{"tf":1.0},"45":{"tf":1.0},"48":{"tf":1.0},"56":{"tf":1.0},"70":{"tf":1.0},"71":{"tf":1.0},"77":{"tf":1.0},"80":{"tf":1.0},"83":{"tf":1.0},"89":{"tf":1.0},"95":{"tf":1.0}}}},"l":{"df":0,"docs":{},"f":{"df":0,"docs":{},"o":{"df":0,"docs":{},"r":{"df":0,"docs":{},"m":{"df":1,"docs":{"11":{"tf":1.0}}}}}},"i":{"c":{"df":0,"docs":{},"i":{"df":1,"docs":{"77":{"tf":1.0}}}},"df":0,"docs":{}}},"n":{"df":0,"docs":{},"i":{"df":6,"docs":{"108":{"tf":1.0},"109":{"tf":1.0},"146":{"tf":1.7320508075688772},"20":{"tf":1.0},"70":{"tf":1.0},"98":{"tf":1.0}},"p":{"df":0,"docs":{},"u":{"df":0,"docs":{},"l":{"df":1,"docs":{"118":{"tf":1.0}}}}}}},"p":{"df":3,"docs":{"101":{"tf":1.0},"113":{"tf":1.4142135623730951},"14":{"tf":2.23606797749979}}},"r":{"df":0,"docs":{},"k":{"df":1,"docs":{"3":{"tf":1.0}}}},"t":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":0,"docs":{},"i":{"df":1,"docs":{"144":{"tf":1.0}}}}},"h":{"/":{"df":0,"docs":{},"s":{"df":0,"docs":{},"r":{"c":{"/":{"df":0,"docs":{},"p":{"df":0,"docs":{},"o":{"df":0,"docs":{},"l":{"df":0,"docs":{},"y":{"df":0,"docs":{},"n":{"df":0,"docs":{},"o":{"df":0,"docs":{},"m":{"df":0,"docs":{},"i":{"a":{"df":0,"docs":{},"l":{".":{"df":0,"docs":{},"r":{"df":0,"docs":{},"s":{":":{"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"df":0,"docs":{},"t":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":0,"docs":{},"p":{"df":0,"docs":{},"o":{"df":0,"docs":{},"l":{"a":{"df":0,"docs":{},"t":{"df":0,"docs":{},"e":{"_":{"df":0,"docs":{},"f":{"df":0,"docs":{},"f":{"df":0,"docs":{},"t":{"df":1,"docs":{"3":{"tf":1.0}}}}}},"df":0,"docs":{}}}},"df":1,"docs":{"3":{"tf":1.0}}}}}}}}}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}}}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":2,"docs":{"1":{"tf":1.0},"146":{"tf":1.0}},"e":{"df":0,"docs":{},"m":{"a":{"df":0,"docs":{},"t":{"df":4,"docs":{"131":{"tf":1.0},"46":{"tf":1.0},"70":{"tf":1.0},"8":{"tf":1.0}}}},"df":0,"docs":{}}}},"r":{"df":0,"docs":{},"i":{"c":{"df":9,"docs":{"12":{"tf":1.0},"13":{"tf":2.0},"14":{"tf":1.4142135623730951},"147":{"tf":1.0},"16":{"tf":2.23606797749979},"34":{"tf":1.4142135623730951},"36":{"tf":1.4142135623730951},"8":{"tf":1.4142135623730951},"9":{"tf":1.4142135623730951}}},"df":0,"docs":{},"x":{"df":24,"docs":{"10":{"tf":2.0},"11":{"tf":2.23606797749979},"12":{"tf":1.7320508075688772},"13":{"tf":2.6457513110645907},"14":{"tf":2.0},"141":{"tf":1.0},"142":{"tf":2.23606797749979},"147":{"tf":2.8284271247461903},"16":{"tf":2.449489742783178},"20":{"tf":1.7320508075688772},"23":{"tf":1.4142135623730951},"26":{"tf":1.0},"32":{"tf":1.0},"34":{"tf":1.0},"35":{"tf":1.0},"36":{"tf":2.0},"5":{"tf":1.0},"69":{"tf":1.7320508075688772},"70":{"tf":1.0},"8":{"tf":1.4142135623730951},"83":{"tf":1.0},"9":{"tf":1.7320508075688772},"91":{"tf":2.0},"94":{"tf":1.4142135623730951}}}}},"t":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":3,"docs":{"14":{"tf":1.0},"146":{"tf":1.0},"16":{"tf":1.0}}}}}},"x":{"df":5,"docs":{"103":{"tf":1.0},"119":{"tf":1.0},"130":{"tf":1.0},"134":{"tf":1.0},"146":{"tf":1.0}},"i":{"df":0,"docs":{},"m":{"df":0,"docs":{},"u":{"df":0,"docs":{},"m":{"df":3,"docs":{"142":{"tf":1.0},"36":{"tf":1.0},"38":{"tf":1.4142135623730951}}}}}}}},"df":12,"docs":{"133":{"tf":1.4142135623730951},"14":{"tf":1.0},"141":{"tf":1.0},"142":{"tf":2.449489742783178},"146":{"tf":2.449489742783178},"20":{"tf":1.0},"5":{"tf":1.0},"70":{"tf":1.4142135623730951},"72":{"tf":1.0},"73":{"tf":1.7320508075688772},"75":{"tf":1.0},"91":{"tf":1.7320508075688772}},"e":{"a":{"df":0,"docs":{},"n":{"df":32,"docs":{"101":{"tf":1.0},"105":{"tf":1.0},"106":{"tf":1.0},"108":{"tf":1.0},"114":{"tf":1.0},"123":{"tf":1.0},"124":{"tf":1.4142135623730951},"127":{"tf":1.0},"131":{"tf":1.0},"133":{"tf":1.4142135623730951},"134":{"tf":1.0},"14":{"tf":1.7320508075688772},"142":{"tf":1.0},"146":{"tf":1.4142135623730951},"147":{"tf":1.0},"21":{"tf":1.0},"23":{"tf":1.0},"28":{"tf":1.0},"35":{"tf":1.0},"36":{"tf":1.0},"52":{"tf":1.0},"65":{"tf":1.4142135623730951},"66":{"tf":1.0},"68":{"tf":1.4142135623730951},"70":{"tf":1.0},"72":{"tf":1.0},"73":{"tf":1.0},"75":{"tf":1.0},"78":{"tf":1.0},"9":{"tf":1.0},"92":{"tf":1.0},"95":{"tf":1.0}}}},"c":{"df":0,"docs":{},"h":{"a":{"df":0,"docs":{},"n":{"df":1,"docs":{"4":{"tf":1.0}}}},"df":0,"docs":{}}},"df":0,"docs":{},"m":{"_":{"a":{"df":3,"docs":{"142":{"tf":2.0},"146":{"tf":1.0},"147":{"tf":1.4142135623730951}}},"df":0,"docs":{},"p":{"df":1,"docs":{"147":{"tf":1.0}}},"v":{"df":3,"docs":{"142":{"tf":1.7320508075688772},"146":{"tf":1.0},"147":{"tf":1.4142135623730951}}}},"df":3,"docs":{"133":{"tf":1.0},"134":{"tf":1.4142135623730951},"135":{"tf":1.0}},"o":{"df":0,"docs":{},"r":{"df":0,"docs":{},"i":{"df":11,"docs":{"127":{"tf":1.0},"133":{"tf":3.7416573867739413},"134":{"tf":2.8284271247461903},"135":{"tf":2.0},"141":{"tf":1.0},"142":{"tf":1.7320508075688772},"144":{"tf":2.23606797749979},"146":{"tf":5.5677643628300215},"147":{"tf":2.8284271247461903},"149":{"tf":1.0},"151":{"tf":3.1622776601683795}}},"y":{"_":{"c":{"df":0,"docs":{},"e":{"df":0,"docs":{},"l":{"df":1,"docs":{"134":{"tf":1.0}},"l":{".":{"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"df":0,"docs":{},"l":{"df":1,"docs":{"134":{"tf":1.0}}}}}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"n":{"df":0,"docs":{},"t":{"a":{"df":0,"docs":{},"l":{"df":1,"docs":{"50":{"tf":1.0}}}},"df":0,"docs":{},"i":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"df":14,"docs":{"142":{"tf":1.0},"146":{"tf":1.4142135623730951},"15":{"tf":1.0},"35":{"tf":1.0},"37":{"tf":1.0},"51":{"tf":1.0},"55":{"tf":1.0},"58":{"tf":1.0},"7":{"tf":1.0},"70":{"tf":1.0},"77":{"tf":1.0},"83":{"tf":1.0},"84":{"tf":1.0},"92":{"tf":1.0}}}}}}},"r":{"df":0,"docs":{},"g":{"df":1,"docs":{"12":{"tf":1.0}}},"k":{"df":0,"docs":{},"l":{"df":13,"docs":{"101":{"tf":1.4142135623730951},"110":{"tf":1.0},"112":{"tf":1.4142135623730951},"113":{"tf":1.4142135623730951},"119":{"tf":1.0},"60":{"tf":1.4142135623730951},"62":{"tf":1.4142135623730951},"63":{"tf":1.4142135623730951},"72":{"tf":1.4142135623730951},"76":{"tf":1.0},"80":{"tf":2.449489742783178},"91":{"tf":1.0},"92":{"tf":2.23606797749979}}}}},"s":{"df":0,"docs":{},"s":{"a":{"df":0,"docs":{},"g":{"df":2,"docs":{"40":{"tf":1.4142135623730951},"42":{"tf":1.0}},"e":{"_":{"1":{"df":1,"docs":{"40":{"tf":2.23606797749979}}},"2":{"df":1,"docs":{"40":{"tf":1.4142135623730951}}},"3":{"df":1,"docs":{"40":{"tf":1.0}}},"4":{"df":1,"docs":{"40":{"tf":1.4142135623730951}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"t":{"df":1,"docs":{"121":{"tf":1.0}},"h":{"df":0,"docs":{},"o":{"d":{"df":12,"docs":{"105":{"tf":1.0},"106":{"tf":1.4142135623730951},"107":{"tf":1.7320508075688772},"113":{"tf":1.0},"114":{"tf":2.23606797749979},"117":{"tf":1.0},"123":{"tf":1.0},"14":{"tf":1.4142135623730951},"3":{"tf":1.4142135623730951},"35":{"tf":1.4142135623730951},"40":{"tf":1.7320508075688772},"70":{"tf":1.0}}},"df":0,"docs":{}}}}},"i":{"+":{"1":{"df":0,"docs":{},"​":{"=":{"df":0,"docs":{},"m":{"df":0,"docs":{},"i":{"df":0,"docs":{},"​":{"+":{"1":{"df":1,"docs":{"151":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}}},"df":0,"docs":{}},",":{"df":0,"docs":{},"j":{"df":1,"docs":{"5":{"tf":1.0}}}},"d":{"d":{"df":0,"docs":{},"l":{"df":1,"docs":{"35":{"tf":1.0}}}},"df":0,"docs":{}},"df":1,"docs":{"151":{"tf":1.0}},"l":{"df":0,"docs":{},"l":{"df":0,"docs":{},"i":{"df":0,"docs":{},"s":{"df":0,"docs":{},"e":{"c":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"d":{"df":1,"docs":{"3":{"tf":1.0}}},"df":0,"docs":{}}}},"df":0,"docs":{}}}}}},"n":{"d":{"df":4,"docs":{"124":{"tf":1.0},"146":{"tf":1.0},"49":{"tf":1.0},"50":{"tf":1.0}}},"df":0,"docs":{},"i":{"df":0,"docs":{},"m":{"df":0,"docs":{},"u":{"df":0,"docs":{},"m":{"df":1,"docs":{"142":{"tf":1.0}}}}}},"u":{"df":0,"docs":{},"t":{"df":1,"docs":{"14":{"tf":1.0}}}}},"s":{"df":0,"docs":{},"s":{"df":4,"docs":{"128":{"tf":1.0},"130":{"tf":1.0},"146":{"tf":1.0},"151":{"tf":1.0}}}},"x":{"df":2,"docs":{"150":{"tf":1.0},"66":{"tf":1.0}}}},"j":{"df":1,"docs":{"94":{"tf":1.0}}},"m":{"df":0,"docs":{},"e":{"df":0,"docs":{},"m":{"df":1,"docs":{"147":{"tf":1.0}},"r":{"a":{"df":0,"docs":{},"p":{"2":{"df":1,"docs":{"147":{"tf":1.4142135623730951}}},"df":0,"docs":{}}},"df":0,"docs":{},"e":{"df":0,"docs":{},"p":{"df":0,"docs":{},"l":{"df":1,"docs":{"147":{"tf":1.0}},"s":{"df":0,"docs":{},"o":{"df":0,"docs":{},"r":{"df":0,"docs":{},"t":{"df":1,"docs":{"147":{"tf":1.4142135623730951}}}}}},"​":{"df":0,"docs":{},"∈":{"df":0,"docs":{},"f":{"2":{"df":0,"docs":{},"n":{"+":{"2":{"df":0,"docs":{},"×":{"2":{"df":1,"docs":{"147":{"tf":1.0}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}}}}}},"​":{"df":0,"docs":{},"∈":{"df":0,"docs":{},"f":{"2":{"df":0,"docs":{},"n":{"+":{"2":{"df":0,"docs":{},"×":{"2":{"df":1,"docs":{"147":{"tf":1.0}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}}}}},"o":{"d":{"df":0,"docs":{},"e":{"df":1,"docs":{"146":{"tf":1.4142135623730951}},"l":{"df":1,"docs":{"50":{"tf":1.0}}},"r":{"df":0,"docs":{},"n":{"df":1,"docs":{"42":{"tf":1.0}}}}},"i":{"df":0,"docs":{},"f":{"df":0,"docs":{},"i":{"df":4,"docs":{"102":{"tf":1.0},"146":{"tf":1.0},"45":{"tf":1.0},"59":{"tf":1.4142135623730951}}}}},"u":{"df":0,"docs":{},"l":{"df":1,"docs":{"38":{"tf":1.0}},"o":{"df":1,"docs":{"41":{"tf":1.0}}}}}},"df":0,"docs":{},"f":{"df":0,"docs":{},"f":{"df":0,"docs":{},"s":{"df":0,"docs":{},"e":{"df":0,"docs":{},"t":{"df":1,"docs":{"142":{"tf":1.4142135623730951}}}}}}},"m":{"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"df":0,"docs":{},"t":{"df":5,"docs":{"10":{"tf":1.0},"19":{"tf":1.0},"34":{"tf":1.0},"70":{"tf":1.0},"79":{"tf":1.0}}}}}},"r":{"df":0,"docs":{},"e":{"df":27,"docs":{"103":{"tf":1.0},"114":{"tf":1.7320508075688772},"118":{"tf":1.0},"12":{"tf":1.0},"130":{"tf":1.0},"133":{"tf":1.0},"135":{"tf":1.0},"14":{"tf":2.0},"146":{"tf":1.7320508075688772},"150":{"tf":1.0},"151":{"tf":2.0},"16":{"tf":1.0},"21":{"tf":1.0},"34":{"tf":1.0},"41":{"tf":1.4142135623730951},"42":{"tf":1.0},"45":{"tf":1.0},"64":{"tf":1.0},"66":{"tf":1.0},"68":{"tf":1.0},"7":{"tf":1.0},"73":{"tf":1.4142135623730951},"74":{"tf":1.0},"80":{"tf":1.0},"82":{"tf":1.0},"83":{"tf":1.0},"84":{"tf":1.4142135623730951}},"o":{"df":0,"docs":{},"v":{"df":1,"docs":{"14":{"tf":1.0}}}}}},"v":{"df":0,"docs":{},"e":{"df":4,"docs":{"128":{"tf":1.4142135623730951},"149":{"tf":1.0},"50":{"tf":1.0},"53":{"tf":1.0}},"m":{"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"df":0,"docs":{},"t":{"df":1,"docs":{"149":{"tf":1.0}}}}}}}}},"r":{"a":{"df":0,"docs":{},"n":{"df":0,"docs":{},"g":{"df":0,"docs":{},"e":{"c":{"df":0,"docs":{},"h":{"df":0,"docs":{},"e":{"c":{"df":0,"docs":{},"k":{"df":0,"docs":{},"r":{"a":{"df":0,"docs":{},"p":{"2":{"df":1,"docs":{"147":{"tf":1.4142135623730951}}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}}}},"df":0,"docs":{}}}},"p":{"2":{"df":3,"docs":{"147":{"tf":1.0},"83":{"tf":1.0},"94":{"tf":1.0}},"​":{"df":0,"docs":{},"∈":{"df":0,"docs":{},"f":{"2":{"df":0,"docs":{},"n":{"df":0,"docs":{},"×":{"df":0,"docs":{},"m":{"df":1,"docs":{"94":{"tf":1.0}}}}}},"df":0,"docs":{}}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"s":{"df":1,"docs":{"3":{"tf":4.0}}},"u":{"c":{"df":0,"docs":{},"h":{"df":6,"docs":{"116":{"tf":1.0},"123":{"tf":1.4142135623730951},"125":{"tf":1.0},"21":{"tf":1.0},"37":{"tf":1.0},"66":{"tf":1.0}}}},"df":0,"docs":{},"l":{"df":3,"docs":{"142":{"tf":1.0},"146":{"tf":1.7320508075688772},"147":{"tf":1.0}},"t":{"df":0,"docs":{},"i":{"df":1,"docs":{"139":{"tf":1.0}},"p":{"df":0,"docs":{},"l":{"df":16,"docs":{"10":{"tf":1.4142135623730951},"101":{"tf":1.0},"106":{"tf":1.0},"107":{"tf":1.0},"12":{"tf":1.0},"14":{"tf":1.0},"142":{"tf":1.4142135623730951},"149":{"tf":1.4142135623730951},"151":{"tf":1.0},"44":{"tf":1.0},"65":{"tf":1.7320508075688772},"66":{"tf":1.7320508075688772},"7":{"tf":1.0},"9":{"tf":1.0},"92":{"tf":1.0},"98":{"tf":1.0}},"i":{"df":4,"docs":{"125":{"tf":1.0},"14":{"tf":1.0},"45":{"tf":1.0},"52":{"tf":1.0}}}}},"v":{"a":{"df":0,"docs":{},"r":{"df":0,"docs":{},"i":{"df":2,"docs":{"70":{"tf":1.0},"84":{"tf":1.0}}}}},"df":0,"docs":{}}}}},"t":{"a":{"df":0,"docs":{},"t":{"df":1,"docs":{"118":{"tf":1.4142135623730951}}}},"df":3,"docs":{"114":{"tf":1.0},"44":{"tf":1.0},"45":{"tf":1.7320508075688772}}}},"′":{",":{"df":0,"docs":{},"v":{"df":1,"docs":{"146":{"tf":1.0}}}},"=":{"3":{"3":{"df":1,"docs":{"147":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{},"′":{"=":{"1":{"8":{"df":1,"docs":{"147":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"∈":{"df":0,"docs":{},"f":{"(":{"df":0,"docs":{},"l":{"+":{"df":0,"docs":{},"l":{"df":0,"docs":{},"′":{")":{"df":0,"docs":{},"×":{"3":{"3":{"df":1,"docs":{"142":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}}},"+":{"df":0,"docs":{},"l":{"df":0,"docs":{},"′":{"df":0,"docs":{},"′":{")":{"df":0,"docs":{},"×":{"3":{"3":{"df":1,"docs":{"142":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{},"l":{"df":0,"docs":{},"×":{"3":{"3":{"df":1,"docs":{"142":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"−":{"1":{"df":2,"docs":{"73":{"tf":2.23606797749979},"79":{"tf":1.0}}},"df":0,"docs":{}},"≥":{"df":0,"docs":{},"n":{"df":1,"docs":{"20":{"tf":1.0}}}}},"n":{"+":{"1":{"df":1,"docs":{"26":{"tf":1.0}}},"df":0,"docs":{},"m":{"+":{"1":{")":{"df":0,"docs":{},"×":{"3":{"df":1,"docs":{"14":{"tf":1.0}}},"5":{"df":1,"docs":{"14":{"tf":1.0}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"<":{"df":0,"docs":{},"m":{"df":3,"docs":{"73":{"tf":1.0},"75":{"tf":1.0},"80":{"tf":1.0}}}},"=":{"2":{"df":0,"docs":{},"n":{"df":3,"docs":{"73":{"tf":1.0},"75":{"tf":1.0},"80":{"tf":1.0}}}},"3":{"df":1,"docs":{"51":{"tf":1.0}}},"df":0,"docs":{},"n":{"+":{"df":0,"docs":{},"m":{"+":{"1":{"df":1,"docs":{"14":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"a":{"df":0,"docs":{},"i":{"df":0,"docs":{},"v":{"df":3,"docs":{"101":{"tf":1.0},"123":{"tf":1.0},"3":{"tf":1.0}}}},"m":{"df":0,"docs":{},"e":{"df":3,"docs":{"102":{"tf":1.0},"13":{"tf":1.0},"130":{"tf":1.0}}}},"r":{"df":0,"docs":{},"r":{"df":0,"docs":{},"o":{"df":0,"docs":{},"w":{"df":1,"docs":{"50":{"tf":1.0}}}}}},"t":{"df":0,"docs":{},"u":{"df":0,"docs":{},"r":{"df":2,"docs":{"101":{"tf":1.0},"84":{"tf":1.0}}}}}},"c":{"df":1,"docs":{"28":{"tf":1.0}}},"df":14,"docs":{"124":{"tf":2.23606797749979},"127":{"tf":1.0},"14":{"tf":2.449489742783178},"151":{"tf":1.0},"16":{"tf":1.0},"20":{"tf":1.0},"23":{"tf":1.4142135623730951},"32":{"tf":1.0},"35":{"tf":1.7320508075688772},"68":{"tf":1.4142135623730951},"75":{"tf":1.4142135623730951},"78":{"tf":1.4142135623730951},"79":{"tf":1.0},"91":{"tf":1.4142135623730951}},"e":{"a":{"df":0,"docs":{},"r":{"df":0,"docs":{},"e":{"df":0,"docs":{},"s":{"df":0,"docs":{},"t":{"df":1,"docs":{"142":{"tf":1.0}}}}}}},"c":{"df":0,"docs":{},"e":{"df":0,"docs":{},"s":{"df":0,"docs":{},"s":{"a":{"df":0,"docs":{},"r":{"df":0,"docs":{},"i":{"df":6,"docs":{"103":{"tf":1.0},"110":{"tf":1.0},"112":{"tf":1.0},"13":{"tf":1.0},"133":{"tf":1.0},"33":{"tf":1.0}},"l":{"df":0,"docs":{},"i":{"df":1,"docs":{"14":{"tf":1.0}}}}}}},"df":0,"docs":{}}}}},"df":0,"docs":{},"e":{"d":{"df":42,"docs":{"102":{"tf":1.4142135623730951},"105":{"tf":1.0},"107":{"tf":2.0},"109":{"tf":1.0},"111":{"tf":1.4142135623730951},"114":{"tf":2.0},"117":{"tf":1.4142135623730951},"118":{"tf":1.4142135623730951},"119":{"tf":1.4142135623730951},"123":{"tf":1.0},"125":{"tf":1.0},"130":{"tf":1.0},"133":{"tf":1.0},"14":{"tf":1.4142135623730951},"144":{"tf":1.0},"146":{"tf":3.3166247903554},"149":{"tf":1.0},"151":{"tf":1.7320508075688772},"19":{"tf":1.0},"20":{"tf":1.0},"21":{"tf":1.0},"32":{"tf":2.0},"34":{"tf":1.0},"35":{"tf":1.4142135623730951},"36":{"tf":1.4142135623730951},"44":{"tf":1.4142135623730951},"48":{"tf":1.0},"5":{"tf":1.0},"57":{"tf":1.0},"58":{"tf":1.4142135623730951},"59":{"tf":1.0},"60":{"tf":1.7320508075688772},"68":{"tf":1.7320508075688772},"69":{"tf":1.0},"70":{"tf":1.7320508075688772},"74":{"tf":1.4142135623730951},"75":{"tf":1.0},"8":{"tf":1.4142135623730951},"80":{"tf":1.7320508075688772},"82":{"tf":1.0},"88":{"tf":1.0},"9":{"tf":1.0}}},"df":0,"docs":{}},"g":{"df":0,"docs":{},"l":{"df":0,"docs":{},"i":{"df":0,"docs":{},"g":{"df":1,"docs":{"73":{"tf":1.0}}}}}},"v":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":1,"docs":{"95":{"tf":1.0}}}}},"w":{"_":{"df":0,"docs":{},"s":{"df":0,"docs":{},"i":{"df":0,"docs":{},"m":{"df":0,"docs":{},"p":{"df":0,"docs":{},"l":{"df":1,"docs":{"106":{"tf":1.0}}}}}}}},"df":10,"docs":{"106":{"tf":1.0},"13":{"tf":1.0},"14":{"tf":1.0},"142":{"tf":1.0},"146":{"tf":1.7320508075688772},"20":{"tf":1.4142135623730951},"59":{"tf":1.0},"68":{"tf":1.0},"83":{"tf":1.4142135623730951},"94":{"tf":1.0}}},"x":{"df":0,"docs":{},"t":{"df":15,"docs":{"10":{"tf":1.0},"102":{"tf":1.4142135623730951},"118":{"tf":1.0},"128":{"tf":1.0},"134":{"tf":1.4142135623730951},"14":{"tf":1.4142135623730951},"144":{"tf":1.0},"149":{"tf":1.0},"23":{"tf":1.0},"35":{"tf":1.0},"49":{"tf":1.0},"66":{"tf":1.0},"74":{"tf":1.0},"77":{"tf":1.0},"9":{"tf":1.0}}}}},"i":{"c":{"df":0,"docs":{},"e":{"df":1,"docs":{"14":{"tf":1.0}}}},"df":0,"docs":{}},"o":{"df":0,"docs":{},"n":{"c":{"df":2,"docs":{"119":{"tf":1.0},"88":{"tf":1.4142135623730951}}},"df":1,"docs":{"28":{"tf":1.4142135623730951}},"i":{"df":0,"docs":{},"n":{"df":0,"docs":{},"t":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"a":{"c":{"df":0,"docs":{},"t":{"df":1,"docs":{"89":{"tf":1.0}}}},"df":0,"docs":{}},"df":0,"docs":{}}}}}},"z":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":0,"docs":{},"o":{"df":2,"docs":{"73":{"tf":1.0},"75":{"tf":1.0}}}}}}},"t":{"a":{"df":0,"docs":{},"t":{"df":11,"docs":{"142":{"tf":1.0},"147":{"tf":1.0},"34":{"tf":1.0},"35":{"tf":1.0},"5":{"tf":1.4142135623730951},"70":{"tf":1.4142135623730951},"87":{"tf":1.0},"9":{"tf":1.0},"90":{"tf":1.4142135623730951},"92":{"tf":1.7320508075688772},"95":{"tf":1.4142135623730951}}}},"df":0,"docs":{},"e":{"df":14,"docs":{"104":{"tf":1.0},"125":{"tf":1.7320508075688772},"128":{"tf":1.0},"131":{"tf":1.0},"146":{"tf":1.0},"44":{"tf":1.0},"68":{"tf":1.0},"74":{"tf":1.0},"79":{"tf":1.0},"80":{"tf":1.4142135623730951},"84":{"tf":1.0},"92":{"tf":1.0},"95":{"tf":1.0},"96":{"tf":1.0}}},"h":{"df":1,"docs":{"114":{"tf":1.0}}},"i":{"c":{"df":8,"docs":{"10":{"tf":1.0},"114":{"tf":1.0},"118":{"tf":1.0},"13":{"tf":1.0},"14":{"tf":1.4142135623730951},"21":{"tf":1.0},"68":{"tf":1.0},"7":{"tf":1.0}}},"df":0,"docs":{}}},"w":{"df":17,"docs":{"11":{"tf":1.4142135623730951},"12":{"tf":1.0},"121":{"tf":1.0},"123":{"tf":1.0},"13":{"tf":1.4142135623730951},"14":{"tf":2.23606797749979},"146":{"tf":1.0},"149":{"tf":1.7320508075688772},"150":{"tf":1.0},"33":{"tf":1.0},"44":{"tf":1.0},"58":{"tf":1.0},"65":{"tf":1.0},"66":{"tf":1.0},"7":{"tf":1.0},"77":{"tf":1.0},"83":{"tf":1.0}}}},"u":{"df":0,"docs":{},"m":{"_":{"c":{"df":0,"docs":{},"o":{"df":0,"docs":{},"l":{"df":1,"docs":{"108":{"tf":1.7320508075688772}}}}},"df":0,"docs":{},"t":{"df":0,"docs":{},"r":{"a":{"df":0,"docs":{},"n":{"df":0,"docs":{},"s":{"df":0,"docs":{},"i":{"df":0,"docs":{},"t":{"df":0,"docs":{},"i":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"_":{"c":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"df":0,"docs":{},"s":{"df":0,"docs":{},"t":{"df":0,"docs":{},"r":{"a":{"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"df":0,"docs":{},"t":{"df":1,"docs":{"109":{"tf":1.4142135623730951}}}}}},"df":0,"docs":{}}}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}}}}}}},"df":0,"docs":{}}}},"b":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":25,"docs":{"106":{"tf":1.0},"109":{"tf":2.0},"118":{"tf":1.7320508075688772},"119":{"tf":1.0},"124":{"tf":1.4142135623730951},"125":{"tf":1.0},"128":{"tf":1.4142135623730951},"133":{"tf":1.0},"142":{"tf":1.0},"144":{"tf":1.0},"146":{"tf":1.4142135623730951},"147":{"tf":1.0},"149":{"tf":1.0},"151":{"tf":1.7320508075688772},"21":{"tf":1.0},"32":{"tf":1.0},"35":{"tf":1.0},"49":{"tf":2.23606797749979},"50":{"tf":1.0},"56":{"tf":1.0},"59":{"tf":1.0},"69":{"tf":1.0},"73":{"tf":1.4142135623730951},"88":{"tf":1.0},"91":{"tf":2.6457513110645907}}}}},"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":4,"docs":{"114":{"tf":1.0},"123":{"tf":1.0},"125":{"tf":1.4142135623730951},"55":{"tf":1.0}}}}}},"×":{"1":{"df":2,"docs":{"14":{"tf":1.0},"16":{"tf":1.4142135623730951}}},"3":{"df":3,"docs":{"14":{"tf":1.0},"16":{"tf":1.4142135623730951},"23":{"tf":1.0}}},"df":0,"docs":{}},"−":{"1":{")":{"df":0,"docs":{},"t":{"df":1,"docs":{"33":{"tf":1.0}}}},"df":7,"docs":{"14":{"tf":1.0},"20":{"tf":1.0},"73":{"tf":1.4142135623730951},"74":{"tf":1.4142135623730951},"78":{"tf":1.4142135623730951},"79":{"tf":1.0},"80":{"tf":1.0}}},"df":0,"docs":{}}},"o":{"b":{"df":0,"docs":{},"j":{"df":0,"docs":{},"e":{"c":{"df":0,"docs":{},"t":{"df":2,"docs":{"87":{"tf":1.0},"89":{"tf":1.0}}}},"df":0,"docs":{}}},"s":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":0,"docs":{},"v":{"df":3,"docs":{"13":{"tf":1.0},"151":{"tf":1.0},"7":{"tf":1.0}}}}}},"t":{"a":{"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"df":14,"docs":{"114":{"tf":1.4142135623730951},"124":{"tf":1.0},"14":{"tf":1.7320508075688772},"142":{"tf":1.7320508075688772},"146":{"tf":1.7320508075688772},"32":{"tf":1.0},"34":{"tf":1.0},"44":{"tf":1.4142135623730951},"7":{"tf":1.0},"83":{"tf":1.0},"84":{"tf":1.4142135623730951},"91":{"tf":1.0},"94":{"tf":1.7320508075688772},"95":{"tf":1.0}}}}},"df":0,"docs":{}},"v":{"df":0,"docs":{},"i":{"df":0,"docs":{},"o":{"df":0,"docs":{},"u":{"df":3,"docs":{"107":{"tf":1.0},"14":{"tf":1.0},"16":{"tf":1.0}}}}}}},"c":{"c":{"df":0,"docs":{},"u":{"df":0,"docs":{},"r":{"df":1,"docs":{"41":{"tf":1.0}},"r":{"df":1,"docs":{"14":{"tf":1.7320508075688772}}}}}},"df":0,"docs":{}},"d":{"d":{"df":4,"docs":{"119":{"tf":1.0},"67":{"tf":1.0},"7":{"tf":1.0},"94":{"tf":1.0}}},"df":0,"docs":{}},"df":4,"docs":{"11":{"tf":1.0},"12":{"tf":1.0},"13":{"tf":1.0},"14":{"tf":1.0}},"f":{"df":0,"docs":{},"f":{"0":{"df":1,"docs":{"130":{"tf":1.0}}},"1":{"df":1,"docs":{"130":{"tf":1.0}}},"2":{"df":1,"docs":{"130":{"tf":1.0}}},"_":{"d":{"df":0,"docs":{},"s":{"df":0,"docs":{},"t":{"df":4,"docs":{"130":{"tf":1.4142135623730951},"142":{"tf":1.0},"146":{"tf":1.7320508075688772},"147":{"tf":1.0}}}}},"df":0,"docs":{},"o":{"df":0,"docs":{},"p":{"0":{"df":4,"docs":{"130":{"tf":1.4142135623730951},"142":{"tf":1.0},"146":{"tf":1.7320508075688772},"147":{"tf":1.0}}},"1":{"df":4,"docs":{"130":{"tf":1.4142135623730951},"142":{"tf":1.0},"146":{"tf":1.7320508075688772},"147":{"tf":1.0}}},"df":0,"docs":{}}}},"df":0,"docs":{},"s":{"df":0,"docs":{},"e":{"df":0,"docs":{},"t":{"df":12,"docs":{"109":{"tf":1.0},"125":{"tf":2.23606797749979},"128":{"tf":1.0},"130":{"tf":2.23606797749979},"133":{"tf":3.4641016151377544},"134":{"tf":1.0},"136":{"tf":2.449489742783178},"139":{"tf":1.4142135623730951},"142":{"tf":1.4142135623730951},"146":{"tf":2.23606797749979},"147":{"tf":1.7320508075688772},"151":{"tf":1.0}},"s":{"/":{"df":0,"docs":{},"r":{"a":{"df":0,"docs":{},"n":{"df":0,"docs":{},"g":{"df":1,"docs":{"146":{"tf":1.0}}}}},"df":0,"docs":{}}},"_":{"b":{"df":1,"docs":{"147":{"tf":1.0}}},"df":0,"docs":{},"p":{"df":1,"docs":{"147":{"tf":1.0}}}},"df":0,"docs":{}}}}}}},"m":{"df":0,"docs":{},"e":{"df":0,"docs":{},"g":{"a":{"^":{"2":{"df":1,"docs":{"68":{"tf":1.4142135623730951}}},"df":0,"docs":{},"i":{"df":1,"docs":{"68":{"tf":1.4142135623730951}}},"{":{"1":{"6":{"df":1,"docs":{"68":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":2,"docs":{"35":{"tf":1.0},"68":{"tf":1.7320508075688772}}},"df":0,"docs":{}}},"i":{"df":0,"docs":{},"s":{"df":0,"docs":{},"s":{"df":1,"docs":{"64":{"tf":1.0}}}}}},"n":{"c":{"df":7,"docs":{"111":{"tf":1.0},"134":{"tf":1.0},"44":{"tf":1.0},"67":{"tf":1.0},"8":{"tf":1.0},"80":{"tf":1.4142135623730951},"98":{"tf":1.0}}},"df":47,"docs":{"10":{"tf":1.0},"101":{"tf":1.0},"102":{"tf":1.0},"105":{"tf":1.0},"106":{"tf":1.0},"107":{"tf":2.0},"109":{"tf":1.0},"11":{"tf":1.4142135623730951},"114":{"tf":2.0},"118":{"tf":1.0},"123":{"tf":1.0},"124":{"tf":1.0},"125":{"tf":1.4142135623730951},"13":{"tf":2.0},"130":{"tf":1.0},"131":{"tf":1.0},"132":{"tf":1.0},"133":{"tf":1.0},"135":{"tf":1.0},"136":{"tf":1.0},"14":{"tf":2.449489742783178},"142":{"tf":1.0},"145":{"tf":1.0},"146":{"tf":3.3166247903554},"149":{"tf":2.23606797749979},"151":{"tf":1.7320508075688772},"19":{"tf":1.0},"21":{"tf":1.0},"22":{"tf":1.0},"34":{"tf":1.0},"49":{"tf":2.23606797749979},"5":{"tf":1.0},"50":{"tf":1.0},"51":{"tf":1.0},"52":{"tf":1.4142135623730951},"57":{"tf":1.0},"59":{"tf":1.4142135623730951},"65":{"tf":2.0},"66":{"tf":1.7320508075688772},"68":{"tf":1.0},"70":{"tf":1.0},"73":{"tf":1.4142135623730951},"80":{"tf":2.0},"84":{"tf":2.0},"9":{"tf":1.7320508075688772},"92":{"tf":1.0},"99":{"tf":1.0}},"w":{"a":{"df":0,"docs":{},"r":{"d":{"df":1,"docs":{"52":{"tf":1.0}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"o":{"d":{"df":2,"docs":{"113":{"tf":1.0},"117":{"tf":1.4142135623730951}}},"df":0,"docs":{}},"p":{"0":{"_":{"a":{"d":{"d":{"df":0,"docs":{},"r":{"[":{"0":{"df":1,"docs":{"147":{"tf":1.0}}},"df":0,"docs":{}},"df":3,"docs":{"142":{"tf":1.0},"146":{"tf":2.0},"147":{"tf":1.4142135623730951}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{},"r":{"df":0,"docs":{},"e":{"df":0,"docs":{},"g":{"df":1,"docs":{"146":{"tf":1.0}}}}}},"df":4,"docs":{"133":{"tf":2.0},"142":{"tf":1.0},"146":{"tf":2.0},"147":{"tf":1.4142135623730951}},"r":{"df":0,"docs":{},"e":{"df":0,"docs":{},"g":{"df":1,"docs":{"149":{"tf":1.0}}}}}},"1":{"_":{"a":{"d":{"d":{"df":0,"docs":{},"r":{"[":{"0":{"df":1,"docs":{"147":{"tf":1.0}}},"df":0,"docs":{}},"df":3,"docs":{"142":{"tf":1.0},"146":{"tf":1.7320508075688772},"147":{"tf":1.4142135623730951}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{},"s":{"df":0,"docs":{},"r":{"c":{"df":1,"docs":{"146":{"tf":1.0}}},"df":0,"docs":{}}}},"df":4,"docs":{"133":{"tf":2.0},"142":{"tf":1.0},"146":{"tf":2.0},"147":{"tf":1.4142135623730951}}},"c":{"df":0,"docs":{},"o":{"d":{"df":2,"docs":{"146":{"tf":1.7320508075688772},"149":{"tf":1.0}}},"df":0,"docs":{}}},"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"(":{"a":{",":{"a":{"b":{"df":0,"docs":{},"i":{"df":1,"docs":{"92":{"tf":1.0}}}},"df":0,"docs":{}},"df":0,"docs":{},"i":{"df":2,"docs":{"92":{"tf":1.4142135623730951},"95":{"tf":1.0}}}},"df":0,"docs":{}},"df":0,"docs":{},"f":{"df":1,"docs":{"19":{"tf":1.0}}},"h":{"1":{"df":0,"docs":{},"​":{"(":{"d":{"df":0,"docs":{},"l":{"d":{"df":0,"docs":{},"e":{"df":0,"docs":{},"​":{")":{",":{"df":0,"docs":{},"υ":{"df":1,"docs":{"94":{"tf":1.0}}},"−":{"df":0,"docs":{},"υ":{"df":1,"docs":{"94":{"tf":1.0}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"2":{"df":0,"docs":{},"​":{"(":{"d":{"df":0,"docs":{},"l":{"d":{"df":0,"docs":{},"e":{"df":0,"docs":{},"​":{")":{",":{"df":0,"docs":{},"υ":{"df":1,"docs":{"94":{"tf":1.0}}},"−":{"df":0,"docs":{},"υ":{"df":1,"docs":{"94":{"tf":1.0}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"p":{"(":{"d":{")":{",":{"df":0,"docs":{},"h":{"df":0,"docs":{},"ω":{"df":0,"docs":{},"i":{"df":1,"docs":{"101":{"tf":1.0}}}}},"−":{"df":0,"docs":{},"h":{"df":0,"docs":{},"ω":{"df":0,"docs":{},"i":{"df":1,"docs":{"101":{"tf":1.0}}}}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{},"k":{"df":0,"docs":{},"​":{"(":{"d":{"df":0,"docs":{},"k":{"df":0,"docs":{},"​":{")":{",":{"df":0,"docs":{},"υ":{"df":0,"docs":{},"s":{"2":{"df":0,"docs":{},"k":{"df":1,"docs":{"94":{"tf":1.0}}}},"df":0,"docs":{}}},"−":{"df":0,"docs":{},"υ":{"df":0,"docs":{},"s":{"2":{"df":0,"docs":{},"k":{"df":1,"docs":{"94":{"tf":1.0}}}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"t":{"df":0,"docs":{},"j":{"df":0,"docs":{},"​":{"(":{"d":{"df":0,"docs":{},"l":{"d":{"df":0,"docs":{},"e":{"df":0,"docs":{},"​":{")":{",":{"df":0,"docs":{},"υ":{"df":2,"docs":{"94":{"tf":1.0},"95":{"tf":1.0}}},"−":{"df":0,"docs":{},"υ":{"df":1,"docs":{"94":{"tf":1.0}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":17,"docs":{"101":{"tf":1.4142135623730951},"102":{"tf":1.0},"119":{"tf":1.0},"19":{"tf":1.0},"21":{"tf":2.8284271247461903},"28":{"tf":1.4142135623730951},"33":{"tf":1.4142135623730951},"35":{"tf":1.4142135623730951},"69":{"tf":1.0},"71":{"tf":1.0},"72":{"tf":1.0},"75":{"tf":2.0},"77":{"tf":1.0},"80":{"tf":2.23606797749979},"86":{"tf":1.4142135623730951},"94":{"tf":1.0},"95":{"tf":1.7320508075688772}}},"r":{"a":{"df":0,"docs":{},"n":{"d":{"df":3,"docs":{"11":{"tf":1.0},"20":{"tf":1.0},"9":{"tf":1.4142135623730951}}},"df":0,"docs":{}}},"df":5,"docs":{"13":{"tf":1.0},"45":{"tf":2.0},"50":{"tf":1.0},"92":{"tf":2.0},"99":{"tf":1.0}}}},"s":{"_":{"df":0,"docs":{},"m":{"df":0,"docs":{},"u":{"df":0,"docs":{},"l":{"df":1,"docs":{"136":{"tf":1.4142135623730951}}}}}},"df":0,"docs":{}},"t":{"df":0,"docs":{},"i":{"df":0,"docs":{},"m":{"df":9,"docs":{"12":{"tf":1.0},"136":{"tf":1.0},"146":{"tf":1.4142135623730951},"21":{"tf":1.0},"67":{"tf":1.0},"80":{"tf":1.0},"94":{"tf":1.7320508075688772},"96":{"tf":1.0},"99":{"tf":1.0}}},"o":{"df":0,"docs":{},"n":{"df":4,"docs":{"104":{"tf":2.0},"109":{"tf":1.4142135623730951},"110":{"tf":1.0},"91":{"tf":1.0}}}}}}},"r":{"d":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"_":{"df":0,"docs":{},"r":{"_":{"df":0,"docs":{},"m":{"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"df":0,"docs":{},"u":{"df":0,"docs":{},"s":{"_":{"1":{"_":{"df":0,"docs":{},"r":{"df":0,"docs":{},"o":{"df":0,"docs":{},"o":{"df":0,"docs":{},"t":{"_":{"df":0,"docs":{},"u":{"df":0,"docs":{},"n":{"df":1,"docs":{"44":{"tf":1.0}}}}},"df":0,"docs":{}}}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}}}}},"df":0,"docs":{}}},"df":16,"docs":{"101":{"tf":2.0},"118":{"tf":1.0},"130":{"tf":1.0},"132":{"tf":1.0},"134":{"tf":1.0},"135":{"tf":1.0},"142":{"tf":1.4142135623730951},"146":{"tf":2.449489742783178},"147":{"tf":1.4142135623730951},"20":{"tf":1.0},"36":{"tf":1.0},"38":{"tf":1.7320508075688772},"41":{"tf":1.7320508075688772},"45":{"tf":1.0},"94":{"tf":1.4142135623730951},"95":{"tf":1.0}}}}},"df":0,"docs":{},"g":{"a":{"df":0,"docs":{},"n":{"df":2,"docs":{"149":{"tf":1.0},"69":{"tf":1.0}}}},"df":0,"docs":{}},"i":{"df":0,"docs":{},"g":{"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"df":6,"docs":{"13":{"tf":1.0},"146":{"tf":1.0},"19":{"tf":1.4142135623730951},"74":{"tf":1.0},"83":{"tf":1.0},"84":{"tf":1.0}}}}}}},"t":{"df":0,"docs":{},"h":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":4,"docs":{"107":{"tf":1.0},"109":{"tf":1.0},"149":{"tf":1.0},"41":{"tf":1.0}},"w":{"df":0,"docs":{},"i":{"df":0,"docs":{},"s":{"df":5,"docs":{"104":{"tf":1.0},"124":{"tf":1.0},"19":{"tf":1.0},"33":{"tf":1.0},"7":{"tf":1.0}}}}}}}}},"u":{"df":0,"docs":{},"t":{"c":{"df":0,"docs":{},"o":{"df":0,"docs":{},"m":{"df":1,"docs":{"118":{"tf":1.0}}}}},"df":16,"docs":{"101":{"tf":1.0},"112":{"tf":1.0},"115":{"tf":1.0},"117":{"tf":1.4142135623730951},"119":{"tf":1.0},"16":{"tf":1.0},"41":{"tf":1.0},"42":{"tf":1.0},"50":{"tf":1.0},"59":{"tf":1.4142135623730951},"60":{"tf":1.0},"62":{"tf":1.0},"66":{"tf":1.0},"7":{"tf":1.0},"72":{"tf":1.0},"79":{"tf":1.0}},"p":{"df":0,"docs":{},"u":{"df":0,"docs":{},"t":{"df":16,"docs":{"104":{"tf":1.0},"11":{"tf":1.0},"13":{"tf":1.7320508075688772},"146":{"tf":1.0},"151":{"tf":1.0},"16":{"tf":1.0},"19":{"tf":1.0},"32":{"tf":1.0},"33":{"tf":2.0},"34":{"tf":1.4142135623730951},"35":{"tf":2.0},"4":{"tf":1.0},"40":{"tf":1.4142135623730951},"7":{"tf":2.8284271247461903},"74":{"tf":1.0},"9":{"tf":2.449489742783178}}}}},"s":{"df":0,"docs":{},"i":{"d":{"df":1,"docs":{"20":{"tf":1.0}}},"df":0,"docs":{}}}}},"v":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":16,"docs":{"103":{"tf":1.0},"104":{"tf":1.4142135623730951},"109":{"tf":1.0},"111":{"tf":1.0},"112":{"tf":1.0},"125":{"tf":2.0},"131":{"tf":1.4142135623730951},"149":{"tf":1.4142135623730951},"44":{"tf":1.0},"62":{"tf":1.0},"68":{"tf":1.4142135623730951},"70":{"tf":1.0},"80":{"tf":1.0},"82":{"tf":1.4142135623730951},"83":{"tf":1.0},"84":{"tf":1.0}},"v":{"df":0,"docs":{},"i":{"df":0,"docs":{},"e":{"df":0,"docs":{},"w":{"df":2,"docs":{"69":{"tf":1.0},"92":{"tf":1.0}}}}}},"w":{"df":0,"docs":{},"h":{"df":0,"docs":{},"e":{"df":0,"docs":{},"l":{"df":0,"docs":{},"m":{"df":3,"docs":{"14":{"tf":2.6457513110645907},"19":{"tf":1.0},"85":{"tf":1.0}}}}}}}}}}},"p":{"(":{"1":{")":{"=":{"1":{"df":0,"docs":{},"p":{"(":{"df":0,"docs":{},"g":{")":{"=":{"1":{"df":1,"docs":{"54":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"d":{"1":{"df":0,"docs":{},"​":{")":{",":{"df":0,"docs":{},"…":{",":{"df":0,"docs":{},"p":{"(":{"d":{"df":0,"docs":{},"m":{"df":3,"docs":{"73":{"tf":1.4142135623730951},"76":{"tf":1.0},"80":{"tf":1.0}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{},"i":{"df":0,"docs":{},"​":{")":{"=":{"df":0,"docs":{},"y":{"df":0,"docs":{},"i":{"df":1,"docs":{"73":{"tf":1.0}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{},"h":{")":{",":{"df":0,"docs":{},"p":{"(":{"df":0,"docs":{},"h":{"df":0,"docs":{},"ω":{")":{",":{"df":0,"docs":{},"p":{"(":{"df":0,"docs":{},"h":{"df":0,"docs":{},"ω":{"2":{")":{",":{"df":0,"docs":{},"…":{",":{"df":0,"docs":{},"p":{"(":{"df":0,"docs":{},"h":{"df":0,"docs":{},"ω":{"2":{"df":0,"docs":{},"k":{"df":0,"docs":{},"−":{"1":{"df":1,"docs":{"101":{"tf":1.0}}},"df":0,"docs":{}}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{},"ω":{"df":0,"docs":{},"i":{"df":1,"docs":{"101":{"tf":1.4142135623730951}}},"σ":{"(":{"df":0,"docs":{},"i":{"df":1,"docs":{"101":{"tf":1.4142135623730951}}}},"df":0,"docs":{}}}},"x":{"df":1,"docs":{"54":{"tf":1.0}}},"z":{"df":2,"docs":{"77":{"tf":1.0},"78":{"tf":1.0}}},"ζ":{")":{"=":{"a":{"(":{"df":0,"docs":{},"ζ":{")":{"df":0,"docs":{},"q":{"df":0,"docs":{},"l":{"df":0,"docs":{},"​":{"(":{"df":0,"docs":{},"ζ":{")":{"+":{"b":{"(":{"df":0,"docs":{},"ζ":{")":{"df":0,"docs":{},"q":{"df":0,"docs":{},"r":{"df":1,"docs":{"21":{"tf":1.0}}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{},"p":{"df":0,"docs":{},"ˉ":{"df":0,"docs":{},"​":{"c":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"p":{"df":0,"docs":{},"ˉ":{"df":0,"docs":{},"​":{"df":0,"docs":{},"n":{"c":{"df":1,"docs":{"32":{"tf":1.0}}},"df":0,"docs":{}}}}}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"z":{"df":0,"docs":{},"h":{"df":0,"docs":{},"​":{"(":{"df":0,"docs":{},"ζ":{")":{"df":0,"docs":{},"t":{"df":1,"docs":{"21":{"tf":1.0}}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}},"−":{"df":0,"docs":{},"h":{"df":0,"docs":{},"ω":{"df":0,"docs":{},"i":{"df":1,"docs":{"101":{"tf":1.0}}},"σ":{"(":{"df":0,"docs":{},"i":{"df":1,"docs":{"101":{"tf":1.0}}}},"df":0,"docs":{}}}}}},",":{"a":{",":{"b":{",":{"df":0,"docs":{},"z":{"df":1,"docs":{"5":{"tf":1.0}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{},"q":{"df":0,"docs":{},"∈":{"df":0,"docs":{},"f":{"[":{"df":0,"docs":{},"x":{"df":1,"docs":{"90":{"tf":1.0}}}},"df":0,"docs":{}}}}},".":{"df":0,"docs":{},"i":{"df":1,"docs":{"32":{"tf":1.0}}}},"/":{"df":0,"docs":{},"q":{"df":1,"docs":{"90":{"tf":1.0}}}},"0":{"df":1,"docs":{"94":{"tf":1.0}}},"1":{"df":0,"docs":{},"​":{"(":{"d":{"1":{"df":0,"docs":{},"​":{")":{"df":0,"docs":{},"∣":{"df":0,"docs":{},"∣":{"df":0,"docs":{},"…":{"df":0,"docs":{},"∣":{"df":0,"docs":{},"∣":{"df":0,"docs":{},"p":{"df":0,"docs":{},"l":{"df":0,"docs":{},"​":{"(":{"d":{"1":{"df":0,"docs":{},"​":{")":{",":{"df":0,"docs":{},"…":{",":{"df":0,"docs":{},"p":{"df":0,"docs":{},"l":{"df":0,"docs":{},"​":{"(":{"d":{"df":0,"docs":{},"m":{"df":0,"docs":{},"​":{")":{"df":0,"docs":{},"∣":{"df":0,"docs":{},"∣":{"df":0,"docs":{},"…":{"df":0,"docs":{},"∣":{"df":0,"docs":{},"∣":{"df":0,"docs":{},"p":{"df":0,"docs":{},"n":{"df":0,"docs":{},"​":{"(":{"d":{"df":0,"docs":{},"m":{"df":1,"docs":{"80":{"tf":1.0}}}},"df":0,"docs":{}},"df":0,"docs":{}}}}}}}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}}}}}}}},"df":0,"docs":{}}},"df":0,"docs":{},"i":{"df":0,"docs":{},"​":{")":{",":{"df":0,"docs":{},"…":{",":{"df":0,"docs":{},"p":{"df":0,"docs":{},"l":{"df":0,"docs":{},"​":{"(":{"d":{"df":0,"docs":{},"i":{"df":1,"docs":{"80":{"tf":1.0}}}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}}},"df":0,"docs":{},"∣":{"df":0,"docs":{},"∣":{"df":0,"docs":{},"…":{"df":0,"docs":{},"∣":{"df":0,"docs":{},"∣":{"df":0,"docs":{},"p":{"df":0,"docs":{},"l":{"df":0,"docs":{},"​":{"(":{"d":{"df":0,"docs":{},"i":{"df":1,"docs":{"80":{"tf":1.0}}}},"df":0,"docs":{}},"df":0,"docs":{}}}}}}}}}},"df":0,"docs":{}}}},"df":0,"docs":{},"z":{"1":{"df":0,"docs":{},"​":{")":{",":{"df":0,"docs":{},"…":{",":{"df":0,"docs":{},"p":{"df":0,"docs":{},"k":{"df":0,"docs":{},"​":{"(":{"df":0,"docs":{},"z":{"df":0,"docs":{},"l":{"df":1,"docs":{"80":{"tf":1.0}}}}},"df":0,"docs":{}}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{},"p":{"2":{"df":0,"docs":{},"​":{"df":0,"docs":{},"p":{"3":{"df":0,"docs":{},"​":{"df":0,"docs":{},"​":{"=":{"a":{"df":0,"docs":{},"q":{"df":0,"docs":{},"l":{"df":0,"docs":{},"​":{"+":{"b":{"df":0,"docs":{},"q":{"df":0,"docs":{},"r":{"df":0,"docs":{},"​":{"+":{"a":{"b":{"df":0,"docs":{},"q":{"df":0,"docs":{},"m":{"df":0,"docs":{},"​":{"+":{"c":{"df":0,"docs":{},"q":{"df":0,"docs":{},"o":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"q":{"c":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"p":{"df":0,"docs":{},"i":{"=":{"(":{"a":{"+":{"df":0,"docs":{},"β":{"df":0,"docs":{},"x":{"+":{"df":0,"docs":{},"γ":{")":{"(":{"b":{"+":{"df":0,"docs":{},"β":{"df":0,"docs":{},"k":{"1":{"df":0,"docs":{},"​":{"df":0,"docs":{},"x":{"+":{"df":0,"docs":{},"γ":{")":{"(":{"c":{"+":{"df":0,"docs":{},"β":{"df":0,"docs":{},"k":{"2":{"df":0,"docs":{},"​":{"df":0,"docs":{},"x":{"+":{"df":0,"docs":{},"γ":{")":{"df":0,"docs":{},"z":{"df":0,"docs":{},"−":{"(":{"a":{"+":{"df":0,"docs":{},"β":{"df":0,"docs":{},"s":{"df":0,"docs":{},"σ":{"1":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"γ":{")":{"(":{"b":{"+":{"df":0,"docs":{},"β":{"df":0,"docs":{},"s":{"df":0,"docs":{},"σ":{"2":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"γ":{")":{"(":{"c":{"+":{"df":0,"docs":{},"β":{"df":0,"docs":{},"s":{"df":0,"docs":{},"σ":{"3":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"γ":{")":{"df":0,"docs":{},"z":{"(":{"df":0,"docs":{},"ω":{"df":0,"docs":{},"x":{")":{"=":{"(":{"df":0,"docs":{},"z":{"df":0,"docs":{},"−":{"1":{")":{"df":0,"docs":{},"l":{"1":{"df":1,"docs":{"26":{"tf":1.0}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}}},"df":0,"docs":{}}}},"=":{"1":{"df":1,"docs":{"73":{"tf":1.0}}},"a":{"df":0,"docs":{},"q":{"df":0,"docs":{},"l":{"df":0,"docs":{},"​":{"+":{"b":{"df":0,"docs":{},"q":{"df":0,"docs":{},"r":{"df":0,"docs":{},"​":{"+":{"a":{"b":{"df":0,"docs":{},"q":{"df":0,"docs":{},"m":{"df":1,"docs":{"21":{"tf":1.0}}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{},"p":{"1":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"α":{"df":0,"docs":{},"p":{"2":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"α":{"2":{"df":0,"docs":{},"p":{"3":{"df":1,"docs":{"26":{"tf":1.0}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"t":{"df":0,"docs":{},"z":{"df":0,"docs":{},"h":{"df":1,"docs":{"26":{"tf":1.0}}}}},"{":{"df":0,"docs":{},"p":{"1":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"…":{",":{"df":0,"docs":{},"p":{"df":0,"docs":{},"l":{"df":1,"docs":{"80":{"tf":1.0}}},"n":{"df":1,"docs":{"80":{"tf":1.0}}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"^":{"df":0,"docs":{},"​":{"df":0,"docs":{},"n":{"c":{"1":{"df":0,"docs":{},"​":{"]":{"1":{"df":0,"docs":{},"​":{"[":{"df":0,"docs":{},"p":{"^":{"df":0,"docs":{},"​":{"df":0,"docs":{},"n":{"c":{"2":{"df":0,"docs":{},"​":{"]":{"1":{"df":0,"docs":{},"​":{"[":{"df":0,"docs":{},"p":{"^":{"df":0,"docs":{},"​":{"df":0,"docs":{},"n":{"c":{"3":{"df":0,"docs":{},"​":{"]":{"1":{"df":0,"docs":{},"​":{"df":0,"docs":{},"​":{"=":{"a":{"df":0,"docs":{},"ˉ":{"[":{"df":0,"docs":{},"q":{"df":0,"docs":{},"l":{"df":0,"docs":{},"​":{"]":{"1":{"df":0,"docs":{},"​":{"+":{"b":{"df":0,"docs":{},"ˉ":{"[":{"df":0,"docs":{},"q":{"df":0,"docs":{},"r":{"df":0,"docs":{},"​":{"]":{"1":{"df":0,"docs":{},"​":{"+":{"(":{"a":{"df":0,"docs":{},"ˉ":{"b":{"df":0,"docs":{},"ˉ":{")":{"[":{"df":0,"docs":{},"q":{"df":0,"docs":{},"m":{"df":0,"docs":{},"​":{"]":{"1":{"df":0,"docs":{},"​":{"+":{"c":{"df":0,"docs":{},"ˉ":{"[":{"df":0,"docs":{},"q":{"df":0,"docs":{},"o":{"df":0,"docs":{},"​":{"]":{"1":{"df":0,"docs":{},"​":{"+":{"[":{"df":0,"docs":{},"q":{"c":{"df":0,"docs":{},"​":{"]":{"1":{"df":0,"docs":{},"​":{"=":{"(":{"a":{"df":0,"docs":{},"ˉ":{"+":{"df":0,"docs":{},"β":{"df":0,"docs":{},"ζ":{"+":{"df":0,"docs":{},"γ":{")":{"(":{"b":{"df":0,"docs":{},"ˉ":{"+":{"df":0,"docs":{},"β":{"df":0,"docs":{},"k":{"1":{"df":0,"docs":{},"​":{"df":0,"docs":{},"ζ":{"+":{"df":0,"docs":{},"γ":{")":{"(":{"c":{"df":0,"docs":{},"ˉ":{"+":{"df":0,"docs":{},"β":{"df":0,"docs":{},"k":{"2":{"df":0,"docs":{},"​":{"df":0,"docs":{},"ζ":{"+":{"df":0,"docs":{},"γ":{")":{"[":{"df":0,"docs":{},"z":{"]":{"1":{"df":0,"docs":{},"​":{"df":0,"docs":{},"−":{"(":{"a":{"df":0,"docs":{},"ˉ":{"+":{"df":0,"docs":{},"β":{"df":0,"docs":{},"s":{"df":0,"docs":{},"ˉ":{"df":0,"docs":{},"σ":{"1":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"γ":{")":{"(":{"b":{"df":0,"docs":{},"ˉ":{"+":{"df":0,"docs":{},"β":{"df":0,"docs":{},"s":{"df":0,"docs":{},"ˉ":{"df":0,"docs":{},"σ":{"2":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"γ":{")":{"df":0,"docs":{},"β":{"df":0,"docs":{},"z":{"df":0,"docs":{},"ˉ":{"df":0,"docs":{},"ω":{"df":0,"docs":{},"​":{"[":{"df":0,"docs":{},"s":{"df":0,"docs":{},"σ":{"3":{"df":0,"docs":{},"​":{"]":{"1":{"df":0,"docs":{},"​":{"=":{"df":0,"docs":{},"l":{"1":{"df":0,"docs":{},"​":{"(":{"df":0,"docs":{},"ζ":{")":{"[":{"df":0,"docs":{},"z":{"]":{"1":{"df":1,"docs":{"32":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}}}}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{},"p":{"^":{"df":0,"docs":{},"​":{"df":0,"docs":{},"n":{"c":{"2":{"df":0,"docs":{},"​":{"df":0,"docs":{},"p":{"^":{"df":0,"docs":{},"​":{"df":0,"docs":{},"n":{"c":{"3":{"df":0,"docs":{},"​":{"df":0,"docs":{},"​":{"=":{"a":{"df":0,"docs":{},"ˉ":{"df":0,"docs":{},"q":{"df":0,"docs":{},"l":{"df":0,"docs":{},"​":{"+":{"b":{"df":0,"docs":{},"ˉ":{"df":0,"docs":{},"q":{"df":0,"docs":{},"r":{"df":0,"docs":{},"​":{"+":{"a":{"df":0,"docs":{},"ˉ":{"b":{"df":0,"docs":{},"ˉ":{"df":0,"docs":{},"q":{"df":0,"docs":{},"m":{"df":0,"docs":{},"​":{"+":{"c":{"df":0,"docs":{},"ˉ":{"df":0,"docs":{},"q":{"df":0,"docs":{},"o":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"q":{"c":{"df":0,"docs":{},"​":{"=":{"(":{"a":{"df":0,"docs":{},"ˉ":{"+":{"df":0,"docs":{},"β":{"df":0,"docs":{},"ζ":{"+":{"df":0,"docs":{},"γ":{")":{"(":{"b":{"df":0,"docs":{},"ˉ":{"+":{"df":0,"docs":{},"β":{"df":0,"docs":{},"k":{"1":{"df":0,"docs":{},"​":{"df":0,"docs":{},"ζ":{"+":{"df":0,"docs":{},"γ":{")":{"(":{"c":{"df":0,"docs":{},"ˉ":{"+":{"df":0,"docs":{},"β":{"df":0,"docs":{},"k":{"2":{"df":0,"docs":{},"​":{"df":0,"docs":{},"ζ":{"+":{"df":0,"docs":{},"γ":{")":{"df":0,"docs":{},"z":{"df":0,"docs":{},"−":{"(":{"a":{"df":0,"docs":{},"ˉ":{"+":{"df":0,"docs":{},"β":{"df":0,"docs":{},"s":{"df":0,"docs":{},"ˉ":{"df":0,"docs":{},"σ":{"1":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"γ":{")":{"(":{"b":{"df":0,"docs":{},"ˉ":{"+":{"df":0,"docs":{},"β":{"df":0,"docs":{},"s":{"df":0,"docs":{},"ˉ":{"df":0,"docs":{},"σ":{"2":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"γ":{")":{"df":0,"docs":{},"β":{"df":0,"docs":{},"z":{"df":0,"docs":{},"ˉ":{"df":0,"docs":{},"ω":{"df":0,"docs":{},"​":{"df":0,"docs":{},"s":{"df":0,"docs":{},"σ":{"3":{"df":0,"docs":{},"​":{"=":{"df":0,"docs":{},"l":{"1":{"df":0,"docs":{},"​":{"(":{"df":0,"docs":{},"ζ":{")":{"df":0,"docs":{},"z":{"df":1,"docs":{"28":{"tf":1.0}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}}}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"_":{"df":0,"docs":{},"n":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"_":{"c":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"df":0,"docs":{},"s":{"df":0,"docs":{},"t":{"a":{"df":0,"docs":{},"n":{"df":0,"docs":{},"t":{"_":{"df":0,"docs":{},"z":{"df":0,"docs":{},"e":{"df":0,"docs":{},"t":{"a":{"df":1,"docs":{"35":{"tf":1.0}}},"df":0,"docs":{}}}}},"df":0,"docs":{}}}},"df":1,"docs":{"35":{"tf":1.0}}}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"a":{"c":{"df":0,"docs":{},"k":{"a":{"df":0,"docs":{},"g":{"df":1,"docs":{"38":{"tf":1.0}}}},"df":0,"docs":{}}},"d":{"df":5,"docs":{"101":{"tf":1.0},"121":{"tf":1.0},"142":{"tf":1.0},"146":{"tf":2.0},"36":{"tf":1.7320508075688772}}},"df":0,"docs":{},"i":{"df":0,"docs":{},"r":{"df":9,"docs":{"102":{"tf":1.0},"135":{"tf":1.0},"14":{"tf":3.4641016151377544},"146":{"tf":2.23606797749979},"151":{"tf":1.0},"3":{"tf":1.0},"38":{"tf":1.0},"92":{"tf":1.0},"95":{"tf":1.0}}}},"p":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":3,"docs":{"146":{"tf":1.4142135623730951},"16":{"tf":1.0},"4":{"tf":1.0}}}},"i":{"df":0,"docs":{},"n":{"df":0,"docs":{},"i":{"df":1,"docs":{"103":{"tf":1.0}}}}}},"r":{"a":{"df":0,"docs":{},"m":{"df":0,"docs":{},"e":{"df":0,"docs":{},"t":{"df":7,"docs":{"109":{"tf":1.7320508075688772},"128":{"tf":1.0},"68":{"tf":1.0},"73":{"tf":1.4142135623730951},"75":{"tf":1.0},"80":{"tf":1.4142135623730951},"91":{"tf":1.0}}}}}},"df":0,"docs":{},"t":{"df":22,"docs":{"114":{"tf":1.0},"117":{"tf":1.0},"123":{"tf":1.0},"13":{"tf":1.0},"14":{"tf":1.0},"146":{"tf":1.0},"147":{"tf":1.0},"149":{"tf":1.0},"151":{"tf":1.0},"16":{"tf":1.0},"28":{"tf":1.0},"32":{"tf":1.0},"35":{"tf":1.7320508075688772},"50":{"tf":1.0},"53":{"tf":1.0},"58":{"tf":1.0},"69":{"tf":1.0},"7":{"tf":1.0},"70":{"tf":1.0},"73":{"tf":1.0},"9":{"tf":1.0},"94":{"tf":1.0}},"i":{"a":{"df":0,"docs":{},"l":{"df":1,"docs":{"28":{"tf":1.7320508075688772}}}},"c":{"df":0,"docs":{},"u":{"df":0,"docs":{},"l":{"a":{"df":0,"docs":{},"r":{"df":14,"docs":{"10":{"tf":1.4142135623730951},"11":{"tf":1.0},"13":{"tf":1.4142135623730951},"134":{"tf":1.0},"146":{"tf":1.4142135623730951},"21":{"tf":1.0},"22":{"tf":1.0},"28":{"tf":1.0},"35":{"tf":1.4142135623730951},"36":{"tf":1.0},"43":{"tf":1.0},"69":{"tf":1.0},"70":{"tf":1.4142135623730951},"8":{"tf":1.4142135623730951}}}},"df":0,"docs":{}}}},"df":5,"docs":{"19":{"tf":1.4142135623730951},"4":{"tf":1.0},"7":{"tf":1.0},"73":{"tf":1.4142135623730951},"74":{"tf":1.0}}}}},"s":{"df":0,"docs":{},"s":{"df":10,"docs":{"11":{"tf":1.0},"110":{"tf":1.4142135623730951},"114":{"tf":1.4142135623730951},"117":{"tf":1.0},"144":{"tf":1.0},"33":{"tf":1.0},"40":{"tf":2.0},"50":{"tf":1.4142135623730951},"57":{"tf":1.0},"75":{"tf":1.0}}}},"t":{"df":0,"docs":{},"h":{"df":7,"docs":{"101":{"tf":1.7320508075688772},"102":{"tf":1.0},"35":{"tf":1.0},"72":{"tf":1.4142135623730951},"80":{"tf":1.4142135623730951},"92":{"tf":1.4142135623730951},"95":{"tf":1.7320508075688772}}}}},"c":{"[":{"0":{"df":1,"docs":{"147":{"tf":1.0}}},"1":{"df":1,"docs":{"147":{"tf":1.0}}},"df":0,"docs":{}},"_":{"df":0,"docs":{},"j":{"df":0,"docs":{},"n":{"df":0,"docs":{},"z":{"df":1,"docs":{"146":{"tf":1.0}}}}},"u":{"df":0,"docs":{},"p":{"d":{"a":{"df":0,"docs":{},"t":{"df":1,"docs":{"146":{"tf":1.0}}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":8,"docs":{"133":{"tf":1.0},"136":{"tf":1.4142135623730951},"141":{"tf":1.0},"142":{"tf":1.0},"144":{"tf":1.0},"146":{"tf":2.23606797749979},"147":{"tf":1.4142135623730951},"19":{"tf":1.4142135623730951}},"i":{"df":1,"docs":{"142":{"tf":1.0}},"​":{",":{"a":{"df":0,"docs":{},"p":{"df":0,"docs":{},"i":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"f":{"df":0,"docs":{},"p":{"df":0,"docs":{},"i":{"df":1,"docs":{"142":{"tf":1.0}}}}}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":14,"docs":{"101":{"tf":1.0},"147":{"tf":2.0},"21":{"tf":2.6457513110645907},"28":{"tf":1.0},"32":{"tf":1.4142135623730951},"33":{"tf":1.0},"54":{"tf":1.4142135623730951},"73":{"tf":2.6457513110645907},"75":{"tf":2.8284271247461903},"76":{"tf":2.0},"77":{"tf":1.4142135623730951},"78":{"tf":1.4142135623730951},"79":{"tf":1.7320508075688772},"80":{"tf":2.449489742783178}},"e":{"df":0,"docs":{},"r":{"df":3,"docs":{"107":{"tf":1.0},"65":{"tf":1.0},"9":{"tf":1.0}},"f":{"df":0,"docs":{},"o":{"df":0,"docs":{},"r":{"df":0,"docs":{},"m":{"df":10,"docs":{"100":{"tf":1.0},"109":{"tf":1.0},"123":{"tf":1.0},"125":{"tf":1.4142135623730951},"151":{"tf":1.0},"35":{"tf":1.0},"66":{"tf":1.0},"7":{"tf":1.0},"82":{"tf":1.0},"99":{"tf":1.0}}}}}},"m":{"df":0,"docs":{},"u":{"df":0,"docs":{},"t":{"df":6,"docs":{"101":{"tf":1.4142135623730951},"138":{"tf":1.0},"139":{"tf":1.0},"14":{"tf":2.0},"146":{"tf":2.0},"147":{"tf":1.4142135623730951}}}}}}},"h":{"a":{"df":0,"docs":{},"n":{"df":0,"docs":{},"t":{"df":0,"docs":{},"o":{"df":0,"docs":{},"m":{"d":{"a":{"df":0,"docs":{},"t":{"a":{"<":{"df":0,"docs":{},"f":{"df":1,"docs":{"35":{"tf":1.4142135623730951}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":1,"docs":{"35":{"tf":1.4142135623730951}}}}}},"s":{"df":0,"docs":{},"e":{"df":4,"docs":{"127":{"tf":1.0},"22":{"tf":1.0},"35":{"tf":1.0},"94":{"tf":1.4142135623730951}}}}},"df":0,"docs":{}},"i":{")":{"df":0,"docs":{},"i":{"df":0,"docs":{},"​":{"=":{"0":{"df":2,"docs":{"14":{"tf":1.0},"16":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":7,"docs":{"13":{"tf":2.0},"14":{"tf":1.4142135623730951},"16":{"tf":2.449489742783178},"26":{"tf":1.4142135623730951},"32":{"tf":2.449489742783178},"36":{"tf":1.0},"80":{"tf":1.0}},"e":{"c":{"df":2,"docs":{"114":{"tf":1.0},"116":{"tf":1.0}}},"df":0,"docs":{}},"t":{"df":0,"docs":{},"f":{"a":{"df":0,"docs":{},"l":{"df":1,"docs":{"42":{"tf":1.0}}}},"df":0,"docs":{}}},"​":{":":{"=":{"df":0,"docs":{},"j":{"=":{"0":{"df":0,"docs":{},"∏":{"df":0,"docs":{},"i":{"df":0,"docs":{},"​":{"df":0,"docs":{},"z":{"df":0,"docs":{},"−":{"(":{"a":{"df":0,"docs":{},"i":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"α":{"df":0,"docs":{},"v":{"df":0,"docs":{},"i":{"df":0,"docs":{},"​":{")":{"df":0,"docs":{},"z":{"df":0,"docs":{},"−":{"(":{"a":{"df":0,"docs":{},"i":{"df":0,"docs":{},"′":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"α":{"df":0,"docs":{},"v":{"df":0,"docs":{},"i":{"df":1,"docs":{"147":{"tf":1.0}}}}}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}}}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"′":{"df":0,"docs":{},"​":{":":{"=":{"df":0,"docs":{},"j":{"=":{"0":{"df":0,"docs":{},"∏":{"df":0,"docs":{},"i":{"df":0,"docs":{},"​":{"df":0,"docs":{},"z":{"df":0,"docs":{},"′":{"df":0,"docs":{},"−":{"b":{"df":0,"docs":{},"i":{"df":0,"docs":{},"​":{"df":0,"docs":{},"z":{"df":0,"docs":{},"′":{"df":0,"docs":{},"−":{"b":{"df":0,"docs":{},"i":{"df":1,"docs":{"147":{"tf":1.0}}}},"df":0,"docs":{}}}}}}},"df":0,"docs":{}}}}}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"j":{"b":{"df":2,"docs":{"70":{"tf":1.0},"91":{"tf":1.0}}},"df":0,"docs":{}},"k":{"+":{"1":{"df":0,"docs":{},"​":{"(":{"df":0,"docs":{},"υ":{"2":{"df":0,"docs":{},"k":{"+":{"1":{"df":1,"docs":{"102":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":3,"docs":{"102":{"tf":1.4142135623730951},"94":{"tf":1.0},"95":{"tf":1.0}},"t":{"df":2,"docs":{"70":{"tf":1.0},"91":{"tf":1.0}},"​":{"(":{"df":0,"docs":{},"t":{"df":0,"docs":{},"i":{",":{"0":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"…":{",":{"df":0,"docs":{},"t":{"df":0,"docs":{},"i":{",":{"df":0,"docs":{},"m":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"t":{"df":0,"docs":{},"i":{"+":{"1":{",":{"0":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"…":{",":{"df":0,"docs":{},"t":{"df":0,"docs":{},"i":{"+":{"1":{",":{"df":0,"docs":{},"m":{"df":0,"docs":{},"​":{")":{"=":{"0":{"df":1,"docs":{"70":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"x":{"1":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"…":{",":{"df":0,"docs":{},"x":{"df":0,"docs":{},"m":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"y":{"1":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"…":{",":{"df":0,"docs":{},"y":{"df":0,"docs":{},"m":{"df":1,"docs":{"70":{"tf":1.0}}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"​":{"(":{"df":0,"docs":{},"υ":{"2":{"df":0,"docs":{},"k":{"+":{"1":{"df":1,"docs":{"102":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},":":{"=":{"df":0,"docs":{},"p":{"df":0,"docs":{},"k":{"df":0,"docs":{},"−":{"1":{"df":0,"docs":{},"o":{"d":{"d":{"df":0,"docs":{},"​":{"(":{"df":0,"docs":{},"x":{")":{"+":{"df":0,"docs":{},"ζ":{"df":0,"docs":{},"k":{"df":0,"docs":{},"−":{"1":{"df":0,"docs":{},"​":{"df":0,"docs":{},"p":{"df":0,"docs":{},"k":{"df":0,"docs":{},"−":{"1":{"df":0,"docs":{},"e":{"df":0,"docs":{},"v":{"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"df":0,"docs":{},"​":{"(":{"df":0,"docs":{},"x":{"df":1,"docs":{"94":{"tf":1.0}}}},"df":0,"docs":{}}}}}}},"df":0,"docs":{}}}}}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"]":{":":{"=":{"c":{"df":0,"docs":{},"o":{"df":0,"docs":{},"m":{"df":0,"docs":{},"m":{"df":0,"docs":{},"i":{"df":0,"docs":{},"t":{"(":{"df":0,"docs":{},"p":{"df":0,"docs":{},"k":{"df":0,"docs":{},"​":{"(":{"d":{"df":0,"docs":{},"k":{"df":1,"docs":{"94":{"tf":1.0}}}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}}}}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"−":{"1":{"df":1,"docs":{"94":{"tf":1.0}},"​":{"=":{"df":0,"docs":{},"p":{"df":0,"docs":{},"k":{"df":0,"docs":{},"−":{"1":{"df":0,"docs":{},"o":{"d":{"d":{"df":0,"docs":{},"​":{"(":{"df":0,"docs":{},"x":{"2":{")":{"+":{"df":0,"docs":{},"x":{"df":0,"docs":{},"p":{"df":0,"docs":{},"k":{"df":0,"docs":{},"−":{"1":{"df":0,"docs":{},"e":{"df":0,"docs":{},"v":{"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"df":0,"docs":{},"​":{"(":{"df":0,"docs":{},"x":{"2":{"df":1,"docs":{"94":{"tf":1.0}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}}}},"df":0,"docs":{}}}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"l":{"a":{"c":{"df":0,"docs":{},"e":{"df":3,"docs":{"123":{"tf":1.4142135623730951},"146":{"tf":1.0},"151":{"tf":1.0}}}},"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"df":2,"docs":{"127":{"tf":1.4142135623730951},"149":{"tf":1.0}}}},"y":{"df":1,"docs":{"16":{"tf":1.0}}}},"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"df":0,"docs":{},"t":{"df":0,"docs":{},"i":{"df":1,"docs":{"130":{"tf":1.0}}}}}},"o":{"df":0,"docs":{},"n":{"df":0,"docs":{},"k":{"df":9,"docs":{"12":{"tf":1.4142135623730951},"14":{"tf":1.4142135623730951},"19":{"tf":1.0},"21":{"tf":1.4142135623730951},"34":{"tf":1.4142135623730951},"4":{"tf":1.7320508075688772},"44":{"tf":1.0},"7":{"tf":1.7320508075688772},"8":{"tf":1.0}}}}},"u":{"df":0,"docs":{},"g":{"df":1,"docs":{"70":{"tf":1.0}}}}},"n":{"c":{"df":0,"docs":{},"​":{"]":{"1":{"df":1,"docs":{"32":{"tf":1.4142135623730951}},"​":{"=":{"[":{"df":0,"docs":{},"p":{"df":0,"docs":{},"n":{"c":{"1":{"df":0,"docs":{},"​":{"]":{"1":{"df":0,"docs":{},"​":{"+":{"[":{"df":0,"docs":{},"p":{"df":0,"docs":{},"n":{"c":{"2":{"df":0,"docs":{},"​":{"]":{"1":{"df":0,"docs":{},"​":{"+":{"[":{"df":0,"docs":{},"p":{"df":0,"docs":{},"n":{"c":{"3":{"df":0,"docs":{},"​":{"]":{"1":{"df":1,"docs":{"32":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{},"t":{"df":0,"docs":{},"p":{"a":{"df":0,"docs":{},"r":{"df":0,"docs":{},"t":{"df":0,"docs":{},"i":{"a":{"df":0,"docs":{},"l":{"df":0,"docs":{},"​":{"df":0,"docs":{},"​":{"=":{"df":0,"docs":{},"p":{"df":0,"docs":{},"n":{"c":{"1":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"α":{"df":0,"docs":{},"p":{"df":0,"docs":{},"n":{"c":{"2":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"α":{"2":{"df":0,"docs":{},"p":{"df":0,"docs":{},"n":{"c":{"3":{"df":0,"docs":{},"​":{"=":{"df":0,"docs":{},"t":{"df":0,"docs":{},"l":{"df":0,"docs":{},"o":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"ζ":{"df":0,"docs":{},"n":{"+":{"2":{"df":0,"docs":{},"t":{"df":0,"docs":{},"m":{"df":0,"docs":{},"i":{"d":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"ζ":{"2":{"(":{"df":0,"docs":{},"n":{"+":{"2":{")":{"df":0,"docs":{},"t":{"df":0,"docs":{},"h":{"df":0,"docs":{},"i":{"df":1,"docs":{"28":{"tf":1.0}}}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}}}},"df":0,"docs":{}}}}},"df":0,"docs":{}}}}},"df":1,"docs":{"94":{"tf":1.4142135623730951}},"​":{"df":0,"docs":{},"∈":{"df":0,"docs":{},"f":{"df":1,"docs":{"94":{"tf":1.0}}}}},"−":{"1":{"df":1,"docs":{"146":{"tf":1.0}},"​":{"=":{"1":{"df":1,"docs":{"146":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"o":{"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"df":0,"docs":{},"t":{"df":23,"docs":{"101":{"tf":1.0},"112":{"tf":1.4142135623730951},"114":{"tf":2.8284271247461903},"115":{"tf":1.0},"117":{"tf":1.0},"123":{"tf":1.7320508075688772},"133":{"tf":1.0},"142":{"tf":1.0},"149":{"tf":1.0},"19":{"tf":1.0},"3":{"tf":1.0},"40":{"tf":1.0},"49":{"tf":1.0},"58":{"tf":1.4142135623730951},"59":{"tf":1.0},"60":{"tf":1.7320508075688772},"62":{"tf":1.4142135623730951},"68":{"tf":1.7320508075688772},"69":{"tf":1.4142135623730951},"80":{"tf":1.0},"85":{"tf":1.0},"88":{"tf":1.0},"95":{"tf":1.0}},"e":{"df":0,"docs":{},"r":{"df":3,"docs":{"142":{"tf":1.4142135623730951},"146":{"tf":2.449489742783178},"147":{"tf":1.4142135623730951}}}},"l":{"df":0,"docs":{},"e":{"df":0,"docs":{},"s":{"df":0,"docs":{},"s":{"df":1,"docs":{"7":{"tf":1.0}}}}}}}}},"l":{"df":0,"docs":{},"i":{"df":1,"docs":{"119":{"tf":1.0}}},"y":{"df":0,"docs":{},"l":{"df":0,"docs":{},"o":{"df":0,"docs":{},"g":{"a":{"df":0,"docs":{},"r":{"df":0,"docs":{},"i":{"df":0,"docs":{},"t":{"df":0,"docs":{},"h":{"df":0,"docs":{},"m":{"df":1,"docs":{"81":{"tf":1.0}}}}}}}},"df":0,"docs":{}}}},"n":{"df":0,"docs":{},"o":{"df":0,"docs":{},"m":{"df":0,"docs":{},"i":{"a":{"df":0,"docs":{},"l":{"'":{"df":3,"docs":{"19":{"tf":1.0},"3":{"tf":1.0},"68":{"tf":1.0}}},"<":{"df":0,"docs":{},"f":{"df":0,"docs":{},"i":{"df":0,"docs":{},"e":{"df":0,"docs":{},"l":{"d":{"df":0,"docs":{},"e":{"df":0,"docs":{},"l":{"df":0,"docs":{},"e":{"df":0,"docs":{},"m":{"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"df":0,"docs":{},"t":{"<":{"df":0,"docs":{},"f":{"df":1,"docs":{"35":{"tf":2.8284271247461903}}}},"df":0,"docs":{}}}}}}}}},"df":0,"docs":{}}}}}},"df":0,"docs":{}}},"df":70,"docs":{"100":{"tf":1.4142135623730951},"101":{"tf":1.0},"102":{"tf":1.0},"112":{"tf":2.23606797749979},"113":{"tf":1.7320508075688772},"114":{"tf":3.3166247903554},"116":{"tf":1.0},"117":{"tf":1.4142135623730951},"119":{"tf":1.7320508075688772},"121":{"tf":1.0},"123":{"tf":3.4641016151377544},"124":{"tf":1.0},"125":{"tf":2.6457513110645907},"14":{"tf":5.830951894845301},"149":{"tf":1.0},"15":{"tf":1.0},"16":{"tf":3.0},"19":{"tf":3.1622776601683795},"20":{"tf":3.1622776601683795},"21":{"tf":2.0},"23":{"tf":1.0},"24":{"tf":1.4142135623730951},"25":{"tf":1.4142135623730951},"26":{"tf":1.0},"28":{"tf":1.4142135623730951},"3":{"tf":2.23606797749979},"32":{"tf":1.0},"35":{"tf":3.605551275463989},"4":{"tf":1.4142135623730951},"42":{"tf":1.0},"48":{"tf":1.7320508075688772},"5":{"tf":2.6457513110645907},"52":{"tf":2.23606797749979},"53":{"tf":2.23606797749979},"54":{"tf":2.449489742783178},"55":{"tf":2.0},"56":{"tf":1.7320508075688772},"57":{"tf":1.7320508075688772},"58":{"tf":1.0},"59":{"tf":3.0},"60":{"tf":1.0},"62":{"tf":2.23606797749979},"65":{"tf":2.23606797749979},"66":{"tf":2.0},"67":{"tf":1.0},"68":{"tf":1.7320508075688772},"69":{"tf":2.8284271247461903},"70":{"tf":1.7320508075688772},"71":{"tf":2.23606797749979},"73":{"tf":2.6457513110645907},"74":{"tf":1.7320508075688772},"75":{"tf":3.0},"76":{"tf":1.0},"77":{"tf":1.0},"78":{"tf":1.4142135623730951},"79":{"tf":1.4142135623730951},"8":{"tf":1.4142135623730951},"80":{"tf":3.7416573867739413},"81":{"tf":1.0},"82":{"tf":1.0},"83":{"tf":1.0},"84":{"tf":4.123105625617661},"85":{"tf":1.0},"86":{"tf":1.0},"90":{"tf":1.4142135623730951},"91":{"tf":1.4142135623730951},"94":{"tf":2.449489742783178},"95":{"tf":2.0},"97":{"tf":1.0},"99":{"tf":1.0}}}}}}}},"o":{"df":0,"docs":{},"l":{"df":5,"docs":{"130":{"tf":1.0},"133":{"tf":1.0},"134":{"tf":1.4142135623730951},"135":{"tf":1.0},"138":{"tf":1.4142135623730951}}}},"p":{"df":0,"docs":{},"u":{"df":0,"docs":{},"l":{"a":{"df":0,"docs":{},"r":{"df":2,"docs":{"19":{"tf":1.0},"4":{"tf":1.0}}}},"df":0,"docs":{}}}},"s":{"df":0,"docs":{},"e":{"df":0,"docs":{},"i":{"d":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"df":1,"docs":{"151":{"tf":1.0}}}}},"df":0,"docs":{}}},"i":{"df":0,"docs":{},"t":{"df":4,"docs":{"101":{"tf":1.4142135623730951},"14":{"tf":1.4142135623730951},"151":{"tf":1.7320508075688772},"72":{"tf":1.0}}}},"s":{"df":0,"docs":{},"i":{"b":{"df":0,"docs":{},"l":{"df":6,"docs":{"114":{"tf":1.0},"149":{"tf":1.4142135623730951},"36":{"tf":1.0},"38":{"tf":1.0},"50":{"tf":1.0},"71":{"tf":1.0}}}},"df":0,"docs":{}}}},"w":{"(":{"df":0,"docs":{},"s":{"df":0,"docs":{},"y":{"df":0,"docs":{},"s":{"df":0,"docs":{},"t":{"df":0,"docs":{},"e":{"df":0,"docs":{},"m":{"df":1,"docs":{"45":{"tf":1.0}}}}}}}},"x":{"df":1,"docs":{"45":{"tf":1.0}}}},"df":1,"docs":{"45":{"tf":1.0}},"e":{"df":0,"docs":{},"r":{"df":15,"docs":{"121":{"tf":1.0},"124":{"tf":1.4142135623730951},"125":{"tf":1.0},"142":{"tf":1.0},"146":{"tf":1.0},"149":{"tf":1.0},"28":{"tf":1.4142135623730951},"36":{"tf":1.0},"38":{"tf":1.4142135623730951},"52":{"tf":1.0},"68":{"tf":1.7320508075688772},"73":{"tf":1.0},"75":{"tf":1.0},"95":{"tf":1.0},"97":{"tf":1.0}}}}}},"r":{"a":{"c":{"df":0,"docs":{},"t":{"df":0,"docs":{},"i":{"c":{"df":1,"docs":{"149":{"tf":1.0}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{},"e":{"c":{"df":0,"docs":{},"e":{"d":{"df":4,"docs":{"49":{"tf":1.0},"52":{"tf":1.0},"70":{"tf":1.0},"84":{"tf":1.0}}},"df":0,"docs":{}},"i":{"df":0,"docs":{},"s":{"df":6,"docs":{"14":{"tf":1.0},"22":{"tf":1.0},"34":{"tf":1.0},"73":{"tf":1.4142135623730951},"74":{"tf":1.0},"87":{"tf":1.0}}}},"o":{"df":0,"docs":{},"m":{"df":0,"docs":{},"p":{"df":0,"docs":{},"u":{"df":0,"docs":{},"t":{"df":2,"docs":{"34":{"tf":1.0},"56":{"tf":1.0}}}}}}}},"d":{"df":0,"docs":{},"e":{"df":0,"docs":{},"f":{"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"df":1,"docs":{"70":{"tf":1.0}}}}}},"i":{"c":{"df":0,"docs":{},"t":{"df":1,"docs":{"118":{"tf":1.0}}}},"df":0,"docs":{}}},"df":0,"docs":{},"f":{"df":0,"docs":{},"i":{"df":0,"docs":{},"x":{"df":1,"docs":{"88":{"tf":1.0}}}}},"i":{"df":0,"docs":{},"m":{"a":{"df":0,"docs":{},"g":{"df":1,"docs":{"7":{"tf":1.0}}}},"df":0,"docs":{}}},"p":{"df":0,"docs":{},"r":{"df":0,"docs":{},"o":{"c":{"df":0,"docs":{},"e":{"df":0,"docs":{},"s":{"df":0,"docs":{},"s":{"df":6,"docs":{"15":{"tf":1.4142135623730951},"23":{"tf":1.0},"35":{"tf":1.7320508075688772},"42":{"tf":1.0},"70":{"tf":1.0},"83":{"tf":1.0}}}}}},"df":0,"docs":{}}}},"t":{"df":0,"docs":{},"t":{"df":0,"docs":{},"i":{"df":5,"docs":{"113":{"tf":1.0},"132":{"tf":1.0},"14":{"tf":1.0},"151":{"tf":1.0},"37":{"tf":1.0}}}}},"v":{"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"df":0,"docs":{},"t":{"df":2,"docs":{"42":{"tf":1.0},"71":{"tf":1.0}}}}},"i":{"df":0,"docs":{},"o":{"df":0,"docs":{},"u":{"df":10,"docs":{"107":{"tf":1.7320508075688772},"109":{"tf":1.0},"11":{"tf":1.0},"14":{"tf":2.6457513110645907},"145":{"tf":1.0},"149":{"tf":1.4142135623730951},"45":{"tf":1.0},"70":{"tf":1.0},"84":{"tf":1.0},"86":{"tf":1.0}}}}}}},"i":{"df":0,"docs":{},"m":{"a":{"df":0,"docs":{},"r":{"df":0,"docs":{},"i":{"df":1,"docs":{"70":{"tf":1.0}}}}},"df":0,"docs":{},"e":{"df":2,"docs":{"14":{"tf":1.0},"41":{"tf":1.0}}},"i":{"df":0,"docs":{},"t":{"df":17,"docs":{"114":{"tf":1.0},"124":{"tf":3.7416573867739413},"125":{"tf":1.7320508075688772},"14":{"tf":2.23606797749979},"16":{"tf":1.0},"23":{"tf":1.0},"34":{"tf":1.0},"35":{"tf":1.0},"36":{"tf":1.0},"38":{"tf":1.0},"5":{"tf":1.0},"52":{"tf":1.4142135623730951},"68":{"tf":1.4142135623730951},"71":{"tf":1.0},"73":{"tf":1.0},"75":{"tf":1.0},"91":{"tf":1.0}}}}},"o":{"df":0,"docs":{},"r":{"df":1,"docs":{"91":{"tf":1.0}},"i":{"df":1,"docs":{"101":{"tf":1.0}}}}},"v":{"a":{"df":0,"docs":{},"t":{"df":5,"docs":{"34":{"tf":1.0},"35":{"tf":1.7320508075688772},"44":{"tf":1.7320508075688772},"69":{"tf":1.0},"7":{"tf":2.0}}}},"df":0,"docs":{}}},"o":{"b":{"a":{"b":{"df":0,"docs":{},"l":{"df":5,"docs":{"111":{"tf":1.0},"14":{"tf":2.6457513110645907},"19":{"tf":1.0},"41":{"tf":1.0},"85":{"tf":1.0}}}},"df":0,"docs":{}},"df":0,"docs":{},"l":{"df":0,"docs":{},"e":{"df":0,"docs":{},"m":{"df":4,"docs":{"125":{"tf":1.4142135623730951},"149":{"tf":1.0},"21":{"tf":1.0},"50":{"tf":1.0}}}}}},"c":{"df":0,"docs":{},"e":{"df":2,"docs":{"33":{"tf":1.0},"80":{"tf":1.0}},"s":{"df":0,"docs":{},"s":{"df":9,"docs":{"14":{"tf":1.0},"151":{"tf":1.0},"20":{"tf":1.4142135623730951},"4":{"tf":1.0},"40":{"tf":1.0},"69":{"tf":1.0},"77":{"tf":1.0},"8":{"tf":1.0},"94":{"tf":1.0}}}}}},"d":{"df":0,"docs":{},"u":{"c":{"df":5,"docs":{"141":{"tf":1.0},"19":{"tf":1.4142135623730951},"20":{"tf":1.0},"75":{"tf":1.0},"8":{"tf":1.0}},"t":{"df":6,"docs":{"123":{"tf":1.4142135623730951},"138":{"tf":1.4142135623730951},"139":{"tf":1.4142135623730951},"14":{"tf":1.4142135623730951},"144":{"tf":1.0},"146":{"tf":2.449489742783178}}}},"df":0,"docs":{}}},"df":0,"docs":{},"g":{"df":0,"docs":{},"r":{"a":{"df":0,"docs":{},"m":{"df":25,"docs":{"10":{"tf":1.0},"12":{"tf":1.4142135623730951},"127":{"tf":1.0},"13":{"tf":2.23606797749979},"133":{"tf":1.4142135623730951},"14":{"tf":1.0},"141":{"tf":1.0},"142":{"tf":1.0},"144":{"tf":1.7320508075688772},"146":{"tf":2.0},"147":{"tf":1.0},"16":{"tf":1.0},"23":{"tf":1.0},"34":{"tf":1.4142135623730951},"35":{"tf":1.4142135623730951},"4":{"tf":1.0},"43":{"tf":1.0},"44":{"tf":1.0},"45":{"tf":1.4142135623730951},"50":{"tf":2.0},"69":{"tf":1.0},"7":{"tf":3.4641016151377544},"8":{"tf":1.4142135623730951},"9":{"tf":1.7320508075688772},"91":{"tf":1.4142135623730951}}}},"df":0,"docs":{},"e":{"df":0,"docs":{},"s":{"df":0,"docs":{},"s":{"df":1,"docs":{"0":{"tf":1.0}}}}}}},"o":{"df":0,"docs":{},"f":{"<":{"df":0,"docs":{},"f":{"df":1,"docs":{"35":{"tf":1.0}}}},"_":{"df":0,"docs":{},"o":{"df":0,"docs":{},"p":{"df":0,"docs":{},"t":{"df":2,"docs":{"104":{"tf":1.4142135623730951},"110":{"tf":1.4142135623730951}},"i":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"df":0,"docs":{},"s":{")":{".":{"df":0,"docs":{},"u":{"df":0,"docs":{},"n":{"df":0,"docs":{},"w":{"df":0,"docs":{},"r":{"a":{"df":0,"docs":{},"p":{"df":1,"docs":{"104":{"tf":1.0}}}},"df":0,"docs":{}}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}}}}}}},"df":39,"docs":{"102":{"tf":1.4142135623730951},"104":{"tf":3.0},"109":{"tf":1.4142135623730951},"110":{"tf":2.0},"112":{"tf":1.0},"113":{"tf":1.0},"119":{"tf":2.0},"13":{"tf":1.0},"14":{"tf":1.0},"144":{"tf":1.0},"146":{"tf":1.7320508075688772},"16":{"tf":1.0},"19":{"tf":1.4142135623730951},"21":{"tf":2.449489742783178},"22":{"tf":1.0},"28":{"tf":1.4142135623730951},"29":{"tf":1.4142135623730951},"32":{"tf":1.0},"33":{"tf":1.4142135623730951},"34":{"tf":1.4142135623730951},"35":{"tf":2.8284271247461903},"4":{"tf":1.0},"42":{"tf":1.0},"44":{"tf":1.7320508075688772},"50":{"tf":2.0},"57":{"tf":1.0},"59":{"tf":1.0},"60":{"tf":1.0},"61":{"tf":1.0},"62":{"tf":1.0},"63":{"tf":1.0},"7":{"tf":2.0},"77":{"tf":1.0},"8":{"tf":1.0},"80":{"tf":1.4142135623730951},"88":{"tf":1.0},"91":{"tf":1.0},"94":{"tf":1.4142135623730951},"95":{"tf":1.4142135623730951}},"o":{"df":0,"docs":{},"p":{"df":0,"docs":{},"t":{"df":1,"docs":{"109":{"tf":1.4142135623730951}},"i":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"df":0,"docs":{},"s":{":":{":":{"d":{"df":0,"docs":{},"e":{"df":0,"docs":{},"f":{"a":{"df":0,"docs":{},"u":{"df":0,"docs":{},"l":{"df":0,"docs":{},"t":{"_":{"df":0,"docs":{},"t":{"df":0,"docs":{},"e":{"df":0,"docs":{},"s":{"df":0,"docs":{},"t":{"_":{"df":0,"docs":{},"o":{"df":0,"docs":{},"p":{"df":0,"docs":{},"t":{"df":1,"docs":{"104":{"tf":1.0}}}}}},"df":0,"docs":{}}}}}},"df":0,"docs":{}}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}}}}}}}},"p":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":0,"docs":{},"l":{"df":0,"docs":{},"i":{"df":2,"docs":{"146":{"tf":1.0},"85":{"tf":1.0}}}},"t":{"df":0,"docs":{},"i":{"df":8,"docs":{"14":{"tf":1.0},"146":{"tf":1.0},"19":{"tf":1.4142135623730951},"20":{"tf":1.0},"52":{"tf":1.0},"70":{"tf":2.0},"73":{"tf":1.0},"84":{"tf":1.0}}}}}},"o":{"df":0,"docs":{},"r":{"df":0,"docs":{},"t":{"df":1,"docs":{"73":{"tf":1.0}}}}}},"t":{"df":0,"docs":{},"o":{"c":{"df":0,"docs":{},"o":{"df":0,"docs":{},"l":{"df":37,"docs":{"101":{"tf":1.4142135623730951},"102":{"tf":1.0},"109":{"tf":1.7320508075688772},"118":{"tf":1.4142135623730951},"123":{"tf":1.0},"14":{"tf":1.0},"142":{"tf":1.0},"16":{"tf":1.7320508075688772},"17":{"tf":1.0},"20":{"tf":1.4142135623730951},"34":{"tf":1.0},"37":{"tf":1.0},"4":{"tf":1.0},"57":{"tf":1.4142135623730951},"58":{"tf":1.0},"59":{"tf":1.0},"68":{"tf":1.0},"69":{"tf":2.0},"70":{"tf":1.4142135623730951},"71":{"tf":1.4142135623730951},"73":{"tf":2.449489742783178},"74":{"tf":1.0},"75":{"tf":2.23606797749979},"77":{"tf":1.4142135623730951},"78":{"tf":1.0},"80":{"tf":1.7320508075688772},"82":{"tf":1.4142135623730951},"83":{"tf":1.0},"84":{"tf":1.4142135623730951},"85":{"tf":1.0},"86":{"tf":1.4142135623730951},"87":{"tf":1.4142135623730951},"88":{"tf":1.4142135623730951},"89":{"tf":1.0},"92":{"tf":1.0},"93":{"tf":1.0},"94":{"tf":1.0}}}}},"df":0,"docs":{}}},"v":{"a":{"b":{"df":0,"docs":{},"l":{"df":1,"docs":{"149":{"tf":1.4142135623730951}}}},"df":0,"docs":{}},"df":0,"docs":{},"e":{"(":{"&":{"df":0,"docs":{},"t":{"df":0,"docs":{},"r":{"a":{"c":{"df":0,"docs":{},"e":{"_":{"df":0,"docs":{},"t":{"df":1,"docs":{"110":{"tf":1.0}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}},":":{":":{"<":{"df":0,"docs":{},"f":{"df":1,"docs":{"104":{"tf":1.0}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":34,"docs":{"102":{"tf":1.0},"103":{"tf":1.0},"104":{"tf":1.4142135623730951},"105":{"tf":1.0},"108":{"tf":1.0},"109":{"tf":1.0},"110":{"tf":1.7320508075688772},"111":{"tf":1.0},"113":{"tf":1.0},"114":{"tf":1.4142135623730951},"118":{"tf":1.0},"138":{"tf":1.0},"14":{"tf":1.0},"144":{"tf":1.0},"146":{"tf":1.7320508075688772},"151":{"tf":1.7320508075688772},"16":{"tf":1.0},"19":{"tf":1.4142135623730951},"23":{"tf":1.4142135623730951},"34":{"tf":1.0},"35":{"tf":1.0},"4":{"tf":1.0},"42":{"tf":1.0},"43":{"tf":1.0},"44":{"tf":1.0},"46":{"tf":1.0},"48":{"tf":1.0},"49":{"tf":1.0},"50":{"tf":1.0},"70":{"tf":1.0},"73":{"tf":1.4142135623730951},"74":{"tf":1.0},"84":{"tf":1.0},"85":{"tf":1.0}},"r":{"'":{"df":2,"docs":{"35":{"tf":1.0},"7":{"tf":1.0}}},".":{"df":0,"docs":{},"p":{"df":0,"docs":{},"r":{"df":0,"docs":{},"o":{"df":0,"docs":{},"v":{"df":1,"docs":{"35":{"tf":1.4142135623730951}},"e":{"(":{"&":{"df":0,"docs":{},"w":{"df":0,"docs":{},"i":{"df":0,"docs":{},"t":{"df":1,"docs":{"44":{"tf":1.0}}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}}}}},":":{":":{"df":0,"docs":{},"n":{"df":0,"docs":{},"e":{"df":0,"docs":{},"w":{"(":{"df":0,"docs":{},"k":{"df":0,"docs":{},"z":{"df":0,"docs":{},"g":{".":{"c":{"df":0,"docs":{},"l":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"df":2,"docs":{"35":{"tf":1.4142135623730951},"44":{"tf":1.0}}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"<":{"df":0,"docs":{},"f":{"df":1,"docs":{"35":{"tf":1.0}}}},"df":60,"docs":{"101":{"tf":1.0},"102":{"tf":1.4142135623730951},"103":{"tf":1.0},"104":{"tf":1.4142135623730951},"111":{"tf":1.4142135623730951},"112":{"tf":1.0},"113":{"tf":2.0},"117":{"tf":1.4142135623730951},"118":{"tf":2.8284271247461903},"126":{"tf":1.4142135623730951},"127":{"tf":1.4142135623730951},"13":{"tf":1.4142135623730951},"131":{"tf":1.0},"144":{"tf":1.4142135623730951},"146":{"tf":1.7320508075688772},"147":{"tf":1.4142135623730951},"151":{"tf":1.4142135623730951},"20":{"tf":2.0},"21":{"tf":2.449489742783178},"23":{"tf":1.0},"31":{"tf":1.0},"32":{"tf":1.0},"34":{"tf":1.0},"35":{"tf":2.23606797749979},"44":{"tf":1.4142135623730951},"45":{"tf":1.0},"46":{"tf":1.4142135623730951},"50":{"tf":1.4142135623730951},"51":{"tf":1.0},"53":{"tf":1.0},"56":{"tf":1.0},"57":{"tf":1.0},"58":{"tf":2.0},"59":{"tf":2.0},"60":{"tf":2.0},"61":{"tf":1.0},"62":{"tf":1.0},"63":{"tf":2.0},"66":{"tf":1.4142135623730951},"69":{"tf":1.7320508075688772},"7":{"tf":1.4142135623730951},"71":{"tf":1.0},"72":{"tf":1.7320508075688772},"73":{"tf":1.4142135623730951},"74":{"tf":1.0},"75":{"tf":2.449489742783178},"77":{"tf":2.23606797749979},"78":{"tf":1.0},"79":{"tf":2.0},"80":{"tf":1.7320508075688772},"82":{"tf":1.0},"83":{"tf":2.23606797749979},"84":{"tf":2.0},"85":{"tf":1.0},"86":{"tf":1.0},"88":{"tf":1.0},"91":{"tf":2.23606797749979},"94":{"tf":1.0},"95":{"tf":1.0},"97":{"tf":1.0}}}},"i":{"d":{"df":19,"docs":{"103":{"tf":1.0},"109":{"tf":1.0},"112":{"tf":2.0},"113":{"tf":1.7320508075688772},"117":{"tf":1.0},"118":{"tf":1.0},"125":{"tf":1.0},"34":{"tf":1.0},"48":{"tf":1.0},"50":{"tf":1.4142135623730951},"57":{"tf":1.0},"58":{"tf":1.0},"59":{"tf":1.7320508075688772},"60":{"tf":1.4142135623730951},"62":{"tf":2.0},"63":{"tf":1.7320508075688772},"66":{"tf":1.4142135623730951},"68":{"tf":1.0},"80":{"tf":1.0}}},"df":0,"docs":{}}}}},"u":{"b":{"_":{"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"df":0,"docs":{},"p":{"df":0,"docs":{},"u":{"df":0,"docs":{},"t":{"df":2,"docs":{"104":{"tf":1.7320508075688772},"110":{"tf":1.4142135623730951}}}}}}}},"df":4,"docs":{"133":{"tf":1.4142135623730951},"135":{"tf":1.4142135623730951},"35":{"tf":6.928203230275509},"45":{"tf":1.0}},"l":{"c":{"df":1,"docs":{"135":{"tf":1.0}}},"df":0,"docs":{},"i":{"c":{"_":{"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"df":0,"docs":{},"p":{"df":0,"docs":{},"u":{"df":0,"docs":{},"t":{"df":2,"docs":{"35":{"tf":2.449489742783178},"44":{"tf":1.7320508075688772}}}}}}}},"df":26,"docs":{"104":{"tf":2.0},"106":{"tf":1.4142135623730951},"110":{"tf":1.0},"13":{"tf":2.0},"133":{"tf":2.23606797749979},"135":{"tf":1.4142135623730951},"14":{"tf":1.4142135623730951},"142":{"tf":1.0},"144":{"tf":1.4142135623730951},"146":{"tf":2.8284271247461903},"147":{"tf":1.0},"151":{"tf":1.0},"16":{"tf":1.7320508075688772},"23":{"tf":1.4142135623730951},"26":{"tf":1.0},"32":{"tf":1.4142135623730951},"34":{"tf":1.4142135623730951},"35":{"tf":1.4142135623730951},"36":{"tf":1.0},"42":{"tf":1.0},"44":{"tf":2.0},"58":{"tf":1.0},"69":{"tf":1.0},"7":{"tf":1.0},"94":{"tf":1.0},"95":{"tf":1.0}},"l":{"df":0,"docs":{},"i":{"df":2,"docs":{"144":{"tf":1.0},"19":{"tf":1.0}}}}},"df":0,"docs":{},"s":{"df":0,"docs":{},"h":{"df":1,"docs":{"22":{"tf":1.0}}}}}}},"df":0,"docs":{},"r":{"df":0,"docs":{},"p":{"df":0,"docs":{},"o":{"df":0,"docs":{},"s":{"df":4,"docs":{"149":{"tf":1.0},"34":{"tf":1.0},"50":{"tf":1.4142135623730951},"56":{"tf":1.0}}}}}},"t":{"df":7,"docs":{"10":{"tf":1.0},"13":{"tf":1.0},"14":{"tf":1.4142135623730951},"147":{"tf":1.0},"151":{"tf":1.0},"45":{"tf":1.0},"9":{"tf":1.4142135623730951}}}},"ˉ":{"df":0,"docs":{},"​":{"c":{"df":0,"docs":{},"​":{":":{"=":{"df":0,"docs":{},"p":{"df":0,"docs":{},"i":{"(":{"df":0,"docs":{},"ζ":{")":{"+":{"df":0,"docs":{},"α":{"df":0,"docs":{},"z":{"df":0,"docs":{},"ˉ":{"df":0,"docs":{},"ω":{"df":0,"docs":{},"​":{"(":{"c":{"df":0,"docs":{},"ˉ":{"+":{"df":0,"docs":{},"γ":{")":{"(":{"a":{"df":0,"docs":{},"ˉ":{"+":{"df":0,"docs":{},"β":{"df":0,"docs":{},"s":{"df":0,"docs":{},"ˉ":{"df":0,"docs":{},"σ":{"1":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"γ":{")":{"(":{"b":{"df":0,"docs":{},"ˉ":{"+":{"df":0,"docs":{},"β":{"df":0,"docs":{},"s":{"df":0,"docs":{},"ˉ":{"df":0,"docs":{},"σ":{"2":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"γ":{")":{"df":0,"docs":{},"−":{"df":0,"docs":{},"α":{"2":{"df":0,"docs":{},"l":{"1":{"df":1,"docs":{"32":{"tf":1.0}}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{},"n":{"c":{"df":1,"docs":{"32":{"tf":1.0}},"​":{":":{"=":{"df":0,"docs":{},"p":{"df":0,"docs":{},"n":{"c":{"df":1,"docs":{"28":{"tf":1.0}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"∈":{"df":0,"docs":{},"f":{"[":{"df":0,"docs":{},"x":{"df":1,"docs":{"90":{"tf":1.0}}}},"df":0,"docs":{}}},"−":{"df":0,"docs":{},"y":{"=":{"(":{"df":0,"docs":{},"x":{"df":0,"docs":{},"−":{"df":0,"docs":{},"z":{")":{"df":0,"docs":{},"q":{"df":1,"docs":{"78":{"tf":1.0}}}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"q":{"(":{"d":{"1":{"df":0,"docs":{},"​":{")":{",":{"df":0,"docs":{},"…":{",":{"df":0,"docs":{},"q":{"(":{"d":{"df":0,"docs":{},"m":{"df":1,"docs":{"78":{"tf":1.0}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},",":{"df":0,"docs":{},"v":{",":{"df":0,"docs":{},"t":{",":{"df":0,"docs":{},"p":{"df":0,"docs":{},"i":{"df":1,"docs":{"36":{"tf":1.0}}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"c":{"_":{"1":{"df":1,"docs":{"35":{"tf":1.0}}},"df":0,"docs":{}},"df":4,"docs":{"10":{"tf":2.23606797749979},"12":{"tf":1.0},"13":{"tf":2.0},"35":{"tf":1.0}}},"df":15,"docs":{"10":{"tf":1.4142135623730951},"11":{"tf":1.0},"12":{"tf":1.0},"13":{"tf":1.7320508075688772},"14":{"tf":1.0},"16":{"tf":1.4142135623730951},"34":{"tf":1.4142135623730951},"36":{"tf":1.0},"74":{"tf":1.0},"78":{"tf":1.4142135623730951},"8":{"tf":1.0},"80":{"tf":1.4142135623730951},"9":{"tf":1.0},"90":{"tf":1.0},"91":{"tf":1.0}},"k":{"df":0,"docs":{},"t":{"df":0,"docs":{},"​":{"(":{"df":0,"docs":{},"x":{")":{"=":{"df":0,"docs":{},"p":{"df":0,"docs":{},"k":{"df":0,"docs":{},"t":{"df":0,"docs":{},"​":{"(":{"df":0,"docs":{},"t":{"1":{"df":0,"docs":{},"​":{"(":{"df":0,"docs":{},"x":{")":{",":{"df":0,"docs":{},"…":{",":{"df":0,"docs":{},"t":{"df":0,"docs":{},"m":{"df":0,"docs":{},"​":{"(":{"df":0,"docs":{},"x":{")":{",":{"df":0,"docs":{},"t":{"1":{"df":0,"docs":{},"​":{"(":{"df":0,"docs":{},"g":{"df":0,"docs":{},"x":{")":{",":{"df":0,"docs":{},"…":{",":{"df":0,"docs":{},"t":{"df":0,"docs":{},"m":{"df":0,"docs":{},"​":{"(":{"df":0,"docs":{},"ω":{"df":0,"docs":{},"x":{"df":1,"docs":{"84":{"tf":1.0}}}}},"df":0,"docs":{}}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"l":{"_":{"1":{"df":1,"docs":{"35":{"tf":1.0}}},"df":0,"docs":{}},"df":5,"docs":{"10":{"tf":2.23606797749979},"12":{"tf":1.0},"13":{"tf":1.4142135623730951},"21":{"tf":2.0},"35":{"tf":1.0}},"​":{"(":{"df":0,"docs":{},"ζ":{")":{",":{"df":0,"docs":{},"q":{"df":0,"docs":{},"r":{"df":1,"docs":{"21":{"tf":1.0}}}}},"df":0,"docs":{}},"df":0,"docs":{}}},",":{"df":0,"docs":{},"q":{"df":0,"docs":{},"r":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"q":{"df":0,"docs":{},"m":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"q":{"df":0,"docs":{},"o":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"q":{"c":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"s":{"df":0,"docs":{},"σ":{"1":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"s":{"df":0,"docs":{},"σ":{"2":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"s":{"df":0,"docs":{},"σ":{"3":{"df":2,"docs":{"15":{"tf":1.0},"16":{"tf":1.0}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}},"df":0,"docs":{}}},"o":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"q":{"df":0,"docs":{},"m":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"q":{"c":{"df":1,"docs":{"10":{"tf":1.0}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}},"df":0,"docs":{}}}}},"df":0,"docs":{}}}}},"]":{"1":{"df":1,"docs":{"21":{"tf":1.0}},"​":{",":{"[":{"df":0,"docs":{},"q":{"df":0,"docs":{},"r":{"df":0,"docs":{},"​":{"]":{"1":{"df":0,"docs":{},"​":{",":{"[":{"df":0,"docs":{},"q":{"df":0,"docs":{},"m":{"df":0,"docs":{},"​":{"]":{"1":{"df":0,"docs":{},"​":{",":{"[":{"df":0,"docs":{},"q":{"df":0,"docs":{},"o":{"df":0,"docs":{},"​":{"]":{"1":{"df":0,"docs":{},"​":{",":{"[":{"df":0,"docs":{},"q":{"c":{"df":0,"docs":{},"​":{"]":{"1":{"df":0,"docs":{},"​":{",":{"[":{"df":0,"docs":{},"s":{"df":0,"docs":{},"σ":{"1":{"df":0,"docs":{},"​":{"]":{"1":{"df":0,"docs":{},"​":{",":{"[":{"df":0,"docs":{},"s":{"df":0,"docs":{},"σ":{"2":{"df":0,"docs":{},"​":{"]":{"1":{"df":0,"docs":{},"​":{",":{"[":{"df":0,"docs":{},"s":{"df":0,"docs":{},"σ":{"3":{"df":0,"docs":{},"​":{"]":{"1":{"df":1,"docs":{"22":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"m":{"_":{"1":{"df":1,"docs":{"35":{"tf":1.0}}},"df":0,"docs":{}},"df":4,"docs":{"10":{"tf":2.23606797749979},"12":{"tf":1.0},"13":{"tf":1.4142135623730951},"35":{"tf":1.0}}},"o":{"_":{"1":{"df":1,"docs":{"35":{"tf":1.0}}},"df":0,"docs":{}},"df":4,"docs":{"10":{"tf":2.23606797749979},"12":{"tf":1.0},"13":{"tf":1.4142135623730951},"35":{"tf":1.0}}},"r":{"_":{"1":{"df":1,"docs":{"35":{"tf":1.0}}},"df":0,"docs":{}},"df":5,"docs":{"10":{"tf":2.23606797749979},"12":{"tf":1.0},"13":{"tf":1.4142135623730951},"21":{"tf":2.0},"35":{"tf":1.0}},"​":{"]":{"1":{"df":1,"docs":{"21":{"tf":1.4142135623730951}}},"df":0,"docs":{}},"df":0,"docs":{}}},"u":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":0,"docs":{},"i":{"df":4,"docs":{"109":{"tf":1.0},"119":{"tf":1.0},"91":{"tf":1.0},"94":{"tf":1.0}}}},"s":{"df":0,"docs":{},"t":{"df":0,"docs":{},"i":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"df":1,"docs":{"14":{"tf":1.0}}}}}}}},"i":{"c":{"df":0,"docs":{},"k":{"df":1,"docs":{"131":{"tf":1.0}}}},"df":0,"docs":{},"t":{"df":1,"docs":{"146":{"tf":1.0}}}},"o":{"df":0,"docs":{},"t":{"df":0,"docs":{},"i":{"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"df":0,"docs":{},"t":{"df":2,"docs":{"35":{"tf":1.7320508075688772},"69":{"tf":1.0}}}}}}}}}},"r":{"0":{"df":1,"docs":{"151":{"tf":1.4142135623730951}}},"a":{"d":{"df":0,"docs":{},"i":{"df":0,"docs":{},"x":{"df":1,"docs":{"121":{"tf":1.0}}}}},"df":0,"docs":{},"n":{"d":{"df":0,"docs":{},"o":{"df":0,"docs":{},"m":{"_":{"df":0,"docs":{},"g":{"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":1,"docs":{"35":{"tf":2.23606797749979}}}}}}}},"df":19,"docs":{"118":{"tf":1.4142135623730951},"14":{"tf":2.8284271247461903},"16":{"tf":2.0},"20":{"tf":1.7320508075688772},"24":{"tf":1.0},"25":{"tf":1.0},"26":{"tf":1.0},"3":{"tf":1.0},"35":{"tf":1.0},"58":{"tf":1.0},"60":{"tf":1.4142135623730951},"69":{"tf":1.4142135623730951},"70":{"tf":1.4142135623730951},"79":{"tf":1.0},"83":{"tf":1.0},"84":{"tf":1.0},"85":{"tf":1.0},"94":{"tf":1.4142135623730951},"95":{"tf":1.4142135623730951}},"l":{"df":0,"docs":{},"i":{"df":3,"docs":{"118":{"tf":1.0},"59":{"tf":1.0},"85":{"tf":1.0}}}}}}},"df":0,"docs":{},"g":{"df":9,"docs":{"130":{"tf":1.4142135623730951},"134":{"tf":1.0},"138":{"tf":1.0},"14":{"tf":1.4142135623730951},"144":{"tf":1.0},"146":{"tf":2.23606797749979},"147":{"tf":1.4142135623730951},"151":{"tf":2.449489742783178},"70":{"tf":1.0}},"e":{"_":{"c":{"df":0,"docs":{},"h":{"df":0,"docs":{},"e":{"c":{"df":0,"docs":{},"k":{"_":{"c":{"df":0,"docs":{},"e":{"df":0,"docs":{},"l":{"df":0,"docs":{},"l":{".":{"df":0,"docs":{},"h":{"df":1,"docs":{"130":{"tf":1.0}}},"i":{"df":0,"docs":{},"n":{"df":0,"docs":{},"l":{"df":1,"docs":{"130":{"tf":1.0}}}}}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"p":{"2":{"df":1,"docs":{"83":{"tf":1.0}}},"df":3,"docs":{"147":{"tf":1.0},"91":{"tf":1.7320508075688772},"94":{"tf":1.0}}},"t":{"df":0,"docs":{},"i":{"df":0,"docs":{},"o":{"df":2,"docs":{"149":{"tf":1.0},"151":{"tf":1.0}},"n":{"df":3,"docs":{"54":{"tf":1.0},"55":{"tf":1.0},"56":{"tf":1.0}}}}}},"w":{"df":2,"docs":{"144":{"tf":1.0},"40":{"tf":1.0}}}},"c":{"1":{"6":{"/":{"df":0,"docs":{},"s":{"df":0,"docs":{},"o":{"df":0,"docs":{},"r":{"df":0,"docs":{},"t":{"df":1,"docs":{"132":{"tf":1.0}}}}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":5,"docs":{"130":{"tf":1.4142135623730951},"131":{"tf":1.0},"132":{"tf":1.0},"138":{"tf":1.4142135623730951},"146":{"tf":1.0}},"m":{"a":{"df":0,"docs":{},"x":{"df":1,"docs":{"146":{"tf":1.0}}}},"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"df":1,"docs":{"146":{"tf":1.0}}}}}},"df":10,"docs":{"11":{"tf":1.0},"12":{"tf":1.0},"13":{"tf":1.0},"14":{"tf":1.0},"142":{"tf":2.6457513110645907},"35":{"tf":1.7320508075688772},"38":{"tf":1.0},"9":{"tf":1.0},"91":{"tf":1.0},"92":{"tf":1.4142135623730951}},"e":{"a":{"c":{"df":0,"docs":{},"h":{"df":1,"docs":{"146":{"tf":1.0}}}},"d":{"df":2,"docs":{"111":{"tf":1.4142135623730951},"69":{"tf":1.0}},"e":{"df":0,"docs":{},"r":{"df":1,"docs":{"125":{"tf":1.0}}}},"i":{"df":1,"docs":{"146":{"tf":1.0}}}},"df":0,"docs":{},"l":{"df":4,"docs":{"128":{"tf":1.7320508075688772},"135":{"tf":1.0},"146":{"tf":1.0},"149":{"tf":1.0}},"i":{"df":0,"docs":{},"t":{"df":0,"docs":{},"i":{"df":1,"docs":{"146":{"tf":1.0}}}}},"l":{"df":0,"docs":{},"i":{"df":4,"docs":{"116":{"tf":1.0},"123":{"tf":1.0},"14":{"tf":1.0},"50":{"tf":1.0}}}}},"r":{"df":0,"docs":{},"r":{"a":{"df":0,"docs":{},"n":{"df":0,"docs":{},"g":{"df":1,"docs":{"14":{"tf":1.0}}}}},"df":0,"docs":{}}},"s":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"df":6,"docs":{"117":{"tf":1.0},"123":{"tf":1.0},"146":{"tf":1.0},"20":{"tf":1.0},"50":{"tf":1.0},"56":{"tf":1.0}}}}}},"c":{"a":{"df":0,"docs":{},"l":{"c":{"df":0,"docs":{},"u":{"df":0,"docs":{},"l":{"df":1,"docs":{"146":{"tf":1.0}}}}},"df":7,"docs":{"111":{"tf":1.0},"125":{"tf":1.0},"14":{"tf":2.0},"16":{"tf":1.0},"54":{"tf":1.0},"91":{"tf":1.0},"95":{"tf":1.0}}},"p":{"df":13,"docs":{"103":{"tf":1.0},"105":{"tf":1.0},"111":{"tf":1.0},"116":{"tf":1.0},"34":{"tf":1.4142135623730951},"35":{"tf":1.0},"36":{"tf":1.0},"46":{"tf":1.0},"47":{"tf":1.0},"69":{"tf":1.0},"7":{"tf":1.0},"70":{"tf":1.0},"84":{"tf":1.0}}}},"df":0,"docs":{},"e":{"df":0,"docs":{},"i":{"df":0,"docs":{},"v":{"df":5,"docs":{"114":{"tf":1.0},"21":{"tf":1.0},"77":{"tf":1.0},"79":{"tf":1.0},"95":{"tf":1.0}}}}},"o":{"df":0,"docs":{},"g":{"df":0,"docs":{},"n":{"df":1,"docs":{"16":{"tf":1.0}}}},"m":{"df":0,"docs":{},"m":{"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"d":{"df":1,"docs":{"69":{"tf":1.0}}},"df":0,"docs":{}}}}},"n":{"df":0,"docs":{},"s":{"df":0,"docs":{},"t":{"df":0,"docs":{},"r":{"df":0,"docs":{},"u":{"c":{"df":0,"docs":{},"t":{"df":8,"docs":{"102":{"tf":1.4142135623730951},"112":{"tf":1.0},"113":{"tf":1.0},"114":{"tf":1.4142135623730951},"115":{"tf":1.0},"117":{"tf":1.0},"63":{"tf":1.0},"95":{"tf":1.0}}}},"df":0,"docs":{}}}}}},"v":{"df":3,"docs":{"114":{"tf":1.7320508075688772},"124":{"tf":1.0},"95":{"tf":1.0}}}},"u":{"df":0,"docs":{},"r":{"df":0,"docs":{},"s":{"df":2,"docs":{"14":{"tf":1.0},"25":{"tf":1.0}}}}}},"d":{"df":0,"docs":{},"u":{"c":{"df":6,"docs":{"101":{"tf":1.0},"12":{"tf":1.0},"14":{"tf":1.7320508075688772},"144":{"tf":1.0},"21":{"tf":1.4142135623730951},"80":{"tf":1.7320508075688772}}},"df":0,"docs":{},"n":{"d":{"df":2,"docs":{"102":{"tf":1.0},"70":{"tf":1.0}}},"df":0,"docs":{}}}},"df":4,"docs":{"136":{"tf":1.4142135623730951},"142":{"tf":1.4142135623730951},"146":{"tf":2.449489742783178},"147":{"tf":1.4142135623730951}},"f":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":11,"docs":{"126":{"tf":1.0},"130":{"tf":1.0},"134":{"tf":1.0},"139":{"tf":1.0},"146":{"tf":1.0},"3":{"tf":1.0},"70":{"tf":1.0},"81":{"tf":1.0},"83":{"tf":1.0},"87":{"tf":1.0},"95":{"tf":2.0}}}},"l":{"df":0,"docs":{},"e":{"c":{"df":0,"docs":{},"t":{"df":1,"docs":{"133":{"tf":1.0}}}},"df":0,"docs":{}}},"o":{"df":0,"docs":{},"r":{"df":0,"docs":{},"m":{"df":0,"docs":{},"u":{"df":0,"docs":{},"l":{"df":1,"docs":{"16":{"tf":1.0}}}}}}}},"g":{"a":{"df":0,"docs":{},"r":{"d":{"df":1,"docs":{"126":{"tf":1.0}}},"df":0,"docs":{}}},"df":1,"docs":{"146":{"tf":1.4142135623730951}},"i":{"df":0,"docs":{},"s":{"df":0,"docs":{},"t":{"df":8,"docs":{"127":{"tf":1.0},"133":{"tf":1.0},"136":{"tf":1.7320508075688772},"141":{"tf":1.0},"144":{"tf":1.4142135623730951},"146":{"tf":1.4142135623730951},"149":{"tf":1.0},"50":{"tf":1.0}},"e":{"df":0,"docs":{},"r":{"df":0,"docs":{},"s":{"df":0,"docs":{},"t":{"df":1,"docs":{"146":{"tf":1.0}}}}}}}}}},"j":{"df":0,"docs":{},"e":{"c":{"df":0,"docs":{},"t":{"df":4,"docs":{"19":{"tf":1.0},"33":{"tf":1.0},"7":{"tf":1.0},"92":{"tf":1.0}}}},"df":0,"docs":{}}},"l":{"a":{"df":0,"docs":{},"t":{"df":6,"docs":{"109":{"tf":2.0},"13":{"tf":1.0},"136":{"tf":1.0},"146":{"tf":1.0},"68":{"tf":1.0},"69":{"tf":1.0}},"i":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"df":0,"docs":{},"s":{"df":0,"docs":{},"h":{"df":0,"docs":{},"i":{"df":0,"docs":{},"p":{"df":4,"docs":{"107":{"tf":1.0},"151":{"tf":1.0},"53":{"tf":1.0},"66":{"tf":1.0}}}}}}}}}}},"df":2,"docs":{"146":{"tf":1.0},"70":{"tf":1.0}},"e":{"df":0,"docs":{},"v":{"df":2,"docs":{"139":{"tf":1.0},"87":{"tf":1.0}}}},"i":{"df":2,"docs":{"34":{"tf":1.0},"4":{"tf":1.0}}},"o":{"c":{"df":1,"docs":{"144":{"tf":1.0}}},"df":0,"docs":{}}},"m":{"a":{"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"df":6,"docs":{"125":{"tf":1.0},"130":{"tf":1.0},"59":{"tf":1.0},"65":{"tf":1.0},"66":{"tf":1.0},"67":{"tf":1.0}}}}},"df":0,"docs":{},"e":{"df":0,"docs":{},"m":{"b":{"df":1,"docs":{"114":{"tf":1.4142135623730951}}},"df":0,"docs":{}}},"i":{"df":0,"docs":{},"n":{"d":{"df":1,"docs":{"68":{"tf":1.0}}},"df":0,"docs":{}}},"o":{"df":0,"docs":{},"v":{"df":2,"docs":{"118":{"tf":1.0},"13":{"tf":1.0}}}}},"p":{"df":0,"docs":{},"e":{"a":{"df":0,"docs":{},"t":{"df":2,"docs":{"135":{"tf":1.0},"83":{"tf":1.0}}}},"df":0,"docs":{}},"h":{"df":0,"docs":{},"r":{"a":{"df":0,"docs":{},"s":{"df":1,"docs":{"14":{"tf":1.0}}}},"df":0,"docs":{}}},"l":{"a":{"c":{"df":3,"docs":{"135":{"tf":1.4142135623730951},"28":{"tf":1.0},"98":{"tf":1.0}}},"df":0,"docs":{},"y":{"df":1,"docs":{"95":{"tf":1.0}}}},"df":0,"docs":{}},"r":{"df":0,"docs":{},"e":{"df":0,"docs":{},"s":{"df":7,"docs":{"10":{"tf":2.23606797749979},"106":{"tf":1.0},"127":{"tf":1.4142135623730951},"146":{"tf":1.7320508075688772},"41":{"tf":1.0},"44":{"tf":1.0},"82":{"tf":1.0}},"e":{"df":0,"docs":{},"n":{"df":0,"docs":{},"t":{"df":6,"docs":{"101":{"tf":1.7320508075688772},"12":{"tf":1.0},"128":{"tf":1.0},"146":{"tf":1.7320508075688772},"4":{"tf":1.4142135623730951},"48":{"tf":1.4142135623730951}}}}}}}}},"q":{"df":0,"docs":{},"u":{"df":0,"docs":{},"i":{"df":0,"docs":{},"r":{"df":8,"docs":{"105":{"tf":1.0},"109":{"tf":1.0},"114":{"tf":1.0},"121":{"tf":1.0},"134":{"tf":1.0},"151":{"tf":1.0},"53":{"tf":1.0},"61":{"tf":1.0}}}}}},"s":{"_":{"df":0,"docs":{},"l":{"df":0,"docs":{},"o":{"df":0,"docs":{},"g":{"df":1,"docs":{"146":{"tf":1.4142135623730951}}}}}},"c":{"df":0,"docs":{},"u":{"df":1,"docs":{"14":{"tf":1.0}}}},"df":0,"docs":{},"e":{"df":0,"docs":{},"t":{"df":1,"docs":{"40":{"tf":1.0}}}},"h":{"a":{"df":0,"docs":{},"p":{"df":1,"docs":{"147":{"tf":1.7320508075688772}}}},"df":0,"docs":{}},"o":{"df":0,"docs":{},"u":{"df":0,"docs":{},"r":{"c":{"df":1,"docs":{"42":{"tf":1.0}}},"df":0,"docs":{}}}},"p":{"df":0,"docs":{},"e":{"c":{"df":0,"docs":{},"t":{"df":9,"docs":{"109":{"tf":1.0},"14":{"tf":2.449489742783178},"142":{"tf":1.0},"144":{"tf":1.0},"146":{"tf":1.4142135623730951},"147":{"tf":1.0},"16":{"tf":1.0},"3":{"tf":1.0},"32":{"tf":1.0}}}},"df":0,"docs":{}},"o":{"df":0,"docs":{},"n":{"df":0,"docs":{},"s":{"df":1,"docs":{"13":{"tf":1.0}}}}}},"t":{"df":2,"docs":{"114":{"tf":1.4142135623730951},"84":{"tf":1.0}}},"u":{"df":0,"docs":{},"l":{"df":0,"docs":{},"t":{"_":{"df":0,"docs":{},"t":{"df":0,"docs":{},"i":{"df":0,"docs":{},"m":{"df":0,"docs":{},"e":{"df":0,"docs":{},"s":{"_":{"b":{"a":{"df":0,"docs":{},"s":{"df":1,"docs":{"45":{"tf":1.4142135623730951}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}}}}},"df":23,"docs":{"11":{"tf":1.0},"112":{"tf":1.0},"13":{"tf":1.0},"135":{"tf":1.0},"14":{"tf":1.4142135623730951},"142":{"tf":1.4142135623730951},"146":{"tf":1.0},"147":{"tf":1.7320508075688772},"151":{"tf":1.4142135623730951},"16":{"tf":1.4142135623730951},"3":{"tf":1.0},"40":{"tf":1.4142135623730951},"41":{"tf":1.0},"45":{"tf":2.449489742783178},"5":{"tf":1.0},"56":{"tf":1.0},"62":{"tf":1.0},"73":{"tf":1.0},"75":{"tf":1.0},"77":{"tf":1.4142135623730951},"8":{"tf":1.0},"84":{"tf":1.4142135623730951},"9":{"tf":1.4142135623730951}}}}}},"t":{"df":0,"docs":{},"u":{"df":0,"docs":{},"r":{"df":0,"docs":{},"n":{"df":9,"docs":{"106":{"tf":1.0},"107":{"tf":1.4142135623730951},"114":{"tf":1.7320508075688772},"146":{"tf":1.0},"35":{"tf":1.4142135623730951},"40":{"tf":1.0},"74":{"tf":1.0},"80":{"tf":1.0},"92":{"tf":1.7320508075688772}}}}}},"u":{"df":0,"docs":{},"s":{"df":1,"docs":{"151":{"tf":1.0}}}},"v":{"df":0,"docs":{},"e":{"a":{"df":0,"docs":{},"l":{"df":6,"docs":{"19":{"tf":1.0},"20":{"tf":1.0},"69":{"tf":1.0},"7":{"tf":1.4142135623730951},"72":{"tf":1.0},"80":{"tf":1.0}}}},"df":0,"docs":{},"r":{"df":0,"docs":{},"s":{"df":1,"docs":{"101":{"tf":2.23606797749979}}}}},"o":{"df":0,"docs":{},"l":{"df":0,"docs":{},"v":{"df":1,"docs":{"104":{"tf":1.0}}}}}}},"i":{"d":{"df":1,"docs":{"14":{"tf":1.0}}},"df":0,"docs":{},"g":{"df":0,"docs":{},"h":{"df":0,"docs":{},"t":{"df":6,"docs":{"11":{"tf":1.0},"118":{"tf":1.0},"121":{"tf":1.0},"14":{"tf":1.0},"16":{"tf":1.0},"9":{"tf":2.23606797749979}}}}}},"m":{"a":{"df":0,"docs":{},"x":{"df":1,"docs":{"142":{"tf":1.4142135623730951}}}},"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"df":1,"docs":{"142":{"tf":1.4142135623730951}}}}},"n":{"df":0,"docs":{},"g":{"df":1,"docs":{"118":{"tf":1.0}}}},"o":{"df":0,"docs":{},"o":{"df":0,"docs":{},"t":{"df":29,"docs":{"112":{"tf":1.4142135623730951},"113":{"tf":1.0},"114":{"tf":1.4142135623730951},"119":{"tf":1.4142135623730951},"123":{"tf":2.449489742783178},"124":{"tf":4.0},"125":{"tf":2.8284271247461903},"14":{"tf":3.0},"16":{"tf":1.0},"23":{"tf":1.0},"34":{"tf":1.0},"36":{"tf":1.0},"38":{"tf":1.0},"5":{"tf":1.0},"52":{"tf":2.0},"54":{"tf":1.7320508075688772},"60":{"tf":1.0},"62":{"tf":1.4142135623730951},"63":{"tf":1.0},"68":{"tf":2.8284271247461903},"72":{"tf":1.7320508075688772},"73":{"tf":1.0},"75":{"tf":1.0},"76":{"tf":1.0},"80":{"tf":1.0},"82":{"tf":1.4142135623730951},"90":{"tf":1.0},"91":{"tf":1.0},"92":{"tf":2.0}}}},"u":{"df":0,"docs":{},"n":{"d":{"df":14,"docs":{"24":{"tf":1.0},"25":{"tf":1.0},"26":{"tf":1.0},"27":{"tf":1.0},"28":{"tf":1.0},"35":{"tf":2.23606797749979},"37":{"tf":1.0},"82":{"tf":1.7320508075688772},"83":{"tf":1.7320508075688772},"84":{"tf":1.7320508075688772},"85":{"tf":1.4142135623730951},"86":{"tf":1.7320508075688772},"91":{"tf":1.0},"94":{"tf":3.1622776601683795}}},"df":0,"docs":{}}},"w":{"df":32,"docs":{"10":{"tf":3.1622776601683795},"104":{"tf":1.0},"106":{"tf":1.7320508075688772},"107":{"tf":2.23606797749979},"108":{"tf":1.4142135623730951},"109":{"tf":1.7320508075688772},"119":{"tf":1.0},"12":{"tf":1.0},"125":{"tf":1.0},"127":{"tf":1.7320508075688772},"128":{"tf":2.0},"13":{"tf":3.1622776601683795},"130":{"tf":2.23606797749979},"133":{"tf":3.605551275463989},"134":{"tf":2.449489742783178},"136":{"tf":2.449489742783178},"139":{"tf":1.4142135623730951},"142":{"tf":2.449489742783178},"147":{"tf":2.6457513110645907},"149":{"tf":2.449489742783178},"20":{"tf":1.0},"23":{"tf":1.0},"36":{"tf":2.449489742783178},"49":{"tf":1.7320508075688772},"5":{"tf":1.0},"52":{"tf":2.0},"66":{"tf":1.7320508075688772},"69":{"tf":1.0},"70":{"tf":1.7320508075688772},"84":{"tf":1.4142135623730951},"9":{"tf":1.0},"91":{"tf":1.4142135623730951}}}},"u":{"df":0,"docs":{},"f":{"df":0,"docs":{},"f":{"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"df":0,"docs":{},"i":{"'":{"df":1,"docs":{"100":{"tf":1.4142135623730951}}},"df":0,"docs":{}}}}}},"g":{"df":1,"docs":{"64":{"tf":1.0}}},"l":{"df":0,"docs":{},"e":{"df":4,"docs":{"100":{"tf":1.4142135623730951},"48":{"tf":1.4142135623730951},"70":{"tf":1.0},"85":{"tf":1.0}}}},"n":{"df":5,"docs":{"144":{"tf":1.0},"44":{"tf":1.0},"80":{"tf":1.7320508075688772},"86":{"tf":1.0},"94":{"tf":1.0}},"n":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":1,"docs":{"104":{"tf":1.0}}}}}}},"−":{"1":{"df":1,"docs":{"38":{"tf":1.0}}},"df":0,"docs":{}}},"s":{"1":{"_":{"1":{"df":1,"docs":{"35":{"tf":1.0}}},"df":0,"docs":{},"l":{"a":{"df":0,"docs":{},"g":{"df":0,"docs":{},"r":{"a":{"df":0,"docs":{},"n":{"df":0,"docs":{},"g":{"df":1,"docs":{"35":{"tf":1.0}}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"z":{"df":0,"docs":{},"e":{"df":0,"docs":{},"t":{"a":{"df":1,"docs":{"35":{"tf":1.0}}},"df":0,"docs":{}}}}},"df":1,"docs":{"35":{"tf":1.0}}},"2":{"_":{"1":{"df":1,"docs":{"35":{"tf":1.0}}},"df":0,"docs":{},"l":{"a":{"df":0,"docs":{},"g":{"df":0,"docs":{},"r":{"a":{"df":0,"docs":{},"n":{"df":0,"docs":{},"g":{"df":1,"docs":{"35":{"tf":1.0}}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"z":{"df":0,"docs":{},"e":{"df":0,"docs":{},"t":{"a":{"df":1,"docs":{"35":{"tf":1.0}}},"df":0,"docs":{}}}}},"df":1,"docs":{"35":{"tf":1.0}},"k":{"df":1,"docs":{"102":{"tf":1.4142135623730951}}}},"3":{"_":{"1":{"df":1,"docs":{"35":{"tf":1.0}}},"df":0,"docs":{},"l":{"a":{"df":0,"docs":{},"g":{"df":0,"docs":{},"r":{"a":{"df":0,"docs":{},"n":{"df":0,"docs":{},"g":{"df":1,"docs":{"35":{"tf":1.0}}}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":1,"docs":{"35":{"tf":1.0}}},"=":{"0":{",":{"df":0,"docs":{},"…":{",":{"df":0,"docs":{},"q":{"df":0,"docs":{},"−":{"1":{"df":2,"docs":{"94":{"tf":1.0},"95":{"tf":1.7320508075688772}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"_":{"df":0,"docs":{},"σ":{"1":{"df":1,"docs":{"35":{"tf":1.0}}},"2":{"df":1,"docs":{"35":{"tf":1.0}}},"df":0,"docs":{}}},"a":{"df":0,"docs":{},"k":{"df":0,"docs":{},"e":{"df":1,"docs":{"21":{"tf":1.0}}}},"m":{"df":0,"docs":{},"e":{"df":33,"docs":{"10":{"tf":1.0},"101":{"tf":1.0},"104":{"tf":1.0},"110":{"tf":1.0},"115":{"tf":1.0},"118":{"tf":2.0},"12":{"tf":1.0},"124":{"tf":1.0},"125":{"tf":1.0},"132":{"tf":1.0},"133":{"tf":1.0},"135":{"tf":1.0},"14":{"tf":1.7320508075688772},"146":{"tf":1.7320508075688772},"149":{"tf":2.23606797749979},"151":{"tf":1.0},"31":{"tf":1.0},"36":{"tf":1.0},"52":{"tf":1.0},"54":{"tf":1.0},"55":{"tf":1.0},"59":{"tf":1.0},"60":{"tf":1.0},"65":{"tf":1.0},"66":{"tf":1.4142135623730951},"67":{"tf":1.0},"68":{"tf":1.0},"69":{"tf":1.0},"75":{"tf":1.0},"80":{"tf":1.0},"84":{"tf":1.7320508075688772},"95":{"tf":1.0},"97":{"tf":1.0}}},"p":{"df":0,"docs":{},"l":{"df":19,"docs":{"112":{"tf":1.4142135623730951},"118":{"tf":1.7320508075688772},"147":{"tf":1.0},"20":{"tf":1.0},"24":{"tf":1.0},"25":{"tf":1.4142135623730951},"26":{"tf":1.4142135623730951},"27":{"tf":1.0},"28":{"tf":1.0},"32":{"tf":2.0},"56":{"tf":1.0},"59":{"tf":1.0},"62":{"tf":1.4142135623730951},"83":{"tf":1.4142135623730951},"84":{"tf":1.0},"89":{"tf":1.0},"94":{"tf":2.6457513110645907},"95":{"tf":2.6457513110645907},"97":{"tf":2.0}}}}},"t":{"df":0,"docs":{},"i":{"df":0,"docs":{},"s":{"df":0,"docs":{},"f":{"df":0,"docs":{},"i":{"df":28,"docs":{"10":{"tf":1.0},"11":{"tf":1.0},"113":{"tf":1.4142135623730951},"114":{"tf":1.0},"116":{"tf":1.0},"12":{"tf":1.0},"124":{"tf":1.0},"130":{"tf":1.0},"14":{"tf":1.0},"146":{"tf":1.0},"19":{"tf":1.0},"20":{"tf":1.4142135623730951},"48":{"tf":1.0},"49":{"tf":1.4142135623730951},"52":{"tf":1.7320508075688772},"53":{"tf":1.4142135623730951},"54":{"tf":1.7320508075688772},"55":{"tf":1.4142135623730951},"56":{"tf":1.0},"57":{"tf":1.0},"59":{"tf":1.0},"63":{"tf":1.4142135623730951},"66":{"tf":1.0},"67":{"tf":1.0},"68":{"tf":1.0},"69":{"tf":1.0},"75":{"tf":1.0},"84":{"tf":1.4142135623730951}}}}}}},"v":{"df":0,"docs":{},"e":{"df":2,"docs":{"151":{"tf":1.0},"21":{"tf":1.0}}}},"w":{"df":3,"docs":{"10":{"tf":1.0},"14":{"tf":1.0},"16":{"tf":1.0}}},"y":{"df":1,"docs":{"56":{"tf":1.0}}}},"c":{"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"a":{"df":0,"docs":{},"r":{"df":0,"docs":{},"i":{"df":0,"docs":{},"o":{"df":1,"docs":{"100":{"tf":1.0}}}}}},"df":0,"docs":{}}},"h":{"df":0,"docs":{},"e":{"df":0,"docs":{},"m":{"df":0,"docs":{},"e":{"df":11,"docs":{"19":{"tf":1.7320508075688772},"35":{"tf":1.7320508075688772},"38":{"tf":1.7320508075688772},"44":{"tf":1.0},"71":{"tf":2.449489742783178},"74":{"tf":1.0},"75":{"tf":1.4142135623730951},"81":{"tf":1.0},"82":{"tf":1.0},"86":{"tf":1.0},"92":{"tf":1.0}}}}},"w":{"a":{"df":0,"docs":{},"r":{"df":0,"docs":{},"t":{"df":0,"docs":{},"z":{"df":1,"docs":{"14":{"tf":1.4142135623730951}}}}}},"df":0,"docs":{}}}},"df":2,"docs":{"92":{"tf":1.4142135623730951},"95":{"tf":1.7320508075688772}},"e":{"c":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"d":{"_":{"df":0,"docs":{},"r":{"df":0,"docs":{},"o":{"df":0,"docs":{},"w":{"[":{"0":{"df":2,"docs":{"107":{"tf":1.4142135623730951},"114":{"tf":1.0}}},"df":0,"docs":{}},"df":2,"docs":{"107":{"tf":1.0},"114":{"tf":1.0}}}}}},"df":13,"docs":{"11":{"tf":1.0},"13":{"tf":1.0},"14":{"tf":1.7320508075688772},"145":{"tf":1.0},"147":{"tf":1.0},"149":{"tf":1.4142135623730951},"3":{"tf":1.0},"49":{"tf":1.0},"59":{"tf":1.0},"66":{"tf":1.0},"70":{"tf":1.0},"9":{"tf":1.0},"91":{"tf":1.0}}},"df":0,"docs":{}}},"r":{"df":0,"docs":{},"e":{"df":0,"docs":{},"t":{"df":2,"docs":{"20":{"tf":1.0},"21":{"tf":1.0}}}}},"t":{"df":0,"docs":{},"i":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"df":34,"docs":{"103":{"tf":1.0},"11":{"tf":1.0},"111":{"tf":1.0},"117":{"tf":1.0},"126":{"tf":1.0},"130":{"tf":1.0},"131":{"tf":1.0},"138":{"tf":1.0},"139":{"tf":1.0},"14":{"tf":2.6457513110645907},"142":{"tf":1.0},"144":{"tf":1.0},"145":{"tf":1.0},"146":{"tf":4.0},"19":{"tf":1.0},"21":{"tf":1.0},"23":{"tf":1.0},"34":{"tf":1.4142135623730951},"37":{"tf":1.0},"4":{"tf":1.0},"43":{"tf":1.0},"45":{"tf":1.0},"46":{"tf":1.0},"49":{"tf":1.0},"69":{"tf":1.4142135623730951},"70":{"tf":1.0},"74":{"tf":1.0},"77":{"tf":1.0},"80":{"tf":1.0},"82":{"tf":1.0},"84":{"tf":1.0},"87":{"tf":1.0},"9":{"tf":1.0},"95":{"tf":1.0}}}}}},"u":{"df":0,"docs":{},"r":{"df":5,"docs":{"109":{"tf":1.4142135623730951},"151":{"tf":1.0},"68":{"tf":1.0},"73":{"tf":1.0},"91":{"tf":1.0}}}}},"df":0,"docs":{},"e":{"df":15,"docs":{"101":{"tf":1.0},"134":{"tf":1.0},"14":{"tf":2.0},"149":{"tf":1.0},"151":{"tf":1.0},"16":{"tf":1.4142135623730951},"20":{"tf":1.0},"3":{"tf":1.4142135623730951},"34":{"tf":1.0},"71":{"tf":1.0},"75":{"tf":1.0},"77":{"tf":1.0},"80":{"tf":1.0},"9":{"tf":1.7320508075688772},"98":{"tf":1.0}},"m":{"df":2,"docs":{"20":{"tf":1.0},"66":{"tf":1.0}},"i":{"df":0,"docs":{},"n":{"df":0,"docs":{},"g":{"df":0,"docs":{},"l":{"df":0,"docs":{},"i":{"df":1,"docs":{"20":{"tf":1.0}}}}}}}},"n":{"df":7,"docs":{"10":{"tf":1.0},"130":{"tf":1.0},"131":{"tf":1.0},"133":{"tf":1.0},"14":{"tf":1.0},"149":{"tf":1.0},"16":{"tf":1.0}}}},"l":{"df":0,"docs":{},"f":{".":{"a":{"df":0,"docs":{},"i":{"df":0,"docs":{},"r":{".":{"c":{"df":0,"docs":{},"o":{"df":0,"docs":{},"m":{"df":0,"docs":{},"p":{"df":0,"docs":{},"u":{"df":0,"docs":{},"t":{"df":0,"docs":{},"e":{"_":{"df":0,"docs":{},"t":{"df":0,"docs":{},"r":{"a":{"df":0,"docs":{},"n":{"df":0,"docs":{},"s":{"df":0,"docs":{},"i":{"df":0,"docs":{},"t":{"df":0,"docs":{},"i":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"(":{"&":{"df":0,"docs":{},"f":{"df":0,"docs":{},"r":{"a":{"df":0,"docs":{},"m":{"df":1,"docs":{"114":{"tf":1.0}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}}}}}}}},"df":0,"docs":{}}}},"df":0,"docs":{}}}}}},"n":{"df":0,"docs":{},"t":{"df":0,"docs":{},"e":{"df":0,"docs":{},"x":{"df":0,"docs":{},"t":{"(":{")":{".":{"df":0,"docs":{},"t":{"df":0,"docs":{},"r":{"a":{"df":0,"docs":{},"n":{"df":0,"docs":{},"s":{"df":0,"docs":{},"i":{"df":0,"docs":{},"t":{"df":0,"docs":{},"i":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"_":{"df":0,"docs":{},"o":{"df":0,"docs":{},"f":{"df":0,"docs":{},"f":{"df":0,"docs":{},"s":{"df":0,"docs":{},"e":{"df":0,"docs":{},"t":{"df":1,"docs":{"114":{"tf":1.0}}}}}}}}},"df":0,"docs":{}}}}}}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{},"p":{"df":0,"docs":{},"u":{"b":{"_":{"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"df":0,"docs":{},"p":{"df":0,"docs":{},"u":{"df":0,"docs":{},"t":{"df":0,"docs":{},"s":{".":{"a":{"0":{".":{"c":{"df":0,"docs":{},"l":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"df":1,"docs":{"106":{"tf":1.0}}}}}},"df":0,"docs":{}},"df":0,"docs":{}},"1":{".":{"c":{"df":0,"docs":{},"l":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"df":1,"docs":{"106":{"tf":1.0}}}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}},":":{":":{"df":0,"docs":{},"r":{"a":{"df":0,"docs":{},"p":{"c":{"df":0,"docs":{},"h":{"a":{"df":0,"docs":{},"l":{"df":0,"docs":{},"l":{"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"df":0,"docs":{},"g":{"df":2,"docs":{"106":{"tf":1.0},"107":{"tf":1.0}}}}}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":3,"docs":{"106":{"tf":1.0},"107":{"tf":1.0},"114":{"tf":1.0}}}},"n":{"d":{"df":13,"docs":{"101":{"tf":1.4142135623730951},"102":{"tf":1.0},"147":{"tf":1.4142135623730951},"21":{"tf":2.0},"7":{"tf":1.4142135623730951},"72":{"tf":1.4142135623730951},"75":{"tf":1.7320508075688772},"77":{"tf":1.0},"79":{"tf":1.4142135623730951},"80":{"tf":1.4142135623730951},"83":{"tf":1.0},"84":{"tf":1.0},"94":{"tf":1.0}}},"df":0,"docs":{},"s":{"df":4,"docs":{"19":{"tf":1.0},"45":{"tf":1.0},"70":{"tf":1.0},"77":{"tf":1.0}},"i":{"b":{"df":0,"docs":{},"l":{"df":1,"docs":{"7":{"tf":1.0}}}},"df":0,"docs":{}}},"t":{"df":1,"docs":{"91":{"tf":1.0}}}},"q":{"df":0,"docs":{},"u":{"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"c":{"df":5,"docs":{"105":{"tf":1.0},"16":{"tf":1.0},"49":{"tf":1.7320508075688772},"51":{"tf":1.0},"9":{"tf":1.0}}},"df":0,"docs":{}}}}},"r":{"df":0,"docs":{},"v":{"df":4,"docs":{"101":{"tf":1.0},"7":{"tf":1.0},"83":{"tf":1.0},"95":{"tf":1.0}}}},"t":{"(":{"d":{"df":1,"docs":{"90":{"tf":1.0}}},"df":0,"docs":{}},"df":24,"docs":{"106":{"tf":1.4142135623730951},"112":{"tf":1.0},"119":{"tf":1.0},"124":{"tf":1.4142135623730951},"125":{"tf":2.0},"14":{"tf":6.48074069840786},"142":{"tf":1.7320508075688772},"149":{"tf":1.0},"150":{"tf":1.4142135623730951},"151":{"tf":1.0},"3":{"tf":1.0},"4":{"tf":1.0},"45":{"tf":1.0},"62":{"tf":1.0},"65":{"tf":1.0},"68":{"tf":2.0},"69":{"tf":1.0},"73":{"tf":1.0},"8":{"tf":1.4142135623730951},"80":{"tf":1.4142135623730951},"82":{"tf":1.7320508075688772},"83":{"tf":1.0},"90":{"tf":1.4142135623730951},"92":{"tf":1.0}},"u":{"df":0,"docs":{},"p":{"(":{"&":{"c":{"df":0,"docs":{},"o":{"df":0,"docs":{},"m":{"df":0,"docs":{},"m":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"_":{"df":0,"docs":{},"p":{"df":0,"docs":{},"r":{"df":0,"docs":{},"e":{"df":0,"docs":{},"p":{"df":0,"docs":{},"r":{"df":0,"docs":{},"o":{"c":{"df":0,"docs":{},"e":{"df":0,"docs":{},"s":{"df":0,"docs":{},"s":{"df":0,"docs":{},"e":{"d":{"_":{"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"df":0,"docs":{},"p":{"df":0,"docs":{},"u":{"df":0,"docs":{},"t":{"df":1,"docs":{"35":{"tf":1.4142135623730951}}}}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}}}},"df":0,"docs":{}}}}}}}},"df":1,"docs":{"44":{"tf":1.0}}}}}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":4,"docs":{"22":{"tf":1.4142135623730951},"34":{"tf":1.0},"35":{"tf":1.4142135623730951},"44":{"tf":1.0}}}}},"v":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":7,"docs":{"10":{"tf":1.0},"124":{"tf":1.0},"45":{"tf":1.0},"65":{"tf":1.4142135623730951},"66":{"tf":1.0},"70":{"tf":1.4142135623730951},"80":{"tf":1.4142135623730951}}}}}},"h":{"a":{"2":{"5":{"6":{"(":{"df":1,"docs":{"7":{"tf":1.0}}},"df":1,"docs":{"7":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{},"h":{"a":{"df":0,"docs":{},"r":{"df":1,"docs":{"103":{"tf":1.0}}}},"df":0,"docs":{}},"m":{"df":0,"docs":{},"i":{"df":0,"docs":{},"r":{"df":7,"docs":{"118":{"tf":1.4142135623730951},"39":{"tf":1.0},"40":{"tf":1.0},"42":{"tf":2.0},"89":{"tf":1.0},"94":{"tf":1.0},"95":{"tf":1.0}}}}},"p":{"df":0,"docs":{},"e":{"df":2,"docs":{"73":{"tf":1.0},"9":{"tf":1.0}}}},"r":{"df":0,"docs":{},"e":{"df":4,"docs":{"104":{"tf":1.0},"150":{"tf":2.0},"151":{"tf":1.4142135623730951},"19":{"tf":1.0}}}}},"df":0,"docs":{},"o":{"df":0,"docs":{},"w":{"c":{"a":{"df":0,"docs":{},"s":{"df":1,"docs":{"12":{"tf":1.0}}}},"df":0,"docs":{}},"df":11,"docs":{"114":{"tf":1.0},"14":{"tf":1.0},"53":{"tf":1.0},"54":{"tf":1.7320508075688772},"55":{"tf":1.0},"56":{"tf":1.0},"57":{"tf":1.0},"58":{"tf":1.0},"59":{"tf":2.0},"60":{"tf":1.4142135623730951},"69":{"tf":1.0}},"n":{"df":1,"docs":{"149":{"tf":1.0}}}}},"u":{"df":0,"docs":{},"f":{"df":0,"docs":{},"f":{"df":0,"docs":{},"l":{"df":1,"docs":{"79":{"tf":1.0}}}}}}},"i":{"d":{"df":0,"docs":{},"e":{"df":9,"docs":{"112":{"tf":1.0},"113":{"tf":1.0},"146":{"tf":1.0},"58":{"tf":1.0},"60":{"tf":1.0},"62":{"tf":1.0},"63":{"tf":1.0},"64":{"tf":1.0},"80":{"tf":1.0}}}},"df":0,"docs":{},"g":{"df":0,"docs":{},"n":{"a":{"df":0,"docs":{},"t":{"df":0,"docs":{},"u":{"df":0,"docs":{},"r":{"df":1,"docs":{"151":{"tf":1.0}}}}}},"df":0,"docs":{}}},"m":{"df":0,"docs":{},"i":{"df":0,"docs":{},"l":{"a":{"df":0,"docs":{},"r":{"df":5,"docs":{"114":{"tf":1.0},"14":{"tf":1.4142135623730951},"146":{"tf":1.0},"151":{"tf":1.0},"85":{"tf":1.0}},"l":{"df":0,"docs":{},"i":{"df":5,"docs":{"13":{"tf":1.0},"139":{"tf":1.0},"14":{"tf":1.4142135623730951},"80":{"tf":1.0},"92":{"tf":1.0}}}}}},"df":0,"docs":{}}},"p":{"df":0,"docs":{},"l":{"df":8,"docs":{"110":{"tf":1.0},"117":{"tf":1.0},"118":{"tf":1.0},"128":{"tf":1.0},"20":{"tf":1.0},"44":{"tf":1.4142135623730951},"45":{"tf":1.0},"49":{"tf":1.0}},"e":{"_":{"df":0,"docs":{},"f":{"df":0,"docs":{},"i":{"b":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"a":{"c":{"c":{"df":0,"docs":{},"i":{":":{":":{"df":0,"docs":{},"f":{"df":0,"docs":{},"i":{"b":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"a":{"c":{"c":{"df":0,"docs":{},"i":{"_":{"df":0,"docs":{},"t":{"df":0,"docs":{},"r":{"a":{"c":{"df":0,"docs":{},"e":{"(":{"[":{"df":0,"docs":{},"f":{"df":0,"docs":{},"e":{":":{":":{"df":0,"docs":{},"f":{"df":0,"docs":{},"r":{"df":0,"docs":{},"o":{"df":0,"docs":{},"m":{"(":{"1":{"df":1,"docs":{"104":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}}},"df":0,"docs":{},"r":{"df":2,"docs":{"14":{"tf":1.0},"149":{"tf":1.0}}}},"i":{"c":{"df":2,"docs":{"21":{"tf":1.0},"64":{"tf":1.0}}},"df":4,"docs":{"106":{"tf":1.0},"109":{"tf":1.0},"114":{"tf":1.0},"125":{"tf":1.0}},"f":{"df":2,"docs":{"149":{"tf":1.0},"64":{"tf":1.0}},"i":{"df":1,"docs":{"56":{"tf":1.0}}}}}}},"u":{"df":0,"docs":{},"l":{"df":0,"docs":{},"t":{"a":{"df":0,"docs":{},"n":{"df":3,"docs":{"101":{"tf":1.0},"59":{"tf":1.0},"80":{"tf":1.0}}}},"df":0,"docs":{}}}}},"n":{"df":0,"docs":{},"g":{"df":0,"docs":{},"l":{"df":14,"docs":{"101":{"tf":1.4142135623730951},"12":{"tf":1.7320508075688772},"128":{"tf":1.0},"146":{"tf":1.0},"16":{"tf":1.0},"28":{"tf":1.0},"33":{"tf":1.0},"35":{"tf":1.0},"5":{"tf":1.0},"69":{"tf":1.0},"80":{"tf":1.4142135623730951},"84":{"tf":1.0},"92":{"tf":1.0},"97":{"tf":1.4142135623730951}}}}},"t":{"df":1,"docs":{"13":{"tf":1.0}},"e":{"df":1,"docs":{"0":{"tf":1.0}}},"u":{"a":{"df":0,"docs":{},"t":{"df":2,"docs":{"14":{"tf":1.0},"70":{"tf":1.0}}}},"df":0,"docs":{}}},"z":{"df":0,"docs":{},"e":{"df":9,"docs":{"12":{"tf":1.0},"121":{"tf":1.4142135623730951},"14":{"tf":1.0},"141":{"tf":1.0},"147":{"tf":2.6457513110645907},"23":{"tf":1.0},"3":{"tf":2.0},"36":{"tf":1.0},"80":{"tf":1.0}}}}},"k":{"df":0,"docs":{},"e":{"df":0,"docs":{},"t":{"c":{"df":0,"docs":{},"h":{"df":1,"docs":{"79":{"tf":1.0}}}},"df":0,"docs":{}}},"i":{"df":0,"docs":{},"p":{"df":1,"docs":{"102":{"tf":1.0}}}}},"l":{"df":0,"docs":{},"i":{"df":0,"docs":{},"g":{"df":0,"docs":{},"h":{"df":0,"docs":{},"t":{"df":0,"docs":{},"l":{"df":0,"docs":{},"i":{"df":1,"docs":{"59":{"tf":1.0}}}}}}}},"o":{"df":0,"docs":{},"w":{"df":2,"docs":{"3":{"tf":1.0},"98":{"tf":1.0}}}}},"m":{"a":{"df":0,"docs":{},"l":{"df":0,"docs":{},"l":{"df":2,"docs":{"151":{"tf":1.0},"74":{"tf":1.0}},"e":{"df":0,"docs":{},"r":{"df":2,"docs":{"123":{"tf":1.0},"142":{"tf":1.0}}},"s":{"df":0,"docs":{},"t":{"df":1,"docs":{"142":{"tf":1.0}}}}}}}},"df":0,"docs":{}},"o":{"df":0,"docs":{},"l":{"df":0,"docs":{},"u":{"df":0,"docs":{},"t":{"df":4,"docs":{"101":{"tf":1.0},"14":{"tf":1.0},"4":{"tf":1.0},"44":{"tf":1.0}}}},"v":{"df":2,"docs":{"146":{"tf":1.0},"95":{"tf":1.0}}}},"m":{"df":0,"docs":{},"e":{"df":0,"docs":{},"h":{"df":0,"docs":{},"o":{"df":0,"docs":{},"w":{"df":2,"docs":{"114":{"tf":1.0},"70":{"tf":1.0}}}}},"o":{"df":0,"docs":{},"n":{"df":1,"docs":{"114":{"tf":1.0}}}},"t":{"df":0,"docs":{},"h":{"df":6,"docs":{"130":{"tf":1.0},"14":{"tf":1.4142135623730951},"146":{"tf":2.23606797749979},"45":{"tf":1.0},"50":{"tf":1.0},"95":{"tf":1.0}}},"i":{"df":0,"docs":{},"m":{"df":2,"docs":{"48":{"tf":1.0},"52":{"tf":1.0}}}}},"w":{"df":0,"docs":{},"h":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":1,"docs":{"21":{"tf":1.0}}}}}}}},"p":{"df":0,"docs":{},"h":{"df":0,"docs":{},"i":{"df":0,"docs":{},"s":{"df":0,"docs":{},"t":{"df":1,"docs":{"12":{"tf":1.0}}}}}}},"r":{"df":0,"docs":{},"t":{"df":6,"docs":{"132":{"tf":1.4142135623730951},"135":{"tf":1.7320508075688772},"138":{"tf":1.0},"14":{"tf":1.0},"147":{"tf":2.23606797749979},"75":{"tf":1.0}}}},"u":{"df":0,"docs":{},"n":{"d":{"df":3,"docs":{"7":{"tf":1.0},"79":{"tf":1.4142135623730951},"88":{"tf":1.0}}},"df":0,"docs":{}},"r":{"c":{"df":3,"docs":{"118":{"tf":1.0},"127":{"tf":1.0},"34":{"tf":1.0}}},"df":0,"docs":{}}}},"p":{"a":{"c":{"df":0,"docs":{},"e":{"df":1,"docs":{"149":{"tf":1.0}}}},"df":0,"docs":{}},"df":0,"docs":{},"e":{"a":{"df":0,"docs":{},"k":{"df":1,"docs":{"16":{"tf":1.0}}}},"c":{"df":0,"docs":{},"i":{"a":{"df":0,"docs":{},"l":{"df":2,"docs":{"120":{"tf":1.0},"151":{"tf":1.0}}}},"df":0,"docs":{},"f":{"df":14,"docs":{"100":{"tf":1.0},"106":{"tf":1.0},"108":{"tf":1.0},"109":{"tf":1.0},"114":{"tf":1.0},"13":{"tf":1.4142135623730951},"146":{"tf":1.0},"151":{"tf":1.7320508075688772},"19":{"tf":1.0},"49":{"tf":1.0},"70":{"tf":1.4142135623730951},"73":{"tf":1.0},"91":{"tf":1.0},"94":{"tf":1.0}},"i":{"df":7,"docs":{"105":{"tf":1.0},"106":{"tf":1.0},"107":{"tf":1.4142135623730951},"109":{"tf":1.4142135623730951},"128":{"tf":1.0},"146":{"tf":1.0},"44":{"tf":1.0}}}}}},"df":0,"docs":{}},"l":{"df":0,"docs":{},"i":{"df":0,"docs":{},"t":{"df":2,"docs":{"151":{"tf":1.0},"82":{"tf":1.0}}}}}},"q":{"df":0,"docs":{},"u":{"a":{"df":0,"docs":{},"r":{"df":1,"docs":{"45":{"tf":1.0}}},"s":{"df":0,"docs":{},"h":{"df":2,"docs":{"12":{"tf":1.0},"14":{"tf":1.0}}}}},"df":0,"docs":{}}},"r":{"c":{"df":1,"docs":{"146":{"tf":1.0}}},"df":2,"docs":{"35":{"tf":1.4142135623730951},"44":{"tf":1.0}}},"t":{"a":{"c":{"df":0,"docs":{},"k":{"df":2,"docs":{"14":{"tf":1.0},"147":{"tf":1.7320508075688772}}}},"df":0,"docs":{},"g":{"df":0,"docs":{},"e":{"df":1,"docs":{"145":{"tf":1.4142135623730951}}}},"n":{"d":{"df":2,"docs":{"13":{"tf":1.0},"28":{"tf":1.0}}},"df":0,"docs":{}},"r":{"df":0,"docs":{},"k":{"df":19,"docs":{"103":{"tf":1.0},"104":{"tf":1.0},"109":{"tf":1.7320508075688772},"123":{"tf":1.0},"142":{"tf":1.0},"46":{"tf":1.4142135623730951},"47":{"tf":1.0},"49":{"tf":1.0},"50":{"tf":1.4142135623730951},"51":{"tf":1.0},"61":{"tf":1.0},"69":{"tf":1.0},"71":{"tf":1.0},"73":{"tf":1.0},"74":{"tf":1.4142135623730951},"75":{"tf":1.0},"80":{"tf":1.0},"84":{"tf":1.0},"87":{"tf":1.4142135623730951}},"w":{"a":{"df":0,"docs":{},"r":{"df":1,"docs":{"103":{"tf":1.0}},"e":{"'":{"df":1,"docs":{"126":{"tf":1.0}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"t":{"df":12,"docs":{"109":{"tf":1.0},"14":{"tf":1.4142135623730951},"149":{"tf":1.0},"151":{"tf":1.0},"16":{"tf":1.0},"40":{"tf":1.0},"50":{"tf":1.0},"69":{"tf":1.4142135623730951},"8":{"tf":1.0},"82":{"tf":1.0},"94":{"tf":1.0},"95":{"tf":1.0}}}},"t":{"df":0,"docs":{},"e":{"df":12,"docs":{"118":{"tf":1.7320508075688772},"127":{"tf":2.0},"133":{"tf":1.0},"144":{"tf":1.4142135623730951},"146":{"tf":1.7320508075688772},"149":{"tf":1.0},"40":{"tf":2.23606797749979},"48":{"tf":1.0},"70":{"tf":3.0},"84":{"tf":1.0},"88":{"tf":1.7320508075688772},"94":{"tf":1.0}},"m":{"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"df":0,"docs":{},"t":{"df":5,"docs":{"11":{"tf":1.4142135623730951},"14":{"tf":1.0},"146":{"tf":1.4142135623730951},"34":{"tf":1.0},"84":{"tf":1.0}}}}}}}}},"df":0,"docs":{},"e":{"df":0,"docs":{},"p":{"df":27,"docs":{"102":{"tf":1.0},"107":{"tf":1.7320508075688772},"111":{"tf":1.0},"113":{"tf":1.0},"127":{"tf":2.0},"128":{"tf":1.4142135623730951},"130":{"tf":1.7320508075688772},"131":{"tf":1.0},"133":{"tf":4.123105625617661},"136":{"tf":2.449489742783178},"139":{"tf":1.4142135623730951},"144":{"tf":1.0},"146":{"tf":2.449489742783178},"147":{"tf":1.4142135623730951},"149":{"tf":1.4142135623730951},"16":{"tf":1.0},"31":{"tf":1.0},"32":{"tf":1.0},"48":{"tf":1.0},"49":{"tf":1.0},"51":{"tf":2.0},"52":{"tf":1.0},"53":{"tf":1.4142135623730951},"57":{"tf":1.0},"61":{"tf":1.0},"70":{"tf":1.0},"95":{"tf":2.0}}}},"i":{"c":{"df":0,"docs":{},"k":{"df":1,"docs":{"7":{"tf":1.0}}}},"df":0,"docs":{},"l":{"df":0,"docs":{},"l":{"df":3,"docs":{"0":{"tf":1.0},"125":{"tf":1.0},"58":{"tf":1.0}}}}},"o":{"df":0,"docs":{},"n":{"df":0,"docs":{},"e":{"df":4,"docs":{"126":{"tf":1.4142135623730951},"127":{"tf":1.7320508075688772},"130":{"tf":1.0},"131":{"tf":1.0}}}},"r":{"df":0,"docs":{},"e":{"df":5,"docs":{"136":{"tf":1.0},"146":{"tf":1.4142135623730951},"151":{"tf":1.0},"35":{"tf":2.0},"70":{"tf":1.0}}}}},"r":{"a":{"df":0,"docs":{},"i":{"df":0,"docs":{},"g":{"df":0,"docs":{},"h":{"df":0,"docs":{},"t":{"df":0,"docs":{},"f":{"df":0,"docs":{},"o":{"df":0,"docs":{},"r":{"df":0,"docs":{},"w":{"a":{"df":0,"docs":{},"r":{"d":{"df":4,"docs":{"132":{"tf":1.0},"14":{"tf":1.4142135623730951},"146":{"tf":1.0},"151":{"tf":1.0}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}}}}}},"t":{"df":0,"docs":{},"e":{"df":0,"docs":{},"g":{"df":0,"docs":{},"i":{"df":1,"docs":{"40":{"tf":1.0}}}}}}},"df":0,"docs":{},"i":{"c":{"df":0,"docs":{},"t":{"df":0,"docs":{},"l":{"df":0,"docs":{},"i":{"df":1,"docs":{"73":{"tf":1.4142135623730951}}}}}},"df":0,"docs":{},"n":{"df":0,"docs":{},"g":{"df":3,"docs":{"41":{"tf":1.0},"88":{"tf":1.7320508075688772},"91":{"tf":1.0}}}}},"o":{"df":0,"docs":{},"n":{"df":0,"docs":{},"g":{"df":3,"docs":{"42":{"tf":1.0},"94":{"tf":1.0},"95":{"tf":1.0}}}}},"u":{"c":{"df":0,"docs":{},"t":{"df":8,"docs":{"106":{"tf":1.4142135623730951},"107":{"tf":1.0},"108":{"tf":1.0},"109":{"tf":1.4142135623730951},"110":{"tf":1.0},"114":{"tf":1.4142135623730951},"118":{"tf":1.0},"35":{"tf":3.3166247903554}},"u":{"df":0,"docs":{},"r":{"df":2,"docs":{"146":{"tf":1.0},"83":{"tf":1.0}}}}}},"df":0,"docs":{}}},"u":{"df":0,"docs":{},"f":{"df":0,"docs":{},"f":{"df":1,"docs":{"56":{"tf":1.0}}}}}},"u":{"b":{"c":{"df":0,"docs":{},"o":{"df":0,"docs":{},"l":{"df":0,"docs":{},"u":{"df":0,"docs":{},"m":{"df":0,"docs":{},"n":{"df":4,"docs":{"146":{"tf":1.4142135623730951},"148":{"tf":1.0},"150":{"tf":1.7320508075688772},"151":{"tf":1.0}}}}}}}},"df":2,"docs":{"133":{"tf":1.7320508075688772},"134":{"tf":1.0}},"g":{"df":0,"docs":{},"r":{"df":0,"docs":{},"o":{"df":0,"docs":{},"u":{"df":0,"docs":{},"p":{"df":1,"docs":{"38":{"tf":1.0}}}}}}},"m":{"a":{"df":0,"docs":{},"t":{"df":0,"docs":{},"r":{"df":0,"docs":{},"i":{"df":0,"docs":{},"x":{"df":2,"docs":{"142":{"tf":1.7320508075688772},"147":{"tf":1.7320508075688772}}}}}}},"df":0,"docs":{}},"p":{"df":0,"docs":{},"r":{"df":0,"docs":{},"o":{"df":0,"docs":{},"g":{"df":0,"docs":{},"r":{"a":{"df":0,"docs":{},"m":{"df":1,"docs":{"151":{"tf":1.4142135623730951}}}},"df":0,"docs":{}}}}}},"s":{"c":{"df":0,"docs":{},"r":{"df":0,"docs":{},"i":{"df":0,"docs":{},"p":{"df":0,"docs":{},"t":{"df":1,"docs":{"28":{"tf":1.4142135623730951}}}}}}},"df":0,"docs":{},"e":{"c":{"df":0,"docs":{},"t":{"df":1,"docs":{"146":{"tf":1.4142135623730951}}}},"df":0,"docs":{},"q":{"df":0,"docs":{},"u":{"df":3,"docs":{"11":{"tf":1.0},"114":{"tf":1.0},"40":{"tf":1.0}}}},"t":{"df":3,"docs":{"128":{"tf":1.4142135623730951},"150":{"tf":1.0},"90":{"tf":1.4142135623730951}}}}},"t":{"a":{"b":{"df":0,"docs":{},"l":{"df":1,"docs":{"127":{"tf":1.0}}}},"df":0,"docs":{}},"df":0,"docs":{},"r":{"a":{"c":{"df":0,"docs":{},"t":{"df":1,"docs":{"9":{"tf":1.4142135623730951}}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"c":{"c":{"df":0,"docs":{},"e":{"df":1,"docs":{"78":{"tf":1.0}}}},"df":0,"docs":{},"h":{"df":23,"docs":{"100":{"tf":1.0},"109":{"tf":1.0},"11":{"tf":1.0},"124":{"tf":1.4142135623730951},"13":{"tf":1.0},"14":{"tf":3.3166247903554},"144":{"tf":1.0},"151":{"tf":1.0},"16":{"tf":2.23606797749979},"19":{"tf":1.0},"20":{"tf":1.7320508075688772},"26":{"tf":1.0},"44":{"tf":1.0},"45":{"tf":1.4142135623730951},"5":{"tf":1.0},"66":{"tf":1.0},"70":{"tf":1.0},"71":{"tf":1.0},"73":{"tf":1.7320508075688772},"79":{"tf":1.0},"88":{"tf":1.0},"94":{"tf":1.7320508075688772},"95":{"tf":1.0}}}},"df":0,"docs":{},"f":{"df":0,"docs":{},"f":{"df":0,"docs":{},"i":{"df":0,"docs":{},"x":{"df":1,"docs":{"35":{"tf":1.0}}}}}},"m":{"df":6,"docs":{"114":{"tf":1.0},"125":{"tf":1.0},"19":{"tf":1.0},"49":{"tf":1.0},"52":{"tf":1.0},"65":{"tf":1.0}},"m":{"a":{"df":0,"docs":{},"n":{"d":{"df":1,"docs":{"59":{"tf":1.4142135623730951}}},"df":0,"docs":{}},"r":{"df":1,"docs":{"61":{"tf":1.0}},"i":{"df":8,"docs":{"131":{"tf":1.0},"138":{"tf":1.0},"14":{"tf":1.0},"146":{"tf":1.0},"150":{"tf":1.0},"61":{"tf":1.0},"69":{"tf":1.0},"81":{"tf":1.0}}}}},"df":0,"docs":{}}},"p":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":0,"docs":{},"s":{"c":{"df":0,"docs":{},"r":{"df":0,"docs":{},"i":{"df":0,"docs":{},"p":{"df":0,"docs":{},"t":{"df":1,"docs":{"95":{"tf":1.4142135623730951}}}}}}},"df":0,"docs":{}}}},"p":{"df":0,"docs":{},"o":{"df":0,"docs":{},"r":{"df":0,"docs":{},"t":{"df":3,"docs":{"107":{"tf":1.0},"121":{"tf":1.0},"34":{"tf":1.0}}}},"s":{"df":5,"docs":{"14":{"tf":2.23606797749979},"45":{"tf":1.0},"73":{"tf":1.4142135623730951},"74":{"tf":1.4142135623730951},"79":{"tf":1.0}}}}}},"r":{"df":0,"docs":{},"e":{"df":2,"docs":{"125":{"tf":1.0},"151":{"tf":1.0}}}}},"w":{"df":0,"docs":{},"e":{"df":0,"docs":{},"e":{"df":0,"docs":{},"p":{"df":1,"docs":{"64":{"tf":1.0}}}}}},"y":{"df":0,"docs":{},"m":{"b":{"df":0,"docs":{},"o":{"df":0,"docs":{},"l":{"df":3,"docs":{"36":{"tf":1.0},"5":{"tf":2.0},"9":{"tf":1.0}}}}},"df":0,"docs":{},"m":{"df":0,"docs":{},"e":{"df":0,"docs":{},"t":{"df":0,"docs":{},"r":{"df":1,"docs":{"73":{"tf":1.0}}}}}}},"s":{"df":0,"docs":{},"t":{"df":0,"docs":{},"e":{"df":0,"docs":{},"m":{"'":{"df":1,"docs":{"70":{"tf":1.0}}},".":{"a":{"df":0,"docs":{},"s":{"df":0,"docs":{},"s":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":0,"docs":{},"t":{"_":{"df":0,"docs":{},"e":{"df":0,"docs":{},"q":{"(":{"&":{"df":0,"docs":{},"i":{"df":2,"docs":{"44":{"tf":1.0},"45":{"tf":1.0}}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}}}}}},"df":0,"docs":{},"i":{"df":0,"docs":{},"f":{"_":{"df":0,"docs":{},"e":{"df":0,"docs":{},"l":{"df":0,"docs":{},"s":{"df":0,"docs":{},"e":{"(":{"&":{"df":0,"docs":{},"e":{"df":0,"docs":{},"x":{"df":0,"docs":{},"p":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"df":0,"docs":{},"t":{"_":{"b":{"df":0,"docs":{},"i":{"df":0,"docs":{},"t":{"df":0,"docs":{},"s":{"[":{"df":0,"docs":{},"i":{"df":1,"docs":{"45":{"tf":1.0}}}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}}}}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}}}},"df":0,"docs":{}}},"m":{"df":0,"docs":{},"u":{"df":0,"docs":{},"l":{"(":{"&":{"df":0,"docs":{},"r":{"df":0,"docs":{},"e":{"df":0,"docs":{},"s":{"df":0,"docs":{},"u":{"df":0,"docs":{},"l":{"df":0,"docs":{},"t":{"df":1,"docs":{"45":{"tf":1.4142135623730951}}}}}}}},"x":{"df":1,"docs":{"44":{"tf":1.0}}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"n":{"df":0,"docs":{},"e":{"df":0,"docs":{},"w":{"_":{"c":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"df":0,"docs":{},"s":{"df":0,"docs":{},"t":{"a":{"df":0,"docs":{},"n":{"df":0,"docs":{},"t":{"(":{"df":0,"docs":{},"f":{"df":0,"docs":{},"i":{"df":0,"docs":{},"e":{"df":0,"docs":{},"l":{"d":{"df":0,"docs":{},"e":{"df":0,"docs":{},"l":{"df":0,"docs":{},"e":{"df":0,"docs":{},"m":{"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"df":0,"docs":{},"t":{":":{":":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"df":1,"docs":{"45":{"tf":1.0}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}}}}}}},"df":0,"docs":{}}}}}},"df":0,"docs":{}}}},"df":0,"docs":{}}}}}},"df":0,"docs":{},"p":{"df":0,"docs":{},"u":{"b":{"df":0,"docs":{},"l":{"df":0,"docs":{},"i":{"c":{"_":{"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"df":0,"docs":{},"p":{"df":0,"docs":{},"u":{"df":0,"docs":{},"t":{"df":2,"docs":{"44":{"tf":1.4142135623730951},"45":{"tf":1.4142135623730951}}}}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"u":{"3":{"2":{"(":{"&":{"df":0,"docs":{},"e":{"df":0,"docs":{},"x":{"df":0,"docs":{},"p":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"df":1,"docs":{"45":{"tf":1.0}}}}}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"v":{"a":{"df":0,"docs":{},"r":{"df":0,"docs":{},"i":{"df":2,"docs":{"44":{"tf":1.0},"45":{"tf":1.0}}}}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"p":{"df":0,"docs":{},"u":{"b":{"df":0,"docs":{},"l":{"df":0,"docs":{},"i":{"c":{"_":{"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"df":0,"docs":{},"p":{"df":0,"docs":{},"u":{"df":0,"docs":{},"t":{"_":{"df":0,"docs":{},"v":{"a":{"df":0,"docs":{},"l":{"df":0,"docs":{},"u":{"df":0,"docs":{},"e":{"df":0,"docs":{},"s":{"(":{"&":{"a":{"df":0,"docs":{},"s":{"df":0,"docs":{},"s":{"df":0,"docs":{},"i":{"df":0,"docs":{},"g":{"df":0,"docs":{},"n":{"df":1,"docs":{"44":{"tf":1.0}}}}}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"s":{"df":0,"docs":{},"o":{"df":0,"docs":{},"l":{"df":0,"docs":{},"v":{"df":0,"docs":{},"e":{"(":{"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"df":0,"docs":{},"p":{"df":0,"docs":{},"u":{"df":0,"docs":{},"t":{"df":0,"docs":{},"s":{")":{".":{"df":0,"docs":{},"u":{"df":0,"docs":{},"n":{"df":0,"docs":{},"w":{"df":0,"docs":{},"r":{"a":{"df":0,"docs":{},"p":{"df":1,"docs":{"44":{"tf":1.0}}}},"df":0,"docs":{}}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}}}}}},"df":0,"docs":{}}}}}}},"df":17,"docs":{"103":{"tf":1.0},"104":{"tf":1.0},"118":{"tf":1.0},"119":{"tf":1.0},"144":{"tf":1.0},"146":{"tf":1.0},"151":{"tf":1.0},"4":{"tf":1.4142135623730951},"42":{"tf":1.0},"43":{"tf":1.0},"44":{"tf":2.8284271247461903},"45":{"tf":2.23606797749979},"46":{"tf":1.0},"48":{"tf":1.4142135623730951},"49":{"tf":1.0},"70":{"tf":1.0},"95":{"tf":1.0}}}}}}},"σ":{"1":{"df":1,"docs":{"14":{"tf":1.0}},"​":{"]":{"1":{"df":0,"docs":{},"​":{",":{"[":{"df":0,"docs":{},"s":{"df":0,"docs":{},"σ":{"2":{"df":0,"docs":{},"​":{"]":{"1":{"df":0,"docs":{},"​":{",":{"[":{"df":0,"docs":{},"s":{"df":0,"docs":{},"σ":{"3":{"]":{"1":{"df":0,"docs":{},"​":{",":{"[":{"df":0,"docs":{},"q":{"df":0,"docs":{},"l":{"df":0,"docs":{},"​":{"]":{"1":{"df":0,"docs":{},"​":{",":{"[":{"df":0,"docs":{},"q":{"df":0,"docs":{},"r":{"df":0,"docs":{},"​":{"]":{"1":{"df":0,"docs":{},"​":{",":{"[":{"df":0,"docs":{},"q":{"df":0,"docs":{},"m":{"df":0,"docs":{},"​":{"]":{"1":{"df":0,"docs":{},"​":{",":{"[":{"df":0,"docs":{},"q":{"df":0,"docs":{},"o":{"df":0,"docs":{},"​":{"]":{"1":{"df":0,"docs":{},"​":{",":{"[":{"df":0,"docs":{},"q":{"c":{"df":0,"docs":{},"​":{"]":{"1":{"df":1,"docs":{"24":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{},"​":{"]":{"1":{"df":0,"docs":{},"​":{",":{"[":{"df":0,"docs":{},"q":{"df":0,"docs":{},"l":{"df":0,"docs":{},"​":{"]":{"1":{"df":0,"docs":{},"​":{",":{"[":{"df":0,"docs":{},"q":{"df":0,"docs":{},"r":{"df":0,"docs":{},"​":{"]":{"1":{"df":0,"docs":{},"​":{",":{"[":{"df":0,"docs":{},"q":{"df":0,"docs":{},"m":{"df":0,"docs":{},"​":{"]":{"1":{"df":0,"docs":{},"​":{",":{"[":{"df":0,"docs":{},"q":{"df":0,"docs":{},"o":{"df":0,"docs":{},"​":{"]":{"1":{"df":0,"docs":{},"​":{",":{"[":{"df":0,"docs":{},"q":{"c":{"df":0,"docs":{},"​":{"]":{"1":{"df":1,"docs":{"31":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"2":{"df":1,"docs":{"14":{"tf":1.0}}},"3":{"df":1,"docs":{"14":{"tf":1.0}}},"df":0,"docs":{},"i":{"df":1,"docs":{"35":{"tf":1.0}}}},"​":{":":{"=":{"df":0,"docs":{},"h":{"df":0,"docs":{},"ω":{"df":0,"docs":{},"ι":{"df":2,"docs":{"94":{"tf":1.0},"95":{"tf":1.0}}}}}},"df":0,"docs":{}},"df":0,"docs":{},"∈":{"[":{"0":{",":{"2":{"df":0,"docs":{},"n":{"+":{"df":0,"docs":{},"l":{"df":0,"docs":{},"−":{"1":{"df":2,"docs":{"94":{"tf":1.0},"95":{"tf":1.0}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}},"t":{"(":{"1":{")":{"df":0,"docs":{},"−":{"df":0,"docs":{},"p":{"(":{"1":{")":{"=":{"1":{"df":0,"docs":{},"−":{"1":{"=":{"0":{"df":1,"docs":{"54":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{},"g":{")":{"df":0,"docs":{},"−":{"df":0,"docs":{},"p":{"(":{"df":0,"docs":{},"g":{")":{"=":{"1":{"df":0,"docs":{},"−":{"1":{"=":{"0":{"df":1,"docs":{"54":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"^":{"0":{"df":2,"docs":{"52":{"tf":1.0},"54":{"tf":1.0}}},"df":0,"docs":{}},"df":2,"docs":{"52":{"tf":1.0},"54":{"tf":1.0}},"i":{")":{"=":{"a":{"df":0,"docs":{},"i":{"df":1,"docs":{"52":{"tf":1.0}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"x":{")":{"'":{"df":1,"docs":{"114":{"tf":1.0}}},"df":0,"docs":{}},"0":{"df":2,"docs":{"112":{"tf":1.4142135623730951},"113":{"tf":1.4142135623730951}},"​":{")":{"df":0,"docs":{},"t":{"(":{"df":0,"docs":{},"x":{"0":{"df":0,"docs":{},"​":{"df":0,"docs":{},"g":{")":{"df":0,"docs":{},"t":{"(":{"df":0,"docs":{},"x":{"0":{"df":0,"docs":{},"​":{"df":0,"docs":{},"g":{"2":{"df":1,"docs":{"114":{"tf":1.0}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{},"g":{"2":{")":{"df":0,"docs":{},"−":{"df":0,"docs":{},"t":{"(":{"df":0,"docs":{},"x":{"0":{"df":0,"docs":{},"​":{"df":0,"docs":{},"g":{")":{"df":0,"docs":{},"−":{"df":0,"docs":{},"t":{"(":{"df":0,"docs":{},"x":{"0":{"df":1,"docs":{"114":{"tf":1.0}}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"_":{"0":{"df":3,"docs":{"60":{"tf":1.7320508075688772},"62":{"tf":1.4142135623730951},"63":{"tf":1.4142135623730951}}},"df":0,"docs":{}},"df":3,"docs":{"35":{"tf":1.7320508075688772},"52":{"tf":1.4142135623730951},"54":{"tf":1.0}},"g":{"2":{")":{"df":0,"docs":{},"−":{"df":0,"docs":{},"t":{"(":{"df":0,"docs":{},"x":{"df":0,"docs":{},"g":{")":{"df":0,"docs":{},"−":{"df":0,"docs":{},"t":{"(":{"df":0,"docs":{},"x":{"df":1,"docs":{"114":{"tf":1.0}}}},"df":0,"docs":{}}}},"df":0,"docs":{}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":1,"docs":{"52":{"tf":1.0}}}},"z":{"df":7,"docs":{"112":{"tf":1.0},"117":{"tf":1.0},"58":{"tf":1.0},"59":{"tf":1.0},"60":{"tf":1.0},"62":{"tf":1.0},"63":{"tf":1.0}},"g":{"2":{"df":2,"docs":{"112":{"tf":1.0},"117":{"tf":1.0}}},"^":{"2":{"df":5,"docs":{"58":{"tf":1.0},"59":{"tf":1.0},"60":{"tf":1.0},"62":{"tf":1.0},"63":{"tf":1.0}}},"df":0,"docs":{}},"df":7,"docs":{"112":{"tf":1.0},"117":{"tf":1.0},"58":{"tf":1.0},"59":{"tf":1.0},"60":{"tf":1.0},"62":{"tf":1.0},"63":{"tf":1.0}}}},"ω":{"0":{")":{"df":0,"docs":{},"t":{"(":{"df":0,"docs":{},"ω":{"1":{")":{"df":0,"docs":{},"…":{"df":0,"docs":{},"t":{"(":{"df":0,"docs":{},"ω":{"1":{"5":{"df":1,"docs":{"114":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"2":{")":{"df":0,"docs":{},"t":{"(":{"df":0,"docs":{},"ω":{"4":{"df":1,"docs":{"114":{"tf":1.0}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{},"g":{"2":{")":{"df":0,"docs":{},"−":{"df":0,"docs":{},"t":{"(":{"df":0,"docs":{},"ω":{"0":{"df":0,"docs":{},"g":{")":{"df":0,"docs":{},"−":{"df":0,"docs":{},"t":{"(":{"df":0,"docs":{},"ω":{"0":{")":{"df":0,"docs":{},"t":{"(":{"df":0,"docs":{},"ω":{"df":0,"docs":{},"g":{"2":{")":{"df":0,"docs":{},"−":{"df":0,"docs":{},"t":{"(":{"df":0,"docs":{},"ω":{"df":0,"docs":{},"g":{")":{"df":0,"docs":{},"−":{"df":0,"docs":{},"t":{"(":{"df":0,"docs":{},"ω":{")":{"df":0,"docs":{},"t":{"(":{"df":0,"docs":{},"ω":{"2":{"df":0,"docs":{},"g":{"2":{")":{"df":0,"docs":{},"−":{"df":0,"docs":{},"t":{"(":{"df":0,"docs":{},"ω":{"2":{"df":0,"docs":{},"g":{")":{"df":0,"docs":{},"−":{"df":0,"docs":{},"t":{"(":{"df":0,"docs":{},"ω":{"2":{")":{"df":0,"docs":{},"⋮":{"df":0,"docs":{},"t":{"(":{"df":0,"docs":{},"ω":{"1":{"5":{"df":0,"docs":{},"g":{"2":{")":{"df":0,"docs":{},"−":{"df":0,"docs":{},"t":{"(":{"df":0,"docs":{},"ω":{"1":{"5":{"df":0,"docs":{},"g":{")":{"df":0,"docs":{},"−":{"df":0,"docs":{},"t":{"(":{"df":0,"docs":{},"ω":{"1":{"5":{"df":1,"docs":{"114":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"2":{"df":0,"docs":{},"i":{")":{":":{"0":{"df":0,"docs":{},"≤":{"df":0,"docs":{},"i":{"df":0,"docs":{},"≤":{"1":{"5":{"df":0,"docs":{},"}":{"=":{"df":0,"docs":{},"{":{"df":0,"docs":{},"t":{"(":{"df":0,"docs":{},"g":{"df":0,"docs":{},"i":{")":{":":{"0":{"df":0,"docs":{},"≤":{"df":0,"docs":{},"i":{"df":0,"docs":{},"≤":{"7":{"df":1,"docs":{"68":{"tf":1.0}}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"4":{")":{"df":0,"docs":{},"−":{"df":0,"docs":{},"t":{"(":{"df":0,"docs":{},"ω":{"2":{")":{"df":0,"docs":{},"−":{"df":0,"docs":{},"t":{"(":{"df":0,"docs":{},"ω":{"0":{")":{"df":0,"docs":{},"t":{"(":{"df":0,"docs":{},"ω":{"5":{")":{"df":0,"docs":{},"−":{"df":0,"docs":{},"t":{"(":{"df":0,"docs":{},"ω":{"3":{")":{"df":0,"docs":{},"−":{"df":0,"docs":{},"t":{"(":{"df":0,"docs":{},"ω":{")":{"df":0,"docs":{},"t":{"(":{"df":0,"docs":{},"ω":{"6":{")":{"df":0,"docs":{},"−":{"df":0,"docs":{},"t":{"(":{"df":0,"docs":{},"ω":{"4":{")":{"df":0,"docs":{},"−":{"df":0,"docs":{},"t":{"(":{"df":0,"docs":{},"ω":{"2":{")":{"df":0,"docs":{},"⋮":{"df":0,"docs":{},"t":{"(":{"df":0,"docs":{},"ω":{"3":{")":{"df":0,"docs":{},"−":{"df":0,"docs":{},"t":{"(":{"df":0,"docs":{},"ω":{")":{"df":0,"docs":{},"−":{"df":0,"docs":{},"t":{"(":{"df":0,"docs":{},"ω":{"1":{"5":{"df":1,"docs":{"114":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"0":{",":{"df":0,"docs":{},"i":{"df":1,"docs":{"20":{"tf":1.0}}},"j":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"s":{"df":0,"docs":{},"σ":{"1":{"df":0,"docs":{},"​":{"(":{"df":0,"docs":{},"ω":{"df":0,"docs":{},"i":{")":{"df":0,"docs":{},"β":{"+":{"df":0,"docs":{},"γ":{")":{"(":{"df":0,"docs":{},"t":{"0":{",":{"df":0,"docs":{},"j":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"s":{"df":0,"docs":{},"σ":{"2":{"df":0,"docs":{},"​":{"(":{"df":0,"docs":{},"ω":{"df":0,"docs":{},"i":{")":{"df":0,"docs":{},"β":{"+":{"df":0,"docs":{},"γ":{")":{"(":{"df":0,"docs":{},"t":{"0":{",":{"df":0,"docs":{},"j":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"s":{"df":0,"docs":{},"σ":{"3":{"df":0,"docs":{},"​":{"(":{"df":0,"docs":{},"ω":{"df":0,"docs":{},"i":{")":{"df":0,"docs":{},"β":{"+":{"df":0,"docs":{},"γ":{")":{":":{"0":{"df":0,"docs":{},"≤":{"df":0,"docs":{},"i":{"<":{"df":0,"docs":{},"n":{"df":1,"docs":{"14":{"tf":1.0}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"ω":{"df":0,"docs":{},"i":{"df":0,"docs":{},"β":{"+":{"df":0,"docs":{},"γ":{")":{"(":{"df":0,"docs":{},"t":{"1":{",":{"df":0,"docs":{},"j":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"ω":{"df":0,"docs":{},"i":{"df":0,"docs":{},"k":{"1":{"df":0,"docs":{},"​":{"df":0,"docs":{},"β":{"+":{"df":0,"docs":{},"γ":{")":{"(":{"df":0,"docs":{},"t":{"2":{",":{"df":0,"docs":{},"j":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"ω":{"df":0,"docs":{},"i":{"df":0,"docs":{},"k":{"2":{"df":0,"docs":{},"​":{"df":0,"docs":{},"β":{"+":{"df":0,"docs":{},"γ":{")":{":":{"0":{"df":0,"docs":{},"≤":{"df":0,"docs":{},"i":{"<":{"df":0,"docs":{},"n":{"df":1,"docs":{"14":{"tf":1.0}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}}},"df":0,"docs":{}}}},"df":4,"docs":{"136":{"tf":1.0},"142":{"tf":1.0},"146":{"tf":2.0},"147":{"tf":1.0}}},"1":{"df":5,"docs":{"136":{"tf":1.0},"142":{"tf":1.0},"146":{"tf":1.7320508075688772},"147":{"tf":1.0},"16":{"tf":1.0}}},"2":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"t":{"3":{"df":1,"docs":{"16":{"tf":1.0}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"=":{"df":0,"docs":{},"t":{"df":0,"docs":{},"l":{"df":0,"docs":{},"o":{"df":0,"docs":{},"′":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"x":{"df":0,"docs":{},"n":{"+":{"2":{"df":0,"docs":{},"t":{"df":0,"docs":{},"m":{"df":0,"docs":{},"i":{"d":{"df":0,"docs":{},"′":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"x":{"2":{"(":{"df":0,"docs":{},"n":{"+":{"2":{")":{"df":0,"docs":{},"t":{"df":0,"docs":{},"h":{"df":0,"docs":{},"i":{"df":1,"docs":{"26":{"tf":1.0}}}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}}}}}},"_":{"1":{"(":{"df":0,"docs":{},"z":{"df":0,"docs":{},"g":{"df":1,"docs":{"66":{"tf":1.0}}}}},"df":0,"docs":{}},"2":{"(":{"df":0,"docs":{},"z":{"df":1,"docs":{"66":{"tf":1.0}}}},"df":0,"docs":{}},"df":0,"docs":{},"h":{"df":0,"docs":{},"i":{"_":{"1":{"df":1,"docs":{"35":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}}},"i":{"(":{"df":0,"docs":{},"x":{"df":1,"docs":{"65":{"tf":1.0}}}},"df":0,"docs":{}},"l":{"df":0,"docs":{},"o":{"_":{"1":{"df":1,"docs":{"35":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}}},"m":{"df":0,"docs":{},"i":{"d":{"_":{"1":{"df":1,"docs":{"35":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"z":{"df":0,"docs":{},"e":{"df":0,"docs":{},"t":{"a":{"df":1,"docs":{"35":{"tf":1.0}}},"df":0,"docs":{}}}}},"a":{"b":{"df":0,"docs":{},"l":{"df":9,"docs":{"108":{"tf":1.7320508075688772},"119":{"tf":1.0},"127":{"tf":1.0},"137":{"tf":1.0},"145":{"tf":1.0},"48":{"tf":1.0},"49":{"tf":1.4142135623730951},"70":{"tf":1.0},"9":{"tf":1.0}}}},"c":{"df":0,"docs":{},"k":{"df":0,"docs":{},"l":{"df":1,"docs":{"64":{"tf":1.0}}}}},"df":0,"docs":{},"k":{"df":0,"docs":{},"e":{"df":23,"docs":{"104":{"tf":1.4142135623730951},"113":{"tf":2.0},"123":{"tf":1.0},"13":{"tf":1.4142135623730951},"14":{"tf":2.0},"149":{"tf":1.0},"151":{"tf":1.4142135623730951},"19":{"tf":1.7320508075688772},"20":{"tf":1.7320508075688772},"34":{"tf":1.0},"41":{"tf":1.0},"44":{"tf":1.0},"51":{"tf":1.0},"52":{"tf":1.4142135623730951},"56":{"tf":1.0},"63":{"tf":1.4142135623730951},"68":{"tf":1.4142135623730951},"7":{"tf":1.4142135623730951},"70":{"tf":1.4142135623730951},"74":{"tf":1.0},"8":{"tf":1.0},"80":{"tf":1.0},"9":{"tf":1.0}},"n":{"df":1,"docs":{"20":{"tf":1.0}}}}},"l":{"df":0,"docs":{},"k":{"df":4,"docs":{"105":{"tf":1.0},"107":{"tf":1.0},"116":{"tf":1.0},"52":{"tf":1.4142135623730951}}}}},"df":27,"docs":{"11":{"tf":1.0},"112":{"tf":1.7320508075688772},"114":{"tf":1.4142135623730951},"13":{"tf":1.0},"14":{"tf":1.7320508075688772},"142":{"tf":1.4142135623730951},"147":{"tf":1.4142135623730951},"16":{"tf":2.449489742783178},"20":{"tf":1.4142135623730951},"23":{"tf":1.0},"24":{"tf":1.0},"26":{"tf":1.0},"28":{"tf":1.0},"32":{"tf":1.7320508075688772},"34":{"tf":1.4142135623730951},"35":{"tf":1.4142135623730951},"36":{"tf":1.4142135623730951},"52":{"tf":1.7320508075688772},"60":{"tf":1.0},"62":{"tf":1.7320508075688772},"68":{"tf":2.23606797749979},"69":{"tf":1.4142135623730951},"70":{"tf":2.0},"83":{"tf":1.4142135623730951},"84":{"tf":1.7320508075688772},"91":{"tf":1.7320508075688772},"94":{"tf":1.0}},"e":{"c":{"df":0,"docs":{},"h":{"df":0,"docs":{},"n":{"df":0,"docs":{},"i":{"c":{"df":1,"docs":{"52":{"tf":1.0}}},"df":0,"docs":{},"q":{"df":0,"docs":{},"u":{"df":1,"docs":{"88":{"tf":1.0}}}}}}}},"df":0,"docs":{},"l":{"df":0,"docs":{},"l":{"df":3,"docs":{"109":{"tf":1.0},"125":{"tf":1.0},"49":{"tf":1.0}}}},"m":{"df":0,"docs":{},"p":{"df":0,"docs":{},"o":{"df":0,"docs":{},"r":{"a":{"df":0,"docs":{},"r":{"df":0,"docs":{},"i":{"df":3,"docs":{"142":{"tf":1.0},"146":{"tf":1.4142135623730951},"147":{"tf":1.0}},"l":{"df":0,"docs":{},"i":{"df":1,"docs":{"34":{"tf":1.4142135623730951}}}}}}},"df":0,"docs":{}}}}},"r":{"df":0,"docs":{},"m":{"df":14,"docs":{"114":{"tf":1.4142135623730951},"123":{"tf":1.0},"125":{"tf":1.0},"128":{"tf":1.0},"14":{"tf":1.0},"144":{"tf":1.4142135623730951},"16":{"tf":1.0},"20":{"tf":1.0},"52":{"tf":1.4142135623730951},"65":{"tf":1.0},"66":{"tf":1.0},"67":{"tf":1.4142135623730951},"80":{"tf":1.0},"85":{"tf":1.0}},"i":{"df":0,"docs":{},"n":{"df":0,"docs":{},"o":{"df":0,"docs":{},"l":{"df":0,"docs":{},"o":{"df":0,"docs":{},"g":{"df":1,"docs":{"34":{"tf":1.0}}}}}}}}}},"s":{"df":0,"docs":{},"t":{"_":{"c":{"df":0,"docs":{},"o":{"df":0,"docs":{},"m":{"df":0,"docs":{},"m":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"_":{"df":0,"docs":{},"p":{"df":0,"docs":{},"r":{"df":0,"docs":{},"e":{"df":0,"docs":{},"p":{"df":0,"docs":{},"r":{"df":0,"docs":{},"o":{"c":{"df":0,"docs":{},"e":{"df":0,"docs":{},"s":{"df":0,"docs":{},"s":{"df":0,"docs":{},"e":{"d":{"_":{"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"df":0,"docs":{},"p":{"df":0,"docs":{},"u":{"df":0,"docs":{},"t":{"_":{"2":{"df":1,"docs":{"35":{"tf":1.4142135623730951}}},"df":0,"docs":{}},"df":0,"docs":{}}}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}}}},"df":0,"docs":{}}}}}}}},"df":0,"docs":{}}}}}}},"df":0,"docs":{},"p":{"df":0,"docs":{},"r":{"df":0,"docs":{},"o":{"df":0,"docs":{},"v":{"df":0,"docs":{},"e":{"_":{"df":0,"docs":{},"f":{"df":0,"docs":{},"i":{"b":{"df":1,"docs":{"104":{"tf":1.0}}},"df":0,"docs":{}}}},"df":0,"docs":{}}}}}},"s":{"df":0,"docs":{},"r":{"df":0,"docs":{},"s":{"(":{"c":{"df":0,"docs":{},"o":{"df":0,"docs":{},"m":{"df":0,"docs":{},"m":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{".":{"df":0,"docs":{},"n":{"df":1,"docs":{"44":{"tf":1.0}}}},"_":{"df":0,"docs":{},"p":{"df":0,"docs":{},"r":{"df":0,"docs":{},"e":{"df":0,"docs":{},"p":{"df":0,"docs":{},"r":{"df":0,"docs":{},"o":{"c":{"df":0,"docs":{},"e":{"df":0,"docs":{},"s":{"df":0,"docs":{},"s":{"df":0,"docs":{},"e":{"d":{"_":{"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"df":0,"docs":{},"p":{"df":0,"docs":{},"u":{"df":0,"docs":{},"t":{".":{"df":0,"docs":{},"n":{"df":1,"docs":{"35":{"tf":1.4142135623730951}}}},"df":0,"docs":{}}}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}}}},"df":0,"docs":{}}}}}}}},"df":0,"docs":{}}}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"w":{"df":0,"docs":{},"i":{"df":0,"docs":{},"t":{"df":0,"docs":{},"n":{"df":0,"docs":{},"e":{"df":0,"docs":{},"s":{"df":0,"docs":{},"s":{"_":{"2":{"(":{"df":0,"docs":{},"x":{"df":1,"docs":{"35":{"tf":1.7320508075688772}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}}}}}}},"df":5,"docs":{"104":{"tf":1.0},"114":{"tf":1.0},"34":{"tf":1.0},"35":{"tf":1.4142135623730951},"81":{"tf":1.0}},"h":{"a":{"df":0,"docs":{},"s":{"df":0,"docs":{},"h":{"df":1,"docs":{"110":{"tf":1.0}}}}},"df":0,"docs":{}},"r":{"a":{"df":0,"docs":{},"n":{"d":{"df":0,"docs":{},"o":{"df":0,"docs":{},"m":{"df":0,"docs":{},"f":{"df":0,"docs":{},"i":{"df":0,"docs":{},"e":{"df":0,"docs":{},"l":{"d":{"df":0,"docs":{},"g":{"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":2,"docs":{"35":{"tf":1.4142135623730951},"44":{"tf":1.0}}}}}}}},"df":0,"docs":{}}}}}}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}},"h":{"a":{"df":0,"docs":{},"n":{"df":0,"docs":{},"k":{"df":2,"docs":{"103":{"tf":1.0},"81":{"tf":1.0}}}},"t":{"'":{"df":10,"docs":{"142":{"tf":1.4142135623730951},"146":{"tf":1.0},"147":{"tf":1.0},"151":{"tf":1.0},"16":{"tf":1.0},"46":{"tf":1.0},"54":{"tf":1.0},"79":{"tf":1.0},"9":{"tf":1.0},"95":{"tf":1.0}}},"df":0,"docs":{}}},"df":17,"docs":{"112":{"tf":1.0},"123":{"tf":1.0},"124":{"tf":2.8284271247461903},"125":{"tf":1.7320508075688772},"14":{"tf":3.0},"142":{"tf":1.0},"16":{"tf":1.0},"23":{"tf":1.0},"35":{"tf":1.0},"49":{"tf":1.4142135623730951},"52":{"tf":1.7320508075688772},"62":{"tf":1.0},"65":{"tf":1.0},"68":{"tf":2.23606797749979},"70":{"tf":1.7320508075688772},"91":{"tf":1.0},"92":{"tf":2.0}},"e":{"df":0,"docs":{},"o":{"df":0,"docs":{},"r":{"df":0,"docs":{},"e":{"df":0,"docs":{},"m":{"df":1,"docs":{"73":{"tf":1.0}}}}}},"r":{"df":0,"docs":{},"e":{"'":{"df":6,"docs":{"102":{"tf":1.0},"116":{"tf":1.0},"14":{"tf":1.0},"146":{"tf":1.0},"22":{"tf":1.0},"7":{"tf":1.0}}},"df":0,"docs":{},"f":{"df":0,"docs":{},"o":{"df":0,"docs":{},"r":{"df":8,"docs":{"101":{"tf":1.0},"12":{"tf":1.0},"20":{"tf":1.0},"36":{"tf":1.0},"38":{"tf":1.0},"41":{"tf":1.0},"66":{"tf":1.0},"94":{"tf":1.0}}}}}}},"y":{"'":{"df":0,"docs":{},"r":{"df":2,"docs":{"3":{"tf":1.0},"68":{"tf":1.0}}}},"df":0,"docs":{}}},"i":{"df":1,"docs":{"26":{"tf":1.0}},"n":{"df":0,"docs":{},"g":{"df":16,"docs":{"104":{"tf":1.0},"109":{"tf":1.4142135623730951},"111":{"tf":1.0},"113":{"tf":1.0},"124":{"tf":1.0},"14":{"tf":1.7320508075688772},"16":{"tf":1.0},"21":{"tf":1.0},"49":{"tf":1.4142135623730951},"50":{"tf":1.0},"52":{"tf":1.0},"53":{"tf":1.0},"56":{"tf":1.0},"58":{"tf":1.0},"59":{"tf":1.0},"68":{"tf":1.0}}},"k":{"df":4,"docs":{"118":{"tf":1.4142135623730951},"151":{"tf":1.4142135623730951},"52":{"tf":1.0},"70":{"tf":1.0}}}},"r":{"d":{"_":{"df":0,"docs":{},"r":{"df":0,"docs":{},"o":{"df":0,"docs":{},"w":{"[":{"0":{"df":1,"docs":{"107":{"tf":1.0}}},"df":0,"docs":{}},"df":2,"docs":{"107":{"tf":1.0},"114":{"tf":1.0}}}}}},"df":1,"docs":{"13":{"tf":1.0}}},"df":0,"docs":{}}},"o":{"df":0,"docs":{},"s":{"df":0,"docs":{},"e":{"df":3,"docs":{"102":{"tf":1.0},"14":{"tf":1.0},"3":{"tf":1.0}}}},"u":{"df":0,"docs":{},"g":{"df":0,"docs":{},"h":{"df":6,"docs":{"114":{"tf":1.0},"118":{"tf":1.0},"49":{"tf":1.0},"52":{"tf":1.0},"57":{"tf":1.0},"66":{"tf":1.0}}}}}},"r":{"df":0,"docs":{},"e":{"df":0,"docs":{},"e":{"df":16,"docs":{"107":{"tf":1.0},"12":{"tf":1.0},"128":{"tf":1.0},"130":{"tf":1.4142135623730951},"141":{"tf":1.0},"142":{"tf":1.4142135623730951},"144":{"tf":1.4142135623730951},"149":{"tf":1.0},"150":{"tf":1.0},"21":{"tf":1.4142135623730951},"3":{"tf":1.0},"44":{"tf":1.0},"58":{"tf":1.0},"69":{"tf":1.0},"84":{"tf":1.0},"9":{"tf":1.0}}}},"o":{"df":0,"docs":{},"u":{"df":0,"docs":{},"g":{"df":0,"docs":{},"h":{"df":8,"docs":{"105":{"tf":1.0},"106":{"tf":1.0},"111":{"tf":1.0},"113":{"tf":1.0},"4":{"tf":1.0},"46":{"tf":1.0},"48":{"tf":1.4142135623730951},"51":{"tf":1.0}},"o":{"df":0,"docs":{},"u":{"df":0,"docs":{},"t":{"df":4,"docs":{"118":{"tf":1.7320508075688772},"124":{"tf":1.4142135623730951},"49":{"tf":1.0},"7":{"tf":1.0}}}}}}}}}},"u":{"df":3,"docs":{"57":{"tf":1.0},"58":{"tf":1.0},"83":{"tf":1.0}}}},"i":{",":{"df":0,"docs":{},"j":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"η":{"3":{"df":0,"docs":{},"i":{"+":{"df":0,"docs":{},"j":{"df":0,"docs":{},"β":{"+":{"df":0,"docs":{},"γ":{":":{"(":{"df":0,"docs":{},"i":{",":{"df":0,"docs":{},"j":{")":{"df":0,"docs":{},"∈":{"df":0,"docs":{},"i":{"df":1,"docs":{"14":{"tf":1.0}}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"k":{"+":{"df":0,"docs":{},"l":{"df":0,"docs":{},"β":{"+":{"df":0,"docs":{},"γ":{":":{"(":{"df":0,"docs":{},"i":{",":{"df":0,"docs":{},"j":{")":{"df":0,"docs":{},"∈":{"df":0,"docs":{},"i":{",":{"df":0,"docs":{},"σ":{"(":{"(":{"df":0,"docs":{},"i":{",":{"df":0,"docs":{},"j":{")":{")":{"=":{"(":{"df":0,"docs":{},"k":{",":{"df":0,"docs":{},"l":{"df":1,"docs":{"14":{"tf":1.0}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"=":{"df":0,"docs":{},"t":{"df":0,"docs":{},"j":{"df":0,"docs":{},"​":{"(":{"df":0,"docs":{},"g":{"df":0,"docs":{},"i":{"df":2,"docs":{"83":{"tf":1.0},"84":{"tf":1.0}}}}},"df":0,"docs":{}}},"k":{",":{"df":0,"docs":{},"l":{"df":4,"docs":{"11":{"tf":1.0},"13":{"tf":1.0},"14":{"tf":1.0},"16":{"tf":1.0}}}},"df":0,"docs":{}},"σ":{"(":{"(":{"df":0,"docs":{},"i":{",":{"df":0,"docs":{},"j":{"df":1,"docs":{"14":{"tf":1.0}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}}},"df":1,"docs":{"16":{"tf":1.0}},"j":{"df":1,"docs":{"70":{"tf":1.0}}},"m":{"df":0,"docs":{},"e":{"df":10,"docs":{"101":{"tf":1.0},"118":{"tf":2.0},"133":{"tf":1.0},"146":{"tf":1.4142135623730951},"149":{"tf":1.0},"22":{"tf":1.0},"3":{"tf":1.4142135623730951},"45":{"tf":1.0},"59":{"tf":1.0},"80":{"tf":2.0}}}}},"j":{"df":5,"docs":{"70":{"tf":1.7320508075688772},"83":{"tf":1.4142135623730951},"84":{"tf":1.0},"94":{"tf":1.7320508075688772},"95":{"tf":2.6457513110645907}},"​":{"(":{"df":0,"docs":{},"g":{"df":0,"docs":{},"i":{")":{"=":{"df":0,"docs":{},"m":{"^":{"df":0,"docs":{},"i":{",":{"df":0,"docs":{},"j":{"df":1,"docs":{"94":{"tf":1.0}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"t":{"df":0,"docs":{},"i":{",":{"df":0,"docs":{},"j":{"df":1,"docs":{"94":{"tf":1.0}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}},"z":{"df":3,"docs":{"85":{"tf":1.0},"94":{"tf":1.4142135623730951},"95":{"tf":1.0}}}},"z":{"df":3,"docs":{"85":{"tf":1.0},"94":{"tf":1.4142135623730951},"95":{"tf":1.4142135623730951}}}},",":{"df":0,"docs":{},"τ":{"df":0,"docs":{},"j":{"df":0,"docs":{},"z":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"τ":{"df":0,"docs":{},"j":{"df":0,"docs":{},"g":{"df":0,"docs":{},"z":{"df":0,"docs":{},"​":{":":{"0":{"df":0,"docs":{},"≤":{"df":0,"docs":{},"j":{"<":{"df":0,"docs":{},"m":{"df":0,"docs":{},"}":{",":{"df":0,"docs":{},"h":{"1":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"η":{"1":{"df":0,"docs":{},"z":{"2":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"h":{"2":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"η":{"2":{"df":0,"docs":{},"z":{"2":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"{":{"df":0,"docs":{},"p":{"df":0,"docs":{},"k":{"df":0,"docs":{},"​":{":":{"1":{"df":0,"docs":{},"≤":{"df":0,"docs":{},"k":{"<":{"df":0,"docs":{},"n":{"df":0,"docs":{},"}":{",":{"df":0,"docs":{},"π":{",":{"df":0,"docs":{},"y":{",":{"df":0,"docs":{},"{":{"(":{"df":0,"docs":{},"τ":{"df":0,"docs":{},"j":{"df":0,"docs":{},"υ":{"df":0,"docs":{},"s":{"df":0,"docs":{},"​":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"t":{"df":0,"docs":{},"j":{"df":0,"docs":{},"​":{")":{":":{"0":{"df":0,"docs":{},"≤":{"df":0,"docs":{},"j":{"<":{"df":0,"docs":{},"m":{",":{"0":{"df":0,"docs":{},"≤":{"df":0,"docs":{},"s":{"<":{"df":0,"docs":{},"q":{"df":0,"docs":{},"}":{",":{"df":0,"docs":{},"{":{"(":{"df":0,"docs":{},"η":{"1":{"df":0,"docs":{},"υ":{"df":0,"docs":{},"s":{"df":0,"docs":{},"​":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"h":{"1":{"df":0,"docs":{},"​":{")":{":":{"0":{"df":0,"docs":{},"≤":{"df":0,"docs":{},"s":{"<":{"df":0,"docs":{},"q":{"df":0,"docs":{},"}":{"df":0,"docs":{},"{":{"(":{"df":0,"docs":{},"η":{"2":{"df":0,"docs":{},"υ":{"df":0,"docs":{},"s":{"df":0,"docs":{},"​":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"h":{"2":{"df":0,"docs":{},"​":{")":{":":{"0":{"df":0,"docs":{},"≤":{"df":0,"docs":{},"s":{"<":{"df":0,"docs":{},"q":{"df":0,"docs":{},"}":{",":{"df":0,"docs":{},"{":{"(":{"df":0,"docs":{},"π":{"df":0,"docs":{},"k":{"df":0,"docs":{},"υ":{"df":0,"docs":{},"s":{"2":{"df":0,"docs":{},"k":{"df":0,"docs":{},"​":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"p":{"df":0,"docs":{},"k":{"df":0,"docs":{},"​":{")":{":":{"1":{"df":0,"docs":{},"≤":{"df":0,"docs":{},"k":{"<":{"df":0,"docs":{},"n":{",":{"0":{"df":0,"docs":{},"≤":{"df":0,"docs":{},"s":{"<":{"df":0,"docs":{},"q":{"df":0,"docs":{},"}":{",":{"df":0,"docs":{},"{":{"(":{"df":0,"docs":{},"τ":{"df":0,"docs":{},"j":{"df":0,"docs":{},"−":{"df":0,"docs":{},"υ":{"df":0,"docs":{},"s":{"df":0,"docs":{},"​":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"t":{"df":0,"docs":{},"j":{"df":0,"docs":{},"′":{"df":0,"docs":{},"​":{")":{":":{"0":{"df":0,"docs":{},"≤":{"df":0,"docs":{},"j":{"<":{"df":0,"docs":{},"m":{",":{"0":{"df":0,"docs":{},"≤":{"df":0,"docs":{},"s":{"<":{"df":0,"docs":{},"q":{"df":0,"docs":{},"}":{",":{"df":0,"docs":{},"{":{"(":{"df":0,"docs":{},"η":{"1":{"df":0,"docs":{},"−":{"df":0,"docs":{},"υ":{"df":0,"docs":{},"s":{"df":0,"docs":{},"​":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"h":{"1":{"df":0,"docs":{},"′":{"df":0,"docs":{},"​":{")":{":":{"0":{"df":0,"docs":{},"≤":{"df":0,"docs":{},"s":{"<":{"df":0,"docs":{},"q":{"df":0,"docs":{},"}":{"df":0,"docs":{},"{":{"(":{"df":0,"docs":{},"η":{"2":{"df":0,"docs":{},"−":{"df":0,"docs":{},"υ":{"df":0,"docs":{},"s":{"df":0,"docs":{},"​":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"h":{"2":{"df":0,"docs":{},"′":{"df":0,"docs":{},"​":{")":{":":{"0":{"df":0,"docs":{},"≤":{"df":0,"docs":{},"s":{"<":{"df":0,"docs":{},"q":{"df":0,"docs":{},"}":{",":{"df":0,"docs":{},"{":{"(":{"df":0,"docs":{},"π":{"df":0,"docs":{},"k":{"df":0,"docs":{},"−":{"df":0,"docs":{},"υ":{"df":0,"docs":{},"s":{"2":{"df":0,"docs":{},"k":{"df":0,"docs":{},"​":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"p":{"df":0,"docs":{},"k":{"df":0,"docs":{},"′":{"df":0,"docs":{},"​":{")":{":":{"1":{"df":0,"docs":{},"≤":{"df":0,"docs":{},"k":{"<":{"df":0,"docs":{},"n":{",":{"0":{"df":0,"docs":{},"≤":{"df":0,"docs":{},"s":{"<":{"df":0,"docs":{},"q":{"df":1,"docs":{"95":{"tf":1.0}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}}}},"df":0,"docs":{}}}}},"df":0,"docs":{}}}}}}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}}}},"df":0,"docs":{}}}}}}}}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}}}}},"df":0,"docs":{}}}}}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}}}}}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}}}}}},"df":0,"docs":{}}}}}},"]":{",":{"df":0,"docs":{},"t":{"df":0,"docs":{},"j":{"df":0,"docs":{},"​":{"(":{"df":0,"docs":{},"z":{")":{",":{"df":0,"docs":{},"t":{"df":0,"docs":{},"j":{"df":0,"docs":{},"​":{"(":{"df":0,"docs":{},"g":{"df":0,"docs":{},"z":{")":{":":{"0":{"df":0,"docs":{},"≤":{"df":0,"docs":{},"j":{"<":{"df":0,"docs":{},"m":{"df":0,"docs":{},"}":{",":{"[":{"df":0,"docs":{},"h":{"1":{"df":0,"docs":{},"​":{"]":{",":{"df":0,"docs":{},"h":{"1":{"df":0,"docs":{},"​":{"(":{"df":0,"docs":{},"z":{"2":{")":{",":{"[":{"df":0,"docs":{},"h":{"2":{"df":0,"docs":{},"​":{"]":{",":{"df":0,"docs":{},"h":{"2":{"df":0,"docs":{},"​":{"(":{"df":0,"docs":{},"z":{"2":{")":{",":{"df":0,"docs":{},"{":{"[":{"df":0,"docs":{},"p":{"df":0,"docs":{},"k":{"df":0,"docs":{},"​":{"]":{":":{"1":{"df":0,"docs":{},"≤":{"df":0,"docs":{},"k":{"<":{"df":0,"docs":{},"n":{"df":0,"docs":{},"}":{",":{"df":0,"docs":{},"p":{"df":0,"docs":{},"n":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"y":{",":{"df":0,"docs":{},"{":{"df":0,"docs":{},"o":{"df":0,"docs":{},"p":{"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"(":{"df":0,"docs":{},"t":{"df":0,"docs":{},"j":{"df":0,"docs":{},"​":{"(":{"d":{"df":0,"docs":{},"l":{"d":{"df":0,"docs":{},"e":{"df":0,"docs":{},"​":{")":{",":{"df":0,"docs":{},"υ":{"df":0,"docs":{},"s":{"df":0,"docs":{},"​":{")":{":":{"0":{"df":0,"docs":{},"≤":{"df":0,"docs":{},"j":{"<":{"df":0,"docs":{},"m":{",":{"0":{"df":0,"docs":{},"≤":{"df":0,"docs":{},"s":{"<":{"df":0,"docs":{},"q":{"df":0,"docs":{},"}":{",":{"df":0,"docs":{},"{":{"df":0,"docs":{},"o":{"df":0,"docs":{},"p":{"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"(":{"df":0,"docs":{},"h":{"1":{"df":0,"docs":{},"​":{"(":{"d":{"df":0,"docs":{},"l":{"d":{"df":0,"docs":{},"e":{"df":0,"docs":{},"​":{")":{",":{"df":0,"docs":{},"υ":{"df":0,"docs":{},"s":{"df":0,"docs":{},"​":{")":{":":{"0":{"df":0,"docs":{},"≤":{"df":0,"docs":{},"s":{"<":{"df":0,"docs":{},"q":{"df":0,"docs":{},"}":{",":{"df":0,"docs":{},"{":{"df":0,"docs":{},"o":{"df":0,"docs":{},"p":{"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"(":{"df":0,"docs":{},"h":{"2":{"df":0,"docs":{},"​":{"(":{"d":{"df":0,"docs":{},"l":{"d":{"df":0,"docs":{},"e":{"df":0,"docs":{},"​":{")":{",":{"df":0,"docs":{},"υ":{"df":0,"docs":{},"s":{"df":0,"docs":{},"​":{")":{":":{"0":{"df":0,"docs":{},"≤":{"df":0,"docs":{},"s":{"<":{"df":0,"docs":{},"q":{"df":0,"docs":{},"}":{",":{"df":0,"docs":{},"{":{"df":0,"docs":{},"o":{"df":0,"docs":{},"p":{"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"(":{"df":0,"docs":{},"p":{"df":0,"docs":{},"k":{"df":0,"docs":{},"​":{"(":{"d":{"df":0,"docs":{},"k":{"df":0,"docs":{},"​":{")":{",":{"df":0,"docs":{},"υ":{"df":0,"docs":{},"s":{"2":{"df":0,"docs":{},"k":{"df":0,"docs":{},"​":{")":{":":{"1":{"df":0,"docs":{},"≤":{"df":0,"docs":{},"k":{"<":{"df":0,"docs":{},"n":{",":{"0":{"df":0,"docs":{},"≤":{"df":0,"docs":{},"s":{"<":{"df":0,"docs":{},"q":{"df":0,"docs":{},"}":{",":{"df":0,"docs":{},"{":{"df":0,"docs":{},"o":{"df":0,"docs":{},"p":{"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"(":{"df":0,"docs":{},"t":{"df":0,"docs":{},"j":{"df":0,"docs":{},"​":{"(":{"d":{"df":0,"docs":{},"l":{"d":{"df":0,"docs":{},"e":{"df":0,"docs":{},"​":{")":{",":{"df":0,"docs":{},"−":{"df":0,"docs":{},"υ":{"df":0,"docs":{},"s":{"df":0,"docs":{},"​":{")":{":":{"0":{"df":0,"docs":{},"≤":{"df":0,"docs":{},"j":{"<":{"df":0,"docs":{},"m":{",":{"0":{"df":0,"docs":{},"≤":{"df":0,"docs":{},"s":{"<":{"df":0,"docs":{},"q":{"df":0,"docs":{},"}":{",":{"df":0,"docs":{},"{":{"df":0,"docs":{},"o":{"df":0,"docs":{},"p":{"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"(":{"df":0,"docs":{},"h":{"1":{"df":0,"docs":{},"​":{"(":{"d":{"df":0,"docs":{},"l":{"d":{"df":0,"docs":{},"e":{"df":0,"docs":{},"​":{")":{",":{"df":0,"docs":{},"−":{"df":0,"docs":{},"υ":{"df":0,"docs":{},"s":{"df":0,"docs":{},"​":{")":{":":{"0":{"df":0,"docs":{},"≤":{"df":0,"docs":{},"s":{"<":{"df":0,"docs":{},"q":{"df":0,"docs":{},"}":{",":{"df":0,"docs":{},"{":{"df":0,"docs":{},"o":{"df":0,"docs":{},"p":{"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"(":{"df":0,"docs":{},"h":{"2":{"df":0,"docs":{},"​":{"(":{"d":{"df":0,"docs":{},"l":{"d":{"df":0,"docs":{},"e":{"df":0,"docs":{},"​":{")":{",":{"df":0,"docs":{},"−":{"df":0,"docs":{},"υ":{"df":0,"docs":{},"s":{"df":0,"docs":{},"​":{")":{":":{"0":{"df":0,"docs":{},"≤":{"df":0,"docs":{},"s":{"<":{"df":0,"docs":{},"q":{"df":0,"docs":{},"}":{",":{"df":0,"docs":{},"{":{"df":0,"docs":{},"o":{"df":0,"docs":{},"p":{"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"(":{"df":0,"docs":{},"p":{"df":0,"docs":{},"k":{"df":0,"docs":{},"​":{"(":{"d":{"df":0,"docs":{},"k":{"df":0,"docs":{},"​":{")":{",":{"df":0,"docs":{},"−":{"df":0,"docs":{},"υ":{"df":0,"docs":{},"s":{"2":{"df":0,"docs":{},"k":{"df":0,"docs":{},"​":{")":{":":{"1":{"df":0,"docs":{},"≤":{"df":0,"docs":{},"k":{"<":{"df":0,"docs":{},"n":{",":{"0":{"df":0,"docs":{},"≤":{"df":0,"docs":{},"s":{"<":{"df":0,"docs":{},"q":{"df":1,"docs":{"94":{"tf":1.0}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}}}}}}},"df":0,"docs":{}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}}}},"df":0,"docs":{}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}}}},"df":0,"docs":{}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}}}}}}},"df":0,"docs":{}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}}}}}}},"df":0,"docs":{}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}}}},"df":0,"docs":{}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}}}},"df":0,"docs":{}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}}}}}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}},"df":0,"docs":{}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}}},":":{"=":{"c":{"df":0,"docs":{},"o":{"df":0,"docs":{},"m":{"df":0,"docs":{},"m":{"df":0,"docs":{},"i":{"df":0,"docs":{},"t":{"(":{"df":0,"docs":{},"t":{"df":0,"docs":{},"j":{"df":0,"docs":{},"​":{"(":{"d":{"df":0,"docs":{},"l":{"df":1,"docs":{"94":{"tf":1.4142135623730951}}}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}}}}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"l":{"df":0,"docs":{},"o":{"df":0,"docs":{},"​":{"]":{"1":{"df":0,"docs":{},"​":{",":{"[":{"df":0,"docs":{},"t":{"df":0,"docs":{},"m":{"df":0,"docs":{},"i":{"d":{"df":0,"docs":{},"​":{"]":{"1":{"df":0,"docs":{},"​":{",":{"[":{"df":0,"docs":{},"t":{"df":0,"docs":{},"h":{"df":0,"docs":{},"i":{"df":0,"docs":{},"​":{"]":{"1":{"df":2,"docs":{"26":{"tf":1.0},"32":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}}}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{},"t":{"df":0,"docs":{},"m":{"df":0,"docs":{},"i":{"d":{"df":0,"docs":{},"​":{"df":0,"docs":{},"t":{"df":0,"docs":{},"h":{"df":0,"docs":{},"i":{"df":0,"docs":{},"​":{"df":0,"docs":{},"​":{"=":{"df":0,"docs":{},"t":{"df":0,"docs":{},"l":{"df":0,"docs":{},"o":{"df":0,"docs":{},"′":{"df":0,"docs":{},"​":{"+":{"b":{"1":{"0":{"df":0,"docs":{},"​":{"df":0,"docs":{},"x":{"df":0,"docs":{},"n":{"+":{"2":{"=":{"df":0,"docs":{},"t":{"df":0,"docs":{},"m":{"df":0,"docs":{},"i":{"d":{"df":0,"docs":{},"′":{"df":0,"docs":{},"​":{"df":0,"docs":{},"−":{"b":{"1":{"0":{"df":0,"docs":{},"​":{"+":{"b":{"1":{"1":{"df":0,"docs":{},"​":{"df":0,"docs":{},"x":{"df":0,"docs":{},"n":{"+":{"2":{"=":{"df":0,"docs":{},"t":{"df":0,"docs":{},"h":{"df":0,"docs":{},"i":{"df":0,"docs":{},"′":{"df":0,"docs":{},"​":{"df":0,"docs":{},"−":{"b":{"1":{"1":{"df":1,"docs":{"26":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}}}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}}}}},"df":0,"docs":{}}}}}}}},"df":0,"docs":{}}}}},"′":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"t":{"df":0,"docs":{},"m":{"df":0,"docs":{},"i":{"d":{"df":1,"docs":{"26":{"tf":1.0}}},"df":0,"docs":{}}}}},"df":0,"docs":{}}}}},"m":{"df":0,"docs":{},"p":{"0":{"df":1,"docs":{"136":{"tf":1.0}}},"1":{"df":1,"docs":{"136":{"tf":1.0}}},"df":0,"docs":{}},"′":{"+":{"1":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"…":{",":{"df":0,"docs":{},"t":{"df":0,"docs":{},"m":{"df":0,"docs":{},"′":{"+":{"df":0,"docs":{},"m":{"df":1,"docs":{"94":{"tf":1.0}}}},"df":0,"docs":{}}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"o":{"df":0,"docs":{},"g":{"df":0,"docs":{},"e":{"df":0,"docs":{},"t":{"df":0,"docs":{},"h":{"df":1,"docs":{"147":{"tf":1.0}}}}}},"o":{"df":0,"docs":{},"l":{"df":5,"docs":{"19":{"tf":1.0},"50":{"tf":1.0},"70":{"tf":1.0},"8":{"tf":1.0},"9":{"tf":1.0}}}},"p":{"df":1,"docs":{"151":{"tf":1.0}}},"t":{"a":{"df":0,"docs":{},"l":{"df":6,"docs":{"133":{"tf":1.0},"135":{"tf":1.4142135623730951},"144":{"tf":1.0},"147":{"tf":1.0},"73":{"tf":1.0},"91":{"tf":1.0}}}},"df":0,"docs":{}},"w":{"a":{"df":0,"docs":{},"r":{"d":{"df":1,"docs":{"50":{"tf":1.0}}},"df":0,"docs":{}}},"df":0,"docs":{}},"y":{"df":3,"docs":{"34":{"tf":1.0},"7":{"tf":2.23606797749979},"9":{"tf":1.0}}}},"p":{"a":{"df":0,"docs":{},"r":{"df":0,"docs":{},"t":{"df":0,"docs":{},"i":{"a":{"df":0,"docs":{},"l":{"df":0,"docs":{},"​":{"(":{"df":0,"docs":{},"ζ":{")":{"=":{"df":0,"docs":{},"t":{"df":1,"docs":{"28":{"tf":1.0}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"+":{"df":0,"docs":{},"υ":{"df":0,"docs":{},"p":{"df":0,"docs":{},"n":{"c":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"υ":{"2":{"a":{"+":{"df":0,"docs":{},"υ":{"3":{"b":{"+":{"df":0,"docs":{},"υ":{"4":{"c":{"+":{"df":0,"docs":{},"υ":{"5":{"df":0,"docs":{},"s":{"df":0,"docs":{},"σ":{"1":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"υ":{"6":{"df":0,"docs":{},"s":{"df":0,"docs":{},"σ":{"2":{"df":1,"docs":{"28":{"tf":1.0}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}},"]":{"1":{"df":1,"docs":{"32":{"tf":1.0}},"​":{"=":{"[":{"df":0,"docs":{},"t":{"df":0,"docs":{},"l":{"df":0,"docs":{},"o":{"df":0,"docs":{},"​":{"]":{"1":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"ζ":{"df":0,"docs":{},"n":{"+":{"2":{"[":{"df":0,"docs":{},"t":{"df":0,"docs":{},"m":{"df":0,"docs":{},"i":{"d":{"df":0,"docs":{},"​":{"]":{"1":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"ζ":{"2":{"(":{"df":0,"docs":{},"n":{"+":{"2":{")":{"[":{"df":0,"docs":{},"t":{"df":0,"docs":{},"h":{"df":0,"docs":{},"i":{"df":0,"docs":{},"​":{"]":{"1":{"df":1,"docs":{"32":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}}}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"r":{"a":{"c":{"df":0,"docs":{},"e":{"'":{"df":2,"docs":{"68":{"tf":1.0},"70":{"tf":1.0}}},".":{"c":{"df":0,"docs":{},"l":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"df":1,"docs":{"108":{"tf":1.0}}}}}},"df":0,"docs":{}},"_":{"c":{"df":0,"docs":{},"o":{"df":0,"docs":{},"l":{"df":0,"docs":{},"u":{"df":0,"docs":{},"m":{"df":0,"docs":{},"n":{"df":1,"docs":{"109":{"tf":1.4142135623730951}}}}}}}},"df":0,"docs":{},"t":{"a":{"b":{"df":0,"docs":{},"l":{"df":0,"docs":{},"e":{".":{"df":0,"docs":{},"n":{"_":{"c":{"df":0,"docs":{},"o":{"df":0,"docs":{},"l":{"df":1,"docs":{"109":{"tf":1.0}}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":1,"docs":{"108":{"tf":1.4142135623730951}}}},"df":63,"docs":{"10":{"tf":1.7320508075688772},"104":{"tf":1.7320508075688772},"105":{"tf":1.4142135623730951},"107":{"tf":1.0},"108":{"tf":2.6457513110645907},"109":{"tf":2.449489742783178},"11":{"tf":1.0},"110":{"tf":1.0},"112":{"tf":1.7320508075688772},"113":{"tf":1.7320508075688772},"114":{"tf":1.4142135623730951},"117":{"tf":1.0},"119":{"tf":1.7320508075688772},"12":{"tf":1.0},"123":{"tf":1.4142135623730951},"125":{"tf":2.0},"127":{"tf":2.449489742783178},"128":{"tf":1.0},"129":{"tf":1.0},"13":{"tf":1.0},"130":{"tf":1.7320508075688772},"131":{"tf":1.0},"133":{"tf":1.7320508075688772},"134":{"tf":1.0},"137":{"tf":1.4142135623730951},"14":{"tf":1.0},"141":{"tf":1.0},"142":{"tf":1.4142135623730951},"143":{"tf":1.0},"144":{"tf":2.0},"145":{"tf":1.7320508075688772},"146":{"tf":3.0},"147":{"tf":1.0},"149":{"tf":2.0},"16":{"tf":1.0},"20":{"tf":1.4142135623730951},"23":{"tf":1.0},"34":{"tf":1.0},"35":{"tf":1.0},"48":{"tf":1.4142135623730951},"49":{"tf":1.7320508075688772},"50":{"tf":1.7320508075688772},"51":{"tf":1.4142135623730951},"52":{"tf":3.0},"53":{"tf":1.0},"54":{"tf":1.4142135623730951},"58":{"tf":1.7320508075688772},"59":{"tf":1.4142135623730951},"60":{"tf":2.0},"62":{"tf":1.4142135623730951},"63":{"tf":1.4142135623730951},"65":{"tf":2.0},"66":{"tf":1.7320508075688772},"68":{"tf":1.4142135623730951},"69":{"tf":1.7320508075688772},"70":{"tf":2.8284271247461903},"8":{"tf":1.0},"83":{"tf":2.449489742783178},"84":{"tf":2.23606797749979},"9":{"tf":2.0},"91":{"tf":2.23606797749979},"94":{"tf":2.0},"95":{"tf":1.0}},"t":{"df":1,"docs":{"108":{"tf":2.23606797749979}}}}},"d":{"df":0,"docs":{},"i":{"df":0,"docs":{},"t":{"df":1,"docs":{"149":{"tf":1.0}}}},"u":{"c":{"df":1,"docs":{"14":{"tf":1.0}}},"df":0,"docs":{}}},"df":0,"docs":{},"i":{"df":0,"docs":{},"t":{"df":1,"docs":{"105":{"tf":1.0}}}},"n":{"df":0,"docs":{},"s":{"c":{"df":0,"docs":{},"r":{"df":0,"docs":{},"i":{"df":0,"docs":{},"p":{"df":0,"docs":{},"t":{"df":16,"docs":{"113":{"tf":1.0},"118":{"tf":1.7320508075688772},"147":{"tf":1.0},"24":{"tf":1.4142135623730951},"25":{"tf":1.4142135623730951},"26":{"tf":1.4142135623730951},"27":{"tf":1.4142135623730951},"28":{"tf":1.0},"31":{"tf":1.4142135623730951},"32":{"tf":2.0},"40":{"tf":2.23606797749979},"42":{"tf":1.0},"88":{"tf":1.0},"89":{"tf":1.4142135623730951},"94":{"tf":4.123105625617661},"95":{"tf":3.872983346207417}}}}}}},"df":0,"docs":{},"f":{"df":0,"docs":{},"o":{"df":0,"docs":{},"r":{"df":0,"docs":{},"m":{"df":7,"docs":{"123":{"tf":1.0},"3":{"tf":1.0},"4":{"tf":1.0},"69":{"tf":1.0},"74":{"tf":1.4142135623730951},"79":{"tf":1.4142135623730951},"99":{"tf":1.0}}}}}},"i":{"df":0,"docs":{},"t":{"df":20,"docs":{"105":{"tf":1.4142135623730951},"107":{"tf":2.0},"109":{"tf":2.23606797749979},"112":{"tf":1.0},"113":{"tf":1.0},"114":{"tf":1.7320508075688772},"125":{"tf":1.0},"146":{"tf":1.0},"149":{"tf":1.0},"49":{"tf":1.0},"50":{"tf":1.0},"53":{"tf":1.0},"55":{"tf":2.0},"56":{"tf":1.0},"58":{"tf":1.0},"62":{"tf":1.0},"66":{"tf":2.0},"70":{"tf":2.0},"84":{"tf":1.7320508075688772},"91":{"tf":1.4142135623730951}},"i":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"_":{"d":{"df":0,"docs":{},"e":{"df":0,"docs":{},"g":{"df":0,"docs":{},"r":{"df":0,"docs":{},"e":{"df":1,"docs":{"109":{"tf":1.4142135623730951}}}}}},"i":{"df":0,"docs":{},"v":{"df":0,"docs":{},"i":{"df":0,"docs":{},"s":{"df":0,"docs":{},"o":{"df":0,"docs":{},"r":{"df":1,"docs":{"114":{"tf":1.0}}}}}}}}},"df":0,"docs":{},"e":{"df":0,"docs":{},"x":{"df":0,"docs":{},"e":{"df":0,"docs":{},"m":{"df":0,"docs":{},"p":{"df":0,"docs":{},"t":{"df":2,"docs":{"109":{"tf":1.4142135623730951},"114":{"tf":1.0}}}}}}}},"o":{"df":0,"docs":{},"f":{"df":0,"docs":{},"f":{"df":0,"docs":{},"s":{"df":0,"docs":{},"e":{"df":0,"docs":{},"t":{"df":1,"docs":{"109":{"tf":1.4142135623730951}}}}}}}}},"df":0,"docs":{}}}}}},"l":{"a":{"df":0,"docs":{},"t":{"df":1,"docs":{"52":{"tf":1.0}}}},"df":0,"docs":{}},"p":{"a":{"df":0,"docs":{},"r":{"df":1,"docs":{"81":{"tf":1.0}}}},"df":0,"docs":{}}}},"v":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":0,"docs":{},"s":{"df":1,"docs":{"80":{"tf":1.0}}}}}}},"df":0,"docs":{},"e":{"a":{"df":0,"docs":{},"t":{"df":3,"docs":{"151":{"tf":1.7320508075688772},"57":{"tf":1.0},"68":{"tf":1.0}}}},"df":0,"docs":{},"e":{"'":{"df":1,"docs":{"72":{"tf":1.0}}},"df":6,"docs":{"101":{"tf":1.4142135623730951},"72":{"tf":1.4142135623730951},"76":{"tf":1.0},"80":{"tf":2.449489742783178},"91":{"tf":1.0},"92":{"tf":2.23606797749979}}}},"i":{"c":{"df":0,"docs":{},"k":{"df":8,"docs":{"115":{"tf":1.0},"123":{"tf":1.0},"130":{"tf":1.0},"14":{"tf":1.7320508075688772},"146":{"tf":1.0},"18":{"tf":1.0},"21":{"tf":1.4142135623730951},"98":{"tf":1.0}}}},"df":5,"docs":{"111":{"tf":1.0},"14":{"tf":1.0},"16":{"tf":1.0},"79":{"tf":1.0},"80":{"tf":1.0}},"v":{"df":0,"docs":{},"i":{"a":{"df":0,"docs":{},"l":{"df":2,"docs":{"14":{"tf":1.0},"146":{"tf":1.4142135623730951}}}},"df":0,"docs":{}}}},"u":{"df":0,"docs":{},"e":{"df":6,"docs":{"10":{"tf":1.0},"104":{"tf":1.0},"14":{"tf":1.4142135623730951},"146":{"tf":1.0},"21":{"tf":1.0},"84":{"tf":1.0}}}}},"u":{"df":0,"docs":{},"p":{"df":0,"docs":{},"l":{"df":2,"docs":{"14":{"tf":1.7320508075688772},"7":{"tf":1.4142135623730951}}}},"r":{"df":0,"docs":{},"n":{"df":4,"docs":{"14":{"tf":1.0},"65":{"tf":1.0},"70":{"tf":1.0},"79":{"tf":1.0}}}}},"w":{"df":0,"docs":{},"i":{"d":{"d":{"df":0,"docs":{},"l":{"df":1,"docs":{"3":{"tf":1.0}}}},"df":0,"docs":{}},"df":0,"docs":{}},"o":{"df":35,"docs":{"101":{"tf":1.0},"104":{"tf":1.0},"105":{"tf":1.0},"107":{"tf":1.0},"108":{"tf":1.0},"109":{"tf":1.4142135623730951},"116":{"tf":1.0},"121":{"tf":1.0},"123":{"tf":1.0},"128":{"tf":1.0},"13":{"tf":2.23606797749979},"133":{"tf":1.0},"14":{"tf":3.0},"142":{"tf":1.0},"145":{"tf":1.0},"146":{"tf":2.23606797749979},"16":{"tf":1.0},"32":{"tf":1.0},"36":{"tf":1.0},"38":{"tf":1.4142135623730951},"40":{"tf":1.0},"44":{"tf":1.4142135623730951},"48":{"tf":1.0},"49":{"tf":1.7320508075688772},"52":{"tf":2.0},"59":{"tf":1.0},"68":{"tf":1.4142135623730951},"71":{"tf":1.0},"73":{"tf":1.0},"74":{"tf":1.0},"75":{"tf":1.0},"8":{"tf":1.0},"84":{"tf":1.4142135623730951},"9":{"tf":1.4142135623730951},"90":{"tf":1.0}}}},"y":{"df":0,"docs":{},"p":{"df":0,"docs":{},"e":{"df":3,"docs":{"10":{"tf":1.0},"52":{"tf":1.0},"70":{"tf":1.0}}},"i":{"c":{"df":1,"docs":{"106":{"tf":1.4142135623730951}}},"df":0,"docs":{}}}},"ˉ":{"=":{"df":0,"docs":{},"t":{"df":1,"docs":{"28":{"tf":1.0}}}},"df":0,"docs":{}},"∈":{"df":0,"docs":{},"f":{"2":{"df":0,"docs":{},"n":{"df":0,"docs":{},"×":{"3":{"3":{"df":1,"docs":{"142":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}}},"u":{"=":{"6":{"df":1,"docs":{"9":{"tf":1.0}}},"df":0,"docs":{}},"df":2,"docs":{"11":{"tf":1.0},"9":{"tf":1.4142135623730951}},"l":{"df":0,"docs":{},"t":{"df":0,"docs":{},"i":{"df":0,"docs":{},"m":{"df":1,"docs":{"50":{"tf":1.0}}}}}},"n":{"b":{"a":{"df":0,"docs":{},"l":{"df":1,"docs":{"146":{"tf":1.4142135623730951}}}},"df":0,"docs":{}},"c":{"df":0,"docs":{},"h":{"a":{"df":0,"docs":{},"n":{"df":0,"docs":{},"g":{"df":1,"docs":{"20":{"tf":1.0}}}}},"df":0,"docs":{}}},"d":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":3,"docs":{"111":{"tf":1.0},"114":{"tf":1.0},"64":{"tf":1.0}},"l":{"df":0,"docs":{},"i":{"df":2,"docs":{"40":{"tf":1.0},"90":{"tf":1.0}}}},"s":{"df":0,"docs":{},"t":{"a":{"df":0,"docs":{},"n":{"d":{"df":7,"docs":{"111":{"tf":1.0},"14":{"tf":1.0},"151":{"tf":1.0},"46":{"tf":1.0},"50":{"tf":1.0},"68":{"tf":1.0},"8":{"tf":1.0}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}}},"df":0,"docs":{},"i":{"df":0,"docs":{},"q":{"df":0,"docs":{},"u":{"df":4,"docs":{"14":{"tf":1.4142135623730951},"20":{"tf":1.0},"73":{"tf":1.7320508075688772},"75":{"tf":1.0}}}},"t":{"df":0,"docs":{},"i":{"df":20,"docs":{"112":{"tf":1.0},"114":{"tf":1.4142135623730951},"123":{"tf":2.23606797749979},"124":{"tf":3.3166247903554},"125":{"tf":2.23606797749979},"14":{"tf":3.0},"16":{"tf":1.0},"23":{"tf":1.0},"34":{"tf":1.0},"35":{"tf":1.0},"36":{"tf":1.0},"38":{"tf":1.0},"5":{"tf":1.0},"52":{"tf":1.7320508075688772},"62":{"tf":1.0},"68":{"tf":2.8284271247461903},"73":{"tf":1.0},"75":{"tf":1.0},"82":{"tf":1.4142135623730951},"91":{"tf":1.0}}}},"v":{"a":{"df":0,"docs":{},"r":{"df":0,"docs":{},"i":{"df":4,"docs":{"71":{"tf":1.0},"75":{"tf":1.0},"83":{"tf":1.0},"84":{"tf":1.4142135623730951}}}}},"df":0,"docs":{}}},"l":{"df":0,"docs":{},"i":{"df":0,"docs":{},"k":{"df":1,"docs":{"79":{"tf":1.0}}}}},"o":{"df":0,"docs":{},"r":{"d":{"df":1,"docs":{"146":{"tf":1.0}}},"df":0,"docs":{}}},"p":{"a":{"c":{"df":0,"docs":{},"k":{"df":1,"docs":{"142":{"tf":1.0}}}},"df":0,"docs":{}},"df":0,"docs":{},"r":{"df":0,"docs":{},"e":{"d":{"df":0,"docs":{},"i":{"c":{"df":0,"docs":{},"t":{"df":2,"docs":{"56":{"tf":1.0},"79":{"tf":1.0}}}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"t":{"df":0,"docs":{},"i":{"df":0,"docs":{},"l":{"df":7,"docs":{"121":{"tf":1.0},"130":{"tf":1.0},"134":{"tf":1.0},"135":{"tf":1.0},"142":{"tf":1.0},"146":{"tf":1.0},"94":{"tf":1.0}}}},"r":{"df":0,"docs":{},"u":{"df":0,"docs":{},"s":{"df":0,"docs":{},"t":{"df":2,"docs":{"4":{"tf":1.0},"7":{"tf":1.0}}}}}}},"u":{"df":0,"docs":{},"s":{"df":4,"docs":{"130":{"tf":2.23606797749979},"134":{"tf":2.23606797749979},"139":{"tf":1.0},"146":{"tf":1.7320508075688772}}}}},"p":{"d":{"a":{"df":0,"docs":{},"t":{"df":5,"docs":{"11":{"tf":1.0},"13":{"tf":1.4142135623730951},"136":{"tf":1.4142135623730951},"146":{"tf":1.4142135623730951},"40":{"tf":1.4142135623730951}}}},"df":0,"docs":{}},"df":8,"docs":{"10":{"tf":1.0},"12":{"tf":1.0},"121":{"tf":1.0},"13":{"tf":1.4142135623730951},"130":{"tf":1.0},"16":{"tf":1.4142135623730951},"71":{"tf":1.0},"84":{"tf":1.0}},"p":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":1,"docs":{"73":{"tf":1.0}}}}}},"s":{"a":{"df":0,"docs":{},"g":{"df":1,"docs":{"35":{"tf":1.0}}}},"df":65,"docs":{"101":{"tf":1.4142135623730951},"102":{"tf":1.4142135623730951},"104":{"tf":1.0},"106":{"tf":1.0},"107":{"tf":1.0},"108":{"tf":1.0},"109":{"tf":2.0},"110":{"tf":1.0},"111":{"tf":1.4142135623730951},"113":{"tf":1.0},"114":{"tf":1.7320508075688772},"116":{"tf":1.0},"118":{"tf":2.0},"119":{"tf":1.0},"121":{"tf":1.0},"123":{"tf":2.449489742783178},"124":{"tf":1.4142135623730951},"125":{"tf":2.0},"128":{"tf":1.0},"13":{"tf":1.0},"130":{"tf":1.0},"134":{"tf":1.4142135623730951},"136":{"tf":1.4142135623730951},"14":{"tf":2.8284271247461903},"142":{"tf":1.0},"144":{"tf":1.4142135623730951},"146":{"tf":4.0},"147":{"tf":1.0},"149":{"tf":1.4142135623730951},"150":{"tf":1.0},"151":{"tf":2.23606797749979},"19":{"tf":1.0},"21":{"tf":1.0},"33":{"tf":1.0},"34":{"tf":1.4142135623730951},"35":{"tf":1.0},"36":{"tf":1.0},"38":{"tf":1.0},"4":{"tf":1.0},"40":{"tf":1.7320508075688772},"41":{"tf":1.0},"44":{"tf":1.4142135623730951},"45":{"tf":2.0},"48":{"tf":1.4142135623730951},"49":{"tf":1.4142135623730951},"5":{"tf":1.4142135623730951},"50":{"tf":2.23606797749979},"57":{"tf":1.0},"59":{"tf":1.0},"60":{"tf":1.0},"63":{"tf":1.0},"68":{"tf":1.0},"7":{"tf":2.23606797749979},"71":{"tf":1.0},"74":{"tf":1.7320508075688772},"75":{"tf":1.0},"8":{"tf":1.7320508075688772},"87":{"tf":1.0},"89":{"tf":1.0},"9":{"tf":1.0},"91":{"tf":1.4142135623730951},"92":{"tf":1.0},"94":{"tf":1.0},"95":{"tf":1.4142135623730951},"97":{"tf":1.0}},"e":{"df":0,"docs":{},"r":{"df":1,"docs":{"50":{"tf":1.0}}}},"i":{"df":0,"docs":{},"z":{"df":1,"docs":{"35":{"tf":1.0}}}},"u":{"a":{"df":0,"docs":{},"l":{"df":9,"docs":{"123":{"tf":1.0},"133":{"tf":1.0},"14":{"tf":1.0},"151":{"tf":1.0},"5":{"tf":1.0},"53":{"tf":1.0},"70":{"tf":1.0},"74":{"tf":1.0},"75":{"tf":1.0}}}},"df":0,"docs":{}}},"t":{"df":0,"docs":{},"i":{"df":0,"docs":{},"l":{"df":1,"docs":{"99":{"tf":1.0}}}}}},"v":{"=":{"9":{"df":1,"docs":{"9":{"tf":1.0}}},"df":0,"docs":{}},"a":{"df":0,"docs":{},"l":{"df":0,"docs":{},"i":{"d":{"df":23,"docs":{"10":{"tf":1.0},"101":{"tf":1.0},"105":{"tf":1.0},"11":{"tf":1.4142135623730951},"125":{"tf":1.0},"13":{"tf":1.0},"14":{"tf":1.0},"146":{"tf":1.4142135623730951},"16":{"tf":1.7320508075688772},"21":{"tf":1.0},"33":{"tf":1.0},"34":{"tf":1.0},"48":{"tf":1.0},"49":{"tf":1.0},"50":{"tf":1.0},"52":{"tf":1.4142135623730951},"69":{"tf":1.4142135623730951},"70":{"tf":1.0},"77":{"tf":1.0},"80":{"tf":1.0},"84":{"tf":1.7320508075688772},"86":{"tf":1.0},"9":{"tf":1.4142135623730951}}},"df":0,"docs":{}},"u":{"a":{"b":{"df":0,"docs":{},"l":{"df":1,"docs":{"103":{"tf":1.0}}}},"df":0,"docs":{}},"df":58,"docs":{"101":{"tf":2.23606797749979},"102":{"tf":1.7320508075688772},"107":{"tf":1.7320508075688772},"108":{"tf":1.4142135623730951},"114":{"tf":1.4142135623730951},"118":{"tf":2.0},"119":{"tf":1.0},"128":{"tf":1.0},"13":{"tf":2.23606797749979},"130":{"tf":3.0},"131":{"tf":1.7320508075688772},"133":{"tf":3.3166247903554},"134":{"tf":1.7320508075688772},"135":{"tf":2.0},"136":{"tf":1.7320508075688772},"137":{"tf":1.0},"139":{"tf":1.0},"14":{"tf":3.3166247903554},"142":{"tf":2.449489742783178},"146":{"tf":5.196152422706632},"147":{"tf":2.23606797749979},"151":{"tf":2.449489742783178},"16":{"tf":1.4142135623730951},"19":{"tf":1.0},"20":{"tf":2.23606797749979},"21":{"tf":2.8284271247461903},"22":{"tf":1.0},"25":{"tf":1.4142135623730951},"3":{"tf":1.0},"32":{"tf":2.0},"33":{"tf":1.4142135623730951},"34":{"tf":1.7320508075688772},"35":{"tf":3.0},"36":{"tf":1.7320508075688772},"42":{"tf":1.0},"49":{"tf":1.4142135623730951},"5":{"tf":1.0},"52":{"tf":1.4142135623730951},"54":{"tf":1.0},"58":{"tf":1.0},"60":{"tf":1.0},"66":{"tf":1.0},"69":{"tf":1.0},"7":{"tf":2.23606797749979},"70":{"tf":1.4142135623730951},"72":{"tf":1.7320508075688772},"73":{"tf":1.0},"75":{"tf":2.0},"77":{"tf":1.4142135623730951},"79":{"tf":1.0},"80":{"tf":1.7320508075688772},"83":{"tf":1.0},"87":{"tf":1.0},"89":{"tf":1.0},"9":{"tf":2.23606797749979},"91":{"tf":2.23606797749979},"94":{"tf":2.0},"95":{"tf":2.8284271247461903}}}},"n":{"df":0,"docs":{},"i":{"df":0,"docs":{},"s":{"df":0,"docs":{},"h":{"df":5,"docs":{"123":{"tf":2.6457513110645907},"125":{"tf":1.7320508075688772},"20":{"tf":1.0},"55":{"tf":1.0},"84":{"tf":1.0}}}}}},"r":{"df":0,"docs":{},"i":{"a":{"b":{"df":0,"docs":{},"l":{"df":11,"docs":{"11":{"tf":1.0},"13":{"tf":1.4142135623730951},"14":{"tf":1.0},"151":{"tf":1.0},"36":{"tf":1.0},"44":{"tf":1.7320508075688772},"45":{"tf":1.7320508075688772},"5":{"tf":1.0},"70":{"tf":1.7320508075688772},"9":{"tf":1.4142135623730951},"95":{"tf":1.0}}}},"df":0,"docs":{},"n":{"df":0,"docs":{},"t":{"df":5,"docs":{"74":{"tf":1.7320508075688772},"77":{"tf":1.0},"80":{"tf":1.0},"96":{"tf":1.0},"97":{"tf":2.0}}}}},"df":0,"docs":{}}}},"df":13,"docs":{"11":{"tf":1.7320508075688772},"12":{"tf":1.0},"13":{"tf":1.4142135623730951},"14":{"tf":2.23606797749979},"141":{"tf":1.0},"142":{"tf":2.0},"146":{"tf":2.449489742783178},"147":{"tf":1.7320508075688772},"16":{"tf":1.4142135623730951},"34":{"tf":1.4142135623730951},"36":{"tf":1.7320508075688772},"8":{"tf":1.0},"9":{"tf":2.0}},"e":{"c":{"!":{"[":{"0":{"df":1,"docs":{"109":{"tf":1.0}}},"1":{"df":2,"docs":{"108":{"tf":1.0},"109":{"tf":1.0}}},"2":{"df":1,"docs":{"109":{"tf":1.0}}},"df":0,"docs":{},"t":{"df":0,"docs":{},"h":{"df":0,"docs":{},"i":{"df":0,"docs":{},"r":{"d":{"_":{"df":0,"docs":{},"r":{"df":0,"docs":{},"o":{"df":0,"docs":{},"w":{"[":{"0":{"df":2,"docs":{"107":{"tf":1.0},"114":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"x":{".":{"c":{"df":0,"docs":{},"l":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"df":1,"docs":{"35":{"tf":1.4142135623730951}}}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"<":{"df":0,"docs":{},"f":{"df":0,"docs":{},"i":{"df":0,"docs":{},"e":{"df":0,"docs":{},"l":{"d":{"df":0,"docs":{},"e":{"df":0,"docs":{},"l":{"df":0,"docs":{},"e":{"df":0,"docs":{},"m":{"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"df":0,"docs":{},"t":{"<":{"df":0,"docs":{},"f":{"df":1,"docs":{"35":{"tf":2.6457513110645907}}},"s":{"df":0,"docs":{},"e":{"df":0,"docs":{},"l":{"df":0,"docs":{},"f":{":":{":":{"df":0,"docs":{},"f":{"df":0,"docs":{},"i":{"df":0,"docs":{},"e":{"df":0,"docs":{},"l":{"d":{"df":2,"docs":{"107":{"tf":1.0},"114":{"tf":1.0}}},"df":0,"docs":{}}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}}}},"df":0,"docs":{}}}}}}}}},"df":0,"docs":{}}}}}},"df":3,"docs":{"108":{"tf":1.0},"109":{"tf":1.0},"116":{"tf":1.0}},"t":{"df":0,"docs":{},"o":{"df":0,"docs":{},"r":{"'":{"df":1,"docs":{"72":{"tf":1.0}}},"df":18,"docs":{"101":{"tf":1.0},"107":{"tf":1.0},"141":{"tf":1.0},"142":{"tf":1.7320508075688772},"147":{"tf":2.449489742783178},"71":{"tf":1.0},"72":{"tf":1.7320508075688772},"73":{"tf":3.1622776601683795},"74":{"tf":2.449489742783178},"75":{"tf":1.0},"76":{"tf":1.4142135623730951},"78":{"tf":1.4142135623730951},"79":{"tf":2.449489742783178},"80":{"tf":2.23606797749979},"82":{"tf":1.0},"90":{"tf":1.4142135623730951},"91":{"tf":1.0},"92":{"tf":2.449489742783178}}}}}},"df":0,"docs":{},"r":{"df":0,"docs":{},"i":{"df":10,"docs":{"118":{"tf":1.0},"123":{"tf":1.0},"14":{"tf":1.7320508075688772},"20":{"tf":1.0},"49":{"tf":1.0},"50":{"tf":1.0},"69":{"tf":1.0},"73":{"tf":1.0},"79":{"tf":1.0},"98":{"tf":1.0}},"f":{"df":3,"docs":{"30":{"tf":1.0},"4":{"tf":1.0},"7":{"tf":1.4142135623730951}},"i":{"c":{"a":{"df":0,"docs":{},"t":{"df":0,"docs":{},"i":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"df":0,"docs":{},"k":{"df":0,"docs":{},"e":{"df":0,"docs":{},"y":{"<":{"df":0,"docs":{},"g":{"1":{"df":0,"docs":{},"p":{"df":0,"docs":{},"o":{"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"df":0,"docs":{},"t":{"df":1,"docs":{"35":{"tf":1.0}}}}}}}},"df":0,"docs":{}}},"df":1,"docs":{"35":{"tf":1.0}}}}}}}}}},"df":0,"docs":{}},"df":59,"docs":{"102":{"tf":2.0},"104":{"tf":2.0},"109":{"tf":1.0},"110":{"tf":1.4142135623730951},"111":{"tf":1.4142135623730951},"112":{"tf":1.0},"113":{"tf":1.7320508075688772},"115":{"tf":1.4142135623730951},"117":{"tf":1.4142135623730951},"118":{"tf":2.23606797749979},"119":{"tf":1.4142135623730951},"127":{"tf":1.0},"13":{"tf":1.4142135623730951},"137":{"tf":1.0},"144":{"tf":1.0},"145":{"tf":1.0},"146":{"tf":2.449489742783178},"147":{"tf":1.4142135623730951},"151":{"tf":1.0},"20":{"tf":1.0},"21":{"tf":3.3166247903554},"32":{"tf":1.0},"33":{"tf":1.7320508075688772},"34":{"tf":1.0},"35":{"tf":2.23606797749979},"4":{"tf":1.0},"44":{"tf":2.0},"48":{"tf":1.0},"50":{"tf":1.0},"53":{"tf":1.0},"55":{"tf":1.0},"56":{"tf":1.0},"57":{"tf":1.7320508075688772},"58":{"tf":1.0},"59":{"tf":1.4142135623730951},"60":{"tf":1.4142135623730951},"61":{"tf":1.0},"62":{"tf":1.7320508075688772},"63":{"tf":1.4142135623730951},"69":{"tf":1.7320508075688772},"7":{"tf":2.0},"70":{"tf":1.4142135623730951},"71":{"tf":1.0},"72":{"tf":1.7320508075688772},"73":{"tf":1.4142135623730951},"74":{"tf":1.7320508075688772},"75":{"tf":2.449489742783178},"77":{"tf":2.23606797749979},"79":{"tf":1.4142135623730951},"8":{"tf":1.0},"80":{"tf":2.6457513110645907},"82":{"tf":1.4142135623730951},"83":{"tf":1.7320508075688772},"84":{"tf":1.4142135623730951},"85":{"tf":1.7320508075688772},"86":{"tf":1.0},"91":{"tf":1.7320508075688772},"94":{"tf":1.0},"95":{"tf":2.8284271247461903}},"e":{"df":0,"docs":{},"r":{"'":{"df":2,"docs":{"35":{"tf":1.0},"95":{"tf":1.0}}},":":{":":{"df":0,"docs":{},"n":{"df":0,"docs":{},"e":{"df":0,"docs":{},"w":{"(":{"df":0,"docs":{},"k":{"df":0,"docs":{},"z":{"df":0,"docs":{},"g":{"df":2,"docs":{"35":{"tf":1.4142135623730951},"44":{"tf":1.0}}}}}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"<":{"df":0,"docs":{},"f":{"df":1,"docs":{"35":{"tf":1.0}}}},"df":0,"docs":{}}}},"y":{"(":{"(":{"df":0,"docs":{},"υ":{"df":0,"docs":{},"s":{"2":{"df":0,"docs":{},"k":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"x":{")":{",":{"df":0,"docs":{},"p":{"df":0,"docs":{},"k":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"p":{"df":0,"docs":{},"k":{"df":1,"docs":{"102":{"tf":1.0}}}}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}},"π":{"df":0,"docs":{},"k":{"df":0,"docs":{},"υ":{"df":0,"docs":{},"s":{"2":{"df":0,"docs":{},"k":{"df":0,"docs":{},"​":{"df":0,"docs":{},"​":{")":{",":{"df":0,"docs":{},"p":{"df":0,"docs":{},"k":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"p":{"df":0,"docs":{},"k":{"df":1,"docs":{"102":{"tf":1.0}}}}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}}},"−":{"df":0,"docs":{},"υ":{"df":0,"docs":{},"s":{"2":{"df":0,"docs":{},"k":{"df":0,"docs":{},"​":{"df":0,"docs":{},"​":{")":{",":{"df":0,"docs":{},"p":{"df":0,"docs":{},"k":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"p":{"df":0,"docs":{},"k":{"df":1,"docs":{"95":{"tf":1.0}}}}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}}}}}}},"df":0,"docs":{}}}},"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"η":{"1":{"df":0,"docs":{},"υ":{"df":0,"docs":{},"s":{"df":0,"docs":{},"​":{"df":0,"docs":{},"​":{")":{",":{"df":0,"docs":{},"h":{"1":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"h":{"1":{"df":1,"docs":{"95":{"tf":1.0}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}}}},"2":{"df":0,"docs":{},"υ":{"df":0,"docs":{},"s":{"df":0,"docs":{},"​":{"df":0,"docs":{},"​":{")":{",":{"df":0,"docs":{},"h":{"2":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"h":{"2":{"df":1,"docs":{"95":{"tf":1.0}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}}}},"df":0,"docs":{}},"τ":{"df":0,"docs":{},"j":{"df":0,"docs":{},"υ":{"df":0,"docs":{},"s":{"df":0,"docs":{},"​":{"df":0,"docs":{},"​":{")":{",":{"df":0,"docs":{},"t":{"df":0,"docs":{},"j":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"t":{"df":0,"docs":{},"j":{"df":1,"docs":{"95":{"tf":1.0}}}}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}}}}}},"df":0,"docs":{}}}},"−":{"df":0,"docs":{},"υ":{"df":0,"docs":{},"s":{"2":{"df":0,"docs":{},"k":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"π":{"df":0,"docs":{},"k":{"df":0,"docs":{},"−":{"df":0,"docs":{},"υ":{"df":0,"docs":{},"s":{"2":{"df":0,"docs":{},"k":{"df":0,"docs":{},"​":{"df":0,"docs":{},"​":{")":{",":{"df":0,"docs":{},"p":{"df":0,"docs":{},"k":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"p":{"df":0,"docs":{},"k":{"df":1,"docs":{"95":{"tf":1.0}}}}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}}}}}}},"df":0,"docs":{}}}},"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"η":{"1":{"df":0,"docs":{},"υ":{"df":0,"docs":{},"s":{"df":0,"docs":{},"​":{"df":0,"docs":{},"​":{")":{",":{"df":0,"docs":{},"h":{"1":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"h":{"1":{"df":1,"docs":{"95":{"tf":1.0}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}}}},"2":{"df":0,"docs":{},"υ":{"df":0,"docs":{},"s":{"df":0,"docs":{},"​":{"df":0,"docs":{},"​":{")":{",":{"df":0,"docs":{},"h":{"2":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"h":{"2":{"df":1,"docs":{"95":{"tf":1.0}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}}}},"df":0,"docs":{}},"τ":{"df":0,"docs":{},"j":{"df":0,"docs":{},"υ":{"df":0,"docs":{},"s":{"df":0,"docs":{},"​":{"df":0,"docs":{},"​":{")":{",":{"df":0,"docs":{},"t":{"df":0,"docs":{},"j":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"t":{"df":0,"docs":{},"j":{"df":1,"docs":{"95":{"tf":1.0}}}}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}}}}}},"df":0,"docs":{}}}}}},"[":{"df":0,"docs":{},"f":{"]":{"1":{"df":1,"docs":{"19":{"tf":1.0}}},"df":0,"docs":{}},"b":{"a":{"df":0,"docs":{},"t":{"c":{"df":0,"docs":{},"h":{"df":0,"docs":{},"​":{"]":{"1":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"π":{"b":{"a":{"df":0,"docs":{},"t":{"c":{"df":0,"docs":{},"h":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"ζ":{",":{"df":0,"docs":{},"f":{"b":{"a":{"df":0,"docs":{},"t":{"c":{"df":0,"docs":{},"h":{"df":1,"docs":{"33":{"tf":1.0}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"z":{"]":{"1":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"π":{"df":0,"docs":{},"s":{"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"df":0,"docs":{},"g":{"df":0,"docs":{},"l":{"df":0,"docs":{},"e":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"ζ":{"df":0,"docs":{},"ω":{",":{"df":0,"docs":{},"z":{"df":1,"docs":{"33":{"tf":1.0}}}},"df":0,"docs":{}}}},"df":0,"docs":{}}}}}}}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"a":{"b":{"df":0,"docs":{},"i":{",":{"df":0,"docs":{},"y":{",":{"df":0,"docs":{},"r":{",":{"df":1,"docs":{"92":{"tf":1.4142135623730951}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{},"i":{",":{"df":0,"docs":{},"y":{",":{"df":0,"docs":{},"r":{",":{"df":1,"docs":{"92":{"tf":1.4142135623730951}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"df":0,"docs":{},"g":{"_":{"df":0,"docs":{},"k":{"df":0,"docs":{},"e":{"df":0,"docs":{},"y":{"df":1,"docs":{"35":{"tf":2.449489742783178}}}}}},"df":0,"docs":{}}}}}}},"s":{"df":0,"docs":{},"i":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"df":1,"docs":{"13":{"tf":1.0}}}}}}}},"i":{",":{"df":0,"docs":{},"j":{"df":0,"docs":{},"​":{"=":{"df":0,"docs":{},"v":{"df":0,"docs":{},"k":{",":{"df":0,"docs":{},"l":{"df":4,"docs":{"11":{"tf":1.0},"13":{"tf":1.0},"14":{"tf":1.0},"16":{"tf":1.0}}}},"df":0,"docs":{}}}},"df":0,"docs":{}}}},"a":{"df":1,"docs":{"3":{"tf":1.0}}},"df":0,"docs":{},"e":{"df":0,"docs":{},"w":{"df":1,"docs":{"95":{"tf":1.0}}}},"o":{"df":0,"docs":{},"l":{"a":{"df":0,"docs":{},"t":{"df":1,"docs":{"20":{"tf":1.0}}}},"df":0,"docs":{}}},"r":{"df":0,"docs":{},"t":{"df":0,"docs":{},"u":{"a":{"df":0,"docs":{},"l":{"df":11,"docs":{"128":{"tf":2.6457513110645907},"130":{"tf":1.0},"131":{"tf":1.4142135623730951},"133":{"tf":2.0},"134":{"tf":1.0},"136":{"tf":1.0},"139":{"tf":1.0},"148":{"tf":1.0},"149":{"tf":2.23606797749979},"150":{"tf":2.0},"50":{"tf":1.4142135623730951}}}},"df":0,"docs":{}}}},"s":{"df":0,"docs":{},"u":{"a":{"df":0,"docs":{},"l":{"df":3,"docs":{"128":{"tf":1.0},"133":{"tf":1.0},"137":{"tf":1.0}}}},"df":0,"docs":{}}}},"k":{"df":1,"docs":{"44":{"tf":1.7320508075688772}}},"m":{"'":{"df":2,"docs":{"144":{"tf":1.0},"146":{"tf":1.0}}},"df":7,"docs":{"127":{"tf":1.4142135623730951},"133":{"tf":1.0},"144":{"tf":1.7320508075688772},"146":{"tf":1.0},"149":{"tf":1.0},"151":{"tf":2.8284271247461903},"50":{"tf":1.7320508075688772}}},"u":{"df":0,"docs":{},"l":{"df":0,"docs":{},"n":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":1,"docs":{"42":{"tf":1.0}}}}}}}},"w":{"2":{"=":{"df":0,"docs":{},"g":{"df":1,"docs":{"125":{"tf":1.0}}}},"df":1,"docs":{"124":{"tf":1.0}},"k":{"df":1,"docs":{"124":{"tf":1.0}}}},"=":{"8":{"df":1,"docs":{"9":{"tf":1.0}}},"df":0,"docs":{}},"_":{"df":0,"docs":{},"z":{"df":0,"docs":{},"e":{"df":0,"docs":{},"t":{"a":{"_":{"1":{"df":1,"docs":{"35":{"tf":1.0}}},"df":0,"docs":{},"o":{"df":0,"docs":{},"m":{"df":0,"docs":{},"e":{"df":0,"docs":{},"g":{"a":{"_":{"1":{"df":1,"docs":{"35":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"a":{"df":0,"docs":{},"l":{"df":0,"docs":{},"k":{"df":0,"docs":{},"t":{"df":0,"docs":{},"h":{"df":0,"docs":{},"r":{"df":0,"docs":{},"o":{"df":0,"docs":{},"u":{"df":0,"docs":{},"g":{"df":0,"docs":{},"h":{"df":2,"docs":{"51":{"tf":1.0},"64":{"tf":1.0}}}}}}}}}}},"n":{"df":0,"docs":{},"t":{"df":10,"docs":{"103":{"tf":1.0},"123":{"tf":1.4142135623730951},"125":{"tf":1.0},"14":{"tf":1.4142135623730951},"150":{"tf":1.4142135623730951},"151":{"tf":1.0},"16":{"tf":1.0},"44":{"tf":1.0},"5":{"tf":1.0},"50":{"tf":1.0}}}},"y":{"df":22,"docs":{"10":{"tf":1.0},"101":{"tf":1.4142135623730951},"107":{"tf":1.0},"12":{"tf":1.0},"130":{"tf":1.0},"131":{"tf":1.0},"14":{"tf":1.4142135623730951},"146":{"tf":1.0},"149":{"tf":1.0},"151":{"tf":1.0},"31":{"tf":1.0},"50":{"tf":1.0},"52":{"tf":1.0},"54":{"tf":1.0},"66":{"tf":1.4142135623730951},"67":{"tf":1.0},"7":{"tf":1.0},"70":{"tf":1.0},"79":{"tf":1.0},"83":{"tf":1.0},"84":{"tf":1.0},"9":{"tf":1.0}}}},"df":3,"docs":{"11":{"tf":1.0},"124":{"tf":1.4142135623730951},"9":{"tf":1.0}},"e":{"'":{"d":{"df":1,"docs":{"125":{"tf":1.0}}},"df":0,"docs":{},"l":{"df":0,"docs":{},"l":{"df":9,"docs":{"10":{"tf":1.0},"19":{"tf":1.0},"43":{"tf":1.0},"51":{"tf":1.0},"53":{"tf":1.0},"64":{"tf":1.0},"7":{"tf":1.4142135623730951},"80":{"tf":1.0},"9":{"tf":1.7320508075688772}}}},"r":{"df":1,"docs":{"50":{"tf":1.0}}},"v":{"df":1,"docs":{"105":{"tf":1.0}}}},"a":{"df":0,"docs":{},"k":{"df":1,"docs":{"42":{"tf":1.4142135623730951}}}},"df":0,"docs":{},"l":{"df":0,"docs":{},"l":{"df":6,"docs":{"113":{"tf":1.0},"146":{"tf":1.4142135623730951},"34":{"tf":1.0},"36":{"tf":1.0},"4":{"tf":1.0},"98":{"tf":1.0}}}},"r":{"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"'":{"df":0,"docs":{},"t":{"df":2,"docs":{"151":{"tf":1.0},"3":{"tf":1.0}}}},"df":0,"docs":{}}}}},"h":{"a":{"df":0,"docs":{},"t":{"'":{"df":2,"docs":{"15":{"tf":1.0},"69":{"tf":1.0}}},"df":0,"docs":{}}},"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"df":0,"docs":{},"e":{"df":0,"docs":{},"v":{"df":2,"docs":{"118":{"tf":1.0},"123":{"tf":1.0}}}}},"t":{"df":0,"docs":{},"h":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":3,"docs":{"14":{"tf":1.7320508075688772},"84":{"tf":1.0},"92":{"tf":1.4142135623730951}}}}}}},"i":{"df":0,"docs":{},"t":{"df":0,"docs":{},"e":{"df":0,"docs":{},"p":{"a":{"df":0,"docs":{},"p":{"df":5,"docs":{"130":{"tf":1.0},"131":{"tf":1.0},"138":{"tf":1.0},"139":{"tf":1.0},"146":{"tf":1.0}}}},"df":0,"docs":{}}}}},"o":{"df":0,"docs":{},"l":{"df":0,"docs":{},"e":{"df":4,"docs":{"0":{"tf":1.0},"16":{"tf":1.0},"21":{"tf":1.0},"80":{"tf":1.0}}}},"s":{"df":0,"docs":{},"e":{"df":5,"docs":{"101":{"tf":1.0},"114":{"tf":1.0},"123":{"tf":1.0},"149":{"tf":1.0},"54":{"tf":1.0}}}}}},"i":{"df":0,"docs":{},"k":{"df":0,"docs":{},"i":{"df":0,"docs":{},"p":{"df":0,"docs":{},"e":{"d":{"df":0,"docs":{},"i":{"a":{"df":1,"docs":{"101":{"tf":1.0}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}},"n":{"df":0,"docs":{},"t":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":0,"docs":{},"f":{"df":0,"docs":{},"e":{"df":0,"docs":{},"l":{"df":1,"docs":{"103":{"tf":1.0}}}}}}}}},"r":{"df":0,"docs":{},"e":{"df":2,"docs":{"11":{"tf":1.0},"35":{"tf":1.7320508075688772}}}},"s":{"df":0,"docs":{},"h":{"df":2,"docs":{"20":{"tf":1.0},"80":{"tf":1.0}}}},"t":{"df":2,"docs":{"35":{"tf":2.0},"44":{"tf":1.4142135623730951}},"h":{"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"df":1,"docs":{"4":{"tf":1.0}}}},"o":{"df":0,"docs":{},"u":{"df":0,"docs":{},"t":{"df":5,"docs":{"103":{"tf":1.0},"125":{"tf":1.0},"146":{"tf":1.7320508075688772},"19":{"tf":1.0},"7":{"tf":1.0}}}}}},"n":{"df":0,"docs":{},"e":{"df":0,"docs":{},"s":{"df":0,"docs":{},"s":{":":{":":{"df":0,"docs":{},"n":{"df":0,"docs":{},"e":{"df":0,"docs":{},"w":{"(":{"a":{"df":0,"docs":{},"s":{"df":0,"docs":{},"s":{"df":0,"docs":{},"i":{"df":0,"docs":{},"g":{"df":0,"docs":{},"n":{"df":1,"docs":{"44":{"tf":1.0}}}}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"<":{"df":0,"docs":{},"f":{"df":1,"docs":{"35":{"tf":1.0}}}},"df":0,"docs":{}}}}}}},"o":{"df":0,"docs":{},"n":{"'":{"df":0,"docs":{},"t":{"df":9,"docs":{"109":{"tf":1.0},"118":{"tf":1.0},"146":{"tf":1.4142135623730951},"149":{"tf":1.0},"151":{"tf":1.0},"67":{"tf":1.0},"72":{"tf":1.0},"83":{"tf":1.0},"9":{"tf":1.0}}}},"d":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":1,"docs":{"16":{"tf":1.0}}}}},"df":0,"docs":{}},"r":{"d":{"df":2,"docs":{"133":{"tf":1.4142135623730951},"146":{"tf":1.0}}},"df":0,"docs":{},"k":{"df":21,"docs":{"0":{"tf":1.0},"111":{"tf":1.0},"119":{"tf":1.0},"125":{"tf":1.0},"14":{"tf":2.23606797749979},"146":{"tf":1.4142135623730951},"149":{"tf":1.0},"151":{"tf":1.4142135623730951},"16":{"tf":1.0},"19":{"tf":1.0},"20":{"tf":1.0},"21":{"tf":1.0},"4":{"tf":1.0},"46":{"tf":1.0},"50":{"tf":1.0},"52":{"tf":1.0},"56":{"tf":1.0},"69":{"tf":1.0},"73":{"tf":1.0},"75":{"tf":1.4142135623730951},"88":{"tf":1.4142135623730951}}},"t":{"df":0,"docs":{},"h":{"df":2,"docs":{"3":{"tf":1.0},"37":{"tf":1.0}}}}},"u":{"df":0,"docs":{},"l":{"d":{"df":0,"docs":{},"n":{"'":{"df":0,"docs":{},"t":{"df":1,"docs":{"151":{"tf":1.0}}}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"r":{"a":{"df":0,"docs":{},"p":{"df":4,"docs":{"124":{"tf":1.0},"146":{"tf":1.0},"16":{"tf":1.4142135623730951},"45":{"tf":1.0}}}},"df":0,"docs":{},"i":{"df":0,"docs":{},"t":{"df":0,"docs":{},"e":{"df":6,"docs":{"14":{"tf":1.0},"146":{"tf":1.0},"26":{"tf":1.0},"45":{"tf":1.0},"50":{"tf":1.7320508075688772},"92":{"tf":1.4142135623730951}}},"t":{"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"df":1,"docs":{"7":{"tf":1.4142135623730951}}}}}}},"o":{"df":0,"docs":{},"n":{"df":0,"docs":{},"g":{"df":2,"docs":{"124":{"tf":1.0},"42":{"tf":1.0}}}}}}},"x":{",":{"0":{"df":1,"docs":{"7":{"tf":1.0}}},"df":0,"docs":{},"i":{"df":1,"docs":{"34":{"tf":1.0}}}},"0":{"df":1,"docs":{"114":{"tf":1.0}}},"2":{"df":0,"docs":{},"n":{"df":0,"docs":{},"−":{"1":{"df":1,"docs":{"123":{"tf":1.0}},"q":{"df":0,"docs":{},"k":{"df":0,"docs":{},"t":{"df":2,"docs":{"84":{"tf":1.4142135623730951},"85":{"tf":1.0}}}}}},"df":0,"docs":{}}}},":":{"=":{"df":0,"docs":{},"g":{"+":{"df":0,"docs":{},"ζ":{"df":0,"docs":{},"k":{"df":0,"docs":{},"​":{"df":0,"docs":{},"h":{"df":1,"docs":{"102":{"tf":1.0}}}}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"=":{"3":{"df":2,"docs":{"7":{"tf":1.4142135623730951},"9":{"tf":1.0}}},"df":0,"docs":{},"x":{"0":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"x":{"1":{"df":0,"docs":{},"​":{"df":0,"docs":{},"∗":{"2":{"1":{"6":{"+":{"df":0,"docs":{},"x":{"2":{"df":0,"docs":{},"​":{"df":0,"docs":{},"∗":{"2":{"3":{"2":{"+":{"df":0,"docs":{},"x":{"3":{"df":0,"docs":{},"​":{"df":0,"docs":{},"∗":{"2":{"4":{"8":{"+":{".":{".":{".":{"+":{"df":0,"docs":{},"x":{"7":{"df":0,"docs":{},"​":{"df":0,"docs":{},"∗":{"2":{"1":{"1":{"2":{"df":1,"docs":{"151":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"_":{"0":{"df":3,"docs":{"112":{"tf":1.0},"60":{"tf":1.4142135623730951},"62":{"tf":1.0}}},"df":0,"docs":{}},"df":18,"docs":{"102":{"tf":1.0},"106":{"tf":1.0},"11":{"tf":1.0},"124":{"tf":1.0},"14":{"tf":1.0},"142":{"tf":1.4142135623730951},"151":{"tf":1.7320508075688772},"28":{"tf":1.0},"34":{"tf":1.0},"35":{"tf":1.7320508075688772},"44":{"tf":2.23606797749979},"45":{"tf":1.4142135623730951},"5":{"tf":1.7320508075688772},"52":{"tf":1.0},"7":{"tf":2.23606797749979},"88":{"tf":1.4142135623730951},"9":{"tf":2.0},"94":{"tf":1.0}},"i":{"df":2,"docs":{"123":{"tf":1.0},"151":{"tf":1.4142135623730951}}},"n":{"=":{"1":{"df":1,"docs":{"124":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{},"−":{"1":{"df":2,"docs":{"124":{"tf":1.0},"14":{"tf":1.0}}},"df":0,"docs":{}}},"x":{"df":0,"docs":{},"x":{"df":0,"docs":{},"x":{"df":0,"docs":{},"x":{"df":0,"docs":{},"x":{"df":0,"docs":{},"x":{"df":0,"docs":{},"x":{"df":0,"docs":{},"x":{"df":0,"docs":{},"x":{"df":0,"docs":{},"x":{"df":0,"docs":{},"x":{"df":0,"docs":{},"x":{"df":0,"docs":{},"x":{"df":0,"docs":{},"x":{"df":0,"docs":{},"x":{"df":0,"docs":{},"|":{"df":0,"docs":{},"x":{"df":0,"docs":{},"|":{"df":0,"docs":{},"x":{"df":0,"docs":{},"x":{"df":0,"docs":{},"|":{"df":0,"docs":{},"x":{"df":0,"docs":{},"x":{"df":0,"docs":{},"x":{"df":0,"docs":{},"x":{"df":0,"docs":{},"|":{"df":0,"docs":{},"x":{"df":0,"docs":{},"x":{"df":0,"docs":{},"x":{"df":0,"docs":{},"x":{"df":0,"docs":{},"|":{"df":0,"docs":{},"x":{"df":0,"docs":{},"x":{"df":0,"docs":{},"x":{"df":0,"docs":{},"|":{"df":0,"docs":{},"x":{"df":0,"docs":{},"x":{"df":0,"docs":{},"x":{"df":2,"docs":{"142":{"tf":1.0},"146":{"tf":1.0}},"|":{"df":0,"docs":{},"x":{"df":0,"docs":{},"x":{"df":0,"docs":{},"x":{"df":0,"docs":{},"x":{"df":0,"docs":{},"|":{"df":0,"docs":{},"x":{"df":0,"docs":{},"x":{"df":0,"docs":{},"x":{"df":0,"docs":{},"x":{"df":0,"docs":{},"|":{"df":0,"docs":{},"x":{"df":0,"docs":{},"x":{"df":0,"docs":{},"x":{"df":0,"docs":{},"x":{"df":0,"docs":{},"|":{"df":0,"docs":{},"x":{"df":0,"docs":{},"x":{"df":0,"docs":{},"x":{"df":0,"docs":{},"|":{"df":0,"docs":{},"x":{"df":0,"docs":{},"x":{"df":0,"docs":{},"x":{"df":1,"docs":{"147":{"tf":1.0}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}},"−":{"a":{"df":1,"docs":{"100":{"tf":1.0}}},"df":0,"docs":{},"z":{"2":{"df":0,"docs":{},"h":{"1":{"df":0,"docs":{},"​":{"df":0,"docs":{},"−":{"df":0,"docs":{},"h":{"1":{"df":0,"docs":{},"​":{"(":{"df":0,"docs":{},"z":{"2":{")":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"γ":{"df":0,"docs":{},"′":{"df":0,"docs":{},"x":{"df":0,"docs":{},"−":{"df":0,"docs":{},"z":{"2":{"df":0,"docs":{},"h":{"2":{"df":0,"docs":{},"​":{"df":0,"docs":{},"−":{"df":0,"docs":{},"h":{"2":{"df":0,"docs":{},"​":{"(":{"df":0,"docs":{},"z":{"2":{")":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"j":{"df":0,"docs":{},"∑":{"df":0,"docs":{},"​":{"df":0,"docs":{},"γ":{"df":0,"docs":{},"j":{"df":0,"docs":{},"​":{"df":0,"docs":{},"x":{"df":0,"docs":{},"−":{"df":0,"docs":{},"z":{"df":0,"docs":{},"t":{"df":0,"docs":{},"j":{"df":0,"docs":{},"​":{"df":0,"docs":{},"−":{"df":0,"docs":{},"t":{"df":0,"docs":{},"j":{"df":0,"docs":{},"​":{"(":{"df":0,"docs":{},"z":{")":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"γ":{"df":0,"docs":{},"j":{"df":0,"docs":{},"′":{"df":0,"docs":{},"​":{"df":0,"docs":{},"x":{"df":0,"docs":{},"−":{"df":0,"docs":{},"g":{"df":0,"docs":{},"z":{"df":0,"docs":{},"t":{"df":0,"docs":{},"j":{"df":0,"docs":{},"​":{"df":0,"docs":{},"−":{"df":0,"docs":{},"t":{"df":0,"docs":{},"j":{"df":0,"docs":{},"​":{"(":{"df":0,"docs":{},"g":{"df":0,"docs":{},"z":{"df":1,"docs":{"94":{"tf":1.0}}}}},"df":0,"docs":{}}}}}}}}}}}}}}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}}}}}}}}}}}}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"y":{",":{"df":1,"docs":{"95":{"tf":1.0}}},"0":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"…":{",":{"df":0,"docs":{},"y":{"df":0,"docs":{},"m":{"df":1,"docs":{"73":{"tf":1.0}}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"1":{"df":0,"docs":{},"​":{"+":{"d":{"1":{"df":0,"docs":{},"−":{"1":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"…":{",":{"df":0,"docs":{},"y":{"df":0,"docs":{},"m":{"df":0,"docs":{},"​":{"+":{"d":{"df":0,"docs":{},"m":{"df":0,"docs":{},"−":{"1":{"df":1,"docs":{"74":{"tf":1.0}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"=":{"(":{"1":{",":{"1":{",":{"df":0,"docs":{},"…":{",":{"1":{"df":1,"docs":{"73":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{},"p":{"(":{"d":{"1":{"df":0,"docs":{},"​":{")":{",":{"df":0,"docs":{},"…":{",":{"df":0,"docs":{},"p":{"(":{"d":{"df":0,"docs":{},"m":{"df":1,"docs":{"73":{"tf":1.4142135623730951}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"y":{"0":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"…":{",":{"df":0,"docs":{},"y":{"df":0,"docs":{},"m":{"df":1,"docs":{"72":{"tf":1.0}}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"1":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"…":{",":{"df":0,"docs":{},"y":{"df":0,"docs":{},"m":{"df":3,"docs":{"73":{"tf":1.4142135623730951},"74":{"tf":1.0},"79":{"tf":1.0}}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{},"p":{"(":{"df":0,"docs":{},"z":{"df":1,"docs":{"75":{"tf":1.0}}}},"df":0,"docs":{}},"x":{"df":0,"docs":{},"e":{"+":{"5":{"df":1,"docs":{"34":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":15,"docs":{"19":{"tf":1.4142135623730951},"35":{"tf":2.23606797749979},"44":{"tf":2.23606797749979},"45":{"tf":1.4142135623730951},"72":{"tf":2.0},"73":{"tf":2.0},"74":{"tf":1.7320508075688772},"75":{"tf":1.4142135623730951},"77":{"tf":1.4142135623730951},"78":{"tf":1.0},"79":{"tf":1.4142135623730951},"88":{"tf":1.4142135623730951},"92":{"tf":1.7320508075688772},"94":{"tf":1.4142135623730951},"95":{"tf":1.4142135623730951}},"i":{"df":2,"docs":{"72":{"tf":1.0},"80":{"tf":1.0}},"​":{",":{"df":1,"docs":{"92":{"tf":1.0}}},"=":{"df":0,"docs":{},"p":{"df":0,"docs":{},"i":{"df":0,"docs":{},"​":{"(":{"df":0,"docs":{},"z":{"df":0,"docs":{},"i":{"df":1,"docs":{"80":{"tf":1.0}}}}},"df":0,"docs":{}}}}},"df":0,"docs":{},"−":{"df":0,"docs":{},"y":{")":{"/":{"(":{"d":{"df":0,"docs":{},"i":{"df":0,"docs":{},"​":{"df":0,"docs":{},"−":{"df":0,"docs":{},"z":{"df":1,"docs":{"79":{"tf":1.0}}}}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"j":{"(":{"1":{")":{"df":0,"docs":{},"​":{"df":0,"docs":{},"∣":{"df":0,"docs":{},"∣":{"df":0,"docs":{},"…":{"df":0,"docs":{},"∣":{"df":0,"docs":{},"∣":{"df":0,"docs":{},"y":{"df":0,"docs":{},"j":{"(":{"df":0,"docs":{},"k":{"df":1,"docs":{"92":{"tf":1.0}}}},"df":0,"docs":{}}}}}}}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"o":{"df":0,"docs":{},"u":{"'":{"df":0,"docs":{},"l":{"df":0,"docs":{},"l":{"df":2,"docs":{"114":{"tf":1.0},"16":{"tf":1.0}}}},"r":{"df":1,"docs":{"111":{"tf":1.0}}}},"df":0,"docs":{},"r":{"df":0,"docs":{},"s":{"df":0,"docs":{},"e":{"df":0,"docs":{},"l":{"df":0,"docs":{},"f":{"df":2,"docs":{"118":{"tf":1.0},"42":{"tf":1.0}}}}}}}}}},"z":{"(":{"d":{")":{"df":0,"docs":{},"f":{"(":{"d":{")":{"=":{"df":0,"docs":{},"g":{"(":{"d":{")":{"df":0,"docs":{},"z":{"(":{"df":0,"docs":{},"η":{"d":{"df":1,"docs":{"14":{"tf":1.0}}},"df":0,"docs":{}},"ω":{"d":{"df":1,"docs":{"14":{"tf":1.0}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{},"h":{")":{"df":0,"docs":{},"f":{"(":{"df":0,"docs":{},"h":{")":{"=":{"df":0,"docs":{},"g":{"(":{"df":0,"docs":{},"h":{")":{"df":0,"docs":{},"z":{"(":{"df":0,"docs":{},"ω":{"df":0,"docs":{},"h":{"df":1,"docs":{"14":{"tf":1.0}}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"x":{")":{"=":{"df":0,"docs":{},"∏":{"(":{"df":0,"docs":{},"x":{"df":0,"docs":{},"−":{"df":0,"docs":{},"e":{"df":0,"docs":{},"i":{"df":0,"docs":{},"​":{")":{"df":0,"docs":{},"x":{"2":{"df":0,"docs":{},"n":{"df":0,"docs":{},"−":{"1":{"df":1,"docs":{"123":{"tf":1.0}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"x":{"df":0,"docs":{},"i":{"df":1,"docs":{"123":{"tf":1.0}}}}}}},"df":0,"docs":{}}},"df":0,"docs":{},"f":{"(":{"df":0,"docs":{},"x":{")":{"=":{"df":0,"docs":{},"g":{"(":{"df":0,"docs":{},"x":{")":{"df":0,"docs":{},"z":{"(":{"df":0,"docs":{},"ω":{"df":0,"docs":{},"x":{"df":1,"docs":{"14":{"tf":1.7320508075688772}}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":2,"docs":{"35":{"tf":1.0},"5":{"tf":1.0}}},"η":{"0":{")":{"=":{"1":{"df":1,"docs":{"14":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"ω":{"0":{")":{"=":{"1":{"df":1,"docs":{"14":{"tf":2.0}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{},"k":{"df":0,"docs":{},"−":{"1":{")":{"=":{"df":0,"docs":{},"∏":{"df":0,"docs":{},"i":{"=":{"0":{"df":0,"docs":{},"k":{"df":0,"docs":{},"−":{"2":{"df":0,"docs":{},"​":{"(":{"a":{"df":0,"docs":{},"i":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"γ":{")":{"/":{"df":0,"docs":{},"∏":{"df":0,"docs":{},"i":{"=":{"0":{"df":0,"docs":{},"k":{"df":0,"docs":{},"−":{"2":{"df":0,"docs":{},"​":{"(":{"b":{"df":0,"docs":{},"i":{"df":1,"docs":{"14":{"tf":1.0}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{},"g":{"(":{"df":0,"docs":{},"ω":{"df":0,"docs":{},"k":{"df":0,"docs":{},"−":{"1":{")":{"df":0,"docs":{},"f":{"(":{"df":0,"docs":{},"ω":{"df":0,"docs":{},"k":{"df":0,"docs":{},"−":{"1":{")":{"df":0,"docs":{},"​":{"=":{"df":0,"docs":{},"z":{"(":{"df":0,"docs":{},"ω":{"df":0,"docs":{},"k":{")":{"=":{"df":0,"docs":{},"z":{"(":{"df":0,"docs":{},"w":{"0":{")":{"=":{"1":{"df":1,"docs":{"14":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"0":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"…":{",":{"df":0,"docs":{},"z":{"df":0,"docs":{},"n":{"df":0,"docs":{},"−":{"1":{"df":1,"docs":{"25":{"tf":1.0}}},"df":0,"docs":{}}}}},"df":0,"docs":{}}},"=":{"1":{"df":1,"docs":{"25":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}}},"1":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"…":{",":{"df":0,"docs":{},"z":{"df":0,"docs":{},"l":{"df":1,"docs":{"80":{"tf":1.0}}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"=":{"(":{"b":{"7":{"df":0,"docs":{},"​":{"df":0,"docs":{},"x":{"2":{"+":{"b":{"8":{"df":0,"docs":{},"​":{"df":0,"docs":{},"x":{"+":{"b":{"9":{"df":0,"docs":{},"​":{")":{"df":0,"docs":{},"z":{"df":0,"docs":{},"h":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"z":{"df":1,"docs":{"25":{"tf":1.0}}}},"df":0,"docs":{}}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"]":{"1":{"df":3,"docs":{"25":{"tf":1.0},"32":{"tf":1.0},"33":{"tf":1.0}}},"df":0,"docs":{}},"^":{"2":{"df":1,"docs":{"116":{"tf":1.0}}},"df":0,"docs":{}},"_":{"1":{"df":1,"docs":{"35":{"tf":1.0}}},"df":0,"docs":{},"z":{"df":0,"docs":{},"e":{"df":0,"docs":{},"t":{"a":{"_":{"df":0,"docs":{},"o":{"df":0,"docs":{},"m":{"df":0,"docs":{},"e":{"df":0,"docs":{},"g":{"a":{"df":1,"docs":{"35":{"tf":1.0}}},"df":0,"docs":{}}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"a":{"df":0,"docs":{},"v":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":0,"docs":{},"u":{"c":{"df":0,"docs":{},"h":{"a":{"df":3,"docs":{"19":{"tf":1.0},"35":{"tf":1.0},"38":{"tf":1.0}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}}},"df":22,"docs":{"112":{"tf":1.0},"14":{"tf":3.1622776601683795},"16":{"tf":1.0},"25":{"tf":1.0},"28":{"tf":1.0},"33":{"tf":1.4142135623730951},"35":{"tf":1.4142135623730951},"44":{"tf":1.4142135623730951},"45":{"tf":1.4142135623730951},"5":{"tf":1.0},"58":{"tf":1.4142135623730951},"59":{"tf":1.0},"60":{"tf":1.4142135623730951},"62":{"tf":1.0},"69":{"tf":1.4142135623730951},"75":{"tf":1.4142135623730951},"77":{"tf":1.0},"79":{"tf":1.7320508075688772},"85":{"tf":1.7320508075688772},"86":{"tf":1.0},"94":{"tf":1.0},"95":{"tf":1.4142135623730951}},"e":{"df":0,"docs":{},"r":{"df":0,"docs":{},"o":{"df":14,"docs":{"10":{"tf":1.0},"107":{"tf":1.0},"121":{"tf":1.0},"125":{"tf":1.0},"13":{"tf":1.7320508075688772},"142":{"tf":1.4142135623730951},"146":{"tf":1.4142135623730951},"20":{"tf":1.0},"36":{"tf":1.0},"4":{"tf":1.0},"84":{"tf":1.0},"88":{"tf":1.0},"94":{"tf":1.0},"95":{"tf":1.0}},"f":{"df":0,"docs":{},"i":{"df":5,"docs":{"114":{"tf":1.7320508075688772},"123":{"tf":1.4142135623730951},"125":{"tf":2.0},"54":{"tf":1.0},"91":{"tf":1.4142135623730951}}}}}}},"f":{"df":0,"docs":{},"−":{"df":0,"docs":{},"g":{"df":0,"docs":{},"z":{"df":0,"docs":{},"′":{"=":{"df":0,"docs":{},"z":{"df":0,"docs":{},"h":{"df":0,"docs":{},"​":{"df":0,"docs":{},"t":{"2":{"df":1,"docs":{"16":{"tf":1.0}}},"df":0,"docs":{}}}}}},"df":0,"docs":{}}}}}},"h":{"df":2,"docs":{"14":{"tf":1.0},"20":{"tf":1.0}},"​":{"(":{"df":0,"docs":{},"ω":{"df":0,"docs":{},"i":{")":{"=":{"0":{"df":1,"docs":{"20":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}},":":{"=":{"df":0,"docs":{},"x":{"df":0,"docs":{},"n":{"df":0,"docs":{},"−":{"1":{"df":1,"docs":{"23":{"tf":1.0}}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"=":{"df":0,"docs":{},"x":{"df":0,"docs":{},"n":{"df":0,"docs":{},"−":{"1":{"df":2,"docs":{"16":{"tf":1.0},"20":{"tf":1.0}}},"df":0,"docs":{}}}}},"df":0,"docs":{},"t":{"=":{"df":0,"docs":{},"​":{"a":{"df":0,"docs":{},"q":{"df":0,"docs":{},"l":{"df":0,"docs":{},"​":{"+":{"b":{"df":0,"docs":{},"q":{"df":0,"docs":{},"r":{"df":0,"docs":{},"​":{"+":{"a":{"b":{"df":0,"docs":{},"q":{"df":0,"docs":{},"m":{"df":0,"docs":{},"​":{"+":{"c":{"df":0,"docs":{},"q":{"df":0,"docs":{},"o":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"q":{"c":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"p":{"df":0,"docs":{},"i":{"+":{"df":0,"docs":{},"α":{"(":{"df":0,"docs":{},"g":{"df":0,"docs":{},"z":{"df":0,"docs":{},"′":{"df":0,"docs":{},"−":{"df":0,"docs":{},"f":{"df":0,"docs":{},"z":{")":{"+":{"df":0,"docs":{},"α":{"2":{"(":{"df":0,"docs":{},"z":{"df":0,"docs":{},"−":{"1":{")":{"df":0,"docs":{},"l":{"1":{"df":1,"docs":{"16":{"tf":1.0}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}}}}}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"i":{"df":0,"docs":{},"p":{"df":0,"docs":{},"p":{"df":0,"docs":{},"e":{"df":0,"docs":{},"l":{"df":1,"docs":{"14":{"tf":1.4142135623730951}}}}}}},"j":{"b":{"df":1,"docs":{"91":{"tf":1.0}}},"df":0,"docs":{},"t":{"df":1,"docs":{"91":{"tf":1.0}}}},"k":{"+":{"1":{"df":0,"docs":{},"​":{"=":{"df":0,"docs":{},"z":{"df":0,"docs":{},"k":{"df":0,"docs":{},"​":{"(":{"a":{"df":0,"docs":{},"k":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"β":{"df":0,"docs":{},"s":{"df":0,"docs":{},"σ":{"1":{"df":0,"docs":{},"​":{"(":{"df":0,"docs":{},"ω":{"df":0,"docs":{},"k":{")":{"+":{"df":0,"docs":{},"γ":{")":{"(":{"b":{"df":0,"docs":{},"k":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"β":{"df":0,"docs":{},"s":{"df":0,"docs":{},"σ":{"2":{"df":0,"docs":{},"​":{"(":{"df":0,"docs":{},"ω":{"df":0,"docs":{},"k":{")":{"+":{"df":0,"docs":{},"γ":{")":{"(":{"c":{"df":0,"docs":{},"k":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"β":{"df":0,"docs":{},"s":{"df":0,"docs":{},"σ":{"3":{"df":0,"docs":{},"​":{"(":{"df":0,"docs":{},"ω":{"df":0,"docs":{},"k":{")":{"+":{"df":0,"docs":{},"γ":{")":{"(":{"a":{"df":0,"docs":{},"k":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"β":{"df":0,"docs":{},"ω":{"df":0,"docs":{},"k":{"+":{"df":0,"docs":{},"γ":{")":{"(":{"b":{"df":0,"docs":{},"k":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"β":{"df":0,"docs":{},"ω":{"df":0,"docs":{},"k":{"df":0,"docs":{},"k":{"1":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"γ":{")":{"(":{"c":{"df":0,"docs":{},"k":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"β":{"df":0,"docs":{},"ω":{"df":0,"docs":{},"k":{"df":0,"docs":{},"k":{"2":{"df":1,"docs":{"25":{"tf":1.0}}},"df":0,"docs":{}}}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"d":{"df":0,"docs":{},"o":{"c":{"df":1,"docs":{"42":{"tf":1.0}}},"df":0,"docs":{}}},"df":1,"docs":{"4":{"tf":1.0}}},"′":{"(":{"df":0,"docs":{},"x":{")":{"=":{"df":0,"docs":{},"z":{"(":{"df":0,"docs":{},"x":{"df":1,"docs":{"16":{"tf":1.0}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},":":{"=":{"df":0,"docs":{},"z":{"(":{"df":0,"docs":{},"ω":{"df":0,"docs":{},"x":{"df":1,"docs":{"5":{"tf":1.0}}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{},"∈":{"df":0,"docs":{},"f":{"df":1,"docs":{"147":{"tf":1.0}}}}},"∈":{"df":0,"docs":{},"f":{"df":2,"docs":{"147":{"tf":1.0},"85":{"tf":1.0}},"∖":{"d":{"df":0,"docs":{},"l":{"d":{"df":0,"docs":{},"e":{"df":1,"docs":{"94":{"tf":1.0}}}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"−":{"1":{")":{"df":0,"docs":{},"l":{"1":{"df":0,"docs":{},"​":{"=":{"df":0,"docs":{},"z":{"df":0,"docs":{},"h":{"df":0,"docs":{},"​":{"df":0,"docs":{},"t":{"3":{"df":1,"docs":{"16":{"tf":1.0}}},"df":0,"docs":{}}}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"breadcrumbs":{"root":{"0":{")":{"=":{"0":{"df":1,"docs":{"101":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}},",":{"0":{")":{")":{"=":{"(":{"2":{",":{"1":{"df":1,"docs":{"14":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},",":{"df":0,"docs":{},"t":{"0":{",":{"0":{"df":0,"docs":{},"​":{")":{",":{"(":{"(":{"0":{",":{"1":{")":{",":{"df":0,"docs":{},"t":{"0":{",":{"1":{"df":0,"docs":{},"​":{")":{",":{"(":{"(":{"0":{",":{"2":{")":{",":{"df":0,"docs":{},"t":{"0":{",":{"2":{"df":0,"docs":{},"​":{")":{",":{"(":{"(":{"0":{",":{"3":{")":{",":{"df":0,"docs":{},"t":{"0":{",":{"3":{"df":0,"docs":{},"​":{")":{",":{"(":{"(":{"2":{",":{"1":{")":{",":{"df":0,"docs":{},"t":{"2":{",":{"1":{"df":0,"docs":{},"​":{")":{",":{"(":{"(":{"3":{",":{"1":{")":{",":{"df":0,"docs":{},"t":{"3":{",":{"1":{"df":0,"docs":{},"​":{")":{",":{"(":{"(":{"2":{",":{"2":{")":{",":{"df":0,"docs":{},"t":{"2":{",":{"2":{"df":1,"docs":{"14":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":2,"docs":{"133":{"tf":1.0},"135":{"tf":1.0}}},"1":{")":{")":{"=":{"(":{"0":{",":{"3":{"df":1,"docs":{"14":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"4":{"df":1,"docs":{"131":{"tf":1.0}}},"df":0,"docs":{}},"2":{")":{")":{"=":{"(":{"0":{",":{"2":{"df":1,"docs":{"14":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"3":{")":{")":{"=":{"(":{"0":{",":{"1":{"df":1,"docs":{"14":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{},"ω":{",":{"df":0,"docs":{},"ω":{"2":{",":{"df":0,"docs":{},"…":{",":{"df":0,"docs":{},"ω":{"1":{"5":{"df":1,"docs":{"114":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},".":{".":{"3":{"2":{"df":1,"docs":{"45":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}},"2":{"df":1,"docs":{"3":{"tf":1.0}}},"4":{"df":1,"docs":{"3":{"tf":1.0}}},"8":{"df":1,"docs":{"3":{"tf":1.0}}},"df":0,"docs":{}},"0":{"1":{"df":1,"docs":{"101":{"tf":1.0}}},"df":0,"docs":{}},"<":{"df":0,"docs":{},"x":{"df":0,"docs":{},"i":{"df":0,"docs":{},"​":{"<":{"2":{"1":{"6":{"df":1,"docs":{"151":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":29,"docs":{"10":{"tf":4.358898943540674},"106":{"tf":1.0},"109":{"tf":1.0},"11":{"tf":2.23606797749979},"12":{"tf":1.4142135623730951},"127":{"tf":1.4142135623730951},"13":{"tf":5.0},"130":{"tf":1.7320508075688772},"131":{"tf":1.0},"132":{"tf":1.0},"133":{"tf":1.4142135623730951},"134":{"tf":1.7320508075688772},"135":{"tf":2.8284271247461903},"136":{"tf":1.0},"139":{"tf":1.0},"14":{"tf":2.0},"146":{"tf":2.23606797749979},"149":{"tf":1.7320508075688772},"150":{"tf":1.0},"151":{"tf":1.4142135623730951},"36":{"tf":1.0},"45":{"tf":1.0},"52":{"tf":1.4142135623730951},"62":{"tf":1.0},"68":{"tf":1.4142135623730951},"70":{"tf":1.0},"73":{"tf":1.0},"9":{"tf":1.0},"94":{"tf":1.0}},"s":{"df":2,"docs":{"131":{"tf":1.0},"139":{"tf":1.0}}},"x":{"0":{"1":{"2":{"3":{"4":{"5":{"6":{"7":{"8":{"9":{"a":{"b":{"c":{"d":{"df":1,"docs":{"88":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"7":{"3":{"df":0,"docs":{},"e":{"d":{"a":{"7":{"5":{"3":{"2":{"9":{"9":{"d":{"7":{"d":{"4":{"8":{"3":{"3":{"3":{"9":{"d":{"8":{"0":{"8":{"0":{"9":{"a":{"1":{"d":{"8":{"0":{"5":{"5":{"3":{"b":{"d":{"a":{"4":{"0":{"2":{"df":0,"docs":{},"f":{"df":0,"docs":{},"f":{"df":0,"docs":{},"f":{"df":0,"docs":{},"e":{"5":{"b":{"df":0,"docs":{},"f":{"df":0,"docs":{},"e":{"df":0,"docs":{},"f":{"df":0,"docs":{},"f":{"df":0,"docs":{},"f":{"df":0,"docs":{},"f":{"df":0,"docs":{},"f":{"df":0,"docs":{},"f":{"df":0,"docs":{},"f":{"df":0,"docs":{},"f":{"0":{"0":{"0":{"0":{"0":{"0":{"0":{"1":{"df":1,"docs":{"38":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}}}}}}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"υ":{"df":0,"docs":{},"s":{"df":0,"docs":{},"​":{"df":0,"docs":{},"​":{"df":0,"docs":{},"π":{"0":{"df":0,"docs":{},"−":{"df":0,"docs":{},"υ":{"df":0,"docs":{},"s":{"df":0,"docs":{},"​":{"df":0,"docs":{},"​":{"df":0,"docs":{},"​":{":":{"=":{"df":0,"docs":{},"γ":{"df":0,"docs":{},"υ":{"df":0,"docs":{},"s":{"df":0,"docs":{},"​":{"df":0,"docs":{},"−":{"df":0,"docs":{},"z":{"2":{"df":0,"docs":{},"η":{"1":{"df":0,"docs":{},"υ":{"df":0,"docs":{},"s":{"df":0,"docs":{},"​":{"df":0,"docs":{},"​":{"df":0,"docs":{},"−":{"df":0,"docs":{},"η":{"1":{"df":0,"docs":{},"z":{"2":{"df":0,"docs":{},"​":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"γ":{"df":0,"docs":{},"′":{"df":0,"docs":{},"υ":{"df":0,"docs":{},"s":{"df":0,"docs":{},"​":{"df":0,"docs":{},"−":{"df":0,"docs":{},"z":{"2":{"df":0,"docs":{},"η":{"2":{"df":0,"docs":{},"υ":{"df":0,"docs":{},"s":{"df":0,"docs":{},"​":{"df":0,"docs":{},"​":{"df":0,"docs":{},"−":{"df":0,"docs":{},"η":{"2":{"df":0,"docs":{},"z":{"2":{"df":0,"docs":{},"​":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"j":{"df":0,"docs":{},"∑":{"df":0,"docs":{},"​":{"df":0,"docs":{},"γ":{"df":0,"docs":{},"j":{"df":0,"docs":{},"​":{"df":0,"docs":{},"υ":{"df":0,"docs":{},"s":{"df":0,"docs":{},"​":{"df":0,"docs":{},"−":{"df":0,"docs":{},"z":{"df":0,"docs":{},"τ":{"df":0,"docs":{},"j":{"df":0,"docs":{},"υ":{"df":0,"docs":{},"s":{"df":0,"docs":{},"​":{"df":0,"docs":{},"​":{"df":0,"docs":{},"−":{"df":0,"docs":{},"τ":{"df":0,"docs":{},"j":{"df":0,"docs":{},"z":{"df":0,"docs":{},"​":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"γ":{"df":0,"docs":{},"j":{"df":0,"docs":{},"′":{"df":0,"docs":{},"​":{"df":0,"docs":{},"υ":{"df":0,"docs":{},"s":{"df":0,"docs":{},"​":{"df":0,"docs":{},"−":{"df":0,"docs":{},"g":{"df":0,"docs":{},"z":{"df":0,"docs":{},"τ":{"df":0,"docs":{},"j":{"df":0,"docs":{},"υ":{"df":0,"docs":{},"s":{"df":0,"docs":{},"​":{"df":0,"docs":{},"​":{"df":0,"docs":{},"−":{"df":0,"docs":{},"τ":{"df":0,"docs":{},"j":{"df":0,"docs":{},"g":{"df":0,"docs":{},"z":{"df":0,"docs":{},"​":{"df":0,"docs":{},"​":{",":{":":{"=":{"df":0,"docs":{},"γ":{"df":0,"docs":{},"−":{"df":0,"docs":{},"υ":{"df":0,"docs":{},"s":{"df":0,"docs":{},"​":{"df":0,"docs":{},"−":{"df":0,"docs":{},"z":{"2":{"df":0,"docs":{},"η":{"1":{"df":0,"docs":{},"−":{"df":0,"docs":{},"υ":{"df":0,"docs":{},"s":{"df":0,"docs":{},"​":{"df":0,"docs":{},"​":{"df":0,"docs":{},"−":{"df":0,"docs":{},"η":{"1":{"df":0,"docs":{},"z":{"2":{"df":0,"docs":{},"​":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"γ":{"df":0,"docs":{},"′":{"df":0,"docs":{},"−":{"df":0,"docs":{},"υ":{"df":0,"docs":{},"s":{"df":0,"docs":{},"​":{"df":0,"docs":{},"−":{"df":0,"docs":{},"z":{"2":{"df":0,"docs":{},"η":{"2":{"df":0,"docs":{},"−":{"df":0,"docs":{},"υ":{"df":0,"docs":{},"s":{"df":0,"docs":{},"​":{"df":0,"docs":{},"​":{"df":0,"docs":{},"−":{"df":0,"docs":{},"η":{"2":{"df":0,"docs":{},"z":{"2":{"df":0,"docs":{},"​":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"j":{"df":0,"docs":{},"∑":{"df":0,"docs":{},"​":{"df":0,"docs":{},"γ":{"df":0,"docs":{},"j":{"df":0,"docs":{},"​":{"df":0,"docs":{},"−":{"df":0,"docs":{},"υ":{"df":0,"docs":{},"s":{"df":0,"docs":{},"​":{"df":0,"docs":{},"−":{"df":0,"docs":{},"z":{"df":0,"docs":{},"τ":{"df":0,"docs":{},"j":{"df":0,"docs":{},"−":{"df":0,"docs":{},"υ":{"df":0,"docs":{},"s":{"df":0,"docs":{},"​":{"df":0,"docs":{},"​":{"df":0,"docs":{},"−":{"df":0,"docs":{},"τ":{"df":0,"docs":{},"j":{"df":0,"docs":{},"z":{"df":0,"docs":{},"​":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"γ":{"df":0,"docs":{},"j":{"df":0,"docs":{},"′":{"df":0,"docs":{},"​":{"df":0,"docs":{},"−":{"df":0,"docs":{},"υ":{"df":0,"docs":{},"s":{"df":0,"docs":{},"​":{"df":0,"docs":{},"−":{"df":0,"docs":{},"g":{"df":0,"docs":{},"z":{"df":0,"docs":{},"τ":{"df":0,"docs":{},"j":{"df":0,"docs":{},"−":{"df":0,"docs":{},"υ":{"df":0,"docs":{},"s":{"df":0,"docs":{},"​":{"df":0,"docs":{},"​":{"df":0,"docs":{},"−":{"df":0,"docs":{},"τ":{"df":0,"docs":{},"j":{"df":0,"docs":{},"g":{"df":0,"docs":{},"z":{"df":1,"docs":{"95":{"tf":1.0}}}}}}}}}}}}}}}}}}}}}}}}}},"df":0,"docs":{}}}}}}}}}}}}}}}}}}}}}}}}}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}}}}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}}}}}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}}}}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}}}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}}}}}}}}}}}}}}}}}}}}}}},"df":0,"docs":{}}}}}}}}}}}}}}}}}}}}}}}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}}}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}}}}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}}}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}}}}}},"df":0,"docs":{}}}}}},"≤":{"df":0,"docs":{},"i":{"<":{"df":0,"docs":{},"n":{"df":1,"docs":{"14":{"tf":1.0}}}},"df":0,"docs":{}},"j":{"<":{"3":{"df":1,"docs":{"14":{"tf":1.0}}},"df":0,"docs":{},"m":{"df":1,"docs":{"95":{"tf":1.4142135623730951}}}},"df":0,"docs":{},"≤":{"df":0,"docs":{},"j":{"df":1,"docs":{"142":{"tf":1.0}}}}},"k":{"<":{"df":0,"docs":{},"n":{"df":1,"docs":{"25":{"tf":1.0}}}},"df":0,"docs":{}}}},"1":{")":{"=":{"8":{"df":1,"docs":{"101":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}},",":{"b":{"0":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"γ":{"a":{"0":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"γ":{"df":0,"docs":{},"​":{",":{"(":{"b":{"0":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"γ":{")":{"(":{"b":{"1":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"γ":{")":{"(":{"a":{"0":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"γ":{")":{"(":{"a":{"1":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"γ":{")":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"…":{",":{"df":0,"docs":{},"i":{"=":{"0":{"df":0,"docs":{},"∏":{"df":0,"docs":{},"k":{"df":0,"docs":{},"−":{"2":{"df":0,"docs":{},"​":{"b":{"df":0,"docs":{},"i":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"γ":{"a":{"df":0,"docs":{},"i":{"df":1,"docs":{"14":{"tf":1.0}}}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{},"ω":{",":{"df":0,"docs":{},"ω":{"2":{",":{"df":0,"docs":{},"…":{",":{"df":0,"docs":{},"ω":{"df":0,"docs":{},"n":{"df":0,"docs":{},"−":{"1":{"df":1,"docs":{"20":{"tf":1.0}}},"df":0,"docs":{}}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"…":{",":{"df":0,"docs":{},"ω":{"df":0,"docs":{},"k":{"df":0,"docs":{},"−":{"1":{"df":1,"docs":{"14":{"tf":1.0}}},"2":{"df":1,"docs":{"14":{"tf":1.0}}},"df":0,"docs":{}}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},".":{"1":{"df":1,"docs":{"94":{"tf":1.0}}},"2":{"df":1,"docs":{"94":{"tf":1.0}}},"7":{"df":1,"docs":{"3":{"tf":1.0}}},"df":0,"docs":{}},"0":{"0":{"df":1,"docs":{"101":{"tf":1.0}}},"df":3,"docs":{"130":{"tf":1.0},"136":{"tf":1.0},"146":{"tf":1.0}}},"1":{"df":2,"docs":{"130":{"tf":1.0},"146":{"tf":1.0}}},"2":{"df":6,"docs":{"130":{"tf":1.0},"133":{"tf":1.0},"136":{"tf":1.0},"146":{"tf":1.0},"147":{"tf":1.0},"38":{"tf":1.0}}},"3":{"df":4,"docs":{"130":{"tf":1.0},"133":{"tf":1.0},"146":{"tf":1.0},"49":{"tf":1.0}}},"4":{"df":3,"docs":{"130":{"tf":1.0},"134":{"tf":1.4142135623730951},"146":{"tf":1.0}}},"5":{"df":7,"docs":{"127":{"tf":1.0},"130":{"tf":1.0},"131":{"tf":2.0},"134":{"tf":1.4142135623730951},"146":{"tf":1.0},"149":{"tf":1.0},"68":{"tf":1.4142135623730951}}},"6":{"df":11,"docs":{"114":{"tf":1.0},"127":{"tf":1.4142135623730951},"130":{"tf":1.7320508075688772},"131":{"tf":1.0},"133":{"tf":3.0},"136":{"tf":2.449489742783178},"142":{"tf":1.0},"146":{"tf":1.4142135623730951},"147":{"tf":1.0},"151":{"tf":1.0},"68":{"tf":2.0}},"x":{"df":0,"docs":{},"n":{"df":1,"docs":{"127":{"tf":1.0}}}}},"9":{"df":1,"docs":{"11":{"tf":1.0}}},"b":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"…":{",":{"df":0,"docs":{},"α":{"df":0,"docs":{},"m":{"b":{"df":1,"docs":{"95":{"tf":1.0}}},"df":0,"docs":{}}},"β":{"df":0,"docs":{},"m":{"b":{"df":2,"docs":{"94":{"tf":1.0},"95":{"tf":1.0}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":33,"docs":{"10":{"tf":4.0},"101":{"tf":1.0},"106":{"tf":1.7320508075688772},"108":{"tf":2.0},"109":{"tf":2.0},"11":{"tf":1.7320508075688772},"12":{"tf":2.23606797749979},"127":{"tf":1.0},"13":{"tf":4.58257569495584},"130":{"tf":1.4142135623730951},"131":{"tf":1.7320508075688772},"133":{"tf":1.4142135623730951},"134":{"tf":1.4142135623730951},"135":{"tf":1.7320508075688772},"139":{"tf":1.0},"14":{"tf":1.7320508075688772},"142":{"tf":1.0},"146":{"tf":2.23606797749979},"147":{"tf":1.0},"150":{"tf":1.0},"24":{"tf":1.4142135623730951},"3":{"tf":1.0},"35":{"tf":1.0},"49":{"tf":2.449489742783178},"52":{"tf":2.0},"54":{"tf":1.4142135623730951},"68":{"tf":1.4142135623730951},"7":{"tf":1.0},"83":{"tf":2.0},"84":{"tf":1.0},"9":{"tf":2.0},"94":{"tf":1.0},"95":{"tf":1.0}},"s":{"df":1,"docs":{"131":{"tf":1.0}}},"t":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"…":{",":{"df":0,"docs":{},"α":{"df":0,"docs":{},"n":{"df":0,"docs":{},"t":{"df":0,"docs":{},"​":{"df":0,"docs":{},"t":{"df":1,"docs":{"95":{"tf":1.0}}}}}}},"β":{"df":0,"docs":{},"n":{"df":0,"docs":{},"t":{"df":0,"docs":{},"​":{"df":0,"docs":{},"t":{"df":2,"docs":{"94":{"tf":1.0},"95":{"tf":1.0}}}}}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"z":{"2":{"df":1,"docs":{"95":{"tf":1.0}}},"df":0,"docs":{}},"​":{",":{"df":0,"docs":{},"…":{",":{"df":0,"docs":{},"γ":{"df":0,"docs":{},"m":{"df":1,"docs":{"94":{"tf":1.0}},"​":{",":{"df":0,"docs":{},"γ":{"1":{"df":0,"docs":{},"′":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"…":{",":{"df":0,"docs":{},"γ":{"df":0,"docs":{},"m":{"df":1,"docs":{"95":{"tf":1.0}}}}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"′":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"…":{",":{"df":0,"docs":{},"γ":{"df":0,"docs":{},"m":{"df":1,"docs":{"94":{"tf":1.0}}}}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"2":{")":{"=":{"4":{"df":1,"docs":{"101":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}},",":{"1":{")":{")":{"=":{"(":{"0":{",":{"0":{"df":1,"docs":{"14":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},",":{"df":0,"docs":{},"t":{"0":{",":{"0":{"df":0,"docs":{},"​":{")":{",":{"(":{"(":{"0":{",":{"3":{")":{",":{"df":0,"docs":{},"t":{"0":{",":{"1":{"df":0,"docs":{},"​":{")":{",":{"(":{"(":{"0":{",":{"2":{")":{",":{"df":0,"docs":{},"t":{"0":{",":{"2":{"df":0,"docs":{},"​":{")":{",":{"(":{"(":{"0":{",":{"1":{")":{",":{"df":0,"docs":{},"t":{"0":{",":{"3":{"df":0,"docs":{},"​":{")":{",":{"(":{"(":{"0":{",":{"0":{")":{",":{"df":0,"docs":{},"t":{"2":{",":{"1":{"df":0,"docs":{},"​":{")":{",":{"(":{"(":{"2":{",":{"2":{")":{",":{"df":0,"docs":{},"t":{"3":{",":{"1":{"df":0,"docs":{},"​":{")":{",":{"(":{"(":{"3":{",":{"1":{")":{",":{"df":0,"docs":{},"t":{"2":{",":{"2":{"df":1,"docs":{"14":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"2":{")":{")":{"=":{"(":{"3":{",":{"1":{"df":1,"docs":{"14":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},".":{"2":{"df":1,"docs":{"3":{"tf":1.0}}},"df":0,"docs":{}},"0":{"0":{".":{"8":{"df":1,"docs":{"3":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":1,"docs":{"11":{"tf":1.0}}},"1":{"2":{"8":{"df":1,"docs":{"151":{"tf":1.4142135623730951}}},"df":0,"docs":{}},"6":{"df":2,"docs":{"146":{"tf":1.0},"151":{"tf":1.0}}},"df":1,"docs":{"49":{"tf":1.0}}},"2":{"6":{"4":{"df":1,"docs":{"121":{"tf":1.0}}},"df":0,"docs":{}},"df":1,"docs":{"134":{"tf":1.0}}},"3":{"2":{"df":2,"docs":{"36":{"tf":1.0},"38":{"tf":1.0}}},"df":1,"docs":{"134":{"tf":1.0}}},"5":{"5":{"df":1,"docs":{"41":{"tf":1.0}}},"6":{"df":1,"docs":{"41":{"tf":1.4142135623730951}}},"df":0,"docs":{}},"8":{"7":{"4":{"5":{"df":1,"docs":{"3":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"=":{"df":0,"docs":{},"g":{"df":2,"docs":{"114":{"tf":1.4142135623730951},"125":{"tf":1.0}}}},"^":{"1":{"6":{"df":1,"docs":{"146":{"tf":1.0}}},"df":0,"docs":{}},"2":{"1":{"df":1,"docs":{"3":{"tf":1.0}}},"2":{"df":1,"docs":{"3":{"tf":1.0}}},"3":{"df":1,"docs":{"3":{"tf":1.0}}},"4":{"df":1,"docs":{"3":{"tf":1.0}}},"df":0,"docs":{}},"4":{"df":1,"docs":{"3":{"tf":1.0}}},"5":{"df":1,"docs":{"3":{"tf":1.0}}},"6":{"df":1,"docs":{"3":{"tf":1.0}}},"7":{"df":1,"docs":{"3":{"tf":1.0}}},"df":0,"docs":{},"n":{"df":3,"docs":{"51":{"tf":1.0},"62":{"tf":1.4142135623730951},"68":{"tf":1.7320508075688772}}},"{":{"df":0,"docs":{},"n":{"df":1,"docs":{"68":{"tf":1.0}}}}},"df":29,"docs":{"10":{"tf":1.0},"107":{"tf":1.0},"108":{"tf":1.7320508075688772},"109":{"tf":1.7320508075688772},"11":{"tf":2.0},"12":{"tf":1.4142135623730951},"121":{"tf":1.0},"125":{"tf":1.0},"127":{"tf":1.0},"13":{"tf":1.4142135623730951},"130":{"tf":1.4142135623730951},"132":{"tf":1.4142135623730951},"133":{"tf":2.0},"135":{"tf":1.4142135623730951},"136":{"tf":1.0},"139":{"tf":1.4142135623730951},"14":{"tf":1.7320508075688772},"142":{"tf":1.0},"146":{"tf":1.4142135623730951},"147":{"tf":1.0},"149":{"tf":1.0},"25":{"tf":1.4142135623730951},"35":{"tf":1.0},"49":{"tf":1.0},"84":{"tf":1.7320508075688772},"9":{"tf":1.4142135623730951},"91":{"tf":1.0},"94":{"tf":1.0},"95":{"tf":1.0}},"k":{"df":0,"docs":{},"−":{"1":{"df":1,"docs":{"101":{"tf":1.0}}},"df":0,"docs":{}}},"l":{"df":1,"docs":{"91":{"tf":1.0}}},"m":{",":{"df":0,"docs":{},"γ":{"df":0,"docs":{},"′":{":":{"=":{"df":0,"docs":{},"α":{"2":{"df":0,"docs":{},"m":{"+":{"1":{"df":1,"docs":{"97":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":1,"docs":{"70":{"tf":1.0}}},"n":{"+":{"1":{"df":2,"docs":{"124":{"tf":1.0},"125":{"tf":1.0}}},"2":{"df":1,"docs":{"147":{"tf":1.7320508075688772}},"×":{"2":{"df":1,"docs":{"147":{"tf":1.0}}},"df":0,"docs":{}},"−":{"df":0,"docs":{},"l":{"df":0,"docs":{},"p":{"df":0,"docs":{},"u":{"b":{"df":1,"docs":{"147":{"tf":1.0}}},"df":0,"docs":{}}}}}},"df":0,"docs":{},"k":{"df":1,"docs":{"124":{"tf":1.0}}},"l":{"df":2,"docs":{"82":{"tf":1.0},"91":{"tf":1.0}}}},"df":6,"docs":{"112":{"tf":1.0},"123":{"tf":1.4142135623730951},"124":{"tf":1.4142135623730951},"125":{"tf":1.4142135623730951},"82":{"tf":1.0},"91":{"tf":1.4142135623730951}},"×":{"1":{"2":{"df":1,"docs":{"147":{"tf":1.0}}},"8":{"df":1,"docs":{"147":{"tf":1.0}}},"df":0,"docs":{}},"3":{"df":1,"docs":{"147":{"tf":1.0}}},"4":{"df":1,"docs":{"147":{"tf":1.0}}},"6":{"df":1,"docs":{"147":{"tf":1.0}}},"8":{"df":1,"docs":{"147":{"tf":1.0}}},"df":0,"docs":{}}},"z":{"2":{"df":1,"docs":{"95":{"tf":1.0}}},"df":0,"docs":{}},"×":{"0":{"+":{"3":{"df":0,"docs":{},"×":{"0":{"+":{"2":{"df":0,"docs":{},"×":{"3":{"df":0,"docs":{},"×":{"1":{"+":{"6":{"df":0,"docs":{},"×":{"(":{"df":0,"docs":{},"−":{"1":{")":{"+":{"0":{"=":{"0":{"df":1,"docs":{"10":{"tf":1.0}}},"df":0,"docs":{}},"df":1,"docs":{"10":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"1":{"+":{"2":{"df":0,"docs":{},"×":{"3":{"df":0,"docs":{},"×":{"1":{"+":{"8":{"df":0,"docs":{},"×":{"(":{"df":0,"docs":{},"−":{"1":{")":{"+":{"(":{"df":0,"docs":{},"−":{"1":{")":{"=":{"0":{"df":1,"docs":{"12":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"3":{",":{"1":{")":{")":{"=":{"(":{"2":{",":{"2":{"df":1,"docs":{"14":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"8":{"df":1,"docs":{"7":{"tf":1.0}}},"df":0,"docs":{}},"0":{"0":{"0":{"0":{"df":1,"docs":{"3":{"tf":2.6457513110645907}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":3,"docs":{"3":{"tf":1.0},"50":{"tf":1.0},"65":{"tf":1.0}}},"1":{"df":1,"docs":{"127":{"tf":1.0}}},"2":{"4":{"5":{"4":{"4":{"4":{"df":1,"docs":{"135":{"tf":1.4142135623730951}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"5":{"2":{"6":{"4":{"3":{"df":1,"docs":{"135":{"tf":1.4142135623730951}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"8":{"1":{"df":1,"docs":{"38":{"tf":1.0}}},"df":0,"docs":{}},"df":23,"docs":{"10":{"tf":1.4142135623730951},"102":{"tf":1.0},"108":{"tf":1.4142135623730951},"109":{"tf":1.0},"11":{"tf":2.0},"12":{"tf":1.0},"13":{"tf":2.8284271247461903},"130":{"tf":1.4142135623730951},"133":{"tf":1.7320508075688772},"135":{"tf":1.7320508075688772},"14":{"tf":2.23606797749979},"142":{"tf":1.4142135623730951},"146":{"tf":1.7320508075688772},"147":{"tf":2.0},"149":{"tf":1.4142135623730951},"150":{"tf":1.0},"26":{"tf":1.4142135623730951},"35":{"tf":1.0},"49":{"tf":1.0},"85":{"tf":1.7320508075688772},"9":{"tf":1.7320508075688772},"94":{"tf":1.0},"95":{"tf":1.0}},"i":{"+":{"df":0,"docs":{},"j":{",":{"df":0,"docs":{},"v":{"df":1,"docs":{"14":{"tf":1.0}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"k":{"+":{"df":0,"docs":{},"l":{":":{"(":{"df":0,"docs":{},"i":{",":{"0":{")":{"df":0,"docs":{},"∈":{"df":0,"docs":{},"i":{",":{"df":0,"docs":{},"σ":{"(":{"(":{"df":0,"docs":{},"i":{",":{"0":{")":{")":{"=":{"(":{"df":0,"docs":{},"k":{",":{"df":0,"docs":{},"l":{"df":1,"docs":{"14":{"tf":1.0}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"1":{")":{"df":0,"docs":{},"∈":{"df":0,"docs":{},"i":{",":{"df":0,"docs":{},"σ":{"(":{"(":{"df":0,"docs":{},"i":{",":{"1":{")":{")":{"=":{"(":{"df":0,"docs":{},"k":{",":{"df":0,"docs":{},"l":{"df":1,"docs":{"14":{"tf":1.0}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"2":{")":{"df":0,"docs":{},"∈":{"df":0,"docs":{},"i":{",":{"df":0,"docs":{},"σ":{"(":{"(":{"df":0,"docs":{},"i":{",":{"2":{")":{")":{"=":{"(":{"df":0,"docs":{},"k":{",":{"df":0,"docs":{},"l":{"df":1,"docs":{"14":{"tf":1.0}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"n":{"df":1,"docs":{"14":{"tf":1.4142135623730951}}},"⋅":{"2":{"df":0,"docs":{},"n":{"df":1,"docs":{"147":{"tf":1.4142135623730951}}}},"df":0,"docs":{}}},"4":{".":{"1":{".":{"df":0,"docs":{},"k":{"df":1,"docs":{"94":{"tf":1.0}}}},"df":0,"docs":{}},"2":{"df":1,"docs":{"94":{"tf":1.0}}},"3":{"df":1,"docs":{"94":{"tf":1.0}}},"4":{"df":2,"docs":{"130":{"tf":1.0},"146":{"tf":1.0}}},"5":{"df":1,"docs":{"146":{"tf":1.0}}},"df":0,"docs":{}},"2":{".":{"6":{"df":1,"docs":{"3":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}},"3":{"3":{"4":{"5":{"2":{"4":{"df":1,"docs":{"135":{"tf":1.7320508075688772}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":17,"docs":{"104":{"tf":1.0},"108":{"tf":2.0},"11":{"tf":1.4142135623730951},"130":{"tf":1.4142135623730951},"133":{"tf":1.0},"134":{"tf":1.0},"135":{"tf":1.4142135623730951},"136":{"tf":1.0},"142":{"tf":1.4142135623730951},"146":{"tf":1.7320508075688772},"147":{"tf":2.23606797749979},"149":{"tf":1.0},"27":{"tf":1.4142135623730951},"35":{"tf":1.0},"86":{"tf":1.4142135623730951},"94":{"tf":1.0},"95":{"tf":1.0}}},"5":{"df":8,"docs":{"108":{"tf":1.4142135623730951},"130":{"tf":1.7320508075688772},"133":{"tf":1.0},"136":{"tf":1.4142135623730951},"146":{"tf":1.0},"28":{"tf":1.4142135623730951},"35":{"tf":1.4142135623730951},"49":{"tf":1.0}}},"6":{"0":{"df":1,"docs":{"14":{"tf":1.0}}},"3":{"df":1,"docs":{"146":{"tf":1.4142135623730951}}},"df":10,"docs":{"10":{"tf":1.4142135623730951},"108":{"tf":1.4142135623730951},"11":{"tf":1.0},"127":{"tf":1.0},"130":{"tf":1.0},"134":{"tf":1.4142135623730951},"138":{"tf":1.4142135623730951},"139":{"tf":1.0},"146":{"tf":1.0},"9":{"tf":1.4142135623730951}},"×":{"1":{"+":{"3":{"df":0,"docs":{},"×":{"1":{"+":{"2":{"df":0,"docs":{},"×":{"3":{"df":0,"docs":{},"×":{"0":{"+":{"9":{"df":0,"docs":{},"×":{"(":{"df":0,"docs":{},"−":{"1":{")":{"+":{"0":{"df":1,"docs":{"10":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"6":{"df":0,"docs":{},"×":{"3":{"df":0,"docs":{},"×":{"0":{"+":{"9":{"df":0,"docs":{},"×":{"(":{"df":0,"docs":{},"−":{"1":{")":{"+":{"0":{"=":{"0":{"df":1,"docs":{"10":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"7":{"df":5,"docs":{"130":{"tf":1.0},"134":{"tf":1.4142135623730951},"139":{"tf":1.4142135623730951},"146":{"tf":1.0},"52":{"tf":1.0}}},"8":{"df":18,"docs":{"10":{"tf":1.0},"104":{"tf":1.0},"114":{"tf":1.0},"12":{"tf":1.0},"124":{"tf":1.0},"127":{"tf":1.4142135623730951},"13":{"tf":2.8284271247461903},"130":{"tf":1.4142135623730951},"133":{"tf":2.0},"136":{"tf":1.0},"14":{"tf":2.0},"146":{"tf":1.0},"151":{"tf":1.0},"49":{"tf":1.0},"52":{"tf":1.7320508075688772},"68":{"tf":1.4142135623730951},"7":{"tf":1.4142135623730951},"9":{"tf":1.0}}},"9":{".":{"4":{"df":2,"docs":{"131":{"tf":1.0},"138":{"tf":1.0}}},"6":{"df":1,"docs":{"3":{"tf":1.0}}},"7":{"df":1,"docs":{"139":{"tf":1.0}}},"8":{"df":2,"docs":{"139":{"tf":1.0},"146":{"tf":1.0}}},"9":{"df":2,"docs":{"138":{"tf":1.0},"146":{"tf":1.0}}},"df":0,"docs":{}},"df":4,"docs":{"10":{"tf":1.4142135623730951},"133":{"tf":1.0},"146":{"tf":1.0},"9":{"tf":1.4142135623730951}},"×":{"1":{"+":{"0":{"df":0,"docs":{},"×":{"0":{"+":{"9":{"df":0,"docs":{},"×":{"0":{"df":0,"docs":{},"×":{"0":{"+":{"8":{"df":0,"docs":{},"×":{"(":{"df":0,"docs":{},"−":{"1":{")":{"+":{"(":{"df":0,"docs":{},"−":{"1":{")":{"=":{"0":{"df":1,"docs":{"10":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"c":{"df":1,"docs":{"10":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"_":{"1":{"df":1,"docs":{"35":{"tf":1.0}}},"df":0,"docs":{},"r":{"a":{"df":0,"docs":{},"p":{"_":{"c":{"df":0,"docs":{},"h":{"a":{"df":0,"docs":{},"l":{"df":0,"docs":{},"l":{"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"df":0,"docs":{},"g":{"df":2,"docs":{"106":{"tf":1.0},"107":{"tf":1.0}}}}}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"a":{"(":{"df":0,"docs":{},"x":{")":{"df":0,"docs":{},"q":{"df":0,"docs":{},"l":{"df":0,"docs":{},"​":{"(":{"df":0,"docs":{},"x":{")":{"+":{"b":{"(":{"df":0,"docs":{},"x":{")":{"df":0,"docs":{},"q":{"df":0,"docs":{},"r":{"df":0,"docs":{},"​":{"(":{"df":0,"docs":{},"x":{")":{"+":{"a":{"(":{"df":0,"docs":{},"x":{")":{"b":{"(":{"df":0,"docs":{},"x":{")":{"df":0,"docs":{},"q":{"df":0,"docs":{},"m":{"df":0,"docs":{},"​":{"(":{"df":0,"docs":{},"x":{")":{"+":{"c":{"(":{"df":0,"docs":{},"x":{")":{"df":0,"docs":{},"q":{"df":0,"docs":{},"o":{"df":0,"docs":{},"​":{"(":{"df":0,"docs":{},"x":{")":{"+":{"df":0,"docs":{},"q":{"c":{"df":0,"docs":{},"​":{"(":{"df":0,"docs":{},"x":{")":{"+":{"df":0,"docs":{},"p":{"df":0,"docs":{},"i":{"(":{"df":0,"docs":{},"x":{")":{"=":{"0":{"df":1,"docs":{"14":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}}},"df":1,"docs":{"35":{"tf":1.0}}},"ζ":{")":{",":{"b":{"df":1,"docs":{"21":{"tf":1.0}}},"df":0,"docs":{}},"[":{"df":0,"docs":{},"q":{"df":0,"docs":{},"l":{"df":0,"docs":{},"​":{"]":{"1":{"df":0,"docs":{},"​":{"+":{"b":{"(":{"df":0,"docs":{},"ζ":{")":{"[":{"df":0,"docs":{},"q":{"df":0,"docs":{},"r":{"df":0,"docs":{},"​":{"]":{"1":{"df":1,"docs":{"21":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}},"ω":{"df":0,"docs":{},"i":{")":{"=":{"a":{"df":0,"docs":{},"i":{"df":1,"docs":{"14":{"tf":1.0}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}},",":{"b":{",":{"c":{",":{"df":0,"docs":{},"p":{"df":0,"docs":{},"i":{"df":1,"docs":{"16":{"tf":1.0}}}},"q":{"df":0,"docs":{},"l":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"q":{"df":0,"docs":{},"r":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"q":{"df":0,"docs":{},"m":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"q":{"df":0,"docs":{},"o":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"q":{"c":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"p":{"df":0,"docs":{},"i":{"df":1,"docs":{"14":{"tf":1.4142135623730951}}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}},"df":0,"docs":{}}}}},"df":0,"docs":{}}}}},"df":0,"docs":{}}}}},"df":5,"docs":{"11":{"tf":1.0},"13":{"tf":1.0},"14":{"tf":1.0},"16":{"tf":1.4142135623730951},"35":{"tf":1.0}}},"df":0,"docs":{}},"df":1,"docs":{"21":{"tf":1.0}},"∈":{"df":0,"docs":{},"f":{"df":1,"docs":{"92":{"tf":1.0}}}}},"df":0,"docs":{}},"0":{"df":4,"docs":{"104":{"tf":1.0},"106":{"tf":1.4142135623730951},"14":{"tf":1.0},"149":{"tf":2.8284271247461903}},"​":{"+":{"df":0,"docs":{},"x":{")":{"(":{"a":{"1":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"x":{")":{"=":{"(":{"b":{"0":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"x":{")":{"(":{"b":{"1":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"x":{"df":1,"docs":{"14":{"tf":1.4142135623730951}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"β":{"a":{"1":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"γ":{")":{"(":{"a":{"2":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"β":{"a":{"3":{"df":1,"docs":{"14":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"γ":{")":{"(":{"a":{"1":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"γ":{")":{"=":{"(":{"b":{"0":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"γ":{")":{"(":{"b":{"1":{"df":1,"docs":{"14":{"tf":1.4142135623730951}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},",":{"df":0,"docs":{},"…":{",":{"a":{"df":0,"docs":{},"k":{"df":0,"docs":{},"−":{"1":{"df":1,"docs":{"14":{"tf":1.4142135623730951}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},",":{"a":{"1":{"df":1,"docs":{"14":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}},"=":{"df":0,"docs":{},"η":{"3":{"df":0,"docs":{},"i":{"+":{"df":0,"docs":{},"j":{"df":1,"docs":{"14":{"tf":1.0}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"a":{"1":{"df":1,"docs":{"14":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}}},"1":{"df":4,"docs":{"104":{"tf":1.0},"106":{"tf":1.7320508075688772},"14":{"tf":1.0},"149":{"tf":2.8284271247461903}},"​":{",":{"df":0,"docs":{},"…":{",":{"a":{"df":0,"docs":{},"k":{"df":1,"docs":{"92":{"tf":1.0}}},"l":{"df":2,"docs":{"94":{"tf":1.4142135623730951},"95":{"tf":1.0}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"=":{"df":0,"docs":{},"v":{"df":1,"docs":{"14":{"tf":1.0}}}},"df":0,"docs":{}}},"2":{"df":1,"docs":{"149":{"tf":2.8284271247461903}}},"3":{"df":1,"docs":{"149":{"tf":2.8284271247461903}}},":":{"=":{"(":{"b":{"1":{"df":0,"docs":{},"​":{"df":0,"docs":{},"x":{"+":{"b":{"2":{"df":0,"docs":{},"​":{")":{"df":0,"docs":{},"z":{"df":0,"docs":{},"h":{"df":0,"docs":{},"​":{"+":{"a":{"df":1,"docs":{"24":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"=":{"(":{"df":0,"docs":{},"f":{"(":{"a":{")":{",":{"df":0,"docs":{},"f":{"(":{"a":{"b":{")":{",":{"df":0,"docs":{},"f":{"(":{"a":{"b":{"2":{")":{",":{"df":0,"docs":{},"…":{",":{"df":0,"docs":{},"f":{"(":{"a":{"b":{"df":0,"docs":{},"l":{"df":1,"docs":{"92":{"tf":1.0}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"y":{"0":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"…":{",":{"df":0,"docs":{},"y":{"df":0,"docs":{},"l":{"df":1,"docs":{"92":{"tf":1.0}}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{},"{":{"(":{"(":{"df":0,"docs":{},"i":{",":{"df":0,"docs":{},"j":{")":{",":{"df":0,"docs":{},"t":{"df":0,"docs":{},"i":{",":{"df":0,"docs":{},"j":{"df":0,"docs":{},"​":{")":{":":{"(":{"df":0,"docs":{},"i":{",":{"df":0,"docs":{},"j":{")":{"df":0,"docs":{},"∈":{"df":0,"docs":{},"i":{"df":1,"docs":{"14":{"tf":2.0}}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"a":{"0":{"df":0,"docs":{},"​":{",":{"a":{"1":{"df":0,"docs":{},"​":{")":{",":{"(":{"a":{"2":{"df":0,"docs":{},"​":{",":{"a":{"3":{"df":1,"docs":{"14":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{},"η":{"3":{"df":0,"docs":{},"i":{"+":{"df":0,"docs":{},"j":{",":{"df":0,"docs":{},"t":{"df":0,"docs":{},"i":{",":{"df":0,"docs":{},"j":{"df":0,"docs":{},"​":{")":{":":{"(":{"df":0,"docs":{},"i":{",":{"df":0,"docs":{},"j":{")":{"df":0,"docs":{},"∈":{"df":0,"docs":{},"i":{"df":1,"docs":{"14":{"tf":1.0}}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"4":{",":{"1":{"5":{"df":1,"docs":{"14":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"a":{"0":{"df":0,"docs":{},"​":{",":{"a":{"1":{"df":1,"docs":{"14":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{},"…":{",":{"a":{"df":0,"docs":{},"k":{"df":0,"docs":{},"−":{"1":{"df":1,"docs":{"14":{"tf":1.4142135623730951}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{},"ˉ":{"0":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"…":{",":{"a":{"df":0,"docs":{},"ˉ":{"df":0,"docs":{},"k":{"df":0,"docs":{},"−":{"1":{"df":1,"docs":{"14":{"tf":1.0}}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"]":{"1":{"df":0,"docs":{},"​":{",":{"[":{"b":{"]":{"1":{"df":0,"docs":{},"​":{",":{"[":{"c":{"]":{"1":{"df":2,"docs":{"24":{"tf":1.0},"32":{"tf":1.0}},"​":{",":{"[":{"df":0,"docs":{},"z":{"]":{"1":{"df":0,"docs":{},"​":{",":{"[":{"df":0,"docs":{},"t":{"df":0,"docs":{},"l":{"df":0,"docs":{},"o":{"df":0,"docs":{},"​":{"]":{"1":{"df":0,"docs":{},"​":{",":{"[":{"df":0,"docs":{},"t":{"df":0,"docs":{},"m":{"df":0,"docs":{},"i":{"d":{"df":0,"docs":{},"​":{"]":{"1":{"df":0,"docs":{},"​":{",":{"[":{"df":0,"docs":{},"t":{"df":0,"docs":{},"h":{"df":0,"docs":{},"i":{"df":0,"docs":{},"​":{"]":{"1":{"df":0,"docs":{},"​":{",":{"a":{"df":0,"docs":{},"ˉ":{",":{"b":{"df":0,"docs":{},"ˉ":{",":{"c":{"df":0,"docs":{},"ˉ":{",":{"df":0,"docs":{},"s":{"df":0,"docs":{},"σ":{"1":{"df":0,"docs":{},"​":{"df":0,"docs":{},"ˉ":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"s":{"df":0,"docs":{},"σ":{"2":{"df":0,"docs":{},"​":{"df":0,"docs":{},"ˉ":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"z":{"df":0,"docs":{},"ω":{"df":0,"docs":{},"​":{"df":0,"docs":{},"ˉ":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"π":{"b":{"a":{"df":0,"docs":{},"t":{"c":{"df":0,"docs":{},"h":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"π":{"df":0,"docs":{},"s":{"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"df":0,"docs":{},"g":{"df":0,"docs":{},"l":{"df":0,"docs":{},"e":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"p":{"df":0,"docs":{},"ˉ":{"df":0,"docs":{},"​":{"df":0,"docs":{},"n":{"c":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"t":{"df":1,"docs":{"29":{"tf":1.0}}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}}},"df":0,"docs":{}}}}}}}}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}}}}},"df":0,"docs":{}}}}},"df":0,"docs":{}}}},"df":0,"docs":{}}}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{},"q":{"df":0,"docs":{},"l":{"df":0,"docs":{},"​":{"]":{"1":{"df":1,"docs":{"21":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"_":{"0":{"df":2,"docs":{"49":{"tf":1.0},"51":{"tf":1.0}}},"1":{"df":3,"docs":{"35":{"tf":1.0},"49":{"tf":1.0},"51":{"tf":1.0}}},"2":{"df":1,"docs":{"51":{"tf":1.0}}},"3":{"df":1,"docs":{"51":{"tf":1.0}}},"4":{"df":1,"docs":{"51":{"tf":1.0}}},"5":{"df":2,"docs":{"51":{"tf":1.0},"52":{"tf":1.4142135623730951}}},"6":{"df":1,"docs":{"51":{"tf":1.0}}},"7":{"df":1,"docs":{"51":{"tf":1.0}}},"df":0,"docs":{},"i":{"df":2,"docs":{"49":{"tf":1.0},"51":{"tf":1.0}}},"n":{"df":1,"docs":{"49":{"tf":1.4142135623730951}}},"z":{"df":0,"docs":{},"e":{"df":0,"docs":{},"t":{"a":{"df":1,"docs":{"35":{"tf":1.0}}},"df":0,"docs":{}}}},"{":{"df":0,"docs":{},"n":{"+":{"2":{"df":1,"docs":{"49":{"tf":1.0}}},"df":0,"docs":{}},"df":1,"docs":{"49":{"tf":1.0}}}}},"b":{"c":{"df":1,"docs":{"13":{"tf":1.0}}},"df":0,"docs":{},"l":{"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"d":{"df":1,"docs":{"20":{"tf":1.0}},"e":{"d":{"df":0,"docs":{},"​":{"(":{"df":0,"docs":{},"ω":{"df":0,"docs":{},"i":{")":{"=":{"a":{"(":{"df":0,"docs":{},"ω":{"df":0,"docs":{},"i":{"df":1,"docs":{"20":{"tf":1.0}}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":1,"docs":{"20":{"tf":1.0}}}}},":":{"=":{"(":{"b":{"0":{"df":0,"docs":{},"​":{"+":{"b":{"1":{"df":0,"docs":{},"​":{"df":0,"docs":{},"x":{"+":{"df":0,"docs":{},"⋯":{"+":{"b":{"df":0,"docs":{},"k":{"df":0,"docs":{},"​":{"df":0,"docs":{},"x":{"df":0,"docs":{},"k":{")":{"df":0,"docs":{},"z":{"df":0,"docs":{},"h":{"df":0,"docs":{},"​":{"+":{"a":{"df":1,"docs":{"20":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}}}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"o":{"df":0,"docs":{},"v":{"df":22,"docs":{"110":{"tf":1.0},"112":{"tf":1.0},"113":{"tf":1.0},"114":{"tf":2.0},"117":{"tf":1.0},"118":{"tf":1.0},"123":{"tf":1.0},"14":{"tf":1.0},"16":{"tf":1.0},"50":{"tf":1.0},"51":{"tf":1.0},"53":{"tf":1.0},"55":{"tf":1.0},"56":{"tf":1.0},"58":{"tf":1.4142135623730951},"60":{"tf":1.0},"62":{"tf":1.0},"64":{"tf":1.0},"66":{"tf":1.0},"74":{"tf":1.0},"86":{"tf":1.0},"95":{"tf":1.0}}}},"s":{"df":0,"docs":{},"t":{"df":0,"docs":{},"r":{"a":{"c":{"df":0,"docs":{},"t":{"df":2,"docs":{"48":{"tf":1.0},"49":{"tf":1.0}}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"u":{"df":0,"docs":{},"s":{"df":2,"docs":{"90":{"tf":1.0},"92":{"tf":1.0}}}}},"c":{"c":{"df":0,"docs":{},"e":{"df":0,"docs":{},"p":{"df":0,"docs":{},"t":{"df":10,"docs":{"102":{"tf":1.0},"19":{"tf":1.0},"33":{"tf":1.7320508075688772},"7":{"tf":1.0},"73":{"tf":1.0},"74":{"tf":1.0},"77":{"tf":1.4142135623730951},"80":{"tf":1.4142135623730951},"92":{"tf":1.0},"95":{"tf":1.4142135623730951}}}},"s":{"df":0,"docs":{},"s":{"df":5,"docs":{"107":{"tf":1.0},"133":{"tf":1.7320508075688772},"134":{"tf":1.4142135623730951},"135":{"tf":2.0},"146":{"tf":2.0}}}}},"o":{"df":0,"docs":{},"r":{"d":{"df":4,"docs":{"50":{"tf":1.0},"58":{"tf":1.0},"60":{"tf":1.0},"85":{"tf":1.0}}},"df":0,"docs":{}},"u":{"df":0,"docs":{},"n":{"df":0,"docs":{},"t":{"df":1,"docs":{"149":{"tf":1.0}}}}}}},"df":0,"docs":{},"h":{"df":0,"docs":{},"i":{"df":0,"docs":{},"e":{"df":0,"docs":{},"v":{"df":4,"docs":{"133":{"tf":1.0},"14":{"tf":1.0},"44":{"tf":1.0},"60":{"tf":1.0}}}}}},"t":{"df":1,"docs":{"14":{"tf":1.0}},"u":{"a":{"df":0,"docs":{},"l":{"df":22,"docs":{"102":{"tf":1.0},"107":{"tf":1.0},"108":{"tf":1.0},"113":{"tf":1.4142135623730951},"116":{"tf":1.0},"118":{"tf":1.0},"12":{"tf":1.0},"125":{"tf":2.0},"131":{"tf":1.0},"133":{"tf":2.23606797749979},"135":{"tf":1.0},"147":{"tf":1.0},"151":{"tf":1.0},"20":{"tf":1.0},"21":{"tf":1.4142135623730951},"32":{"tf":1.0},"34":{"tf":1.0},"46":{"tf":1.0},"50":{"tf":1.4142135623730951},"63":{"tf":1.4142135623730951},"67":{"tf":1.0},"9":{"tf":1.0}}}},"df":0,"docs":{}}}},"d":{"d":{"df":19,"docs":{"10":{"tf":1.4142135623730951},"102":{"tf":1.0},"114":{"tf":1.0},"13":{"tf":1.0},"145":{"tf":1.0},"146":{"tf":1.4142135623730951},"149":{"tf":1.0},"151":{"tf":1.7320508075688772},"24":{"tf":1.4142135623730951},"25":{"tf":1.0},"26":{"tf":1.0},"27":{"tf":1.0},"32":{"tf":2.0},"40":{"tf":1.0},"44":{"tf":1.0},"57":{"tf":1.0},"83":{"tf":1.0},"94":{"tf":2.8284271247461903},"95":{"tf":2.8284271247461903}},"i":{"df":0,"docs":{},"t":{"df":12,"docs":{"10":{"tf":1.4142135623730951},"128":{"tf":1.0},"13":{"tf":1.0},"130":{"tf":1.0},"133":{"tf":1.4142135623730951},"142":{"tf":1.4142135623730951},"146":{"tf":1.0},"147":{"tf":1.0},"19":{"tf":1.4142135623730951},"57":{"tf":1.0},"87":{"tf":1.0},"9":{"tf":1.0}},"i":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"df":4,"docs":{"146":{"tf":1.0},"150":{"tf":1.0},"151":{"tf":1.0},"68":{"tf":1.0}}}}}}},"r":{"df":2,"docs":{"133":{"tf":2.23606797749979},"135":{"tf":1.0}},"e":{"df":0,"docs":{},"s":{"df":0,"docs":{},"s":{"df":8,"docs":{"133":{"tf":2.8284271247461903},"134":{"tf":2.449489742783178},"135":{"tf":1.4142135623730951},"142":{"tf":1.4142135623730951},"144":{"tf":1.0},"146":{"tf":2.8284271247461903},"147":{"tf":1.7320508075688772},"151":{"tf":1.4142135623730951}}}}}}},"df":14,"docs":{"117":{"tf":1.0},"133":{"tf":1.0},"146":{"tf":2.0},"147":{"tf":1.4142135623730951},"20":{"tf":1.0},"31":{"tf":1.0},"32":{"tf":1.0},"34":{"tf":1.0},"42":{"tf":1.0},"70":{"tf":1.0},"83":{"tf":1.0},"84":{"tf":1.0},"88":{"tf":1.0},"89":{"tf":1.0}},"j":{"df":0,"docs":{},"u":{"df":0,"docs":{},"s":{"df":0,"docs":{},"t":{"df":1,"docs":{"71":{"tf":1.0}}}}}},"v":{"a":{"df":0,"docs":{},"n":{"c":{"df":3,"docs":{"52":{"tf":1.0},"79":{"tf":1.0},"83":{"tf":1.0}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{},"g":{"a":{"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"df":4,"docs":{"111":{"tf":1.0},"146":{"tf":1.0},"66":{"tf":1.0},"67":{"tf":1.0}},"s":{"df":0,"docs":{},"t":{"df":2,"docs":{"108":{"tf":1.0},"146":{"tf":1.0}}}}}}},"df":0,"docs":{},"g":{"df":0,"docs":{},"r":{"df":0,"docs":{},"e":{"df":0,"docs":{},"g":{"df":1,"docs":{"145":{"tf":1.0}}}}}},"r":{"df":0,"docs":{},"e":{"df":2,"docs":{"104":{"tf":1.0},"84":{"tf":1.0}}}}},"i":{",":{"0":{"df":0,"docs":{},"​":{"+":{"a":{"df":0,"docs":{},"i":{",":{"1":{"df":0,"docs":{},"​":{"df":0,"docs":{},"β":{"+":{"df":0,"docs":{},"γ":{",":{"df":0,"docs":{},"…":{",":{"a":{"df":0,"docs":{},"k":{"df":0,"docs":{},"−":{"1":{",":{"0":{"df":0,"docs":{},"​":{"+":{"a":{"df":0,"docs":{},"k":{"df":0,"docs":{},"−":{"1":{",":{"1":{"df":1,"docs":{"14":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{},"m":{"df":2,"docs":{"50":{"tf":1.0},"84":{"tf":1.4142135623730951}}},"r":{":":{":":{"df":0,"docs":{},"f":{"df":0,"docs":{},"r":{"a":{"df":0,"docs":{},"m":{"df":0,"docs":{},"e":{":":{":":{"df":0,"docs":{},"f":{"df":0,"docs":{},"r":{"a":{"df":0,"docs":{},"m":{"df":0,"docs":{},"e":{"<":{"df":0,"docs":{},"s":{"df":0,"docs":{},"e":{"df":0,"docs":{},"l":{"df":0,"docs":{},"f":{":":{":":{"df":0,"docs":{},"f":{"df":0,"docs":{},"i":{"df":0,"docs":{},"e":{"df":0,"docs":{},"l":{"d":{"df":2,"docs":{"107":{"tf":1.0},"114":{"tf":1.0}}},"df":0,"docs":{}}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}}}},"df":0,"docs":{}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"c":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"df":0,"docs":{},"t":{"df":0,"docs":{},"e":{"df":0,"docs":{},"x":{"df":0,"docs":{},"t":{"df":1,"docs":{"109":{"tf":1.4142135623730951}}}}}}}}},"df":14,"docs":{"105":{"tf":2.23606797749979},"106":{"tf":1.0},"108":{"tf":1.0},"109":{"tf":2.23606797749979},"110":{"tf":1.0},"113":{"tf":1.0},"114":{"tf":1.4142135623730951},"48":{"tf":1.0},"50":{"tf":2.449489742783178},"65":{"tf":1.0},"66":{"tf":1.0},"70":{"tf":1.0},"83":{"tf":1.0},"91":{"tf":1.0}}},"​":{"(":{"df":0,"docs":{},"q":{"df":0,"docs":{},"l":{"df":0,"docs":{},"​":{")":{"df":0,"docs":{},"i":{"df":0,"docs":{},"​":{"+":{"b":{"df":0,"docs":{},"i":{"df":0,"docs":{},"​":{"(":{"df":0,"docs":{},"q":{"df":0,"docs":{},"r":{"df":0,"docs":{},"​":{")":{"df":0,"docs":{},"i":{"df":0,"docs":{},"​":{"+":{"a":{"df":0,"docs":{},"i":{"df":0,"docs":{},"​":{"b":{"df":0,"docs":{},"i":{"df":0,"docs":{},"​":{"df":0,"docs":{},"q":{"df":0,"docs":{},"m":{"df":0,"docs":{},"​":{"+":{"c":{"df":0,"docs":{},"i":{"df":0,"docs":{},"​":{"(":{"df":0,"docs":{},"q":{"df":0,"docs":{},"o":{"df":0,"docs":{},"​":{")":{"df":0,"docs":{},"i":{"df":0,"docs":{},"​":{"+":{"(":{"df":0,"docs":{},"q":{"c":{"df":0,"docs":{},"​":{")":{"df":0,"docs":{},"i":{"df":0,"docs":{},"​":{"+":{"(":{"df":0,"docs":{},"p":{"df":0,"docs":{},"i":{")":{"df":0,"docs":{},"i":{"df":0,"docs":{},"​":{"=":{"0":{"df":3,"docs":{"13":{"tf":1.0},"14":{"tf":1.0},"16":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"=":{"0":{"df":2,"docs":{"10":{"tf":1.0},"11":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}}}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}}}},",":{"b":{"df":0,"docs":{},"i":{"df":0,"docs":{},"​":{"df":0,"docs":{},"∈":{"df":0,"docs":{},"f":{"df":1,"docs":{"14":{"tf":1.0}}}}}}},"df":0,"docs":{}},"=":{"(":{"df":0,"docs":{},"y":{"0":{"(":{"df":0,"docs":{},"i":{")":{",":{"df":0,"docs":{},"…":{",":{"df":0,"docs":{},"y":{"df":0,"docs":{},"l":{"(":{"df":0,"docs":{},"i":{"df":1,"docs":{"92":{"tf":1.0}}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"l":{"df":0,"docs":{},"g":{"df":0,"docs":{},"e":{"b":{"df":0,"docs":{},"r":{"a":{"df":1,"docs":{"48":{"tf":1.0}}},"df":0,"docs":{}}},"df":0,"docs":{}},"o":{"df":0,"docs":{},"r":{"df":0,"docs":{},"i":{"df":0,"docs":{},"t":{"df":0,"docs":{},"h":{"df":0,"docs":{},"m":{"df":9,"docs":{"121":{"tf":1.0},"19":{"tf":1.4142135623730951},"23":{"tf":1.7320508075688772},"3":{"tf":1.0},"30":{"tf":1.4142135623730951},"34":{"tf":1.7320508075688772},"45":{"tf":1.0},"7":{"tf":1.7320508075688772},"98":{"tf":1.0}}}}}}}}},"l":{"df":0,"docs":{},"o":{"c":{"df":1,"docs":{"131":{"tf":1.4142135623730951}}},"df":0,"docs":{},"w":{"df":6,"docs":{"124":{"tf":1.0},"131":{"tf":1.0},"14":{"tf":1.0},"19":{"tf":1.0},"64":{"tf":1.0},"75":{"tf":1.0}}}}},"o":{"df":0,"docs":{},"n":{"df":0,"docs":{},"g":{"df":10,"docs":{"103":{"tf":1.0},"110":{"tf":1.0},"113":{"tf":1.4142135623730951},"114":{"tf":1.0},"127":{"tf":1.0},"21":{"tf":2.0},"5":{"tf":1.0},"7":{"tf":1.0},"80":{"tf":1.0},"91":{"tf":1.0}}}}},"p":{"df":0,"docs":{},"h":{"a":{"_":{"a":{"df":0,"docs":{},"n":{"d":{"_":{"b":{"df":0,"docs":{},"e":{"df":0,"docs":{},"t":{"a":{"_":{"b":{"df":0,"docs":{},"o":{"df":0,"docs":{},"u":{"df":0,"docs":{},"n":{"d":{"a":{"df":0,"docs":{},"r":{"df":0,"docs":{},"y":{"_":{"c":{"df":0,"docs":{},"o":{"df":0,"docs":{},"e":{"df":0,"docs":{},"f":{"df":0,"docs":{},"f":{"df":0,"docs":{},"i":{"c":{"df":0,"docs":{},"i":{"df":1,"docs":{"114":{"tf":1.0}}}},"df":0,"docs":{}}}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{},"t":{"df":0,"docs":{},"r":{"a":{"df":0,"docs":{},"n":{"df":0,"docs":{},"s":{"df":0,"docs":{},"i":{"df":0,"docs":{},"t":{"df":0,"docs":{},"i":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"_":{"c":{"df":0,"docs":{},"o":{"df":0,"docs":{},"e":{"df":0,"docs":{},"f":{"df":0,"docs":{},"f":{"df":0,"docs":{},"i":{"c":{"df":0,"docs":{},"i":{"df":1,"docs":{"114":{"tf":1.0}}}},"df":0,"docs":{}}}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}}}}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":3,"docs":{"114":{"tf":1.0},"117":{"tf":1.0},"118":{"tf":1.0}}},"df":0,"docs":{}}},"r":{"df":0,"docs":{},"e":{"a":{"d":{"df":0,"docs":{},"i":{"df":9,"docs":{"10":{"tf":1.0},"14":{"tf":1.4142135623730951},"146":{"tf":1.4142135623730951},"21":{"tf":1.0},"23":{"tf":1.0},"32":{"tf":1.0},"50":{"tf":1.0},"84":{"tf":1.4142135623730951},"91":{"tf":1.0}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"t":{"df":0,"docs":{},"h":{"df":0,"docs":{},"o":{"df":0,"docs":{},"u":{"df":0,"docs":{},"g":{"df":0,"docs":{},"h":{"df":1,"docs":{"130":{"tf":1.0}}}}}}},"o":{"df":0,"docs":{},"g":{"df":0,"docs":{},"e":{"df":0,"docs":{},"t":{"df":0,"docs":{},"h":{"df":2,"docs":{"10":{"tf":1.0},"14":{"tf":1.4142135623730951}}}}}}}},"w":{"a":{"df":0,"docs":{},"y":{"df":9,"docs":{"107":{"tf":1.0},"12":{"tf":1.0},"124":{"tf":1.7320508075688772},"131":{"tf":1.0},"133":{"tf":1.0},"134":{"tf":1.0},"56":{"tf":1.0},"73":{"tf":1.0},"82":{"tf":1.0}}}},"df":0,"docs":{}}},"m":{"b":{"df":0,"docs":{},"i":{"df":0,"docs":{},"g":{"df":0,"docs":{},"u":{"df":1,"docs":{"144":{"tf":1.0}}}}}},"df":0,"docs":{},"o":{"df":0,"docs":{},"u":{"df":0,"docs":{},"n":{"df":0,"docs":{},"t":{"df":3,"docs":{"110":{"tf":1.0},"3":{"tf":1.0},"80":{"tf":1.0}}}}}}},"n":{"a":{"df":0,"docs":{},"l":{"df":0,"docs":{},"y":{"df":0,"docs":{},"z":{"df":2,"docs":{"134":{"tf":1.0},"77":{"tf":1.0}}}}}},"df":0,"docs":{},"o":{"df":0,"docs":{},"t":{"df":0,"docs":{},"h":{"df":7,"docs":{"146":{"tf":1.0},"149":{"tf":1.4142135623730951},"5":{"tf":1.0},"53":{"tf":1.0},"70":{"tf":1.0},"73":{"tf":1.0},"74":{"tf":1.0}}}}},"s":{"df":0,"docs":{},"w":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":1,"docs":{"114":{"tf":1.0}}}}}},"y":{"df":0,"docs":{},"m":{"df":0,"docs":{},"o":{"df":0,"docs":{},"r":{"df":1,"docs":{"116":{"tf":1.0}}}}},"o":{"df":0,"docs":{},"n":{"df":1,"docs":{"75":{"tf":1.0}}}},"t":{"df":0,"docs":{},"h":{"df":1,"docs":{"50":{"tf":1.0}}}},"w":{"a":{"df":0,"docs":{},"y":{"df":2,"docs":{"14":{"tf":1.0},"16":{"tf":1.0}}}},"df":0,"docs":{}}}},"p":{"_":{"df":0,"docs":{},"u":{"df":0,"docs":{},"p":{"d":{"df":1,"docs":{"146":{"tf":1.0}}},"df":0,"docs":{}}}},"a":{"df":0,"docs":{},"r":{"df":0,"docs":{},"t":{"df":1,"docs":{"35":{"tf":1.0}}}}},"df":6,"docs":{"136":{"tf":1.4142135623730951},"141":{"tf":1.0},"142":{"tf":1.0},"144":{"tf":1.0},"146":{"tf":1.7320508075688772},"147":{"tf":1.0}},"i":{"df":12,"docs":{"104":{"tf":1.7320508075688772},"105":{"tf":1.0},"106":{"tf":1.0},"107":{"tf":1.0},"108":{"tf":1.0},"109":{"tf":1.0},"110":{"tf":1.0},"121":{"tf":1.0},"34":{"tf":1.4142135623730951},"43":{"tf":1.7320508075688772},"44":{"tf":1.0},"45":{"tf":1.0}}},"p":{"df":0,"docs":{},"e":{"a":{"df":0,"docs":{},"r":{"df":6,"docs":{"119":{"tf":1.0},"130":{"tf":1.0},"131":{"tf":1.0},"134":{"tf":1.7320508075688772},"146":{"tf":1.0},"95":{"tf":1.0}}}},"df":0,"docs":{},"n":{"d":{"df":3,"docs":{"118":{"tf":1.7320508075688772},"142":{"tf":1.4142135623730951},"40":{"tf":2.449489742783178}}},"df":0,"docs":{}}},"l":{"df":0,"docs":{},"i":{"c":{"df":1,"docs":{"42":{"tf":1.0}}},"df":12,"docs":{"109":{"tf":1.4142135623730951},"125":{"tf":1.0},"14":{"tf":1.4142135623730951},"146":{"tf":1.0},"149":{"tf":1.4142135623730951},"150":{"tf":1.4142135623730951},"3":{"tf":1.0},"57":{"tf":1.0},"59":{"tf":1.0},"68":{"tf":1.0},"94":{"tf":1.7320508075688772},"97":{"tf":1.0}}}},"r":{"df":0,"docs":{},"o":{"a":{"c":{"df":0,"docs":{},"h":{"df":3,"docs":{"101":{"tf":1.0},"14":{"tf":1.0},"146":{"tf":1.0}}}},"df":0,"docs":{}},"df":0,"docs":{},"p":{"df":0,"docs":{},"r":{"df":0,"docs":{},"i":{"df":3,"docs":{"114":{"tf":1.4142135623730951},"13":{"tf":1.0},"66":{"tf":1.0}}}}}}}}},"q":{"df":0,"docs":{},"l":{"df":0,"docs":{},"​":{"+":{"b":{"df":0,"docs":{},"q":{"df":0,"docs":{},"r":{"df":1,"docs":{"21":{"tf":1.0}},"​":{"+":{"a":{"b":{"df":0,"docs":{},"q":{"df":0,"docs":{},"m":{"df":0,"docs":{},"​":{"+":{"c":{"df":0,"docs":{},"q":{"df":0,"docs":{},"o":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"q":{"c":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"p":{"df":0,"docs":{},"i":{"=":{"df":0,"docs":{},"z":{"df":0,"docs":{},"h":{"df":0,"docs":{},"​":{"df":0,"docs":{},"t":{"1":{"df":1,"docs":{"16":{"tf":1.0}}},"df":1,"docs":{"14":{"tf":1.0}}}}}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"r":{"b":{"df":0,"docs":{},"i":{"df":0,"docs":{},"t":{"df":0,"docs":{},"r":{"a":{"df":0,"docs":{},"r":{"df":0,"docs":{},"i":{"df":1,"docs":{"142":{"tf":1.0}}}}},"df":0,"docs":{}}}}},"df":1,"docs":{"28":{"tf":1.0}},"g":{"df":0,"docs":{},"u":{"df":0,"docs":{},"m":{"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"df":0,"docs":{},"t":{"df":4,"docs":{"107":{"tf":1.0},"114":{"tf":1.0},"146":{"tf":1.0},"147":{"tf":1.4142135623730951}}}}}}}},"i":{"df":0,"docs":{},"t":{"df":0,"docs":{},"h":{"df":0,"docs":{},"m":{"df":0,"docs":{},"e":{"df":0,"docs":{},"t":{"df":9,"docs":{"14":{"tf":1.0},"4":{"tf":1.7320508075688772},"50":{"tf":1.0},"69":{"tf":1.4142135623730951},"7":{"tf":1.0},"70":{"tf":1.4142135623730951},"8":{"tf":1.4142135623730951},"83":{"tf":1.4142135623730951},"94":{"tf":1.0}}}}}}}},"o":{"df":0,"docs":{},"u":{"df":0,"docs":{},"n":{"d":{"df":5,"docs":{"104":{"tf":1.0},"124":{"tf":1.0},"50":{"tf":1.0},"65":{"tf":1.0},"68":{"tf":1.0}}},"df":0,"docs":{}}}},"r":{"a":{"df":0,"docs":{},"n":{"df":0,"docs":{},"g":{"df":1,"docs":{"127":{"tf":1.0}}}}},"df":0,"docs":{},"i":{"df":0,"docs":{},"v":{"df":1,"docs":{"15":{"tf":1.0}}}}},"t":{"df":0,"docs":{},"i":{"c":{"df":0,"docs":{},"l":{"df":1,"docs":{"101":{"tf":1.0}}}},"df":0,"docs":{}}}},"s":{"c":{"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"d":{"df":4,"docs":{"130":{"tf":1.0},"132":{"tf":1.0},"134":{"tf":1.0},"135":{"tf":1.0}}},"df":0,"docs":{}}}},"df":0,"docs":{},"i":{"d":{"df":2,"docs":{"13":{"tf":1.0},"7":{"tf":1.0}}},"df":0,"docs":{}},"k":{"df":4,"docs":{"58":{"tf":1.4142135623730951},"60":{"tf":1.0},"72":{"tf":1.0},"80":{"tf":1.0}}},"p":{"df":0,"docs":{},"e":{"c":{"df":0,"docs":{},"t":{"df":1,"docs":{"70":{"tf":1.0}}}},"df":0,"docs":{}}},"s":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":0,"docs":{},"t":{"!":{"(":{"df":0,"docs":{},"v":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":0,"docs":{},"i":{"df":0,"docs":{},"f":{"df":0,"docs":{},"i":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{".":{"df":0,"docs":{},"v":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":0,"docs":{},"i":{"df":0,"docs":{},"f":{"df":0,"docs":{},"i":{"df":1,"docs":{"35":{"tf":1.4142135623730951}}},"y":{"(":{"&":{"df":0,"docs":{},"p":{"df":0,"docs":{},"r":{"df":0,"docs":{},"o":{"df":0,"docs":{},"o":{"df":0,"docs":{},"f":{"df":1,"docs":{"44":{"tf":1.0}}}}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}}}}}},"df":0,"docs":{}}}},"y":{"(":{"&":{"df":0,"docs":{},"p":{"df":0,"docs":{},"r":{"df":0,"docs":{},"o":{"df":0,"docs":{},"o":{"df":0,"docs":{},"f":{"df":1,"docs":{"110":{"tf":1.0}}}}}}}},"df":0,"docs":{}},":":{":":{"<":{"df":0,"docs":{},"f":{"df":1,"docs":{"104":{"tf":1.0}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}}}}}},"df":0,"docs":{}},"(":{"df":0,"docs":{},"x":{"*":{"df":0,"docs":{},"e":{"+":{"5":{"=":{"=":{"df":0,"docs":{},"i":{"df":1,"docs":{"34":{"tf":1.0}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":1,"docs":{"35":{"tf":1.0}}}},"df":4,"docs":{"13":{"tf":1.0},"142":{"tf":1.0},"34":{"tf":1.0},"44":{"tf":1.4142135623730951}}}}},"i":{"df":0,"docs":{},"g":{"df":0,"docs":{},"n":{"df":3,"docs":{"101":{"tf":1.4142135623730951},"151":{"tf":1.0},"44":{"tf":1.0}}}}},"o":{"c":{"df":0,"docs":{},"i":{"df":4,"docs":{"130":{"tf":1.0},"149":{"tf":1.4142135623730951},"151":{"tf":1.4142135623730951},"79":{"tf":1.0}}}},"df":0,"docs":{}},"u":{"df":0,"docs":{},"m":{"df":13,"docs":{"118":{"tf":1.0},"149":{"tf":1.4142135623730951},"150":{"tf":1.0},"151":{"tf":1.0},"21":{"tf":1.0},"23":{"tf":1.0},"51":{"tf":1.0},"70":{"tf":1.0},"77":{"tf":1.0},"84":{"tf":1.0},"89":{"tf":1.0},"91":{"tf":2.23606797749979},"95":{"tf":1.4142135623730951}}}}},"t":{"df":0,"docs":{},"u":{"df":0,"docs":{},"t":{"df":1,"docs":{"13":{"tf":1.0}}}}}},"t":{"df":1,"docs":{"38":{"tf":1.0}},"t":{"a":{"c":{"df":0,"docs":{},"k":{"df":1,"docs":{"42":{"tf":1.0}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"u":{"df":0,"docs":{},"g":{"df":0,"docs":{},"m":{"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"df":0,"docs":{},"t":{"df":3,"docs":{"142":{"tf":1.0},"146":{"tf":1.0},"149":{"tf":1.0}}}}}}},"t":{"df":0,"docs":{},"h":{"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"df":0,"docs":{},"t":{"df":6,"docs":{"101":{"tf":1.7320508075688772},"102":{"tf":1.0},"72":{"tf":1.7320508075688772},"80":{"tf":1.4142135623730951},"92":{"tf":1.4142135623730951},"95":{"tf":1.7320508075688772}}}}}}}},"v":{"a":{"df":0,"docs":{},"i":{"df":0,"docs":{},"l":{"df":2,"docs":{"130":{"tf":1.0},"144":{"tf":1.0}}}}},"df":0,"docs":{},"o":{"df":0,"docs":{},"i":{"d":{"df":2,"docs":{"146":{"tf":1.0},"95":{"tf":1.0}}},"df":0,"docs":{}}}},"ˉ":{",":{"b":{"df":0,"docs":{},"ˉ":{",":{"c":{"df":0,"docs":{},"ˉ":{",":{"df":0,"docs":{},"s":{"df":0,"docs":{},"ˉ":{"df":0,"docs":{},"σ":{"1":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"s":{"df":0,"docs":{},"ˉ":{"df":0,"docs":{},"σ":{"2":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"z":{"df":1,"docs":{"32":{"tf":1.0}}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"=":{"a":{"(":{"df":0,"docs":{},"ζ":{")":{",":{"b":{"df":0,"docs":{},"ˉ":{"=":{"b":{"(":{"df":0,"docs":{},"ζ":{")":{",":{"c":{"df":0,"docs":{},"ˉ":{"=":{"c":{"(":{"df":0,"docs":{},"ζ":{")":{",":{"df":0,"docs":{},"s":{"df":0,"docs":{},"ˉ":{"df":0,"docs":{},"σ":{"1":{"df":0,"docs":{},"​":{"=":{"df":0,"docs":{},"s":{"df":0,"docs":{},"σ":{"1":{"df":0,"docs":{},"​":{"(":{"df":0,"docs":{},"ζ":{")":{",":{"df":0,"docs":{},"s":{"df":0,"docs":{},"ˉ":{"df":0,"docs":{},"σ":{"2":{"df":0,"docs":{},"​":{"=":{"df":0,"docs":{},"s":{"df":0,"docs":{},"σ":{"2":{"df":0,"docs":{},"​":{"(":{"df":0,"docs":{},"ζ":{")":{",":{"df":0,"docs":{},"z":{"df":0,"docs":{},"ˉ":{"df":0,"docs":{},"ω":{"df":0,"docs":{},"​":{"=":{"df":0,"docs":{},"z":{"df":1,"docs":{"27":{"tf":1.0}}}},"df":0,"docs":{}}}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{},"i":{"df":0,"docs":{},"​":{"=":{"(":{"a":{"df":0,"docs":{},"i":{",":{"0":{"df":0,"docs":{},"​":{",":{"a":{"df":0,"docs":{},"i":{",":{"1":{"df":1,"docs":{"14":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}},"′":{",":{"b":{"df":0,"docs":{},"′":{",":{"c":{"df":1,"docs":{"24":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"=":{"df":0,"docs":{},"{":{"a":{"0":{"df":0,"docs":{},"​":{"+":{"a":{"1":{"df":0,"docs":{},"​":{"df":0,"docs":{},"y":{"+":{"df":0,"docs":{},"x":{",":{"a":{"2":{"df":0,"docs":{},"​":{"+":{"a":{"3":{"df":0,"docs":{},"​":{"df":0,"docs":{},"y":{"+":{"df":0,"docs":{},"x":{"df":1,"docs":{"14":{"tf":1.0}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{},"x":{",":{"a":{"1":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"x":{"df":1,"docs":{"14":{"tf":1.0}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"b":{"(":{"df":0,"docs":{},"x":{")":{"=":{"(":{"df":0,"docs":{},"x":{"df":0,"docs":{},"−":{"1":{")":{"(":{"df":0,"docs":{},"x":{"df":0,"docs":{},"−":{"df":0,"docs":{},"g":{")":{"df":0,"docs":{},"t":{"(":{"df":0,"docs":{},"x":{")":{"df":0,"docs":{},"−":{"df":0,"docs":{},"p":{"(":{"df":0,"docs":{},"x":{"df":1,"docs":{"54":{"tf":1.0}}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":3,"docs":{"35":{"tf":1.0},"54":{"tf":1.0},"56":{"tf":1.0}}},"z":{"df":2,"docs":{"113":{"tf":1.0},"63":{"tf":1.0}}}},"0":{"df":1,"docs":{"149":{"tf":2.8284271247461903}},"​":{"+":{"df":0,"docs":{},"β":{"b":{"1":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"γ":{")":{"(":{"b":{"2":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"β":{"b":{"3":{"df":1,"docs":{"14":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"γ":{",":{"df":0,"docs":{},"…":{",":{"b":{"df":0,"docs":{},"k":{"df":0,"docs":{},"−":{"1":{"df":1,"docs":{"14":{"tf":1.4142135623730951}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},",":{"df":0,"docs":{},"…":{",":{"b":{"df":0,"docs":{},"k":{"df":1,"docs":{"20":{"tf":1.0}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"b":{"1":{"df":1,"docs":{"14":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}}},"1":{"0":{"df":0,"docs":{},"​":{",":{"b":{"1":{"1":{"df":1,"docs":{"26":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":1,"docs":{"149":{"tf":2.8284271247461903}},"​":{",":{"b":{"2":{"df":0,"docs":{},"​":{",":{"b":{"3":{"df":0,"docs":{},"​":{",":{"b":{"4":{"df":0,"docs":{},"​":{",":{"b":{"5":{"df":0,"docs":{},"​":{",":{"b":{"6":{"df":1,"docs":{"24":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"2":{"df":1,"docs":{"149":{"tf":2.8284271247461903}}},"3":{"df":1,"docs":{"149":{"tf":2.8284271247461903}}},"7":{"df":0,"docs":{},"​":{",":{"b":{"8":{"df":0,"docs":{},"​":{",":{"b":{"9":{"df":1,"docs":{"25":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},":":{"=":{"(":{"b":{"3":{"df":0,"docs":{},"​":{"df":0,"docs":{},"x":{"+":{"b":{"4":{"df":0,"docs":{},"​":{")":{"df":0,"docs":{},"z":{"df":0,"docs":{},"h":{"df":0,"docs":{},"​":{"+":{"b":{"df":1,"docs":{"24":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"=":{"2":{"df":0,"docs":{},"l":{"df":1,"docs":{"91":{"tf":1.0}}}},"df":0,"docs":{},"m":{"/":{"df":0,"docs":{},"n":{"=":{"2":{"df":0,"docs":{},"m":{"df":0,"docs":{},"−":{"df":0,"docs":{},"n":{"df":1,"docs":{"73":{"tf":1.0}}}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"{":{"(":{"b":{"0":{"df":0,"docs":{},"​":{",":{"b":{"1":{"df":0,"docs":{},"​":{")":{",":{"(":{"b":{"2":{"df":0,"docs":{},"​":{",":{"b":{"3":{"df":1,"docs":{"14":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{},"η":{"3":{"df":0,"docs":{},"k":{"+":{"df":0,"docs":{},"l":{",":{"df":0,"docs":{},"t":{"df":0,"docs":{},"i":{",":{"df":0,"docs":{},"j":{"df":0,"docs":{},"​":{")":{":":{"(":{"df":0,"docs":{},"i":{",":{"df":0,"docs":{},"j":{")":{"df":0,"docs":{},"∈":{"df":0,"docs":{},"i":{",":{"df":0,"docs":{},"σ":{"(":{"(":{"df":0,"docs":{},"i":{",":{"df":0,"docs":{},"j":{")":{")":{"=":{"(":{"df":0,"docs":{},"k":{",":{"df":0,"docs":{},"l":{"df":1,"docs":{"14":{"tf":1.0}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"σ":{"(":{"(":{"df":0,"docs":{},"i":{",":{"df":0,"docs":{},"j":{")":{")":{",":{"df":0,"docs":{},"t":{"df":0,"docs":{},"i":{",":{"df":0,"docs":{},"j":{"df":0,"docs":{},"​":{")":{":":{"(":{"df":0,"docs":{},"i":{",":{"df":0,"docs":{},"j":{")":{"df":0,"docs":{},"∈":{"df":0,"docs":{},"i":{"df":1,"docs":{"14":{"tf":2.0}}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"6":{",":{"1":{"0":{"df":1,"docs":{"14":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"b":{"0":{"df":0,"docs":{},"​":{",":{"b":{"1":{"df":1,"docs":{"14":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{},"…":{",":{"b":{"df":0,"docs":{},"k":{"df":0,"docs":{},"−":{"1":{"df":1,"docs":{"14":{"tf":1.4142135623730951}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{},"ˉ":{"0":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"…":{",":{"b":{"df":0,"docs":{},"ˉ":{"df":0,"docs":{},"k":{"df":0,"docs":{},"−":{"1":{"df":1,"docs":{"14":{"tf":1.0}}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"_":{"1":{"(":{"df":0,"docs":{},"x":{"df":1,"docs":{"65":{"tf":1.0}}}},"df":1,"docs":{"35":{"tf":1.0}}},"df":0,"docs":{},"i":{"df":1,"docs":{"65":{"tf":1.0}}},"k":{"(":{"df":0,"docs":{},"x":{"df":1,"docs":{"65":{"tf":1.0}}}},"df":0,"docs":{}},"z":{"df":0,"docs":{},"e":{"df":0,"docs":{},"t":{"a":{"df":1,"docs":{"35":{"tf":1.0}}},"df":0,"docs":{}}}}},"a":{"c":{"df":0,"docs":{},"k":{"df":2,"docs":{"14":{"tf":1.4142135623730951},"77":{"tf":1.0}},"e":{"df":0,"docs":{},"n":{"d":{"df":1,"docs":{"34":{"tf":1.0}}},"df":0,"docs":{}}}}},"df":0,"docs":{},"l":{"a":{"df":0,"docs":{},"n":{"c":{"df":1,"docs":{"146":{"tf":1.0}}},"df":0,"docs":{}}},"df":0,"docs":{}},"s":{"df":0,"docs":{},"e":{"df":1,"docs":{"45":{"tf":1.4142135623730951}}},"i":{"c":{"df":2,"docs":{"128":{"tf":1.0},"9":{"tf":1.0}}},"df":3,"docs":{"32":{"tf":1.0},"35":{"tf":1.0},"5":{"tf":1.0}}}},"t":{"c":{"df":0,"docs":{},"h":{"df":7,"docs":{"28":{"tf":1.0},"35":{"tf":1.0},"80":{"tf":2.8284271247461903},"86":{"tf":1.7320508075688772},"92":{"tf":1.4142135623730951},"94":{"tf":2.0},"98":{"tf":1.7320508075688772}}}},"df":0,"docs":{}}},"df":20,"docs":{"10":{"tf":1.7320508075688772},"11":{"tf":1.4142135623730951},"112":{"tf":1.4142135623730951},"12":{"tf":1.0},"13":{"tf":1.4142135623730951},"14":{"tf":4.795831523312719},"142":{"tf":1.4142135623730951},"146":{"tf":1.7320508075688772},"147":{"tf":2.449489742783178},"149":{"tf":1.0},"150":{"tf":1.4142135623730951},"16":{"tf":1.7320508075688772},"21":{"tf":1.4142135623730951},"35":{"tf":1.4142135623730951},"36":{"tf":1.0},"54":{"tf":1.7320508075688772},"55":{"tf":1.0},"62":{"tf":1.4142135623730951},"68":{"tf":1.7320508075688772},"9":{"tf":1.0}},"e":{"c":{"df":0,"docs":{},"o":{"df":0,"docs":{},"m":{"df":2,"docs":{"114":{"tf":1.0},"131":{"tf":1.0}}}}},"df":11,"docs":{"109":{"tf":1.0},"12":{"tf":1.0},"124":{"tf":1.0},"14":{"tf":1.4142135623730951},"149":{"tf":1.0},"19":{"tf":1.0},"5":{"tf":1.0},"52":{"tf":1.0},"68":{"tf":1.0},"69":{"tf":1.0},"91":{"tf":1.0}},"f":{"df":0,"docs":{},"o":{"df":0,"docs":{},"r":{"df":9,"docs":{"107":{"tf":1.0},"14":{"tf":1.4142135623730951},"146":{"tf":1.0},"21":{"tf":1.0},"44":{"tf":1.0},"69":{"tf":1.0},"74":{"tf":1.0},"80":{"tf":1.0},"83":{"tf":1.0}},"e":{"df":0,"docs":{},"h":{"a":{"df":0,"docs":{},"n":{"d":{"df":3,"docs":{"104":{"tf":1.4142135623730951},"21":{"tf":1.0},"56":{"tf":1.0}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}}},"g":{"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"df":4,"docs":{"147":{"tf":1.0},"15":{"tf":1.0},"71":{"tf":1.0},"87":{"tf":1.0}}}}},"h":{"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"d":{"df":2,"docs":{"151":{"tf":1.0},"4":{"tf":1.0}}},"df":0,"docs":{}}}},"l":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"df":0,"docs":{},"g":{"df":3,"docs":{"113":{"tf":1.0},"60":{"tf":1.4142135623730951},"63":{"tf":1.0}}}},"w":{"df":4,"docs":{"104":{"tf":1.0},"16":{"tf":1.0},"51":{"tf":1.0},"61":{"tf":1.0}}}}},"n":{"c":{"df":0,"docs":{},"h":{"df":0,"docs":{},"m":{"a":{"df":0,"docs":{},"r":{"df":0,"docs":{},"k":{"df":2,"docs":{"2":{"tf":1.7320508075688772},"3":{"tf":1.7320508075688772}}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"t":{"a":{"_":{"1":{"df":1,"docs":{"56":{"tf":1.0}}},"2":{"df":1,"docs":{"56":{"tf":1.0}}},"df":0,"docs":{}},"df":4,"docs":{"114":{"tf":1.0},"117":{"tf":1.0},"118":{"tf":1.0},"56":{"tf":1.0}}},"df":0,"docs":{},"t":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":7,"docs":{"10":{"tf":1.0},"101":{"tf":1.0},"133":{"tf":1.0},"14":{"tf":1.0},"151":{"tf":1.0},"21":{"tf":1.0},"7":{"tf":1.4142135623730951}}}}},"w":{"df":0,"docs":{},"e":{"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"df":10,"docs":{"107":{"tf":1.0},"118":{"tf":1.4142135623730951},"138":{"tf":1.0},"142":{"tf":1.0},"146":{"tf":1.0},"150":{"tf":1.0},"151":{"tf":2.0},"66":{"tf":1.0},"75":{"tf":1.0},"9":{"tf":1.0}}}}}}}},"i":{",":{"0":{"df":0,"docs":{},"​":{"+":{"b":{"df":0,"docs":{},"i":{",":{"1":{"df":0,"docs":{},"​":{"df":0,"docs":{},"β":{"+":{"df":0,"docs":{},"γ":{",":{"df":0,"docs":{},"…":{",":{"b":{"df":0,"docs":{},"k":{"df":0,"docs":{},"−":{"1":{",":{"0":{"df":0,"docs":{},"​":{"+":{"b":{"df":0,"docs":{},"k":{"df":0,"docs":{},"−":{"1":{",":{"1":{"df":1,"docs":{"14":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"a":{"df":0,"docs":{},"s":{"df":1,"docs":{"146":{"tf":1.7320508075688772}}}},"df":0,"docs":{},"g":{"df":2,"docs":{"114":{"tf":1.0},"41":{"tf":1.0}}},"n":{"a":{"df":0,"docs":{},"r":{"df":0,"docs":{},"i":{"df":3,"docs":{"101":{"tf":1.7320508075688772},"150":{"tf":1.0},"91":{"tf":1.0}}}}},"df":0,"docs":{}},"t":{"df":9,"docs":{"101":{"tf":2.23606797749979},"131":{"tf":2.0},"146":{"tf":2.0},"151":{"tf":1.0},"41":{"tf":1.7320508075688772},"49":{"tf":1.0},"56":{"tf":1.0},"88":{"tf":1.4142135623730951},"9":{"tf":1.0}}}},"j":{"df":0,"docs":{},"​":{":":{"=":{"df":0,"docs":{},"z":{"df":0,"docs":{},"j":{"b":{"df":0,"docs":{},"​":{"(":{"df":0,"docs":{},"z":{")":{"df":0,"docs":{},"τ":{"df":0,"docs":{},"j":{"df":0,"docs":{},"z":{"df":0,"docs":{},"​":{"df":0,"docs":{},"−":{"df":0,"docs":{},"p":{"df":0,"docs":{},"j":{"b":{"df":0,"docs":{},"​":{"(":{"df":0,"docs":{},"z":{"df":1,"docs":{"95":{"tf":1.0}}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}}}}}},"df":0,"docs":{}}},"df":0,"docs":{},"t":{"df":0,"docs":{},"j":{"df":0,"docs":{},"​":{"df":0,"docs":{},"−":{"df":0,"docs":{},"p":{"df":0,"docs":{},"j":{"b":{"df":1,"docs":{"94":{"tf":1.0}}},"df":0,"docs":{}}}}}}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"l":{"a":{"c":{"df":0,"docs":{},"k":{"df":2,"docs":{"57":{"tf":1.0},"68":{"tf":1.0}}}},"df":0,"docs":{}},"df":1,"docs":{"38":{"tf":1.0}},"i":{"df":0,"docs":{},"n":{"d":{"df":2,"docs":{"20":{"tf":2.0},"35":{"tf":1.0}}},"df":0,"docs":{}}},"o":{"c":{"df":0,"docs":{},"k":{"df":1,"docs":{"74":{"tf":1.0}}}},"df":0,"docs":{},"w":{"df":0,"docs":{},"u":{"df":0,"docs":{},"p":{"_":{"df":0,"docs":{},"f":{"a":{"c":{"df":0,"docs":{},"t":{"df":0,"docs":{},"o":{"df":0,"docs":{},"r":{"df":2,"docs":{"109":{"tf":1.4142135623730951},"114":{"tf":1.0}}}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":4,"docs":{"125":{"tf":1.0},"68":{"tf":1.7320508075688772},"73":{"tf":1.0},"91":{"tf":1.4142135623730951}}}}}},"s":{"1":{"2":{"3":{"8":{"1":{"df":1,"docs":{"44":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"o":{"df":0,"docs":{},"i":{"df":0,"docs":{},"l":{"df":3,"docs":{"14":{"tf":1.0},"16":{"tf":1.0},"21":{"tf":1.0}}}},"l":{"d":{"df":1,"docs":{"95":{"tf":1.0}}},"df":0,"docs":{}},"o":{"df":0,"docs":{},"l":{"df":1,"docs":{"35":{"tf":1.0}}}},"t":{"df":0,"docs":{},"h":{"df":18,"docs":{"101":{"tf":1.7320508075688772},"104":{"tf":1.0},"105":{"tf":1.0},"114":{"tf":1.0},"118":{"tf":1.4142135623730951},"123":{"tf":1.0},"14":{"tf":1.0},"16":{"tf":1.0},"34":{"tf":1.0},"53":{"tf":1.0},"58":{"tf":1.0},"59":{"tf":1.0},"60":{"tf":1.0},"61":{"tf":1.0},"7":{"tf":1.0},"74":{"tf":1.0},"75":{"tf":1.0},"91":{"tf":1.0}}},"t":{"df":0,"docs":{},"o":{"df":0,"docs":{},"m":{"df":1,"docs":{"142":{"tf":1.4142135623730951}}}}}},"u":{"df":0,"docs":{},"n":{"d":{"a":{"df":0,"docs":{},"r":{"df":0,"docs":{},"i":{"df":18,"docs":{"105":{"tf":1.4142135623730951},"106":{"tf":2.0},"112":{"tf":1.0},"113":{"tf":1.0},"114":{"tf":1.4142135623730951},"123":{"tf":1.4142135623730951},"125":{"tf":1.0},"49":{"tf":1.0},"53":{"tf":1.0},"54":{"tf":3.0},"56":{"tf":1.0},"58":{"tf":1.0},"62":{"tf":1.0},"65":{"tf":1.4142135623730951},"70":{"tf":1.0},"84":{"tf":1.7320508075688772},"85":{"tf":1.0},"91":{"tf":1.4142135623730951}}},"y":{"_":{"c":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"df":0,"docs":{},"s":{"df":0,"docs":{},"t":{"df":0,"docs":{},"r":{"a":{"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"df":0,"docs":{},"t":{"df":2,"docs":{"105":{"tf":1.0},"106":{"tf":1.0}}}}}},"df":0,"docs":{}}}}}}},"df":0,"docs":{}},"c":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"df":0,"docs":{},"s":{"df":0,"docs":{},"t":{"df":0,"docs":{},"r":{"a":{"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"df":0,"docs":{},"t":{":":{":":{"df":0,"docs":{},"n":{"df":0,"docs":{},"e":{"df":0,"docs":{},"w":{"_":{"df":0,"docs":{},"s":{"df":0,"docs":{},"i":{"df":0,"docs":{},"m":{"df":0,"docs":{},"p":{"df":0,"docs":{},"l":{"df":0,"docs":{},"e":{"(":{"0":{"df":1,"docs":{"106":{"tf":1.0}}},"1":{"df":1,"docs":{"106":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}}}}}}}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":1,"docs":{"106":{"tf":1.4142135623730951}},"s":{":":{":":{"df":0,"docs":{},"f":{"df":0,"docs":{},"r":{"df":0,"docs":{},"o":{"df":0,"docs":{},"m":{"_":{"c":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"df":0,"docs":{},"s":{"df":0,"docs":{},"t":{"df":0,"docs":{},"r":{"a":{"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"df":0,"docs":{},"t":{"df":0,"docs":{},"s":{"(":{"df":0,"docs":{},"v":{"df":0,"docs":{},"e":{"c":{"!":{"[":{"a":{"0":{"df":1,"docs":{"106":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}}}}},"df":0,"docs":{}}}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}}}},"df":0,"docs":{}},"<":{"df":0,"docs":{},"s":{"df":0,"docs":{},"e":{"df":0,"docs":{},"l":{"df":0,"docs":{},"f":{":":{":":{"df":0,"docs":{},"f":{"df":0,"docs":{},"i":{"df":0,"docs":{},"e":{"df":0,"docs":{},"l":{"d":{"df":1,"docs":{"106":{"tf":1.0}}},"df":0,"docs":{}}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}}}},"df":0,"docs":{}}}}}},"df":0,"docs":{}}}}}}},"df":0,"docs":{}}}},"df":2,"docs":{"73":{"tf":1.0},"75":{"tf":1.0}}},"df":0,"docs":{}}},"x":{"df":2,"docs":{"57":{"tf":1.0},"68":{"tf":1.0}}}},"r":{"a":{"df":0,"docs":{},"k":{"df":0,"docs":{},"e":{"df":1,"docs":{"35":{"tf":1.0}}}}},"df":0,"docs":{},"e":{"a":{"df":0,"docs":{},"k":{"df":2,"docs":{"130":{"tf":1.0},"146":{"tf":1.4142135623730951}}}},"df":0,"docs":{}},"o":{"a":{"d":{"df":0,"docs":{},"l":{"df":0,"docs":{},"i":{"df":1,"docs":{"128":{"tf":1.0}}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"u":{"df":0,"docs":{},"i":{"df":0,"docs":{},"l":{"d":{"_":{"c":{"a":{"df":0,"docs":{},"i":{"df":0,"docs":{},"r":{"df":0,"docs":{},"o":{"_":{"df":0,"docs":{},"e":{"df":0,"docs":{},"x":{"df":0,"docs":{},"e":{"c":{"df":0,"docs":{},"u":{"df":0,"docs":{},"t":{"df":0,"docs":{},"i":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"_":{"df":0,"docs":{},"t":{"df":0,"docs":{},"r":{"a":{"c":{"df":1,"docs":{"146":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}}}}}},"df":0,"docs":{}}}}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":20,"docs":{"100":{"tf":1.0},"101":{"tf":1.0},"110":{"tf":1.0},"114":{"tf":1.4142135623730951},"144":{"tf":1.0},"145":{"tf":1.0},"146":{"tf":1.0},"43":{"tf":1.0},"44":{"tf":1.0},"45":{"tf":1.7320508075688772},"50":{"tf":1.0},"56":{"tf":1.0},"71":{"tf":1.0},"72":{"tf":1.0},"74":{"tf":1.0},"84":{"tf":1.0},"9":{"tf":1.0},"92":{"tf":1.0},"94":{"tf":1.4142135623730951},"97":{"tf":1.4142135623730951}}},"df":0,"docs":{},"t":{"df":9,"docs":{"114":{"tf":1.4142135623730951},"127":{"tf":1.4142135623730951},"137":{"tf":1.0},"138":{"tf":1.0},"139":{"tf":1.0},"145":{"tf":1.0},"146":{"tf":1.4142135623730951},"151":{"tf":3.7416573867739413},"80":{"tf":1.4142135623730951}},"i":{"df":0,"docs":{},"n":{"df":2,"docs":{"144":{"tf":1.0},"151":{"tf":2.8284271247461903}}}}}}},"n":{"c":{"df":0,"docs":{},"h":{"df":2,"docs":{"20":{"tf":1.0},"95":{"tf":1.0}}}},"df":0,"docs":{}}},"y":{"df":0,"docs":{},"t":{"df":0,"docs":{},"e":{"c":{"df":0,"docs":{},"o":{"d":{"df":2,"docs":{"146":{"tf":1.4142135623730951},"50":{"tf":1.4142135623730951}}},"df":0,"docs":{}}},"df":1,"docs":{"40":{"tf":1.0}}}}},"ˉ":{"df":0,"docs":{},"i":{"df":1,"docs":{"14":{"tf":1.0}}}},"′":{"=":{"df":0,"docs":{},"{":{"b":{"0":{"df":0,"docs":{},"​":{"+":{"b":{"1":{"df":0,"docs":{},"​":{"df":0,"docs":{},"y":{"+":{"df":0,"docs":{},"x":{",":{"b":{"2":{"df":0,"docs":{},"​":{"+":{"b":{"3":{"df":0,"docs":{},"​":{"df":0,"docs":{},"y":{"+":{"df":0,"docs":{},"x":{"df":1,"docs":{"14":{"tf":1.0}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{},"x":{",":{"b":{"1":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"x":{"df":1,"docs":{"14":{"tf":1.0}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"c":{"(":{"df":0,"docs":{},"x":{")":{"=":{"df":0,"docs":{},"∏":{"df":0,"docs":{},"i":{"=":{"0":{"5":{"df":0,"docs":{},"​":{"(":{"df":0,"docs":{},"x":{"df":0,"docs":{},"−":{"df":0,"docs":{},"g":{"df":0,"docs":{},"i":{")":{"df":0,"docs":{},"t":{"(":{"df":0,"docs":{},"x":{"df":0,"docs":{},"g":{"2":{")":{"df":0,"docs":{},"−":{"df":0,"docs":{},"t":{"(":{"df":0,"docs":{},"x":{"df":0,"docs":{},"g":{")":{"df":0,"docs":{},"−":{"df":0,"docs":{},"t":{"(":{"df":0,"docs":{},"x":{"df":2,"docs":{"114":{"tf":1.0},"55":{"tf":1.0}}}},"df":0,"docs":{}}}},"df":0,"docs":{}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}},"0":{"df":1,"docs":{"114":{"tf":1.0}}},"df":4,"docs":{"114":{"tf":2.8284271247461903},"35":{"tf":1.0},"55":{"tf":1.4142135623730951},"56":{"tf":1.0}}},"z":{"df":2,"docs":{"113":{"tf":1.0},"63":{"tf":1.0}}}},"0":{"df":1,"docs":{"149":{"tf":3.1622776601683795}},"​":{"(":{"df":0,"docs":{},"w":{"df":0,"docs":{},"i":{"df":0,"docs":{},"∗":{"4":{",":{"df":0,"docs":{},"w":{"df":0,"docs":{},"i":{"df":0,"docs":{},"∗":{"4":{"+":{"4":{"df":1,"docs":{"149":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}}},"1":{"df":1,"docs":{"149":{"tf":3.1622776601683795}},"​":{"(":{"df":0,"docs":{},"w":{"df":0,"docs":{},"i":{"df":0,"docs":{},"∗":{"4":{"+":{"1":{",":{"df":0,"docs":{},"w":{"df":0,"docs":{},"i":{"df":0,"docs":{},"∗":{"4":{"+":{"5":{"df":1,"docs":{"149":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}},"x":{")":{"=":{"df":0,"docs":{},"t":{"1":{"df":0,"docs":{},"​":{"(":{"df":0,"docs":{},"g":{"df":0,"docs":{},"x":{")":{"df":0,"docs":{},"−":{"df":0,"docs":{},"t":{"2":{"df":0,"docs":{},"​":{"(":{"df":0,"docs":{},"x":{"df":1,"docs":{"66":{"tf":1.0}}}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"2":{"df":1,"docs":{"149":{"tf":3.1622776601683795}},"​":{"(":{"df":0,"docs":{},"w":{"df":0,"docs":{},"i":{"df":0,"docs":{},"∗":{"4":{"+":{"2":{",":{"df":0,"docs":{},"w":{"df":0,"docs":{},"i":{"df":0,"docs":{},"∗":{"4":{"+":{"6":{"df":1,"docs":{"149":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}}},"3":{"df":1,"docs":{"149":{"tf":2.8284271247461903}}},":":{"=":{"(":{"b":{"5":{"df":0,"docs":{},"​":{"df":0,"docs":{},"x":{"+":{"b":{"6":{"df":0,"docs":{},"​":{")":{"df":0,"docs":{},"z":{"df":0,"docs":{},"h":{"df":0,"docs":{},"​":{"+":{"c":{"df":1,"docs":{"24":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"=":{"df":0,"docs":{},"−":{"1":{"df":1,"docs":{"10":{"tf":1.0}}},"df":0,"docs":{}}},"_":{"1":{"(":{"df":0,"docs":{},"x":{"df":1,"docs":{"66":{"tf":1.4142135623730951}}}},"df":1,"docs":{"35":{"tf":1.0}}},"df":0,"docs":{},"i":{"(":{"df":0,"docs":{},"x":{"df":1,"docs":{"66":{"tf":1.0}}}},"df":0,"docs":{}},"n":{"(":{"df":0,"docs":{},"x":{"df":1,"docs":{"66":{"tf":1.0}}}},"df":0,"docs":{}},"z":{"df":0,"docs":{},"e":{"df":0,"docs":{},"t":{"a":{"df":1,"docs":{"35":{"tf":1.0}}},"df":0,"docs":{}}}}},"a":{"df":0,"docs":{},"i":{"df":0,"docs":{},"r":{"df":0,"docs":{},"o":{"'":{"df":1,"docs":{"50":{"tf":1.7320508075688772}}},"df":21,"docs":{"104":{"tf":1.0},"127":{"tf":1.4142135623730951},"130":{"tf":1.4142135623730951},"131":{"tf":2.0},"133":{"tf":1.7320508075688772},"138":{"tf":1.0},"140":{"tf":1.7320508075688772},"141":{"tf":1.4142135623730951},"142":{"tf":1.0},"143":{"tf":1.7320508075688772},"144":{"tf":2.23606797749979},"145":{"tf":1.0},"146":{"tf":2.8284271247461903},"147":{"tf":1.0},"148":{"tf":1.0},"149":{"tf":1.4142135623730951},"150":{"tf":1.0},"151":{"tf":1.4142135623730951},"152":{"tf":1.0},"50":{"tf":3.0},"65":{"tf":1.0}}}}},"l":{"c":{"df":0,"docs":{},"u":{"df":0,"docs":{},"l":{"df":3,"docs":{"146":{"tf":1.0},"151":{"tf":1.0},"49":{"tf":1.0}}}}},"df":0,"docs":{},"l":{"df":40,"docs":{"10":{"tf":1.0},"11":{"tf":1.0},"110":{"tf":1.4142135623730951},"114":{"tf":2.0},"117":{"tf":1.4142135623730951},"118":{"tf":2.23606797749979},"124":{"tf":1.4142135623730951},"125":{"tf":1.0},"13":{"tf":1.0},"14":{"tf":1.0},"144":{"tf":1.0},"146":{"tf":1.7320508075688772},"149":{"tf":1.4142135623730951},"15":{"tf":1.0},"16":{"tf":1.4142135623730951},"19":{"tf":1.4142135623730951},"21":{"tf":1.4142135623730951},"35":{"tf":1.0},"4":{"tf":1.0},"40":{"tf":2.449489742783178},"42":{"tf":1.0},"44":{"tf":1.0},"48":{"tf":1.0},"49":{"tf":1.0},"52":{"tf":1.0},"53":{"tf":1.0},"54":{"tf":1.4142135623730951},"55":{"tf":1.0},"59":{"tf":1.0},"68":{"tf":1.4142135623730951},"69":{"tf":1.4142135623730951},"70":{"tf":1.7320508075688772},"71":{"tf":1.0},"73":{"tf":1.4142135623730951},"75":{"tf":1.0},"8":{"tf":1.0},"80":{"tf":1.0},"84":{"tf":1.0},"9":{"tf":1.0},"91":{"tf":1.0}}}},"m":{"df":0,"docs":{},"e":{"df":3,"docs":{"146":{"tf":1.0},"16":{"tf":1.0},"7":{"tf":1.0}}}},"n":{"'":{"df":0,"docs":{},"t":{"df":8,"docs":{"114":{"tf":1.0},"118":{"tf":1.7320508075688772},"12":{"tf":1.0},"125":{"tf":1.0},"130":{"tf":1.0},"146":{"tf":1.0},"56":{"tf":1.0},"95":{"tf":1.0}}}},"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"df":1,"docs":{"35":{"tf":1.0}}}}},"p":{"df":0,"docs":{},"i":{"df":0,"docs":{},"t":{"df":1,"docs":{"95":{"tf":1.0}}}},"t":{"df":0,"docs":{},"u":{"df":0,"docs":{},"r":{"df":1,"docs":{"16":{"tf":1.0}}}}}},"r":{"df":0,"docs":{},"e":{"df":4,"docs":{"124":{"tf":1.4142135623730951},"125":{"tf":1.0},"49":{"tf":1.0},"57":{"tf":1.0}}},"r":{"df":0,"docs":{},"i":{"df":1,"docs":{"11":{"tf":1.0}}}}},"s":{"df":0,"docs":{},"e":{"df":26,"docs":{"10":{"tf":1.4142135623730951},"101":{"tf":1.0},"103":{"tf":1.0},"106":{"tf":1.4142135623730951},"107":{"tf":1.4142135623730951},"109":{"tf":1.0},"11":{"tf":1.0},"112":{"tf":1.0},"118":{"tf":1.0},"12":{"tf":1.0},"125":{"tf":1.0},"13":{"tf":1.0},"14":{"tf":2.23606797749979},"146":{"tf":1.7320508075688772},"149":{"tf":2.0},"151":{"tf":1.0},"3":{"tf":1.0},"34":{"tf":1.0},"51":{"tf":1.0},"68":{"tf":1.0},"7":{"tf":1.4142135623730951},"80":{"tf":1.7320508075688772},"84":{"tf":1.0},"9":{"tf":1.0},"92":{"tf":1.0},"97":{"tf":1.0}}}},"t":{"c":{"df":0,"docs":{},"h":{"df":1,"docs":{"57":{"tf":1.0}}}},"df":0,"docs":{}}},"df":19,"docs":{"10":{"tf":2.6457513110645907},"11":{"tf":1.0},"112":{"tf":1.4142135623730951},"12":{"tf":1.0},"13":{"tf":1.0},"14":{"tf":1.4142135623730951},"142":{"tf":1.4142135623730951},"146":{"tf":1.7320508075688772},"147":{"tf":1.4142135623730951},"149":{"tf":1.4142135623730951},"16":{"tf":1.7320508075688772},"35":{"tf":1.4142135623730951},"36":{"tf":1.0},"55":{"tf":1.0},"62":{"tf":1.4142135623730951},"9":{"tf":1.0},"91":{"tf":1.0},"94":{"tf":1.0},"95":{"tf":1.0}},"e":{"df":0,"docs":{},"i":{"df":0,"docs":{},"l":{"df":1,"docs":{"142":{"tf":1.0}}}},"l":{"df":0,"docs":{},"l":{"df":7,"docs":{"127":{"tf":1.7320508075688772},"130":{"tf":2.8284271247461903},"133":{"tf":2.449489742783178},"134":{"tf":2.6457513110645907},"146":{"tf":1.7320508075688772},"149":{"tf":1.0},"151":{"tf":2.449489742783178}}}},"r":{"df":0,"docs":{},"t":{"a":{"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"df":6,"docs":{"130":{"tf":1.0},"19":{"tf":1.7320508075688772},"20":{"tf":1.0},"48":{"tf":1.4142135623730951},"49":{"tf":1.0},"52":{"tf":1.0}}}}},"df":0,"docs":{}}}},"h":{"a":{"df":0,"docs":{},"l":{"df":0,"docs":{},"l":{"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"df":0,"docs":{},"g":{"df":14,"docs":{"118":{"tf":2.0},"147":{"tf":1.4142135623730951},"32":{"tf":2.449489742783178},"40":{"tf":2.0},"41":{"tf":1.0},"69":{"tf":1.0},"80":{"tf":1.0},"82":{"tf":1.0},"83":{"tf":2.0},"84":{"tf":1.0},"89":{"tf":1.0},"91":{"tf":1.0},"95":{"tf":1.0},"97":{"tf":2.449489742783178}}}}}}},"n":{"df":0,"docs":{},"g":{"df":5,"docs":{"146":{"tf":1.7320508075688772},"7":{"tf":1.0},"71":{"tf":1.0},"83":{"tf":1.0},"9":{"tf":1.0}}}},"p":{"df":0,"docs":{},"t":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":2,"docs":{"103":{"tf":1.0},"149":{"tf":1.0}}}}}},"r":{"df":0,"docs":{},"g":{"df":1,"docs":{"131":{"tf":1.0}}}}},"df":0,"docs":{},"e":{"a":{"df":0,"docs":{},"p":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":1,"docs":{"125":{"tf":1.0}}}},"l":{"df":0,"docs":{},"i":{"df":1,"docs":{"21":{"tf":1.0}}}}},"t":{"df":4,"docs":{"118":{"tf":1.0},"21":{"tf":1.0},"58":{"tf":1.0},"79":{"tf":1.4142135623730951}}}},"c":{"df":0,"docs":{},"k":{"df":34,"docs":{"101":{"tf":1.0},"102":{"tf":1.4142135623730951},"103":{"tf":1.0},"11":{"tf":1.0},"113":{"tf":2.0},"115":{"tf":1.0},"117":{"tf":1.4142135623730951},"12":{"tf":1.0},"13":{"tf":1.0},"130":{"tf":1.7320508075688772},"134":{"tf":1.0},"138":{"tf":1.4142135623730951},"14":{"tf":2.6457513110645907},"146":{"tf":3.3166247903554},"147":{"tf":1.4142135623730951},"151":{"tf":2.449489742783178},"16":{"tf":1.7320508075688772},"21":{"tf":2.449489742783178},"33":{"tf":2.8284271247461903},"42":{"tf":1.0},"45":{"tf":1.0},"50":{"tf":1.0},"58":{"tf":1.7320508075688772},"59":{"tf":1.0},"60":{"tf":2.0},"63":{"tf":2.0},"66":{"tf":1.0},"69":{"tf":1.4142135623730951},"70":{"tf":1.4142135623730951},"72":{"tf":1.0},"84":{"tf":1.7320508075688772},"85":{"tf":1.4142135623730951},"92":{"tf":1.4142135623730951},"95":{"tf":2.23606797749979}}}},"df":0,"docs":{}},"o":{"df":0,"docs":{},"i":{"c":{"df":2,"docs":{"68":{"tf":1.0},"72":{"tf":1.0}}},"df":0,"docs":{}},"o":{"df":0,"docs":{},"s":{"df":8,"docs":{"114":{"tf":1.0},"124":{"tf":1.4142135623730951},"16":{"tf":1.4142135623730951},"68":{"tf":1.0},"69":{"tf":1.0},"80":{"tf":1.0},"85":{"tf":1.0},"9":{"tf":1.0}}}},"s":{"df":0,"docs":{},"e":{"df":2,"docs":{"107":{"tf":1.0},"68":{"tf":1.0}},"n":{"df":7,"docs":{"19":{"tf":1.0},"68":{"tf":1.0},"7":{"tf":1.0},"77":{"tf":1.0},"79":{"tf":1.4142135623730951},"85":{"tf":1.0},"91":{"tf":1.0}}}}}}},"i":{"df":1,"docs":{"150":{"tf":1.4142135623730951}},"r":{"c":{"df":0,"docs":{},"u":{"df":0,"docs":{},"i":{"df":0,"docs":{},"t":{"df":15,"docs":{"10":{"tf":1.0},"11":{"tf":1.0},"13":{"tf":1.0},"14":{"tf":1.0},"16":{"tf":1.4142135623730951},"22":{"tf":1.0},"34":{"tf":1.7320508075688772},"35":{"tf":1.4142135623730951},"36":{"tf":1.0},"4":{"tf":1.7320508075688772},"43":{"tf":1.7320508075688772},"44":{"tf":1.0},"45":{"tf":1.4142135623730951},"8":{"tf":1.0},"9":{"tf":1.7320508075688772}}}}}},"df":0,"docs":{}},"​":{"(":{"df":0,"docs":{},"z":{"df":2,"docs":{"115":{"tf":1.0},"117":{"tf":1.0}}}},"df":0,"docs":{}}},"k":{"df":0,"docs":{},"​":{"(":{"df":0,"docs":{},"w":{"df":0,"docs":{},"i":{",":{"df":0,"docs":{},"w":{"df":0,"docs":{},"i":{"+":{"1":{"df":1,"docs":{"149":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}}},":":{"=":{"df":0,"docs":{},"z":{"df":0,"docs":{},"k":{"df":0,"docs":{},"t":{"df":0,"docs":{},"​":{"(":{"df":0,"docs":{},"z":{")":{"df":0,"docs":{},"p":{"df":0,"docs":{},"k":{"df":0,"docs":{},"t":{"df":0,"docs":{},"​":{"(":{"df":0,"docs":{},"τ":{"1":{"df":0,"docs":{},"z":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"…":{",":{"df":0,"docs":{},"τ":{"df":0,"docs":{},"m":{"df":0,"docs":{},"z":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"τ":{"1":{"df":0,"docs":{},"g":{"df":0,"docs":{},"z":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"…":{",":{"df":0,"docs":{},"τ":{"df":0,"docs":{},"m":{"df":0,"docs":{},"g":{"df":0,"docs":{},"z":{"df":1,"docs":{"95":{"tf":1.0}}}}}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}}},"df":0,"docs":{}}},"df":0,"docs":{},"p":{"df":0,"docs":{},"k":{"df":0,"docs":{},"t":{"df":0,"docs":{},"​":{"(":{"df":0,"docs":{},"t":{"1":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"…":{",":{"df":0,"docs":{},"t":{"df":0,"docs":{},"m":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"t":{"1":{"df":0,"docs":{},"​":{"(":{"df":0,"docs":{},"g":{"df":0,"docs":{},"x":{")":{",":{"df":0,"docs":{},"…":{",":{"df":0,"docs":{},"t":{"df":0,"docs":{},"m":{"df":0,"docs":{},"​":{"(":{"df":0,"docs":{},"g":{"df":0,"docs":{},"x":{"df":1,"docs":{"94":{"tf":1.0}}}}},"df":0,"docs":{}}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}}}}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"l":{"a":{"df":0,"docs":{},"i":{"df":0,"docs":{},"m":{"df":10,"docs":{"10":{"tf":2.23606797749979},"11":{"tf":1.7320508075688772},"113":{"tf":1.4142135623730951},"13":{"tf":1.7320508075688772},"14":{"tf":1.4142135623730951},"21":{"tf":1.4142135623730951},"32":{"tf":1.7320508075688772},"34":{"tf":1.0},"63":{"tf":1.4142135623730951},"95":{"tf":2.6457513110645907}}}},"r":{"df":0,"docs":{},"i":{"df":0,"docs":{},"t":{"df":0,"docs":{},"i":{"df":1,"docs":{"7":{"tf":1.0}}}}}}},"df":0,"docs":{},"e":{"a":{"df":0,"docs":{},"r":{"df":1,"docs":{"113":{"tf":1.0}},"l":{"df":0,"docs":{},"i":{"df":1,"docs":{"11":{"tf":1.0}}}}}},"df":0,"docs":{}},"i":{"df":1,"docs":{"152":{"tf":1.7320508075688772}}},"o":{"df":0,"docs":{},"s":{"df":0,"docs":{},"e":{"df":2,"docs":{"73":{"tf":1.0},"80":{"tf":1.0}}}}}},"o":{"d":{"df":0,"docs":{},"e":{"df":16,"docs":{"103":{"tf":1.0},"104":{"tf":1.0},"105":{"tf":1.0},"111":{"tf":1.0},"113":{"tf":1.0},"114":{"tf":2.0},"116":{"tf":1.4142135623730951},"117":{"tf":1.0},"124":{"tf":1.0},"127":{"tf":1.0},"142":{"tf":1.0},"147":{"tf":1.0},"44":{"tf":1.0},"46":{"tf":1.4142135623730951},"50":{"tf":1.4142135623730951},"67":{"tf":1.0}}}},"df":0,"docs":{},"e":{"df":0,"docs":{},"f":{"df":0,"docs":{},"f":{"df":0,"docs":{},"i":{"c":{"df":0,"docs":{},"i":{"df":5,"docs":{"121":{"tf":1.0},"14":{"tf":1.4142135623730951},"19":{"tf":1.0},"3":{"tf":1.7320508075688772},"5":{"tf":1.0}}}},"df":0,"docs":{}}}}},"i":{"df":0,"docs":{},"n":{"c":{"df":0,"docs":{},"i":{"d":{"df":2,"docs":{"14":{"tf":1.7320508075688772},"85":{"tf":1.0}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"l":{"df":0,"docs":{},"l":{"df":0,"docs":{},"e":{"c":{"df":0,"docs":{},"t":{"df":1,"docs":{"45":{"tf":1.0}}}},"df":0,"docs":{}},"o":{"df":0,"docs":{},"q":{"df":0,"docs":{},"u":{"df":0,"docs":{},"i":{"df":1,"docs":{"146":{"tf":1.0}}}}}}},"u":{"df":0,"docs":{},"m":{"df":0,"docs":{},"n":{"df":47,"docs":{"10":{"tf":1.4142135623730951},"106":{"tf":2.0},"108":{"tf":1.0},"109":{"tf":1.7320508075688772},"11":{"tf":1.4142135623730951},"112":{"tf":1.0},"12":{"tf":1.0},"127":{"tf":1.4142135623730951},"128":{"tf":3.872983346207417},"129":{"tf":1.4142135623730951},"13":{"tf":2.23606797749979},"130":{"tf":2.8284271247461903},"131":{"tf":2.23606797749979},"132":{"tf":2.0},"133":{"tf":2.8284271247461903},"134":{"tf":1.0},"135":{"tf":2.0},"136":{"tf":2.0},"137":{"tf":2.0},"138":{"tf":1.7320508075688772},"139":{"tf":2.8284271247461903},"14":{"tf":2.0},"142":{"tf":2.0},"145":{"tf":1.0},"146":{"tf":4.358898943540674},"147":{"tf":3.872983346207417},"148":{"tf":1.7320508075688772},"149":{"tf":3.1622776601683795},"150":{"tf":3.0},"16":{"tf":1.4142135623730951},"20":{"tf":1.0},"24":{"tf":1.0},"35":{"tf":1.0},"36":{"tf":1.4142135623730951},"49":{"tf":1.0},"5":{"tf":1.0},"50":{"tf":1.0},"51":{"tf":1.0},"62":{"tf":1.0},"65":{"tf":2.23606797749979},"66":{"tf":1.7320508075688772},"70":{"tf":2.23606797749979},"83":{"tf":2.6457513110645907},"84":{"tf":1.0},"9":{"tf":1.4142135623730951},"91":{"tf":1.7320508075688772},"94":{"tf":1.4142135623730951}}}}}},"m":{"b":{"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"df":4,"docs":{"12":{"tf":1.0},"71":{"tf":1.0},"84":{"tf":1.0},"85":{"tf":1.4142135623730951}}}}},"df":0,"docs":{},"e":{"df":6,"docs":{"107":{"tf":1.0},"14":{"tf":1.4142135623730951},"16":{"tf":1.4142135623730951},"35":{"tf":1.0},"45":{"tf":1.4142135623730951},"84":{"tf":1.0}}},"m":{"df":0,"docs":{},"i":{"df":0,"docs":{},"t":{"(":{"a":{"df":1,"docs":{"92":{"tf":1.0}}},"df":0,"docs":{},"f":{"df":1,"docs":{"19":{"tf":1.0}}}},"df":40,"docs":{"101":{"tf":1.0},"112":{"tf":1.0},"113":{"tf":1.0},"116":{"tf":1.0},"119":{"tf":1.0},"19":{"tf":2.6457513110645907},"21":{"tf":1.7320508075688772},"22":{"tf":1.0},"32":{"tf":2.0},"33":{"tf":1.0},"35":{"tf":3.4641016151377544},"38":{"tf":2.0},"42":{"tf":1.0},"44":{"tf":1.0},"53":{"tf":1.0},"57":{"tf":2.0},"58":{"tf":1.0},"59":{"tf":1.0},"60":{"tf":1.0},"62":{"tf":1.0},"63":{"tf":1.0},"67":{"tf":1.4142135623730951},"69":{"tf":1.4142135623730951},"71":{"tf":2.6457513110645907},"72":{"tf":2.0},"73":{"tf":2.0},"74":{"tf":2.23606797749979},"75":{"tf":3.0},"76":{"tf":2.0},"77":{"tf":1.0},"79":{"tf":1.4142135623730951},"80":{"tf":2.6457513110645907},"81":{"tf":1.0},"82":{"tf":1.4142135623730951},"83":{"tf":2.23606797749979},"84":{"tf":1.7320508075688772},"86":{"tf":1.0},"92":{"tf":2.0},"94":{"tf":2.8284271247461903},"95":{"tf":1.4142135623730951}},"m":{"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"df":0,"docs":{},"t":{"_":{"df":0,"docs":{},"s":{"c":{"df":0,"docs":{},"h":{"df":0,"docs":{},"e":{"df":0,"docs":{},"m":{"df":1,"docs":{"35":{"tf":1.4142135623730951}}}}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}}}},"o":{"df":0,"docs":{},"n":{"_":{"df":0,"docs":{},"p":{"df":0,"docs":{},"r":{"df":0,"docs":{},"e":{"df":0,"docs":{},"p":{"df":0,"docs":{},"r":{"df":0,"docs":{},"o":{"c":{"df":0,"docs":{},"e":{"df":0,"docs":{},"s":{"df":0,"docs":{},"s":{"df":0,"docs":{},"e":{"d":{"_":{"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"df":0,"docs":{},"p":{"df":0,"docs":{},"u":{"df":0,"docs":{},"t":{"df":1,"docs":{"35":{"tf":2.23606797749979}}}}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}}}},"df":0,"docs":{}}}}}}}},"df":8,"docs":{"15":{"tf":1.7320508075688772},"22":{"tf":1.0},"23":{"tf":1.0},"35":{"tf":1.7320508075688772},"42":{"tf":1.0},"44":{"tf":1.7320508075688772},"45":{"tf":1.4142135623730951},"83":{"tf":1.0}},"p":{"df":0,"docs":{},"r":{"df":0,"docs":{},"e":{"df":0,"docs":{},"p":{"df":0,"docs":{},"r":{"df":0,"docs":{},"o":{"c":{"df":0,"docs":{},"e":{"df":0,"docs":{},"s":{"df":0,"docs":{},"s":{"df":0,"docs":{},"e":{"d":{"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"df":0,"docs":{},"p":{"df":0,"docs":{},"u":{"df":0,"docs":{},"t":{":":{":":{"df":0,"docs":{},"f":{"df":0,"docs":{},"r":{"df":0,"docs":{},"o":{"df":0,"docs":{},"m":{"_":{"c":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"df":0,"docs":{},"s":{"df":0,"docs":{},"t":{"df":0,"docs":{},"r":{"a":{"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"df":0,"docs":{},"t":{"_":{"df":0,"docs":{},"s":{"df":0,"docs":{},"y":{"df":0,"docs":{},"s":{"df":0,"docs":{},"t":{"df":0,"docs":{},"e":{"df":0,"docs":{},"m":{"(":{"&":{"df":0,"docs":{},"s":{"df":0,"docs":{},"y":{"df":0,"docs":{},"s":{"df":0,"docs":{},"t":{"df":0,"docs":{},"e":{"df":0,"docs":{},"m":{"df":1,"docs":{"44":{"tf":1.0}}}}}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}}}}}},"df":0,"docs":{}}}}},"df":0,"docs":{}}}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}}}},"df":0,"docs":{}},"<":{"df":0,"docs":{},"f":{"df":1,"docs":{"35":{"tf":1.0}}}},"df":0,"docs":{}}}}}}},"df":0,"docs":{}}}}}},"df":0,"docs":{}}}}}}}}},"u":{"df":0,"docs":{},"n":{"df":3,"docs":{"151":{"tf":1.0},"4":{"tf":1.0},"81":{"tf":1.0}}}}},"p":{"a":{"c":{"df":0,"docs":{},"t":{"df":1,"docs":{"14":{"tf":1.4142135623730951}}}},"df":0,"docs":{},"r":{"df":1,"docs":{"14":{"tf":1.0}},"i":{"df":0,"docs":{},"s":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"df":1,"docs":{"3":{"tf":1.4142135623730951}}}}}}},"t":{"df":1,"docs":{"92":{"tf":1.0}}}},"df":0,"docs":{},"i":{"df":0,"docs":{},"l":{"df":1,"docs":{"50":{"tf":1.4142135623730951}}}},"l":{"df":0,"docs":{},"e":{"df":0,"docs":{},"t":{"df":4,"docs":{"107":{"tf":1.0},"59":{"tf":1.0},"78":{"tf":1.4142135623730951},"95":{"tf":1.0}}},"x":{"df":7,"docs":{"114":{"tf":1.0},"4":{"tf":1.0},"45":{"tf":1.7320508075688772},"50":{"tf":1.0},"64":{"tf":1.0},"66":{"tf":1.0},"81":{"tf":1.0}}}}},"o":{"df":0,"docs":{},"n":{"df":5,"docs":{"144":{"tf":1.0},"146":{"tf":1.4142135623730951},"6":{"tf":1.4142135623730951},"71":{"tf":1.0},"80":{"tf":1.0}}},"s":{"df":3,"docs":{"45":{"tf":1.0},"5":{"tf":1.0},"84":{"tf":1.0}},"i":{"df":0,"docs":{},"t":{"df":24,"docs":{"100":{"tf":1.0},"112":{"tf":1.4142135623730951},"113":{"tf":1.0},"114":{"tf":1.0},"119":{"tf":1.7320508075688772},"125":{"tf":1.4142135623730951},"149":{"tf":1.0},"53":{"tf":1.7320508075688772},"56":{"tf":1.0},"58":{"tf":1.0},"59":{"tf":2.0},"60":{"tf":1.0},"62":{"tf":1.4142135623730951},"65":{"tf":1.0},"66":{"tf":1.0},"67":{"tf":1.4142135623730951},"68":{"tf":1.0},"69":{"tf":1.4142135623730951},"80":{"tf":1.0},"84":{"tf":2.0},"9":{"tf":1.0},"94":{"tf":1.4142135623730951},"95":{"tf":1.7320508075688772},"97":{"tf":1.4142135623730951}},"i":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"_":{"df":0,"docs":{},"p":{"df":0,"docs":{},"o":{"df":0,"docs":{},"l":{"df":0,"docs":{},"i":{"df":2,"docs":{"114":{"tf":1.0},"116":{"tf":1.0}}},"y":{".":{"df":0,"docs":{},"e":{"df":0,"docs":{},"v":{"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"_":{"df":0,"docs":{},"o":{"d":{"d":{"_":{"d":{"df":0,"docs":{},"e":{"c":{"df":0,"docs":{},"o":{"df":0,"docs":{},"m":{"df":0,"docs":{},"p":{"df":0,"docs":{},"o":{"df":0,"docs":{},"s":{"df":0,"docs":{},"i":{"df":0,"docs":{},"t":{"df":1,"docs":{"116":{"tf":1.0}}}}}}}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}}}},"_":{"df":0,"docs":{},"e":{"df":0,"docs":{},"v":{"a":{"df":0,"docs":{},"l":{"df":0,"docs":{},"u":{"df":1,"docs":{"116":{"tf":1.0}}}}},"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{".":{"df":0,"docs":{},"e":{"df":0,"docs":{},"v":{"a":{"df":0,"docs":{},"l":{"df":0,"docs":{},"u":{"a":{"df":0,"docs":{},"t":{"df":0,"docs":{},"e":{"(":{"&":{"df":0,"docs":{},"z":{"_":{"df":0,"docs":{},"s":{"df":0,"docs":{},"q":{"df":0,"docs":{},"u":{"a":{"df":0,"docs":{},"r":{"df":1,"docs":{"116":{"tf":1.0}}}},"df":0,"docs":{}}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}}},"df":0,"docs":{}}}},"df":1,"docs":{"116":{"tf":1.0}}}}}},"o":{"d":{"d":{".":{"df":0,"docs":{},"e":{"df":0,"docs":{},"v":{"a":{"df":0,"docs":{},"l":{"df":0,"docs":{},"u":{"a":{"df":0,"docs":{},"t":{"df":0,"docs":{},"e":{"(":{"&":{"df":0,"docs":{},"z":{"_":{"df":0,"docs":{},"s":{"df":0,"docs":{},"q":{"df":0,"docs":{},"u":{"a":{"df":0,"docs":{},"r":{"df":1,"docs":{"116":{"tf":1.0}}}},"df":0,"docs":{}}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}}},"df":0,"docs":{}}}},"df":1,"docs":{"116":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}}}},"df":0,"docs":{}}}}}}}},"u":{"df":0,"docs":{},"t":{"a":{"df":0,"docs":{},"t":{"df":0,"docs":{},"i":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"df":1,"docs":{"99":{"tf":1.0}}}}}}},"df":36,"docs":{"102":{"tf":1.0},"104":{"tf":1.0},"105":{"tf":1.4142135623730951},"107":{"tf":1.0},"112":{"tf":1.7320508075688772},"114":{"tf":2.23606797749979},"123":{"tf":1.0},"124":{"tf":1.0},"14":{"tf":1.0},"146":{"tf":1.7320508075688772},"147":{"tf":1.4142135623730951},"151":{"tf":1.0},"16":{"tf":1.0},"21":{"tf":2.23606797749979},"22":{"tf":1.4142135623730951},"23":{"tf":1.0},"24":{"tf":1.4142135623730951},"25":{"tf":1.4142135623730951},"26":{"tf":1.4142135623730951},"27":{"tf":1.0},"28":{"tf":1.0},"32":{"tf":3.7416573867739413},"34":{"tf":1.0},"4":{"tf":1.4142135623730951},"48":{"tf":2.449489742783178},"49":{"tf":2.0},"50":{"tf":1.4142135623730951},"58":{"tf":1.0},"62":{"tf":1.7320508075688772},"8":{"tf":1.4142135623730951},"80":{"tf":2.0},"84":{"tf":1.0},"85":{"tf":1.0},"91":{"tf":1.7320508075688772},"94":{"tf":3.605551275463989},"95":{"tf":1.7320508075688772}},"e":{"_":{"df":0,"docs":{},"t":{"df":0,"docs":{},"r":{"a":{"df":0,"docs":{},"n":{"df":0,"docs":{},"s":{"df":0,"docs":{},"i":{"df":0,"docs":{},"t":{"df":5,"docs":{"105":{"tf":1.0},"107":{"tf":1.4142135623730951},"113":{"tf":1.0},"114":{"tf":1.7320508075688772},"117":{"tf":1.0}}}}}}},"df":0,"docs":{}}}},"df":0,"docs":{}}}}}},"n":{"c":{"a":{"df":0,"docs":{},"t":{"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"df":4,"docs":{"147":{"tf":1.7320508075688772},"80":{"tf":1.4142135623730951},"88":{"tf":1.0},"92":{"tf":1.0}}}}}},"df":0,"docs":{},"e":{"df":0,"docs":{},"p":{"df":0,"docs":{},"t":{"df":3,"docs":{"118":{"tf":1.0},"14":{"tf":1.0},"149":{"tf":1.0}}}}},"r":{"df":0,"docs":{},"e":{"df":0,"docs":{},"t":{"df":3,"docs":{"13":{"tf":1.0},"49":{"tf":1.0},"9":{"tf":1.0}}}}}},"d":{"df":0,"docs":{},"i":{"df":0,"docs":{},"t":{"df":3,"docs":{"14":{"tf":2.8284271247461903},"16":{"tf":1.0},"45":{"tf":1.0}}}}},"df":0,"docs":{},"f":{"df":0,"docs":{},"i":{"df":0,"docs":{},"g":{"df":0,"docs":{},"u":{"df":0,"docs":{},"r":{"df":7,"docs":{"109":{"tf":1.0},"127":{"tf":1.0},"131":{"tf":1.0},"146":{"tf":1.0},"149":{"tf":1.0},"75":{"tf":1.0},"80":{"tf":1.0}}}}}}},"s":{"df":0,"docs":{},"e":{"df":0,"docs":{},"q":{"df":0,"docs":{},"u":{"df":3,"docs":{"69":{"tf":1.0},"70":{"tf":1.0},"9":{"tf":1.0}}}}},"i":{"d":{"df":5,"docs":{"13":{"tf":1.0},"130":{"tf":1.0},"14":{"tf":1.4142135623730951},"7":{"tf":1.0},"77":{"tf":1.0}},"e":{"df":0,"docs":{},"r":{"df":2,"docs":{"120":{"tf":1.4142135623730951},"87":{"tf":1.0}}}}},"df":0,"docs":{},"s":{"df":0,"docs":{},"t":{"df":14,"docs":{"11":{"tf":1.0},"115":{"tf":1.0},"117":{"tf":1.0},"124":{"tf":1.0},"19":{"tf":1.0},"36":{"tf":1.0},"49":{"tf":1.4142135623730951},"51":{"tf":1.0},"58":{"tf":1.4142135623730951},"59":{"tf":1.0},"60":{"tf":1.7320508075688772},"66":{"tf":1.0},"70":{"tf":1.0},"71":{"tf":1.0}}}}},"t":{"a":{"df":0,"docs":{},"n":{"df":0,"docs":{},"t":{"df":6,"docs":{"10":{"tf":1.7320508075688772},"28":{"tf":1.4142135623730951},"32":{"tf":1.0},"73":{"tf":1.0},"9":{"tf":1.0},"94":{"tf":1.0}}}}},"df":0,"docs":{},"r":{"a":{"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"df":2,"docs":{"146":{"tf":1.0},"151":{"tf":1.4142135623730951}},"t":{"_":{"df":0,"docs":{},"e":{"df":0,"docs":{},"v":{"a":{"df":0,"docs":{},"l":{"df":0,"docs":{},"u":{"a":{"df":0,"docs":{},"t":{"df":0,"docs":{},"i":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"df":0,"docs":{},"s":{".":{"c":{"df":0,"docs":{},"o":{"df":0,"docs":{},"m":{"df":0,"docs":{},"p":{"df":0,"docs":{},"u":{"df":0,"docs":{},"t":{"df":0,"docs":{},"e":{"_":{"c":{"df":0,"docs":{},"o":{"df":0,"docs":{},"m":{"df":0,"docs":{},"p":{"df":0,"docs":{},"o":{"df":0,"docs":{},"s":{"df":0,"docs":{},"i":{"df":0,"docs":{},"t":{"df":0,"docs":{},"i":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"_":{"df":0,"docs":{},"p":{"df":0,"docs":{},"o":{"df":0,"docs":{},"l":{"df":0,"docs":{},"i":{"df":1,"docs":{"114":{"tf":1.0}}},"y":{"(":{"&":{"df":0,"docs":{},"l":{"d":{"df":0,"docs":{},"e":{"_":{"df":0,"docs":{},"r":{"df":0,"docs":{},"o":{"df":0,"docs":{},"o":{"df":0,"docs":{},"t":{"df":0,"docs":{},"s":{"_":{"df":0,"docs":{},"o":{"df":0,"docs":{},"f":{"_":{"df":0,"docs":{},"u":{"df":0,"docs":{},"n":{"df":0,"docs":{},"i":{"df":0,"docs":{},"t":{"df":0,"docs":{},"y":{"_":{"c":{"df":0,"docs":{},"o":{"df":0,"docs":{},"s":{"df":0,"docs":{},"e":{"df":0,"docs":{},"t":{"df":1,"docs":{"116":{"tf":1.0}}}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}}}}},"df":0,"docs":{}}}},"df":0,"docs":{}}}}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}}}},"df":0,"docs":{}}}}}}}}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}}}}},"df":1,"docs":{"114":{"tf":1.4142135623730951}}}}},"df":0,"docs":{}}}},"df":44,"docs":{"105":{"tf":1.4142135623730951},"106":{"tf":2.23606797749979},"107":{"tf":3.0},"109":{"tf":2.449489742783178},"112":{"tf":1.0},"113":{"tf":1.7320508075688772},"114":{"tf":2.23606797749979},"117":{"tf":1.0},"123":{"tf":2.0},"125":{"tf":1.4142135623730951},"130":{"tf":2.0},"131":{"tf":1.4142135623730951},"133":{"tf":1.4142135623730951},"134":{"tf":1.0},"146":{"tf":2.23606797749979},"147":{"tf":1.4142135623730951},"149":{"tf":2.6457513110645907},"150":{"tf":2.8284271247461903},"151":{"tf":3.0},"19":{"tf":1.0},"35":{"tf":1.4142135623730951},"43":{"tf":1.0},"44":{"tf":2.23606797749979},"45":{"tf":1.4142135623730951},"48":{"tf":2.23606797749979},"49":{"tf":1.4142135623730951},"50":{"tf":1.0},"52":{"tf":1.0},"53":{"tf":1.0},"54":{"tf":2.449489742783178},"55":{"tf":2.23606797749979},"56":{"tf":1.4142135623730951},"57":{"tf":1.0},"58":{"tf":1.0},"59":{"tf":1.0},"62":{"tf":1.0},"65":{"tf":1.4142135623730951},"66":{"tf":2.449489742783178},"69":{"tf":1.4142135623730951},"70":{"tf":2.449489742783178},"83":{"tf":2.0},"84":{"tf":2.8284271247461903},"85":{"tf":1.0},"91":{"tf":2.0}},"e":{"df":0,"docs":{},"v":{"a":{"df":0,"docs":{},"l":{"df":0,"docs":{},"u":{"a":{"df":0,"docs":{},"t":{"df":0,"docs":{},"i":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"df":0,"docs":{},"t":{"df":1,"docs":{"114":{"tf":1.0}}}}}}}},"df":1,"docs":{"114":{"tf":1.0}}}}},"df":0,"docs":{}}},"s":{"df":0,"docs":{},"y":{"df":0,"docs":{},"s":{"df":0,"docs":{},"t":{"df":0,"docs":{},"e":{"df":0,"docs":{},"m":{":":{":":{"<":{"df":0,"docs":{},"f":{"df":0,"docs":{},"r":{"df":0,"docs":{},"f":{"df":0,"docs":{},"i":{"df":0,"docs":{},"e":{"df":0,"docs":{},"l":{"d":{">":{":":{":":{"df":0,"docs":{},"n":{"df":0,"docs":{},"e":{"df":0,"docs":{},"w":{"df":2,"docs":{"44":{"tf":1.0},"45":{"tf":1.0}}}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}}}}}},"df":0,"docs":{}},"df":0,"docs":{}},"<":{"df":0,"docs":{},"f":{"df":0,"docs":{},"r":{"df":0,"docs":{},"f":{"df":0,"docs":{},"i":{"df":0,"docs":{},"e":{"df":0,"docs":{},"l":{"d":{"df":1,"docs":{"45":{"tf":1.0}}},"df":0,"docs":{}}}}}}}},"df":0,"docs":{}}}}}}}}}}},"df":0,"docs":{},"u":{"c":{"df":0,"docs":{},"t":{"df":30,"docs":{"112":{"tf":1.4142135623730951},"113":{"tf":1.0},"114":{"tf":2.0},"117":{"tf":1.0},"118":{"tf":1.0},"12":{"tf":1.0},"125":{"tf":1.0},"14":{"tf":1.4142135623730951},"142":{"tf":1.7320508075688772},"144":{"tf":1.0},"145":{"tf":1.4142135623730951},"146":{"tf":2.6457513110645907},"16":{"tf":1.4142135623730951},"20":{"tf":1.0},"21":{"tf":1.0},"41":{"tf":1.0},"53":{"tf":1.4142135623730951},"54":{"tf":1.4142135623730951},"55":{"tf":1.0},"56":{"tf":1.4142135623730951},"58":{"tf":1.4142135623730951},"60":{"tf":1.0},"62":{"tf":1.4142135623730951},"65":{"tf":1.0},"68":{"tf":1.0},"69":{"tf":1.7320508075688772},"84":{"tf":1.7320508075688772},"85":{"tf":1.4142135623730951},"91":{"tf":1.0},"94":{"tf":1.0}}}},"df":0,"docs":{}}}}},"t":{"a":{"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"df":8,"docs":{"13":{"tf":1.0},"146":{"tf":1.0},"35":{"tf":1.0},"48":{"tf":1.0},"50":{"tf":1.0},"65":{"tf":1.4142135623730951},"68":{"tf":1.4142135623730951},"70":{"tf":1.0}}}}},"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"df":0,"docs":{},"t":{"df":1,"docs":{"133":{"tf":1.0}}}},"x":{"df":0,"docs":{},"t":{"df":5,"docs":{"109":{"tf":1.7320508075688772},"14":{"tf":1.0},"7":{"tf":1.0},"80":{"tf":1.0},"84":{"tf":1.0}}}}},"i":{"df":0,"docs":{},"n":{"df":0,"docs":{},"u":{"df":4,"docs":{"102":{"tf":1.0},"130":{"tf":1.0},"134":{"tf":1.0},"146":{"tf":2.0}}}}},"r":{"df":0,"docs":{},"i":{"df":0,"docs":{},"v":{"df":1,"docs":{"49":{"tf":1.0}}}},"o":{"df":0,"docs":{},"l":{"df":1,"docs":{"118":{"tf":1.0}}}}}},"v":{"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"df":0,"docs":{},"i":{"df":2,"docs":{"35":{"tf":1.0},"92":{"tf":1.0}}},"t":{"df":1,"docs":{"95":{"tf":1.0}}}},"r":{"df":0,"docs":{},"s":{"df":1,"docs":{"14":{"tf":1.4142135623730951}}},"t":{"df":2,"docs":{"14":{"tf":1.0},"4":{"tf":1.0}}}}},"i":{"df":0,"docs":{},"n":{"c":{"df":9,"docs":{"21":{"tf":1.4142135623730951},"53":{"tf":1.0},"55":{"tf":1.0},"57":{"tf":1.0},"58":{"tf":1.0},"69":{"tf":1.4142135623730951},"73":{"tf":1.0},"75":{"tf":1.0},"80":{"tf":1.0}}},"df":0,"docs":{}}}}},"o":{"df":0,"docs":{},"r":{"d":{"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"df":4,"docs":{"14":{"tf":1.4142135623730951},"73":{"tf":1.0},"79":{"tf":1.0},"92":{"tf":1.0}}}}},"df":0,"docs":{}}},"p":{"df":0,"docs":{},"i":{"df":3,"docs":{"142":{"tf":1.7320508075688772},"146":{"tf":1.7320508075688772},"35":{"tf":1.0}}},"r":{"df":0,"docs":{},"i":{"df":0,"docs":{},"m":{"df":1,"docs":{"124":{"tf":1.0}}}}}},"r":{"df":0,"docs":{},"e":{"df":1,"docs":{"144":{"tf":1.0}}},"r":{"df":0,"docs":{},"e":{"c":{"df":0,"docs":{},"t":{"df":6,"docs":{"151":{"tf":1.0},"50":{"tf":1.0},"59":{"tf":1.0},"69":{"tf":1.0},"7":{"tf":1.4142135623730951},"72":{"tf":1.0}},"l":{"df":0,"docs":{},"i":{"df":7,"docs":{"104":{"tf":1.0},"117":{"tf":1.0},"146":{"tf":1.0},"151":{"tf":1.0},"58":{"tf":1.4142135623730951},"60":{"tf":1.0},"84":{"tf":1.0}}}}}},"df":0,"docs":{},"s":{"df":0,"docs":{},"p":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"d":{"df":18,"docs":{"10":{"tf":2.0},"108":{"tf":1.0},"11":{"tf":1.4142135623730951},"13":{"tf":1.0},"134":{"tf":1.0},"136":{"tf":1.0},"14":{"tf":1.0},"142":{"tf":1.4142135623730951},"146":{"tf":2.23606797749979},"149":{"tf":1.7320508075688772},"16":{"tf":1.4142135623730951},"23":{"tf":1.0},"36":{"tf":1.4142135623730951},"4":{"tf":1.0},"52":{"tf":1.0},"72":{"tf":1.0},"73":{"tf":1.0},"75":{"tf":1.0}}},"df":0,"docs":{}}}}}}}},"s":{"df":0,"docs":{},"e":{"df":0,"docs":{},"t":{"_":{"df":0,"docs":{},"o":{"df":0,"docs":{},"f":{"df":0,"docs":{},"f":{"df":0,"docs":{},"s":{"df":0,"docs":{},"e":{"df":0,"docs":{},"t":{"df":1,"docs":{"109":{"tf":1.0}}}}}}}}},"df":3,"docs":{"109":{"tf":1.0},"125":{"tf":2.449489742783178},"91":{"tf":1.0}}}},"t":{"df":1,"docs":{"151":{"tf":1.0}},"l":{"df":0,"docs":{},"i":{"df":1,"docs":{"80":{"tf":1.0}}}}}},"u":{"df":0,"docs":{},"l":{"d":{"df":0,"docs":{},"n":{"'":{"df":0,"docs":{},"t":{"df":1,"docs":{"103":{"tf":1.0}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"p":{"df":0,"docs":{},"l":{"df":1,"docs":{"105":{"tf":1.0}}}},"r":{"df":0,"docs":{},"s":{"df":3,"docs":{"12":{"tf":1.0},"125":{"tf":1.0},"14":{"tf":1.0}}}}}},"p":{"df":0,"docs":{},"u":{"_":{"a":{"df":0,"docs":{},"i":{"df":0,"docs":{},"r":{"_":{"d":{"df":0,"docs":{},"e":{"df":0,"docs":{},"f":{"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"df":0,"docs":{},"i":{"df":0,"docs":{},"t":{"df":0,"docs":{},"i":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"1":{"0":{".":{"df":0,"docs":{},"h":{"df":1,"docs":{"127":{"tf":1.0}}},"i":{"df":0,"docs":{},"n":{"df":0,"docs":{},"l":{"df":1,"docs":{"127":{"tf":1.0}}}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}}}}}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":1,"docs":{"3":{"tf":2.0}}}},"r":{"a":{"df":0,"docs":{},"f":{"df":0,"docs":{},"t":{"df":1,"docs":{"79":{"tf":1.0}}}},"t":{"df":0,"docs":{},"e":{"df":1,"docs":{"1":{"tf":1.4142135623730951}}}}},"df":0,"docs":{},"e":{"a":{"df":0,"docs":{},"t":{"df":5,"docs":{"108":{"tf":1.0},"146":{"tf":1.0},"44":{"tf":1.4142135623730951},"45":{"tf":1.0},"50":{"tf":1.0}},"i":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"df":1,"docs":{"34":{"tf":1.0}}}}}}},"df":0,"docs":{}},"i":{"df":0,"docs":{},"t":{"df":0,"docs":{},"i":{"c":{"df":1,"docs":{"146":{"tf":1.0}}},"df":0,"docs":{}}}},"u":{"c":{"df":0,"docs":{},"i":{"a":{"df":0,"docs":{},"l":{"df":1,"docs":{"71":{"tf":1.0}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"y":{"df":0,"docs":{},"p":{"df":0,"docs":{},"t":{"df":0,"docs":{},"o":{"df":1,"docs":{"1":{"tf":1.0}},"g":{"df":0,"docs":{},"r":{"a":{"df":0,"docs":{},"p":{"df":0,"docs":{},"h":{"df":3,"docs":{"19":{"tf":1.0},"4":{"tf":1.0},"71":{"tf":1.0}}}}},"df":0,"docs":{}}}}}}}},"s":{":":{":":{"c":{"df":0,"docs":{},"o":{"df":0,"docs":{},"m":{"df":0,"docs":{},"m":{"df":0,"docs":{},"i":{"df":0,"docs":{},"t":{"df":1,"docs":{"35":{"tf":3.0}}}}}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":1,"docs":{"35":{"tf":2.449489742783178}}},"u":{"df":0,"docs":{},"m":{"df":1,"docs":{"139":{"tf":1.4142135623730951}},"u":{"df":0,"docs":{},"l":{"df":3,"docs":{"138":{"tf":1.7320508075688772},"139":{"tf":1.0},"146":{"tf":1.7320508075688772}}}}},"r":{"df":0,"docs":{},"r":{"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"df":0,"docs":{},"t":{"df":7,"docs":{"107":{"tf":2.23606797749979},"109":{"tf":1.0},"121":{"tf":1.0},"133":{"tf":1.0},"40":{"tf":1.0},"66":{"tf":1.0},"84":{"tf":1.0}}}}}},"v":{"df":2,"docs":{"38":{"tf":1.0},"44":{"tf":1.0}}}},"s":{"df":0,"docs":{},"t":{"df":0,"docs":{},"o":{"df":0,"docs":{},"m":{"df":1,"docs":{"12":{"tf":1.7320508075688772}}}}}}},"y":{"c":{"df":0,"docs":{},"l":{"df":4,"docs":{"127":{"tf":1.0},"128":{"tf":1.0},"144":{"tf":1.0},"146":{"tf":1.0}},"i":{"c":{"df":1,"docs":{"38":{"tf":1.0}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"d":{"0":{"df":1,"docs":{"149":{"tf":1.7320508075688772}},"​":{":":{"=":{"d":{"df":0,"docs":{},"l":{"d":{"df":0,"docs":{},"e":{"df":1,"docs":{"94":{"tf":1.0}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"1":{"df":1,"docs":{"149":{"tf":1.7320508075688772}}},"2":{"df":1,"docs":{"149":{"tf":1.7320508075688772}}},"3":{"df":1,"docs":{"149":{"tf":1.7320508075688772}}},"=":{"(":{"d":{"1":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"…":{",":{"d":{"df":0,"docs":{},"m":{"df":3,"docs":{"73":{"tf":1.4142135623730951},"75":{"tf":1.0},"80":{"tf":1.0}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{},"y":{"1":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"…":{",":{"df":0,"docs":{},"y":{"df":0,"docs":{},"l":{"df":1,"docs":{"90":{"tf":1.0}}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{},"{":{"1":{",":{"df":0,"docs":{},"η":{",":{"df":0,"docs":{},"η":{"2":{",":{"df":0,"docs":{},"…":{",":{"df":0,"docs":{},"η":{"3":{"df":0,"docs":{},"n":{"df":0,"docs":{},"−":{"1":{"df":1,"docs":{"14":{"tf":1.0}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{},"g":{"df":0,"docs":{},"i":{"df":0,"docs":{},"​":{"df":0,"docs":{},"}":{"df":0,"docs":{},"i":{"=":{"0":{"2":{"df":0,"docs":{},"n":{"df":0,"docs":{},"−":{"1":{"df":1,"docs":{"84":{"tf":1.0}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}}}}}},"a":{"df":0,"docs":{},"t":{"a":{"df":2,"docs":{"146":{"tf":1.4142135623730951},"32":{"tf":1.0}}},"df":0,"docs":{}}},"df":11,"docs":{"114":{"tf":1.0},"14":{"tf":1.0},"142":{"tf":1.4142135623730951},"146":{"tf":2.23606797749979},"147":{"tf":1.4142135623730951},"149":{"tf":1.0},"3":{"tf":1.4142135623730951},"73":{"tf":1.0},"76":{"tf":1.0},"82":{"tf":1.0},"84":{"tf":1.0}},"e":{"c":{"df":0,"docs":{},"e":{"df":0,"docs":{},"i":{"df":0,"docs":{},"v":{"df":1,"docs":{"83":{"tf":1.0}}}}},"o":{"d":{"df":3,"docs":{"142":{"tf":1.0},"146":{"tf":2.0},"147":{"tf":1.0}},"e":{"/":{"df":0,"docs":{},"o":{"df":0,"docs":{},"p":{"c":{"df":0,"docs":{},"o":{"d":{"df":1,"docs":{"131":{"tf":1.4142135623730951}}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{},"m":{"df":0,"docs":{},"p":{"df":0,"docs":{},"o":{"df":0,"docs":{},"s":{"df":2,"docs":{"151":{"tf":1.0},"94":{"tf":1.4142135623730951}},"i":{"df":0,"docs":{},"t":{"df":4,"docs":{"113":{"tf":1.0},"116":{"tf":1.7320508075688772},"146":{"tf":1.0},"67":{"tf":1.7320508075688772}}}}}}}}}},"d":{"df":0,"docs":{},"i":{"c":{"df":2,"docs":{"146":{"tf":1.4142135623730951},"150":{"tf":1.0}}},"df":0,"docs":{}}},"df":0,"docs":{},"e":{"df":0,"docs":{},"p":{"(":{"df":0,"docs":{},"x":{")":{"=":{"df":0,"docs":{},"γ":{"1":{"df":0,"docs":{},"​":{"df":0,"docs":{},"x":{"df":0,"docs":{},"−":{"df":0,"docs":{},"z":{"df":0,"docs":{},"h":{"(":{"df":0,"docs":{},"x":{")":{"df":0,"docs":{},"−":{"df":0,"docs":{},"h":{"(":{"df":0,"docs":{},"z":{")":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"γ":{"2":{"df":0,"docs":{},"​":{"df":0,"docs":{},"x":{"df":0,"docs":{},"−":{"df":0,"docs":{},"z":{"df":0,"docs":{},"t":{"(":{"df":0,"docs":{},"x":{")":{"df":0,"docs":{},"−":{"df":0,"docs":{},"t":{"(":{"df":0,"docs":{},"z":{")":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"γ":{"3":{"df":0,"docs":{},"​":{"df":0,"docs":{},"x":{"df":0,"docs":{},"−":{"df":0,"docs":{},"z":{"df":0,"docs":{},"g":{"df":0,"docs":{},"t":{"(":{"df":0,"docs":{},"x":{")":{"df":0,"docs":{},"−":{"df":0,"docs":{},"t":{"(":{"df":0,"docs":{},"z":{"df":0,"docs":{},"g":{")":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"γ":{"4":{"df":0,"docs":{},"​":{"df":0,"docs":{},"x":{"df":0,"docs":{},"−":{"df":0,"docs":{},"z":{"df":0,"docs":{},"g":{"2":{"df":0,"docs":{},"t":{"(":{"df":0,"docs":{},"x":{")":{"df":0,"docs":{},"−":{"df":0,"docs":{},"t":{"(":{"df":0,"docs":{},"z":{"df":0,"docs":{},"g":{"2":{"df":1,"docs":{"59":{"tf":1.0}}},"df":0,"docs":{}}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"0":{"df":1,"docs":{"113":{"tf":1.0}},"​":{")":{"=":{"df":0,"docs":{},"γ":{"1":{"df":0,"docs":{},"​":{"df":0,"docs":{},"x":{"0":{"df":0,"docs":{},"​":{"df":0,"docs":{},"−":{"df":0,"docs":{},"z":{"df":0,"docs":{},"h":{"(":{"df":0,"docs":{},"x":{"0":{"df":0,"docs":{},"​":{")":{"df":0,"docs":{},"−":{"df":0,"docs":{},"h":{"(":{"df":0,"docs":{},"z":{")":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"γ":{"2":{"df":0,"docs":{},"​":{"df":0,"docs":{},"x":{"0":{"df":0,"docs":{},"​":{"df":0,"docs":{},"−":{"df":0,"docs":{},"z":{"df":0,"docs":{},"t":{"(":{"df":0,"docs":{},"x":{"0":{"df":0,"docs":{},"​":{")":{"df":0,"docs":{},"−":{"df":0,"docs":{},"t":{"(":{"df":0,"docs":{},"z":{")":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"γ":{"3":{"df":0,"docs":{},"​":{"df":0,"docs":{},"x":{"0":{"df":0,"docs":{},"​":{"df":0,"docs":{},"−":{"df":0,"docs":{},"z":{"df":0,"docs":{},"g":{"df":0,"docs":{},"t":{"(":{"df":0,"docs":{},"x":{"0":{"df":0,"docs":{},"​":{")":{"df":0,"docs":{},"−":{"df":0,"docs":{},"t":{"(":{"df":0,"docs":{},"z":{"df":0,"docs":{},"g":{")":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"γ":{"4":{"df":0,"docs":{},"​":{"df":0,"docs":{},"x":{"0":{"df":0,"docs":{},"​":{"df":0,"docs":{},"−":{"df":0,"docs":{},"z":{"df":0,"docs":{},"g":{"2":{"df":0,"docs":{},"t":{"(":{"df":0,"docs":{},"x":{"0":{"df":0,"docs":{},"​":{")":{"df":0,"docs":{},"−":{"df":0,"docs":{},"t":{"(":{"df":0,"docs":{},"z":{"df":0,"docs":{},"g":{"2":{"df":3,"docs":{"113":{"tf":1.0},"60":{"tf":1.0},"63":{"tf":1.0}}},"df":0,"docs":{}}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"_":{"0":{"df":1,"docs":{"63":{"tf":1.0}}},"df":0,"docs":{}},"df":3,"docs":{"112":{"tf":1.4142135623730951},"59":{"tf":1.4142135623730951},"62":{"tf":1.4142135623730951}}}},"df":13,"docs":{"100":{"tf":1.0},"112":{"tf":1.0},"119":{"tf":1.7320508075688772},"125":{"tf":1.0},"59":{"tf":1.7320508075688772},"60":{"tf":1.0},"62":{"tf":1.0},"65":{"tf":1.0},"68":{"tf":1.4142135623730951},"80":{"tf":1.0},"81":{"tf":1.4142135623730951},"95":{"tf":1.0},"97":{"tf":1.0}},"e":{"df":0,"docs":{},"r":{"df":2,"docs":{"103":{"tf":1.0},"69":{"tf":1.0}}}}}},"f":{"a":{"df":0,"docs":{},"u":{"df":0,"docs":{},"l":{"df":0,"docs":{},"t":{"df":1,"docs":{"9":{"tf":1.0}}}}}},"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"df":29,"docs":{"105":{"tf":1.7320508075688772},"108":{"tf":1.0},"109":{"tf":1.4142135623730951},"110":{"tf":1.0},"113":{"tf":1.0},"114":{"tf":1.0},"128":{"tf":1.0},"131":{"tf":1.0},"14":{"tf":1.7320508075688772},"142":{"tf":1.7320508075688772},"147":{"tf":2.449489742783178},"149":{"tf":1.0},"150":{"tf":2.0},"20":{"tf":1.0},"23":{"tf":1.0},"25":{"tf":1.0},"26":{"tf":1.4142135623730951},"28":{"tf":1.4142135623730951},"35":{"tf":1.4142135623730951},"5":{"tf":1.0},"50":{"tf":1.0},"54":{"tf":1.0},"55":{"tf":1.0},"59":{"tf":1.0},"8":{"tf":1.0},"90":{"tf":1.0},"94":{"tf":1.4142135623730951},"95":{"tf":1.0},"97":{"tf":1.7320508075688772}},"i":{"df":0,"docs":{},"t":{"df":3,"docs":{"14":{"tf":1.4142135623730951},"34":{"tf":1.0},"91":{"tf":1.4142135623730951}}}}}}},"g":{"df":0,"docs":{},"r":{"df":0,"docs":{},"e":{"df":18,"docs":{"109":{"tf":1.0},"114":{"tf":1.0},"119":{"tf":1.0},"125":{"tf":1.0},"14":{"tf":1.4142135623730951},"146":{"tf":1.0},"20":{"tf":2.23606797749979},"26":{"tf":1.0},"3":{"tf":1.4142135623730951},"68":{"tf":2.449489742783178},"73":{"tf":3.0},"74":{"tf":1.4142135623730951},"75":{"tf":1.7320508075688772},"78":{"tf":2.0},"79":{"tf":1.7320508075688772},"80":{"tf":1.0},"81":{"tf":1.0},"91":{"tf":1.4142135623730951}}}}},"l":{"df":0,"docs":{},"e":{"df":0,"docs":{},"g":{"df":2,"docs":{"151":{"tf":1.0},"7":{"tf":1.0}}}},"i":{"c":{"df":1,"docs":{"16":{"tf":1.0}}},"df":0,"docs":{}},"v":{"df":1,"docs":{"4":{"tf":1.0}}}},"m":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"df":0,"docs":{},"s":{"df":0,"docs":{},"t":{"df":0,"docs":{},"r":{"df":2,"docs":{"13":{"tf":1.0},"7":{"tf":1.0}}}}}},"s":{"df":0,"docs":{},"t":{"df":0,"docs":{},"r":{"df":1,"docs":{"7":{"tf":1.0}}}}}}},"n":{"df":0,"docs":{},"o":{"df":0,"docs":{},"m":{"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"df":4,"docs":{"114":{"tf":1.0},"123":{"tf":1.0},"125":{"tf":1.4142135623730951},"54":{"tf":1.0}}}}},"t":{"df":18,"docs":{"14":{"tf":1.0},"147":{"tf":1.0},"150":{"tf":1.0},"19":{"tf":1.0},"36":{"tf":1.0},"5":{"tf":3.0},"53":{"tf":1.0},"70":{"tf":1.4142135623730951},"72":{"tf":1.0},"75":{"tf":1.0},"77":{"tf":1.0},"82":{"tf":1.0},"83":{"tf":1.0},"84":{"tf":1.0},"88":{"tf":1.0},"90":{"tf":2.0},"91":{"tf":1.4142135623730951},"95":{"tf":1.4142135623730951}}}}},"p":{"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"d":{"df":9,"docs":{"10":{"tf":1.0},"109":{"tf":1.0},"13":{"tf":1.4142135623730951},"146":{"tf":1.7320508075688772},"70":{"tf":1.4142135623730951},"73":{"tf":1.4142135623730951},"8":{"tf":1.0},"84":{"tf":1.4142135623730951},"92":{"tf":1.0}}},"df":0,"docs":{}}}},"r":{"df":0,"docs":{},"i":{"df":0,"docs":{},"v":{"df":5,"docs":{"142":{"tf":1.0},"146":{"tf":1.7320508075688772},"147":{"tf":1.0},"5":{"tf":1.0},"91":{"tf":1.0}}}}},"s":{"c":{"df":0,"docs":{},"r":{"df":0,"docs":{},"i":{"b":{"df":12,"docs":{"142":{"tf":1.0},"145":{"tf":1.0},"147":{"tf":1.0},"23":{"tf":1.4142135623730951},"37":{"tf":1.0},"40":{"tf":1.0},"74":{"tf":1.0},"77":{"tf":1.0},"80":{"tf":1.0},"86":{"tf":1.0},"87":{"tf":1.0},"95":{"tf":1.0}}},"df":0,"docs":{},"p":{"df":0,"docs":{},"t":{"df":21,"docs":{"127":{"tf":1.0},"128":{"tf":1.0},"129":{"tf":1.0},"130":{"tf":1.0},"131":{"tf":1.0},"132":{"tf":1.0},"133":{"tf":1.0},"134":{"tf":1.0},"135":{"tf":1.0},"136":{"tf":1.0},"137":{"tf":1.0},"138":{"tf":1.0},"139":{"tf":1.0},"141":{"tf":1.0},"142":{"tf":1.4142135623730951},"143":{"tf":1.0},"144":{"tf":1.0},"145":{"tf":1.0},"146":{"tf":1.0},"16":{"tf":1.0},"82":{"tf":1.4142135623730951}}}}}}},"df":0,"docs":{},"i":{"df":0,"docs":{},"g":{"df":0,"docs":{},"n":{"df":2,"docs":{"10":{"tf":1.0},"151":{"tf":1.0}}}},"r":{"df":2,"docs":{"146":{"tf":1.0},"20":{"tf":1.0}}}}},"t":{"a":{"df":0,"docs":{},"i":{"df":0,"docs":{},"l":{"df":16,"docs":{"103":{"tf":1.0},"114":{"tf":1.0},"127":{"tf":1.0},"133":{"tf":1.0},"134":{"tf":1.0},"138":{"tf":1.0},"139":{"tf":1.4142135623730951},"143":{"tf":1.0},"144":{"tf":1.4142135623730951},"145":{"tf":1.7320508075688772},"146":{"tf":1.4142135623730951},"151":{"tf":1.0},"18":{"tf":1.4142135623730951},"34":{"tf":1.0},"37":{"tf":1.7320508075688772},"75":{"tf":1.0}}}}},"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":0,"docs":{},"m":{"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"df":1,"docs":{"91":{"tf":1.0}},"i":{"df":0,"docs":{},"s":{"df":0,"docs":{},"t":{"df":1,"docs":{"137":{"tf":1.0}}}}}}}}}}},"v":{"df":0,"docs":{},"i":{"c":{"df":1,"docs":{"151":{"tf":1.4142135623730951}}},"df":0,"docs":{}}}},"i":{"a":{"df":0,"docs":{},"g":{"df":0,"docs":{},"r":{"a":{"df":0,"docs":{},"m":{"df":1,"docs":{"133":{"tf":1.0}}}},"df":0,"docs":{}}}},"d":{"df":0,"docs":{},"n":{"'":{"df":0,"docs":{},"t":{"df":1,"docs":{"134":{"tf":1.0}}}},"df":0,"docs":{}}},"df":0,"docs":{},"f":{"df":0,"docs":{},"f":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":15,"docs":{"118":{"tf":1.0},"14":{"tf":1.7320508075688772},"146":{"tf":1.0},"149":{"tf":1.0},"20":{"tf":1.0},"3":{"tf":1.0},"50":{"tf":1.4142135623730951},"52":{"tf":1.0},"53":{"tf":1.0},"56":{"tf":1.0},"66":{"tf":1.4142135623730951},"70":{"tf":1.0},"73":{"tf":1.0},"80":{"tf":1.0},"9":{"tf":1.0}}}}}},"g":{"df":0,"docs":{},"e":{"df":0,"docs":{},"s":{"df":0,"docs":{},"t":{"df":1,"docs":{"7":{"tf":1.0}}}}}},"r":{"df":0,"docs":{},"e":{"c":{"df":0,"docs":{},"t":{"df":0,"docs":{},"l":{"df":0,"docs":{},"i":{"df":3,"docs":{"127":{"tf":1.0},"14":{"tf":1.0},"146":{"tf":1.4142135623730951}}}}}},"df":0,"docs":{}}},"s":{"c":{"df":0,"docs":{},"u":{"df":0,"docs":{},"s":{"df":0,"docs":{},"s":{"df":3,"docs":{"34":{"tf":1.0},"43":{"tf":1.0},"56":{"tf":1.0}}}}}},"df":0,"docs":{},"j":{"df":0,"docs":{},"o":{"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"df":0,"docs":{},"t":{"df":1,"docs":{"90":{"tf":1.0}}}}}}},"t":{"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"df":0,"docs":{},"g":{"df":0,"docs":{},"u":{"df":0,"docs":{},"i":{"df":0,"docs":{},"s":{"df":0,"docs":{},"h":{"df":1,"docs":{"9":{"tf":1.0}}}}}}}}}}},"v":{"df":0,"docs":{},"e":{"df":4,"docs":{"103":{"tf":1.0},"46":{"tf":1.0},"69":{"tf":1.0},"81":{"tf":1.0}}},"i":{"d":{"df":7,"docs":{"100":{"tf":1.0},"123":{"tf":1.4142135623730951},"125":{"tf":1.7320508075688772},"146":{"tf":1.4142135623730951},"38":{"tf":1.0},"79":{"tf":1.0},"99":{"tf":1.0}}},"df":0,"docs":{},"s":{"df":4,"docs":{"125":{"tf":1.4142135623730951},"146":{"tf":1.0},"69":{"tf":1.0},"99":{"tf":1.0}}}}},"​":{"=":{"df":0,"docs":{},"h":{"df":0,"docs":{},"ω":{"df":0,"docs":{},"i":{"df":2,"docs":{"73":{"tf":1.0},"75":{"tf":1.0}}}}}},"df":0,"docs":{},"∈":{"d":{"df":1,"docs":{"73":{"tf":1.0}}},"df":0,"docs":{}},"−":{"df":0,"docs":{},"z":{"df":1,"docs":{"79":{"tf":1.0}}}}}},"k":{"df":0,"docs":{},"​":{":":{"=":{"(":{"d":{"0":{"2":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"…":{",":{"d":{"df":0,"docs":{},"l":{"df":0,"docs":{},"−":{"1":{"2":{"df":1,"docs":{"94":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"−":{"1":{"df":0,"docs":{},"​":{"=":{"(":{"d":{"0":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"…":{",":{"d":{"2":{"df":0,"docs":{},"l":{"df":0,"docs":{},"−":{"1":{"df":1,"docs":{"94":{"tf":1.0}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"l":{"d":{"df":0,"docs":{},"e":{"df":1,"docs":{"83":{"tf":1.0}},"​":{"=":{"(":{"df":0,"docs":{},"h":{",":{"df":0,"docs":{},"h":{"df":0,"docs":{},"ω":{",":{"df":0,"docs":{},"h":{"df":0,"docs":{},"ω":{"2":{",":{"df":0,"docs":{},"…":{",":{"df":0,"docs":{},"h":{"df":0,"docs":{},"ω":{"2":{"df":0,"docs":{},"n":{"+":{"df":0,"docs":{},"l":{"df":1,"docs":{"82":{"tf":1.0}},"−":{"1":{"df":1,"docs":{"91":{"tf":1.0}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}},"o":{"c":{"df":0,"docs":{},"u":{"df":0,"docs":{},"m":{"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"df":0,"docs":{},"t":{"df":3,"docs":{"0":{"tf":1.0},"126":{"tf":1.4142135623730951},"46":{"tf":1.0}}}}}}}},"df":5,"docs":{"109":{"tf":1.0},"114":{"tf":1.0},"125":{"tf":1.7320508075688772},"146":{"tf":1.4142135623730951},"149":{"tf":1.0}},"e":{"df":0,"docs":{},"s":{"df":0,"docs":{},"n":{"'":{"df":0,"docs":{},"t":{"df":4,"docs":{"125":{"tf":1.0},"130":{"tf":1.0},"14":{"tf":1.0},"32":{"tf":1.0}}}},"df":0,"docs":{}}}},"m":{"a":{"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"df":24,"docs":{"101":{"tf":1.0},"112":{"tf":1.4142135623730951},"115":{"tf":1.0},"117":{"tf":1.7320508075688772},"119":{"tf":1.0},"125":{"tf":1.4142135623730951},"14":{"tf":2.0},"16":{"tf":1.0},"20":{"tf":1.0},"24":{"tf":1.0},"25":{"tf":1.0},"26":{"tf":1.0},"32":{"tf":1.4142135623730951},"35":{"tf":1.4142135623730951},"5":{"tf":1.0},"59":{"tf":1.4142135623730951},"60":{"tf":1.4142135623730951},"62":{"tf":1.4142135623730951},"66":{"tf":1.0},"68":{"tf":1.4142135623730951},"73":{"tf":1.7320508075688772},"84":{"tf":1.0},"91":{"tf":1.0},"94":{"tf":1.4142135623730951}}}}},"df":0,"docs":{}},"n":{"'":{"df":0,"docs":{},"t":{"df":9,"docs":{"111":{"tf":1.0},"114":{"tf":1.0},"116":{"tf":1.4142135623730951},"118":{"tf":1.0},"123":{"tf":1.4142135623730951},"125":{"tf":1.7320508075688772},"149":{"tf":1.0},"151":{"tf":1.0},"67":{"tf":1.0}}}},"df":0,"docs":{},"e":{"df":12,"docs":{"103":{"tf":1.0},"105":{"tf":1.0},"110":{"tf":1.0},"114":{"tf":1.4142135623730951},"118":{"tf":1.4142135623730951},"130":{"tf":1.0},"146":{"tf":1.4142135623730951},"151":{"tf":1.4142135623730951},"58":{"tf":1.0},"69":{"tf":1.0},"83":{"tf":1.0},"84":{"tf":1.0}}}},"t":{"df":2,"docs":{"65":{"tf":1.0},"66":{"tf":1.0}}},"w":{"df":0,"docs":{},"n":{"df":4,"docs":{"14":{"tf":1.4142135623730951},"16":{"tf":1.0},"21":{"tf":1.0},"35":{"tf":1.0}}}}},"s":{"df":1,"docs":{"94":{"tf":1.4142135623730951}},"t":{"_":{"a":{"d":{"d":{"df":0,"docs":{},"r":{"[":{"0":{"df":1,"docs":{"147":{"tf":1.0}}},"1":{"df":1,"docs":{"147":{"tf":1.0}}},"df":0,"docs":{}},"df":3,"docs":{"142":{"tf":1.0},"146":{"tf":1.7320508075688772},"147":{"tf":1.4142135623730951}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{},"r":{"df":0,"docs":{},"e":{"df":0,"docs":{},"g":{"df":1,"docs":{"146":{"tf":1.0}}}}}},"df":4,"docs":{"133":{"tf":2.0},"142":{"tf":1.0},"146":{"tf":2.23606797749979},"147":{"tf":1.4142135623730951}},"r":{"df":0,"docs":{},"e":{"df":0,"docs":{},"g":{"df":1,"docs":{"149":{"tf":1.0}}}}}},"​":{"=":{"(":{"1":{",":{"df":0,"docs":{},"g":{",":{"df":0,"docs":{},"…":{",":{"df":0,"docs":{},"g":{"2":{"df":0,"docs":{},"n":{"df":0,"docs":{},"−":{"1":{"df":1,"docs":{"91":{"tf":1.0}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{},"{":{"df":0,"docs":{},"g":{"df":0,"docs":{},"i":{"df":0,"docs":{},"}":{"df":0,"docs":{},"i":{"=":{"0":{"2":{"df":0,"docs":{},"n":{"df":0,"docs":{},"−":{"1":{"df":0,"docs":{},"​":{"df":0,"docs":{},"⊆":{"df":0,"docs":{},"f":{"df":1,"docs":{"82":{"tf":1.0}}}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}}}}},"df":0,"docs":{}}},"u":{"df":0,"docs":{},"e":{"df":2,"docs":{"13":{"tf":1.0},"4":{"tf":1.0}}},"m":{"df":0,"docs":{},"m":{"df":0,"docs":{},"i":{"df":5,"docs":{"133":{"tf":1.7320508075688772},"135":{"tf":1.7320508075688772},"146":{"tf":1.7320508075688772},"149":{"tf":1.0},"36":{"tf":1.7320508075688772}}}}},"r":{"df":0,"docs":{},"e":{"df":2,"docs":{"14":{"tf":1.0},"80":{"tf":1.0}}}}}},"df":0,"docs":{},"e":{"+":{"1":{")":{"df":0,"docs":{},"∗":{"df":0,"docs":{},"x":{"df":0,"docs":{},"−":{"1":{"df":1,"docs":{"7":{"tf":1.0}}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}},"=":{"2":{"df":1,"docs":{"9":{"tf":1.0}}},"df":0,"docs":{}},"a":{"c":{"df":0,"docs":{},"h":{"df":30,"docs":{"10":{"tf":1.0},"102":{"tf":1.0},"106":{"tf":1.0},"109":{"tf":1.7320508075688772},"114":{"tf":1.7320508075688772},"125":{"tf":1.0},"128":{"tf":1.0},"130":{"tf":1.4142135623730951},"131":{"tf":2.0},"133":{"tf":1.4142135623730951},"136":{"tf":1.0},"14":{"tf":1.0},"142":{"tf":1.4142135623730951},"146":{"tf":3.3166247903554},"147":{"tf":1.0},"149":{"tf":2.0},"150":{"tf":1.7320508075688772},"151":{"tf":2.23606797749979},"3":{"tf":1.0},"49":{"tf":1.0},"65":{"tf":1.4142135623730951},"66":{"tf":1.4142135623730951},"70":{"tf":1.0},"80":{"tf":1.7320508075688772},"82":{"tf":1.4142135623730951},"83":{"tf":1.0},"84":{"tf":1.0},"94":{"tf":1.4142135623730951},"95":{"tf":1.0},"99":{"tf":1.0}}}},"df":0,"docs":{},"r":{"df":0,"docs":{},"l":{"df":0,"docs":{},"i":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":2,"docs":{"59":{"tf":1.0},"80":{"tf":1.0}}}}}}},"s":{"df":1,"docs":{"70":{"tf":1.0}},"i":{"df":1,"docs":{"14":{"tf":1.0}},"l":{"df":0,"docs":{},"i":{"df":5,"docs":{"114":{"tf":1.0},"133":{"tf":1.0},"45":{"tf":1.0},"7":{"tf":1.0},"70":{"tf":1.0}}}}}}},"df":10,"docs":{"11":{"tf":1.0},"142":{"tf":1.4142135623730951},"146":{"tf":1.7320508075688772},"147":{"tf":1.4142135623730951},"34":{"tf":1.0},"35":{"tf":2.6457513110645907},"44":{"tf":2.6457513110645907},"45":{"tf":2.0},"7":{"tf":2.23606797749979},"9":{"tf":1.7320508075688772}},"f":{"df":0,"docs":{},"f":{"df":0,"docs":{},"e":{"c":{"df":0,"docs":{},"t":{"df":1,"docs":{"59":{"tf":1.0}}}},"df":0,"docs":{}},"i":{"c":{"df":0,"docs":{},"i":{"df":4,"docs":{"146":{"tf":1.0},"151":{"tf":1.0},"4":{"tf":1.0},"73":{"tf":1.0}}}},"df":0,"docs":{}}}},"i":{"df":1,"docs":{"123":{"tf":1.0}},"g":{"df":0,"docs":{},"h":{"df":0,"docs":{},"t":{"df":5,"docs":{"15":{"tf":1.0},"151":{"tf":1.0},"23":{"tf":1.0},"35":{"tf":1.4142135623730951},"8":{"tf":1.0}}}}}},"l":{"df":0,"docs":{},"e":{"df":0,"docs":{},"m":{"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"df":0,"docs":{},"t":{"df":30,"docs":{"101":{"tf":1.0},"107":{"tf":1.0},"121":{"tf":1.0},"123":{"tf":1.0},"125":{"tf":2.0},"128":{"tf":1.7320508075688772},"14":{"tf":5.477225575051661},"144":{"tf":1.0},"16":{"tf":1.0},"19":{"tf":2.449489742783178},"31":{"tf":1.0},"35":{"tf":1.7320508075688772},"41":{"tf":2.0},"52":{"tf":1.0},"54":{"tf":1.0},"55":{"tf":1.0},"68":{"tf":1.0},"7":{"tf":1.0},"70":{"tf":1.4142135623730951},"73":{"tf":1.0},"74":{"tf":1.4142135623730951},"77":{"tf":1.0},"79":{"tf":1.0},"82":{"tf":1.0},"84":{"tf":1.0},"91":{"tf":1.0},"92":{"tf":2.0},"95":{"tf":1.4142135623730951},"98":{"tf":1.4142135623730951},"99":{"tf":1.0}}}}}}}},"m":{"b":{"df":0,"docs":{},"e":{"d":{"df":1,"docs":{"130":{"tf":1.0}}},"df":0,"docs":{}}},"df":0,"docs":{},"p":{"df":0,"docs":{},"h":{"a":{"df":0,"docs":{},"s":{"df":1,"docs":{"5":{"tf":1.0}}}},"df":0,"docs":{}},"l":{"df":0,"docs":{},"o":{"df":0,"docs":{},"y":{"df":2,"docs":{"100":{"tf":1.0},"115":{"tf":1.0}}}}},"t":{"df":0,"docs":{},"i":{"df":2,"docs":{"134":{"tf":1.0},"36":{"tf":1.0}}}}},"u":{"df":0,"docs":{},"l":{"df":1,"docs":{"118":{"tf":1.0}}}}},"n":{"a":{"b":{"df":0,"docs":{},"l":{"df":2,"docs":{"146":{"tf":1.0},"4":{"tf":1.0}}}},"df":0,"docs":{}},"c":{"a":{"df":0,"docs":{},"p":{"df":0,"docs":{},"s":{"df":0,"docs":{},"u":{"df":0,"docs":{},"l":{"df":1,"docs":{"109":{"tf":1.0}}}}}}},"df":0,"docs":{},"o":{"d":{"df":15,"docs":{"10":{"tf":1.0},"105":{"tf":1.0},"107":{"tf":1.0},"11":{"tf":1.0},"133":{"tf":1.0},"48":{"tf":1.0},"50":{"tf":1.0},"52":{"tf":1.0},"54":{"tf":1.0},"55":{"tf":1.0},"66":{"tf":1.0},"69":{"tf":1.4142135623730951},"70":{"tf":1.4142135623730951},"72":{"tf":1.0},"84":{"tf":1.0}}},"df":0,"docs":{}}},"d":{"df":12,"docs":{"103":{"tf":1.0},"109":{"tf":1.0},"116":{"tf":1.0},"12":{"tf":1.0},"13":{"tf":1.0},"130":{"tf":1.0},"134":{"tf":1.0},"144":{"tf":1.0},"40":{"tf":1.0},"46":{"tf":1.0},"8":{"tf":1.0},"95":{"tf":1.0}},"i":{"a":{"df":0,"docs":{},"n":{"df":1,"docs":{"41":{"tf":1.0}}}},"df":0,"docs":{}}},"df":0,"docs":{},"f":{"df":0,"docs":{},"o":{"df":0,"docs":{},"r":{"c":{"df":5,"docs":{"146":{"tf":1.0},"150":{"tf":1.4142135623730951},"151":{"tf":2.0},"49":{"tf":1.0},"70":{"tf":1.0}}},"df":0,"docs":{}}}},"g":{"a":{"df":0,"docs":{},"g":{"df":3,"docs":{"75":{"tf":1.0},"77":{"tf":1.0},"86":{"tf":1.0}}}},"df":0,"docs":{}},"h":{"a":{"df":0,"docs":{},"n":{"c":{"df":1,"docs":{"100":{"tf":1.0}}},"df":0,"docs":{}}},"df":0,"docs":{}},"o":{"df":0,"docs":{},"u":{"df":0,"docs":{},"g":{"df":0,"docs":{},"h":{"df":3,"docs":{"114":{"tf":1.4142135623730951},"146":{"tf":1.0},"49":{"tf":1.0}}}}}},"s":{"df":0,"docs":{},"u":{"df":0,"docs":{},"r":{"df":1,"docs":{"83":{"tf":1.0}}}}},"t":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":1,"docs":{"14":{"tf":1.0}}}},"i":{"df":0,"docs":{},"r":{"df":4,"docs":{"12":{"tf":1.0},"14":{"tf":1.0},"50":{"tf":1.0},"68":{"tf":1.0}}},"t":{"df":0,"docs":{},"i":{"df":1,"docs":{"104":{"tf":1.0}}}}},"r":{"df":0,"docs":{},"i":{"df":7,"docs":{"134":{"tf":1.0},"141":{"tf":1.0},"142":{"tf":2.0},"147":{"tf":2.0},"36":{"tf":1.0},"70":{"tf":1.0},"91":{"tf":1.0}}}}}},"q":{"df":0,"docs":{},"u":{"a":{"df":0,"docs":{},"l":{"df":16,"docs":{"10":{"tf":1.4142135623730951},"101":{"tf":1.0},"106":{"tf":1.7320508075688772},"11":{"tf":1.0},"13":{"tf":1.0},"14":{"tf":5.830951894845301},"142":{"tf":1.4142135623730951},"146":{"tf":2.23606797749979},"147":{"tf":1.0},"16":{"tf":1.4142135623730951},"33":{"tf":1.0},"52":{"tf":1.0},"66":{"tf":1.0},"73":{"tf":1.0},"78":{"tf":1.4142135623730951},"95":{"tf":1.4142135623730951}}},"t":{"df":12,"docs":{"10":{"tf":1.7320508075688772},"11":{"tf":1.0},"12":{"tf":1.0},"13":{"tf":1.0},"14":{"tf":3.0},"149":{"tf":1.4142135623730951},"16":{"tf":1.7320508075688772},"4":{"tf":1.4142135623730951},"58":{"tf":1.0},"60":{"tf":1.0},"69":{"tf":1.0},"95":{"tf":1.0}}}},"df":0,"docs":{},"i":{"df":0,"docs":{},"v":{"a":{"df":0,"docs":{},"l":{"df":6,"docs":{"14":{"tf":2.8284271247461903},"16":{"tf":1.0},"56":{"tf":1.0},"69":{"tf":1.4142135623730951},"80":{"tf":1.0},"84":{"tf":1.0}}}},"df":0,"docs":{}}}}},"s":{"df":0,"docs":{},"s":{"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"c":{"df":1,"docs":{"133":{"tf":1.0}}},"df":0,"docs":{},"t":{"df":0,"docs":{},"i":{"df":1,"docs":{"49":{"tf":1.0}}}}}}}},"t":{"c":{"df":2,"docs":{"134":{"tf":1.0},"50":{"tf":1.0}}},"df":0,"docs":{}},"v":{"a":{"df":0,"docs":{},"l":{"df":0,"docs":{},"u":{"a":{"df":0,"docs":{},"t":{"df":0,"docs":{},"o":{"df":0,"docs":{},"r":{".":{"df":0,"docs":{},"e":{"df":0,"docs":{},"v":{"a":{"df":0,"docs":{},"l":{"df":0,"docs":{},"u":{"df":1,"docs":{"114":{"tf":1.0}}}}},"df":0,"docs":{}}}},"df":0,"docs":{}}}}},"df":39,"docs":{"10":{"tf":1.4142135623730951},"11":{"tf":1.4142135623730951},"112":{"tf":2.0},"113":{"tf":2.6457513110645907},"114":{"tf":4.358898943540674},"115":{"tf":1.0},"116":{"tf":1.0},"117":{"tf":1.7320508075688772},"119":{"tf":1.0},"121":{"tf":2.0},"123":{"tf":2.23606797749979},"125":{"tf":2.23606797749979},"13":{"tf":1.7320508075688772},"14":{"tf":1.4142135623730951},"149":{"tf":2.6457513110645907},"19":{"tf":1.4142135623730951},"28":{"tf":1.0},"35":{"tf":1.0},"52":{"tf":1.0},"58":{"tf":2.23606797749979},"59":{"tf":2.0},"60":{"tf":1.7320508075688772},"62":{"tf":1.7320508075688772},"63":{"tf":2.23606797749979},"66":{"tf":1.4142135623730951},"68":{"tf":2.449489742783178},"70":{"tf":1.0},"73":{"tf":1.7320508075688772},"74":{"tf":1.4142135623730951},"75":{"tf":1.0},"76":{"tf":1.0},"77":{"tf":1.4142135623730951},"78":{"tf":1.0},"79":{"tf":1.0},"80":{"tf":2.0},"85":{"tf":1.7320508075688772},"86":{"tf":1.0},"94":{"tf":1.0},"95":{"tf":2.0}}}}},"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"/":{"df":0,"docs":{},"o":{"d":{"d":{"df":2,"docs":{"113":{"tf":1.0},"116":{"tf":1.7320508075688772}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":10,"docs":{"101":{"tf":1.0},"114":{"tf":1.0},"119":{"tf":1.0},"146":{"tf":1.4142135623730951},"49":{"tf":1.0},"52":{"tf":1.0},"66":{"tf":1.0},"67":{"tf":1.0},"73":{"tf":1.0},"94":{"tf":1.0}}},"r":{"df":0,"docs":{},"y":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"df":1,"docs":{"103":{"tf":1.0}}}},"t":{"df":0,"docs":{},"h":{"df":4,"docs":{"146":{"tf":1.0},"16":{"tf":1.0},"21":{"tf":1.0},"70":{"tf":1.0}}}}}}},"i":{"d":{"df":1,"docs":{"14":{"tf":1.0}}},"df":0,"docs":{}},"o":{"df":0,"docs":{},"l":{"df":0,"docs":{},"u":{"df":0,"docs":{},"t":{"df":1,"docs":{"141":{"tf":1.0}}}}}}},"x":{"a":{"c":{"df":0,"docs":{},"t":{"df":2,"docs":{"146":{"tf":1.0},"95":{"tf":1.0}},"l":{"df":0,"docs":{},"i":{"df":4,"docs":{"114":{"tf":1.0},"20":{"tf":1.0},"21":{"tf":1.4142135623730951},"67":{"tf":1.0}}}}}},"df":0,"docs":{},"m":{"df":0,"docs":{},"p":{"df":0,"docs":{},"l":{"df":42,"docs":{"10":{"tf":1.4142135623730951},"100":{"tf":1.0},"101":{"tf":1.0},"103":{"tf":1.0},"104":{"tf":1.4142135623730951},"108":{"tf":1.0},"109":{"tf":1.4142135623730951},"11":{"tf":1.0},"110":{"tf":1.0},"111":{"tf":1.0},"114":{"tf":1.4142135623730951},"117":{"tf":1.0},"12":{"tf":1.0},"124":{"tf":1.0},"13":{"tf":1.0},"130":{"tf":1.0},"134":{"tf":1.0},"135":{"tf":1.0},"14":{"tf":2.0},"146":{"tf":1.4142135623730951},"149":{"tf":1.7320508075688772},"150":{"tf":1.0},"151":{"tf":1.4142135623730951},"20":{"tf":1.0},"34":{"tf":1.0},"40":{"tf":1.0},"44":{"tf":1.7320508075688772},"48":{"tf":1.0},"49":{"tf":1.7320508075688772},"5":{"tf":1.0},"50":{"tf":2.23606797749979},"52":{"tf":1.0},"64":{"tf":1.0},"66":{"tf":1.0},"68":{"tf":1.0},"7":{"tf":1.7320508075688772},"70":{"tf":1.4142135623730951},"73":{"tf":1.0},"74":{"tf":1.4142135623730951},"84":{"tf":1.0},"9":{"tf":1.0},"95":{"tf":2.0}}}}}},"c":{"df":0,"docs":{},"e":{"df":0,"docs":{},"p":{"df":0,"docs":{},"t":{"df":3,"docs":{"142":{"tf":1.4142135623730951},"44":{"tf":1.0},"72":{"tf":1.0}}}}}},"df":0,"docs":{},"e":{"c":{"df":0,"docs":{},"u":{"df":0,"docs":{},"t":{"df":30,"docs":{"10":{"tf":1.7320508075688772},"108":{"tf":1.0},"11":{"tf":1.0},"110":{"tf":1.7320508075688772},"127":{"tf":1.0},"13":{"tf":2.0},"131":{"tf":1.4142135623730951},"133":{"tf":1.0},"14":{"tf":1.0},"141":{"tf":1.0},"142":{"tf":1.7320508075688772},"143":{"tf":1.4142135623730951},"144":{"tf":2.23606797749979},"145":{"tf":1.0},"146":{"tf":3.605551275463989},"16":{"tf":1.7320508075688772},"22":{"tf":1.0},"34":{"tf":1.0},"4":{"tf":1.0},"43":{"tf":1.0},"48":{"tf":1.4142135623730951},"49":{"tf":1.0},"50":{"tf":2.449489742783178},"69":{"tf":1.7320508075688772},"7":{"tf":2.23606797749979},"8":{"tf":2.0},"83":{"tf":1.4142135623730951},"9":{"tf":2.449489742783178},"91":{"tf":1.4142135623730951},"94":{"tf":1.4142135623730951}}}}},"df":0,"docs":{},"m":{"df":0,"docs":{},"p":{"df":0,"docs":{},"t":{"df":1,"docs":{"123":{"tf":1.4142135623730951}}}}},"r":{"c":{"df":0,"docs":{},"i":{"df":0,"docs":{},"s":{"df":1,"docs":{"40":{"tf":1.0}}}}},"df":0,"docs":{}}},"i":{"df":0,"docs":{},"s":{"df":0,"docs":{},"t":{"df":4,"docs":{"106":{"tf":1.0},"14":{"tf":2.6457513110645907},"149":{"tf":1.0},"70":{"tf":1.0}}}}},"p":{"a":{"df":0,"docs":{},"n":{"d":{"df":1,"docs":{"14":{"tf":1.0}}},"df":0,"docs":{}}},"df":0,"docs":{},"e":{"c":{"df":0,"docs":{},"t":{"df":7,"docs":{"146":{"tf":2.23606797749979},"151":{"tf":1.0},"71":{"tf":1.0},"72":{"tf":1.0},"75":{"tf":1.0},"79":{"tf":1.0},"85":{"tf":1.0}}}},"df":0,"docs":{},"n":{"df":0,"docs":{},"s":{"df":2,"docs":{"123":{"tf":1.0},"21":{"tf":1.0}}}}},"l":{"a":{"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"df":7,"docs":{"103":{"tf":1.0},"117":{"tf":1.0},"118":{"tf":1.0},"144":{"tf":1.0},"146":{"tf":1.7320508075688772},"19":{"tf":1.0},"7":{"tf":1.0}}}},"n":{"df":4,"docs":{"114":{"tf":1.0},"128":{"tf":1.0},"146":{"tf":1.4142135623730951},"51":{"tf":1.0}}}},"df":0,"docs":{},"i":{"c":{"df":0,"docs":{},"i":{"df":0,"docs":{},"t":{"df":1,"docs":{"95":{"tf":1.0}},"l":{"df":0,"docs":{},"i":{"df":2,"docs":{"114":{"tf":1.4142135623730951},"5":{"tf":1.0}}}}}}},"df":0,"docs":{}},"o":{"df":0,"docs":{},"i":{"df":0,"docs":{},"t":{"df":1,"docs":{"10":{"tf":1.0}}}}}},"o":{"df":0,"docs":{},"n":{"df":1,"docs":{"45":{"tf":1.0}},"e":{"df":0,"docs":{},"n":{"df":0,"docs":{},"t":{"_":{"b":{"df":0,"docs":{},"i":{"df":0,"docs":{},"t":{"df":1,"docs":{"45":{"tf":1.0}}}}},"df":0,"docs":{}},"df":0,"docs":{},"i":{"df":2,"docs":{"124":{"tf":1.0},"45":{"tf":1.0}}}}}}},"s":{"df":1,"docs":{"40":{"tf":1.0}}}},"r":{"df":0,"docs":{},"e":{"df":0,"docs":{},"s":{"df":0,"docs":{},"s":{"df":7,"docs":{"107":{"tf":1.0},"12":{"tf":1.0},"123":{"tf":1.4142135623730951},"4":{"tf":1.0},"48":{"tf":1.4142135623730951},"70":{"tf":1.7320508075688772},"83":{"tf":1.0}}}}}}},"t":{"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"d":{"df":9,"docs":{"121":{"tf":1.0},"14":{"tf":1.0},"142":{"tf":1.7320508075688772},"146":{"tf":1.0},"147":{"tf":1.4142135623730951},"45":{"tf":1.0},"83":{"tf":1.4142135623730951},"91":{"tf":1.0},"94":{"tf":1.0}}},"df":0,"docs":{},"s":{"df":8,"docs":{"109":{"tf":1.0},"125":{"tf":1.0},"145":{"tf":1.0},"146":{"tf":1.4142135623730951},"49":{"tf":1.0},"68":{"tf":2.0},"84":{"tf":1.0},"91":{"tf":1.0}}}},"r":{"df":0,"docs":{},"n":{"df":2,"docs":{"151":{"tf":1.0},"34":{"tf":1.0}}}}},"r":{"a":{"c":{"df":0,"docs":{},"t":{"df":1,"docs":{"32":{"tf":1.4142135623730951}}}},"df":2,"docs":{"13":{"tf":1.7320508075688772},"146":{"tf":1.0}}},"df":0,"docs":{},"e":{"df":0,"docs":{},"m":{"df":3,"docs":{"50":{"tf":1.0},"68":{"tf":1.0},"79":{"tf":1.0}}}}}}}},"f":{"(":{"a":{")":{":":{"=":{"df":0,"docs":{},"p":{"(":{"a":{")":{"df":0,"docs":{},"q":{"(":{"a":{")":{"df":0,"docs":{},"−":{"1":{"df":1,"docs":{"90":{"tf":1.0}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":1,"docs":{"90":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},",":{"b":{")":{"=":{"(":{"a":{"df":0,"docs":{},"−":{"df":0,"docs":{},"y":{")":{"/":{"(":{"b":{"df":0,"docs":{},"−":{"df":0,"docs":{},"z":{"df":1,"docs":{"77":{"tf":1.0}}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}},"a":{"+":{"b":{"df":0,"docs":{},"−":{"1":{"df":1,"docs":{"74":{"tf":1.0}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"1":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"…":{",":{"a":{"df":0,"docs":{},"l":{"df":0,"docs":{},"​":{",":{"b":{")":{"=":{"df":0,"docs":{},"i":{"=":{"1":{"df":0,"docs":{},"∑":{"df":0,"docs":{},"l":{"df":0,"docs":{},"​":{"df":0,"docs":{},"γ":{"df":0,"docs":{},"i":{"df":0,"docs":{},"​":{"(":{"a":{"df":0,"docs":{},"i":{"df":0,"docs":{},"​":{"df":0,"docs":{},"−":{"df":0,"docs":{},"y":{"df":0,"docs":{},"i":{"df":0,"docs":{},"​":{")":{"/":{"(":{"b":{"df":0,"docs":{},"−":{"df":0,"docs":{},"z":{"df":0,"docs":{},"i":{"df":1,"docs":{"80":{"tf":1.0}}}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}}}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"d":{"df":1,"docs":{"90":{"tf":1.0}}},"df":0,"docs":{},"h":{")":{"=":{"0":{"df":1,"docs":{"16":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"p":{"(":{"d":{"1":{"df":0,"docs":{},"​":{")":{",":{"d":{"1":{"df":0,"docs":{},"​":{")":{",":{"df":0,"docs":{},"…":{",":{"df":0,"docs":{},"f":{"(":{"df":0,"docs":{},"p":{"(":{"d":{"df":0,"docs":{},"m":{"df":0,"docs":{},"​":{")":{",":{"d":{"df":0,"docs":{},"m":{"df":1,"docs":{"78":{"tf":1.0}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"1":{"df":0,"docs":{},"​":{"(":{"d":{"1":{"df":0,"docs":{},"​":{")":{",":{"df":0,"docs":{},"…":{",":{"df":0,"docs":{},"p":{"df":0,"docs":{},"l":{"df":0,"docs":{},"​":{"(":{"d":{"1":{"df":0,"docs":{},"​":{")":{",":{"d":{"1":{"df":0,"docs":{},"​":{")":{",":{"df":0,"docs":{},"…":{",":{"df":0,"docs":{},"f":{"(":{"df":0,"docs":{},"p":{"1":{"df":0,"docs":{},"​":{"(":{"d":{"df":0,"docs":{},"m":{"df":0,"docs":{},"​":{")":{",":{"df":0,"docs":{},"…":{",":{"df":0,"docs":{},"p":{"df":0,"docs":{},"l":{"df":0,"docs":{},"​":{"(":{"d":{"df":0,"docs":{},"m":{"df":0,"docs":{},"​":{")":{",":{"d":{"df":0,"docs":{},"m":{"df":1,"docs":{"80":{"tf":1.0}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"y":{"1":{"df":0,"docs":{},"​":{")":{",":{"df":0,"docs":{},"…":{",":{"df":0,"docs":{},"f":{"(":{"df":0,"docs":{},"y":{"df":0,"docs":{},"l":{"df":1,"docs":{"90":{"tf":1.0}}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},",":{"d":{"1":{"df":0,"docs":{},"​":{")":{",":{"df":0,"docs":{},"…":{",":{"df":0,"docs":{},"f":{"(":{"df":0,"docs":{},"y":{"df":0,"docs":{},"m":{"df":0,"docs":{},"​":{",":{"d":{"df":0,"docs":{},"m":{"df":1,"docs":{"74":{"tf":1.0}}}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"z":{")":{"=":{"df":0,"docs":{},"i":{"df":1,"docs":{"19":{"tf":1.0}}}},"df":0,"docs":{}},"df":1,"docs":{"69":{"tf":1.0}}}},"+":{"df":0,"docs":{},"g":{"]":{"1":{"df":0,"docs":{},"​":{"=":{"[":{"df":0,"docs":{},"f":{"]":{"1":{"df":0,"docs":{},"​":{"+":{"[":{"df":0,"docs":{},"g":{"]":{"1":{"df":1,"docs":{"19":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},",":{"df":0,"docs":{},"g":{"df":1,"docs":{"14":{"tf":1.0}}}},"/":{"df":0,"docs":{},"g":{"df":1,"docs":{"69":{"tf":1.0}}}},"0":{"df":1,"docs":{"131":{"tf":1.0}}},"1":{"5":{"df":0,"docs":{},"​":{"=":{"0":{"df":1,"docs":{"131":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{},"~":{"df":0,"docs":{},"​":{"=":{"0":{"df":1,"docs":{"131":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}},":":{"a":{"df":0,"docs":{},"→":{"df":0,"docs":{},"f":{"df":1,"docs":{"90":{"tf":1.4142135623730951}}}}},"df":0,"docs":{},"f":{"df":0,"docs":{},"×":{"d":{"df":0,"docs":{},"→":{"df":0,"docs":{},"f":{"df":1,"docs":{"74":{"tf":1.0}}}}},"df":0,"docs":{}}},"s":{"df":0,"docs":{},"e":{"df":0,"docs":{},"t":{"(":{"d":{")":{"df":0,"docs":{},"→":{"df":0,"docs":{},"f":{"df":1,"docs":{"90":{"tf":1.0}}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"=":{"a":{"(":{"df":0,"docs":{},"ζ":{")":{"df":0,"docs":{},"q":{"df":0,"docs":{},"l":{"df":0,"docs":{},"​":{"+":{"b":{"(":{"df":0,"docs":{},"ζ":{")":{"df":0,"docs":{},"q":{"df":0,"docs":{},"r":{"df":1,"docs":{"21":{"tf":1.0}}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{},"z":{"df":0,"docs":{},"h":{"df":0,"docs":{},"​":{"df":0,"docs":{},"t":{"df":1,"docs":{"16":{"tf":1.0}}}}}}},"]":{"1":{"df":3,"docs":{"19":{"tf":1.4142135623730951},"21":{"tf":1.0},"35":{"tf":1.0}}},"df":0,"docs":{}},"a":{"c":{"df":0,"docs":{},"e":{"b":{"df":0,"docs":{},"o":{"df":0,"docs":{},"o":{"df":0,"docs":{},"k":{"'":{"df":1,"docs":{"103":{"tf":1.0}}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"t":{"df":9,"docs":{"13":{"tf":1.0},"131":{"tf":1.0},"139":{"tf":1.0},"14":{"tf":2.8284271247461903},"146":{"tf":1.0},"149":{"tf":1.0},"16":{"tf":1.7320508075688772},"5":{"tf":1.0},"68":{"tf":1.0}},"o":{"df":0,"docs":{},"r":{"df":8,"docs":{"125":{"tf":1.0},"14":{"tf":1.7320508075688772},"146":{"tf":1.0},"28":{"tf":1.0},"3":{"tf":1.0},"68":{"tf":1.7320508075688772},"73":{"tf":1.0},"91":{"tf":2.0}}}}}},"df":0,"docs":{},"l":{"df":0,"docs":{},"s":{"df":1,"docs":{"104":{"tf":1.0}}}},"r":{"df":2,"docs":{"16":{"tf":1.0},"70":{"tf":1.0}}},"s":{"df":0,"docs":{},"h":{"df":0,"docs":{},"i":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"df":2,"docs":{"114":{"tf":1.0},"151":{"tf":1.0}}}}}},"t":{"df":2,"docs":{"123":{"tf":1.4142135623730951},"99":{"tf":1.0}},"e":{"df":0,"docs":{},"r":{"df":1,"docs":{"123":{"tf":1.0}}}}}}},"b":{"a":{"df":0,"docs":{},"t":{"c":{"df":0,"docs":{},"h":{"df":3,"docs":{"28":{"tf":1.0},"32":{"tf":1.7320508075688772},"33":{"tf":1.0}},"​":{"(":{"df":0,"docs":{},"ζ":{")":{"=":{"df":0,"docs":{},"t":{"df":0,"docs":{},"ˉ":{"+":{"df":0,"docs":{},"υ":{"df":0,"docs":{},"p":{"df":0,"docs":{},"ˉ":{"df":0,"docs":{},"​":{"df":0,"docs":{},"n":{"c":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"υ":{"2":{"a":{"df":0,"docs":{},"ˉ":{"+":{"df":0,"docs":{},"υ":{"3":{"b":{"df":0,"docs":{},"ˉ":{"+":{"df":0,"docs":{},"υ":{"4":{"c":{"df":0,"docs":{},"ˉ":{"+":{"df":0,"docs":{},"υ":{"5":{"df":0,"docs":{},"s":{"df":0,"docs":{},"ˉ":{"df":0,"docs":{},"σ":{"1":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"υ":{"6":{"df":0,"docs":{},"s":{"df":0,"docs":{},"ˉ":{"df":0,"docs":{},"σ":{"2":{"df":1,"docs":{"32":{"tf":1.0}}},"df":0,"docs":{}}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"]":{"1":{"df":1,"docs":{"32":{"tf":1.0}},"​":{"=":{"[":{"df":0,"docs":{},"t":{"df":0,"docs":{},"p":{"a":{"df":0,"docs":{},"r":{"df":0,"docs":{},"t":{"df":0,"docs":{},"i":{"a":{"df":0,"docs":{},"l":{"df":0,"docs":{},"​":{"]":{"1":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"υ":{"[":{"df":0,"docs":{},"p":{"df":0,"docs":{},"n":{"c":{"df":0,"docs":{},"​":{"]":{"1":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"υ":{"2":{"[":{"a":{"]":{"1":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"υ":{"3":{"[":{"b":{"]":{"1":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"υ":{"4":{"[":{"c":{"]":{"1":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"υ":{"5":{"[":{"df":0,"docs":{},"s":{"df":0,"docs":{},"σ":{"1":{"df":0,"docs":{},"​":{"]":{"1":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"υ":{"6":{"[":{"df":0,"docs":{},"s":{"df":0,"docs":{},"σ":{"2":{"df":0,"docs":{},"​":{"]":{"1":{"df":1,"docs":{"32":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":21,"docs":{"14":{"tf":2.6457513110645907},"141":{"tf":1.0},"142":{"tf":1.4142135623730951},"146":{"tf":1.7320508075688772},"147":{"tf":1.4142135623730951},"16":{"tf":1.4142135623730951},"19":{"tf":2.449489742783178},"21":{"tf":2.0},"35":{"tf":1.4142135623730951},"5":{"tf":1.7320508075688772},"69":{"tf":1.4142135623730951},"70":{"tf":1.4142135623730951},"73":{"tf":1.0},"74":{"tf":1.4142135623730951},"75":{"tf":1.0},"80":{"tf":1.0},"82":{"tf":1.0},"90":{"tf":1.7320508075688772},"91":{"tf":1.4142135623730951},"94":{"tf":2.0},"95":{"tf":1.4142135623730951}},"e":{"1":{"7":{":":{":":{"df":0,"docs":{},"n":{"df":0,"docs":{},"e":{"df":0,"docs":{},"w":{"(":{"1":{"df":1,"docs":{"108":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},":":{":":{"df":0,"docs":{},"f":{"df":0,"docs":{},"r":{"df":0,"docs":{},"o":{"df":0,"docs":{},"m":{"(":{"1":{"df":1,"docs":{"104":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"o":{"df":0,"docs":{},"n":{"df":1,"docs":{"104":{"tf":1.4142135623730951}}}}},"df":0,"docs":{}},"a":{"df":0,"docs":{},"t":{"df":0,"docs":{},"u":{"df":0,"docs":{},"r":{"df":1,"docs":{"70":{"tf":1.0}}}}}},"df":0,"docs":{},"e":{"d":{"df":1,"docs":{"34":{"tf":1.0}}},"df":0,"docs":{}},"t":{"c":{"df":0,"docs":{},"h":{"df":1,"docs":{"146":{"tf":1.4142135623730951}}}},"df":0,"docs":{}},"w":{"df":10,"docs":{"109":{"tf":1.0},"111":{"tf":1.0},"123":{"tf":1.4142135623730951},"124":{"tf":1.0},"13":{"tf":1.0},"14":{"tf":1.0},"16":{"tf":1.0},"32":{"tf":1.0},"37":{"tf":1.0},"64":{"tf":1.0}}}},"f":{"df":0,"docs":{},"t":{"df":4,"docs":{"121":{"tf":2.23606797749979},"2":{"tf":1.7320508075688772},"3":{"tf":1.7320508075688772},"99":{"tf":1.7320508075688772}}}},"i":{"a":{"df":0,"docs":{},"t":{"df":7,"docs":{"118":{"tf":1.4142135623730951},"39":{"tf":1.4142135623730951},"40":{"tf":1.0},"42":{"tf":2.23606797749979},"89":{"tf":1.0},"94":{"tf":1.0},"95":{"tf":1.0}}}},"b":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"a":{"c":{"c":{"df":0,"docs":{},"i":{"_":{"df":0,"docs":{},"t":{"df":0,"docs":{},"r":{"a":{"c":{"df":1,"docs":{"108":{"tf":1.0}},"e":{"(":{"[":{"df":0,"docs":{},"f":{"df":0,"docs":{},"e":{"1":{"7":{":":{":":{"df":0,"docs":{},"n":{"df":0,"docs":{},"e":{"df":0,"docs":{},"w":{"(":{"1":{"df":1,"docs":{"108":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"a":{"df":0,"docs":{},"i":{"df":0,"docs":{},"r":{"<":{"df":0,"docs":{},"f":{">":{">":{"(":{"&":{"df":0,"docs":{},"p":{"df":0,"docs":{},"r":{"df":0,"docs":{},"o":{"df":0,"docs":{},"o":{"df":0,"docs":{},"f":{"df":1,"docs":{"104":{"tf":1.0}}}}}}},"t":{"df":0,"docs":{},"r":{"a":{"c":{"df":1,"docs":{"104":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":18,"docs":{"104":{"tf":1.7320508075688772},"105":{"tf":1.4142135623730951},"106":{"tf":1.7320508075688772},"107":{"tf":1.0},"108":{"tf":1.0},"109":{"tf":1.0},"110":{"tf":1.0},"111":{"tf":1.0},"112":{"tf":1.0},"114":{"tf":1.7320508075688772},"117":{"tf":1.0},"49":{"tf":2.0},"50":{"tf":1.4142135623730951},"51":{"tf":1.7320508075688772},"64":{"tf":1.0},"66":{"tf":1.0},"68":{"tf":1.0},"84":{"tf":1.0}},"p":{"df":0,"docs":{},"u":{"b":{"df":0,"docs":{},"l":{"df":0,"docs":{},"i":{"c":{"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"df":0,"docs":{},"p":{"df":0,"docs":{},"u":{"df":0,"docs":{},"t":{"df":1,"docs":{"104":{"tf":1.0}}}}}}}},"df":0,"docs":{}}}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":1,"docs":{"131":{"tf":1.0}},"e":{"df":0,"docs":{},"l":{"d":{"df":22,"docs":{"108":{"tf":1.0},"109":{"tf":1.0},"114":{"tf":1.0},"14":{"tf":4.58257569495584},"16":{"tf":1.0},"19":{"tf":1.4142135623730951},"35":{"tf":1.0},"36":{"tf":1.0},"41":{"tf":2.449489742783178},"44":{"tf":1.0},"5":{"tf":1.0},"52":{"tf":1.0},"7":{"tf":1.4142135623730951},"70":{"tf":1.0},"73":{"tf":1.0},"74":{"tf":1.4142135623730951},"82":{"tf":1.0},"90":{"tf":1.0},"91":{"tf":1.0},"95":{"tf":1.0},"98":{"tf":1.0},"99":{"tf":1.0}},"e":{"df":0,"docs":{},"l":{"df":0,"docs":{},"e":{"df":0,"docs":{},"m":{"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"df":0,"docs":{},"t":{":":{":":{"df":0,"docs":{},"f":{"df":0,"docs":{},"r":{"df":0,"docs":{},"o":{"df":0,"docs":{},"m":{"(":{"2":{"_":{"df":0,"docs":{},"u":{"6":{"4":{"df":1,"docs":{"35":{"tf":1.4142135623730951}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"3":{"_":{"df":0,"docs":{},"u":{"6":{"4":{"df":1,"docs":{"35":{"tf":1.4142135623730951}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":1,"docs":{"44":{"tf":1.0}}},"4":{"df":1,"docs":{"44":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}}}}}},"df":0,"docs":{}},"<":{"df":0,"docs":{},"f":{"df":1,"docs":{"35":{"tf":3.1622776601683795}}}},"df":0,"docs":{}}}}}}}}},"df":0,"docs":{}}},"f":{"df":0,"docs":{},"t":{"df":0,"docs":{},"h":{"df":1,"docs":{"52":{"tf":1.0}}}}},"l":{"df":0,"docs":{},"e":{"df":2,"docs":{"144":{"tf":2.23606797749979},"145":{"tf":1.0}}},"l":{"df":6,"docs":{"13":{"tf":1.0},"130":{"tf":2.449489742783178},"134":{"tf":2.0},"139":{"tf":1.0},"146":{"tf":2.23606797749979},"36":{"tf":1.7320508075688772}}}},"n":{"a":{"df":0,"docs":{},"l":{"df":8,"docs":{"13":{"tf":1.0},"135":{"tf":1.0},"144":{"tf":1.4142135623730951},"146":{"tf":2.0},"147":{"tf":1.0},"16":{"tf":1.0},"35":{"tf":1.4142135623730951},"44":{"tf":1.0}}}},"d":{"df":3,"docs":{"128":{"tf":1.4142135623730951},"3":{"tf":1.4142135623730951},"88":{"tf":1.0}}},"df":0,"docs":{},"e":{"df":1,"docs":{"12":{"tf":1.0}}},"i":{"df":0,"docs":{},"t":{"df":10,"docs":{"14":{"tf":1.0},"19":{"tf":1.0},"5":{"tf":1.0},"52":{"tf":1.0},"69":{"tf":1.0},"7":{"tf":1.0},"70":{"tf":1.0},"90":{"tf":1.0},"91":{"tf":1.0},"98":{"tf":1.0}}}}},"r":{"df":0,"docs":{},"s":{"df":0,"docs":{},"t":{"_":{"df":0,"docs":{},"r":{"df":0,"docs":{},"o":{"df":0,"docs":{},"w":{"[":{"0":{"df":2,"docs":{"107":{"tf":1.4142135623730951},"114":{"tf":1.0}}},"df":0,"docs":{}},"df":2,"docs":{"107":{"tf":1.0},"114":{"tf":1.0}}}}}},"df":29,"docs":{"105":{"tf":1.0},"107":{"tf":1.4142135623730951},"108":{"tf":1.0},"114":{"tf":1.4142135623730951},"128":{"tf":1.0},"13":{"tf":1.7320508075688772},"130":{"tf":1.0},"134":{"tf":1.0},"135":{"tf":1.0},"14":{"tf":1.4142135623730951},"145":{"tf":1.0},"146":{"tf":1.7320508075688772},"147":{"tf":2.0},"149":{"tf":1.4142135623730951},"20":{"tf":1.0},"23":{"tf":1.4142135623730951},"31":{"tf":1.0},"32":{"tf":1.4142135623730951},"42":{"tf":1.0},"46":{"tf":1.0},"49":{"tf":1.4142135623730951},"52":{"tf":1.4142135623730951},"59":{"tf":1.7320508075688772},"66":{"tf":1.0},"69":{"tf":1.0},"70":{"tf":1.0},"8":{"tf":1.0},"83":{"tf":1.0},"9":{"tf":1.0}},"l":{"df":0,"docs":{},"i":{"df":1,"docs":{"32":{"tf":1.0}}}}}}},"t":{"df":2,"docs":{"103":{"tf":1.0},"133":{"tf":1.0}}},"v":{"df":0,"docs":{},"e":{"df":1,"docs":{"12":{"tf":1.0}}}},"x":{"df":5,"docs":{"146":{"tf":1.0},"5":{"tf":1.0},"73":{"tf":1.0},"88":{"tf":1.0},"91":{"tf":1.0}}},"​":{"df":0,"docs":{},"~":{"df":0,"docs":{},"​":{"=":{"df":0,"docs":{},"∑":{"df":0,"docs":{},"j":{"=":{"df":0,"docs":{},"i":{"1":{"4":{"df":0,"docs":{},"​":{"2":{"df":0,"docs":{},"j":{"df":0,"docs":{},"−":{"df":0,"docs":{},"i":{"df":0,"docs":{},"f":{"df":0,"docs":{},"i":{"df":1,"docs":{"131":{"tf":1.0}}}}}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{},"−":{"2":{"df":0,"docs":{},"f":{"df":0,"docs":{},"~":{"df":0,"docs":{},"​":{"df":0,"docs":{},"i":{"+":{"1":{"df":0,"docs":{},"​":{")":{"(":{"df":0,"docs":{},"f":{"df":0,"docs":{},"i":{"df":0,"docs":{},"​":{"df":0,"docs":{},"~":{"df":0,"docs":{},"​":{"df":0,"docs":{},"−":{"2":{"df":0,"docs":{},"f":{"df":0,"docs":{},"~":{"df":0,"docs":{},"​":{"df":0,"docs":{},"i":{"+":{"1":{"df":0,"docs":{},"​":{"df":0,"docs":{},"−":{"1":{")":{"=":{"0":{"df":1,"docs":{"131":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}}}}},"df":0,"docs":{}}}}}}}},"df":0,"docs":{}},"=":{"df":0,"docs":{},"f":{"df":0,"docs":{},"i":{"df":1,"docs":{"131":{"tf":1.0}}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}}}},"df":0,"docs":{}}}},"∗":{"(":{"df":0,"docs":{},"f":{"df":0,"docs":{},"i":{"df":0,"docs":{},"​":{"df":0,"docs":{},"−":{"1":{")":{"=":{"0":{"df":1,"docs":{"131":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}}}},"df":0,"docs":{}}}},"l":{"a":{"df":0,"docs":{},"g":{"a":{"df":1,"docs":{"149":{"tf":1.0}}},"b":{"df":1,"docs":{"149":{"tf":1.4142135623730951}}},"df":6,"docs":{"131":{"tf":2.23606797749979},"142":{"tf":1.4142135623730951},"146":{"tf":3.0},"147":{"tf":1.4142135623730951},"149":{"tf":3.1622776601683795},"150":{"tf":2.0}}}},"df":0,"docs":{},"e":{"df":0,"docs":{},"x":{"df":0,"docs":{},"i":{"b":{"df":0,"docs":{},"l":{"df":2,"docs":{"12":{"tf":1.4142135623730951},"4":{"tf":1.0}}}},"df":0,"docs":{}}}},"o":{"df":0,"docs":{},"w":{"df":2,"docs":{"103":{"tf":1.0},"50":{"tf":1.0}}}}},"n":{"df":6,"docs":{"104":{"tf":1.0},"106":{"tf":1.0},"107":{"tf":1.0},"114":{"tf":1.0},"44":{"tf":1.0},"45":{"tf":1.4142135623730951}}},"o":{"c":{"df":0,"docs":{},"u":{"df":1,"docs":{"149":{"tf":1.0}}}},"df":0,"docs":{},"l":{"df":0,"docs":{},"l":{"df":0,"docs":{},"o":{"df":0,"docs":{},"w":{"df":49,"docs":{"10":{"tf":1.4142135623730951},"101":{"tf":1.0},"102":{"tf":1.0},"103":{"tf":1.0},"11":{"tf":1.4142135623730951},"111":{"tf":1.0},"113":{"tf":1.0},"114":{"tf":1.7320508075688772},"123":{"tf":1.0},"128":{"tf":1.0},"13":{"tf":1.4142135623730951},"137":{"tf":1.0},"14":{"tf":3.7416573867739413},"142":{"tf":1.4142135623730951},"146":{"tf":1.4142135623730951},"147":{"tf":1.7320508075688772},"151":{"tf":1.0},"16":{"tf":2.0},"19":{"tf":1.4142135623730951},"21":{"tf":1.0},"22":{"tf":1.0},"24":{"tf":1.0},"31":{"tf":1.0},"32":{"tf":1.4142135623730951},"35":{"tf":1.7320508075688772},"37":{"tf":1.0},"42":{"tf":1.0},"44":{"tf":1.4142135623730951},"45":{"tf":1.0},"48":{"tf":1.0},"49":{"tf":1.0},"5":{"tf":1.0},"50":{"tf":1.0},"52":{"tf":1.0},"54":{"tf":1.0},"55":{"tf":1.0},"56":{"tf":1.0},"59":{"tf":1.4142135623730951},"69":{"tf":1.0},"7":{"tf":1.4142135623730951},"72":{"tf":1.0},"73":{"tf":1.0},"75":{"tf":1.0},"84":{"tf":1.0},"88":{"tf":1.0},"91":{"tf":1.0},"92":{"tf":1.0},"94":{"tf":1.7320508075688772},"95":{"tf":2.23606797749979}}}}}},"r":{"c":{"df":2,"docs":{"13":{"tf":1.4142135623730951},"20":{"tf":1.0}}},"df":0,"docs":{},"m":{"a":{"df":0,"docs":{},"l":{"df":3,"docs":{"141":{"tf":1.0},"142":{"tf":1.0},"69":{"tf":1.0}}},"t":{"df":1,"docs":{"69":{"tf":1.0}}}},"df":12,"docs":{"100":{"tf":1.0},"108":{"tf":1.0},"123":{"tf":1.0},"14":{"tf":2.23606797749979},"146":{"tf":1.0},"35":{"tf":1.0},"52":{"tf":1.0},"68":{"tf":1.0},"73":{"tf":1.0},"9":{"tf":1.0},"92":{"tf":1.0},"99":{"tf":1.0}},"e":{"df":0,"docs":{},"r":{"df":2,"docs":{"19":{"tf":1.0},"73":{"tf":1.0}}}},"u":{"df":0,"docs":{},"l":{"a":{"df":2,"docs":{"58":{"tf":1.0},"60":{"tf":1.0}}},"df":0,"docs":{}}}}},"u":{"df":0,"docs":{},"n":{"d":{"df":8,"docs":{"127":{"tf":1.0},"130":{"tf":1.4142135623730951},"133":{"tf":1.0},"134":{"tf":1.4142135623730951},"137":{"tf":1.0},"138":{"tf":1.0},"38":{"tf":1.0},"4":{"tf":1.0}}},"df":0,"docs":{}},"r":{"df":2,"docs":{"21":{"tf":1.0},"56":{"tf":1.0}},"i":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":3,"docs":{"123":{"tf":1.0},"3":{"tf":1.4142135623730951},"99":{"tf":1.0}}}}}}}},"p":{"df":6,"docs":{"136":{"tf":1.4142135623730951},"141":{"tf":1.0},"142":{"tf":1.0},"144":{"tf":1.0},"146":{"tf":1.4142135623730951},"147":{"tf":1.0}}},"r":{"a":{"df":0,"docs":{},"m":{"df":0,"docs":{},"e":{".":{"df":0,"docs":{},"g":{"df":0,"docs":{},"e":{"df":0,"docs":{},"t":{"_":{"df":0,"docs":{},"r":{"df":0,"docs":{},"o":{"df":0,"docs":{},"w":{"(":{"0":{"df":2,"docs":{"107":{"tf":1.0},"114":{"tf":1.0}}},"1":{"df":2,"docs":{"107":{"tf":1.0},"114":{"tf":1.0}}},"2":{"df":2,"docs":{"107":{"tf":1.0},"114":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}}}}},":":{":":{"df":0,"docs":{},"r":{"df":0,"docs":{},"e":{"a":{"d":{"_":{"df":0,"docs":{},"f":{"df":0,"docs":{},"r":{"df":0,"docs":{},"o":{"df":0,"docs":{},"m":{"_":{"df":0,"docs":{},"t":{"df":0,"docs":{},"r":{"a":{"c":{"df":1,"docs":{"114":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":7,"docs":{"107":{"tf":1.4142135623730951},"109":{"tf":1.0},"113":{"tf":1.0},"114":{"tf":2.8284271247461903},"117":{"tf":2.449489742783178},"149":{"tf":1.7320508075688772},"84":{"tf":1.0}}}}},"df":1,"docs":{"38":{"tf":1.0}},"e":{"df":0,"docs":{},"s":{"df":0,"docs":{},"h":{"df":1,"docs":{"40":{"tf":1.0}}}}},"i":{"_":{"df":0,"docs":{},"n":{"df":0,"docs":{},"u":{"df":0,"docs":{},"m":{"b":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"_":{"df":0,"docs":{},"o":{"df":0,"docs":{},"f":{"_":{"df":0,"docs":{},"q":{"df":0,"docs":{},"u":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":0,"docs":{},"i":{"df":1,"docs":{"109":{"tf":1.0}}}}}}}},"df":0,"docs":{}}}},"df":0,"docs":{}}}},"df":0,"docs":{}}}}},"d":{"a":{"df":0,"docs":{},"y":{"df":1,"docs":{"81":{"tf":1.0}}}},"df":0,"docs":{}},"df":22,"docs":{"102":{"tf":1.0},"109":{"tf":1.0},"112":{"tf":1.4142135623730951},"113":{"tf":1.0},"119":{"tf":1.4142135623730951},"125":{"tf":1.7320508075688772},"57":{"tf":2.0},"59":{"tf":1.7320508075688772},"62":{"tf":1.4142135623730951},"63":{"tf":1.0},"68":{"tf":2.0},"71":{"tf":1.0},"73":{"tf":2.23606797749979},"74":{"tf":2.0},"75":{"tf":1.0},"77":{"tf":1.4142135623730951},"78":{"tf":1.0},"80":{"tf":1.7320508075688772},"81":{"tf":1.7320508075688772},"91":{"tf":1.0},"94":{"tf":1.4142135623730951},"95":{"tf":1.0}},"e":{"df":0,"docs":{},"n":{"d":{"df":0,"docs":{},"l":{"df":0,"docs":{},"i":{"df":1,"docs":{"83":{"tf":1.0}}}}},"df":0,"docs":{}}}}},"u":{"df":0,"docs":{},"l":{"df":0,"docs":{},"l":{"df":2,"docs":{"10":{"tf":1.0},"131":{"tf":1.0}}}},"n":{"c":{"df":0,"docs":{},"t":{"df":0,"docs":{},"i":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"df":24,"docs":{"104":{"tf":1.4142135623730951},"108":{"tf":1.0},"110":{"tf":1.0},"111":{"tf":1.0},"113":{"tf":1.0},"114":{"tf":1.0},"118":{"tf":1.0},"142":{"tf":1.0},"146":{"tf":1.4142135623730951},"35":{"tf":1.7320508075688772},"40":{"tf":1.0},"45":{"tf":2.6457513110645907},"54":{"tf":1.0},"55":{"tf":1.0},"56":{"tf":1.0},"74":{"tf":1.7320508075688772},"77":{"tf":1.0},"80":{"tf":1.0},"84":{"tf":1.4142135623730951},"85":{"tf":1.0},"88":{"tf":1.0},"90":{"tf":1.7320508075688772},"91":{"tf":1.4142135623730951},"95":{"tf":1.0}}}}}}},"df":0,"docs":{}},"r":{"df":0,"docs":{},"t":{"df":0,"docs":{},"h":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":4,"docs":{"100":{"tf":1.0},"147":{"tf":1.0},"21":{"tf":1.0},"74":{"tf":1.0}},"m":{"df":0,"docs":{},"o":{"df":0,"docs":{},"r":{"df":1,"docs":{"13":{"tf":1.0}}}}}}}}}}}},"g":{"+":{"df":0,"docs":{},"ζ":{"df":0,"docs":{},"k":{"df":0,"docs":{},"​":{"df":0,"docs":{},"h":{"df":1,"docs":{"95":{"tf":1.4142135623730951}}}}}}},",":{"df":0,"docs":{},"h":{"df":1,"docs":{"95":{"tf":1.0}}}},"1":{"df":0,"docs":{},"p":{"df":0,"docs":{},"o":{"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"df":0,"docs":{},"t":{"df":1,"docs":{"35":{"tf":2.8284271247461903}}}}}}}},"3":{"df":1,"docs":{"124":{"tf":1.0}}},"=":{"df":0,"docs":{},"ω":{"b":{"df":1,"docs":{"91":{"tf":1.0}}},"df":0,"docs":{}}},"^":{"0":{"df":2,"docs":{"52":{"tf":1.0},"55":{"tf":1.0}}},"1":{"df":2,"docs":{"52":{"tf":1.0},"55":{"tf":1.0}}},"2":{"df":2,"docs":{"52":{"tf":1.4142135623730951},"55":{"tf":1.0}}},"3":{"df":2,"docs":{"52":{"tf":1.0},"55":{"tf":1.0}}},"4":{"df":2,"docs":{"52":{"tf":1.0},"55":{"tf":1.0}}},"5":{"df":2,"docs":{"52":{"tf":1.7320508075688772},"55":{"tf":1.0}}},"8":{"df":1,"docs":{"52":{"tf":1.0}}},"df":0,"docs":{},"i":{"df":2,"docs":{"52":{"tf":1.0},"62":{"tf":1.0}}}},"a":{"df":0,"docs":{},"m":{"df":0,"docs":{},"m":{"a":{"_":{"df":0,"docs":{},"i":{"df":1,"docs":{"59":{"tf":1.0}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"p":{"df":1,"docs":{"134":{"tf":1.0}}},"t":{"df":0,"docs":{},"e":{"df":8,"docs":{"10":{"tf":2.6457513110645907},"11":{"tf":1.0},"12":{"tf":2.449489742783178},"13":{"tf":1.0},"14":{"tf":1.0},"16":{"tf":1.4142135623730951},"20":{"tf":1.0},"9":{"tf":3.3166247903554}}},"h":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":1,"docs":{"126":{"tf":1.0}}}}}},"v":{"df":0,"docs":{},"e":{"df":2,"docs":{"113":{"tf":1.4142135623730951},"63":{"tf":1.4142135623730951}}}}},"df":13,"docs":{"124":{"tf":2.23606797749979},"125":{"tf":1.4142135623730951},"14":{"tf":2.449489742783178},"142":{"tf":1.4142135623730951},"146":{"tf":2.0},"147":{"tf":1.4142135623730951},"16":{"tf":1.0},"19":{"tf":2.0},"52":{"tf":2.23606797749979},"54":{"tf":1.0},"68":{"tf":2.0},"69":{"tf":1.0},"82":{"tf":1.0}},"e":{"df":0,"docs":{},"n":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":26,"docs":{"103":{"tf":1.0},"104":{"tf":1.4142135623730951},"108":{"tf":1.0},"118":{"tf":1.0},"119":{"tf":1.4142135623730951},"124":{"tf":2.0},"144":{"tf":1.4142135623730951},"146":{"tf":1.0},"149":{"tf":1.0},"34":{"tf":1.4142135623730951},"35":{"tf":1.4142135623730951},"4":{"tf":1.0},"44":{"tf":1.4142135623730951},"48":{"tf":1.0},"49":{"tf":1.0},"50":{"tf":1.7320508075688772},"52":{"tf":1.0},"65":{"tf":1.4142135623730951},"68":{"tf":1.0},"70":{"tf":1.0},"77":{"tf":1.0},"8":{"tf":1.0},"80":{"tf":1.4142135623730951},"82":{"tf":1.0},"84":{"tf":1.0},"90":{"tf":1.4142135623730951}}}}},"t":{"_":{"df":0,"docs":{},"r":{"df":0,"docs":{},"o":{"df":0,"docs":{},"w":{"df":1,"docs":{"107":{"tf":1.0}}}}}},"df":4,"docs":{"118":{"tf":1.4142135623730951},"151":{"tf":1.0},"75":{"tf":1.0},"82":{"tf":1.0}}}},"i":{":":{"0":{"df":0,"docs":{},"≤":{"df":0,"docs":{},"i":{"<":{"2":{"df":0,"docs":{},"n":{"df":1,"docs":{"112":{"tf":1.0}}}},"df":0,"docs":{},"n":{"df":1,"docs":{"124":{"tf":1.0}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":1,"docs":{"125":{"tf":1.4142135623730951}},"l":{"df":0,"docs":{},"l":{"df":0,"docs":{},"e":{"df":0,"docs":{},"t":{"df":0,"docs":{},"t":{"'":{"df":1,"docs":{"103":{"tf":1.0}}},"df":0,"docs":{}}}}}},"v":{"df":0,"docs":{},"e":{"df":8,"docs":{"114":{"tf":1.0},"14":{"tf":1.0},"149":{"tf":1.4142135623730951},"151":{"tf":1.7320508075688772},"19":{"tf":1.0},"46":{"tf":1.0},"50":{"tf":1.0},"7":{"tf":1.0}},"n":{"df":14,"docs":{"114":{"tf":1.7320508075688772},"128":{"tf":1.0},"130":{"tf":1.0},"14":{"tf":1.0},"146":{"tf":1.0},"16":{"tf":1.4142135623730951},"23":{"tf":1.0},"36":{"tf":1.0},"63":{"tf":1.0},"72":{"tf":1.0},"75":{"tf":1.0},"76":{"tf":1.0},"90":{"tf":1.0},"92":{"tf":1.0}}}}},"z":{"a":{"df":1,"docs":{"103":{"tf":1.0}}},"df":0,"docs":{}}},"k":{"df":1,"docs":{"124":{"tf":1.0}}},"l":{"df":0,"docs":{},"o":{"b":{"a":{"df":0,"docs":{},"l":{"df":1,"docs":{"70":{"tf":1.4142135623730951}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"n":{"+":{"1":{"=":{"df":0,"docs":{},"g":{"df":0,"docs":{},"g":{"df":0,"docs":{},"n":{"+":{"2":{"=":{"df":0,"docs":{},"g":{"2":{"df":1,"docs":{"124":{"tf":1.0}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}},"=":{"1":{"df":1,"docs":{"124":{"tf":1.0}}},"df":0,"docs":{}},"a":{"df":0,"docs":{},"r":{"df":0,"docs":{},"k":{"df":1,"docs":{"34":{"tf":1.0}}}}},"df":0,"docs":{}},"o":{"a":{"df":0,"docs":{},"l":{"df":4,"docs":{"103":{"tf":1.0},"46":{"tf":1.0},"50":{"tf":1.0},"77":{"tf":1.0}}}},"df":17,"docs":{"103":{"tf":1.0},"104":{"tf":1.4142135623730951},"109":{"tf":1.0},"111":{"tf":1.4142135623730951},"114":{"tf":1.0},"118":{"tf":1.0},"124":{"tf":1.0},"14":{"tf":1.7320508075688772},"42":{"tf":1.0},"46":{"tf":1.0},"48":{"tf":1.0},"49":{"tf":1.0},"50":{"tf":1.0},"51":{"tf":1.0},"53":{"tf":1.0},"57":{"tf":1.0},"68":{"tf":1.0}},"l":{"d":{"b":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":0,"docs":{},"g":{"df":4,"docs":{"103":{"tf":1.0},"19":{"tf":1.0},"35":{"tf":1.0},"38":{"tf":1.0}}}}}},"df":0,"docs":{}},"df":0,"docs":{}},"o":{"d":{"df":4,"docs":{"14":{"tf":1.0},"46":{"tf":1.0},"50":{"tf":1.0},"81":{"tf":1.0}}},"df":0,"docs":{}},"t":{"df":0,"docs":{},"h":{"df":0,"docs":{},"i":{"c":{"df":1,"docs":{"95":{"tf":1.0}}},"df":0,"docs":{}}}}},"p":{"df":0,"docs":{},"u":{"df":1,"docs":{"1":{"tf":1.0}}}},"r":{"a":{"df":0,"docs":{},"n":{"df":0,"docs":{},"t":{"df":1,"docs":{"14":{"tf":1.0}}}}},"df":0,"docs":{},"e":{"a":{"df":0,"docs":{},"t":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":2,"docs":{"3":{"tf":1.0},"68":{"tf":1.0}}}}}},"df":0,"docs":{},"e":{"df":0,"docs":{},"k":{"df":1,"docs":{"95":{"tf":1.0}}}}},"i":{"df":0,"docs":{},"n":{"d":{"df":4,"docs":{"88":{"tf":1.4142135623730951},"91":{"tf":1.0},"94":{"tf":1.0},"95":{"tf":1.0}},"i":{"df":0,"docs":{},"n":{"df":0,"docs":{},"g":{"_":{"df":0,"docs":{},"f":{"a":{"c":{"df":0,"docs":{},"t":{"df":0,"docs":{},"o":{"df":0,"docs":{},"r":{"df":1,"docs":{"88":{"tf":1.4142135623730951}}}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}}},"df":0,"docs":{}}},"o":{"df":0,"docs":{},"u":{"df":0,"docs":{},"p":{"df":3,"docs":{"142":{"tf":1.4142135623730951},"147":{"tf":1.0},"19":{"tf":2.23606797749979}}}}}}},"h":{"(":{"df":0,"docs":{},"h":{"(":{"df":0,"docs":{},"p":{"df":0,"docs":{},"r":{"df":0,"docs":{},"e":{"df":0,"docs":{},"f":{"df":0,"docs":{},"i":{"df":0,"docs":{},"x":{"df":1,"docs":{"88":{"tf":1.0}}}}}}}}},"df":0,"docs":{}},"x":{")":{"=":{"df":0,"docs":{},"h":{"1":{"df":0,"docs":{},"​":{"(":{"df":0,"docs":{},"x":{"2":{")":{"+":{"df":0,"docs":{},"x":{"df":0,"docs":{},"h":{"2":{"df":0,"docs":{},"​":{"(":{"df":0,"docs":{},"x":{"2":{"df":2,"docs":{"116":{"tf":1.0},"67":{"tf":1.0}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"β":{"1":{"df":0,"docs":{},"​":{"b":{"(":{"df":0,"docs":{},"x":{")":{"+":{"df":0,"docs":{},"β":{"2":{"df":0,"docs":{},"​":{"c":{"(":{"df":0,"docs":{},"x":{"df":1,"docs":{"56":{"tf":1.0}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"0":{"df":2,"docs":{"112":{"tf":1.0},"113":{"tf":1.0}}},"_":{"0":{"df":2,"docs":{"62":{"tf":1.0},"63":{"tf":1.0}}},"df":0,"docs":{}},"df":1,"docs":{"56":{"tf":1.0}}},"z":{")":{"=":{"df":0,"docs":{},"f":{"(":{"df":0,"docs":{},"z":{")":{"/":{"df":0,"docs":{},"g":{"(":{"df":0,"docs":{},"z":{"df":1,"docs":{"69":{"tf":1.0}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"β":{"1":{"df":0,"docs":{},"​":{"b":{"(":{"df":0,"docs":{},"z":{")":{"+":{"df":0,"docs":{},"β":{"2":{"df":0,"docs":{},"​":{"c":{"(":{"df":0,"docs":{},"z":{"df":3,"docs":{"113":{"tf":1.0},"58":{"tf":1.0},"63":{"tf":1.0}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":7,"docs":{"112":{"tf":1.0},"113":{"tf":1.4142135623730951},"59":{"tf":1.0},"62":{"tf":1.0},"63":{"tf":1.4142135623730951},"69":{"tf":1.0},"85":{"tf":1.0}}}},".":{"df":0,"docs":{},"t":{"df":0,"docs":{},"h":{"df":0,"docs":{},"i":{"df":1,"docs":{"14":{"tf":1.0}}}}}},"1":{"df":2,"docs":{"94":{"tf":1.4142135623730951},"95":{"tf":1.0}},"​":{"(":{"df":0,"docs":{},"z":{"2":{"df":1,"docs":{"94":{"tf":1.4142135623730951}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"2":{"df":2,"docs":{"94":{"tf":1.4142135623730951},"95":{"tf":1.0}},"​":{"(":{"df":0,"docs":{},"z":{"2":{"df":1,"docs":{"94":{"tf":1.4142135623730951}}},"df":0,"docs":{}}},"df":0,"docs":{}}},":":{"=":{"df":0,"docs":{},"k":{"df":0,"docs":{},"∑":{"df":0,"docs":{},"​":{"df":0,"docs":{},"β":{"df":0,"docs":{},"k":{"df":0,"docs":{},"t":{"df":0,"docs":{},"​":{"c":{"df":0,"docs":{},"k":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"j":{"df":0,"docs":{},"∑":{"df":0,"docs":{},"​":{"df":0,"docs":{},"β":{"df":0,"docs":{},"j":{"b":{"df":0,"docs":{},"​":{"b":{"df":0,"docs":{},"j":{"df":1,"docs":{"94":{"tf":1.0}}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}}}},"df":0,"docs":{}}}},"df":0,"docs":{}}}}}}}},"η":{"1":{"df":0,"docs":{},"z":{"2":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"z":{"df":0,"docs":{},"η":{"2":{"df":0,"docs":{},"z":{"2":{"df":1,"docs":{"95":{"tf":1.0}}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"=":{"df":0,"docs":{},"h":{"1":{"df":0,"docs":{},"​":{"(":{"df":0,"docs":{},"x":{"2":{")":{"+":{"df":0,"docs":{},"x":{"df":0,"docs":{},"h":{"2":{"df":0,"docs":{},"​":{"(":{"df":0,"docs":{},"x":{"2":{"df":1,"docs":{"94":{"tf":1.0}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"k":{"df":0,"docs":{},"∑":{"df":0,"docs":{},"​":{"df":0,"docs":{},"β":{"df":0,"docs":{},"k":{"df":0,"docs":{},"t":{"df":0,"docs":{},"​":{"c":{"df":0,"docs":{},"k":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"j":{"df":0,"docs":{},"∑":{"df":0,"docs":{},"​":{"df":0,"docs":{},"β":{"df":0,"docs":{},"j":{"b":{"df":0,"docs":{},"​":{"b":{"df":0,"docs":{},"j":{"df":1,"docs":{"95":{"tf":1.0}}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}}}},"df":0,"docs":{}}}},"df":0,"docs":{}}}}}}}},"s":{"df":0,"docs":{},"h":{"a":{"3":{"df":1,"docs":{"40":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}}},"{":{"1":{",":{"df":0,"docs":{},"ω":{",":{"df":0,"docs":{},"ω":{"2":{",":{"df":0,"docs":{},"…":{",":{"df":0,"docs":{},"ω":{"df":0,"docs":{},"k":{"df":0,"docs":{},"−":{"1":{"df":1,"docs":{"14":{"tf":1.4142135623730951}}},"df":0,"docs":{}}},"n":{"df":0,"docs":{},"−":{"1":{"df":3,"docs":{"14":{"tf":1.4142135623730951},"16":{"tf":1.0},"23":{"tf":1.0}}},"df":0,"docs":{}}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{},"h":{"0":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"…":{",":{"df":0,"docs":{},"h":{"df":0,"docs":{},"n":{"df":0,"docs":{},"​":{"df":0,"docs":{},"}":{"df":0,"docs":{},"⊂":{"df":0,"docs":{},"f":{"df":1,"docs":{"5":{"tf":1.0}}}}}}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"ω":{"df":0,"docs":{},"i":{":":{"0":{"df":0,"docs":{},"≤":{"df":0,"docs":{},"i":{"<":{"df":0,"docs":{},"n":{"df":1,"docs":{"14":{"tf":1.0}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"_":{"1":{"(":{"df":0,"docs":{},"x":{"df":1,"docs":{"67":{"tf":1.0}}}},"df":2,"docs":{"116":{"tf":1.7320508075688772},"67":{"tf":1.0}}},"2":{"(":{"df":0,"docs":{},"x":{"df":1,"docs":{"67":{"tf":1.0}}}},"df":2,"docs":{"116":{"tf":1.7320508075688772},"67":{"tf":1.0}}},"df":0,"docs":{}},"a":{"df":0,"docs":{},"l":{"df":0,"docs":{},"f":{"df":1,"docs":{"125":{"tf":1.0}}}},"n":{"d":{"c":{"df":0,"docs":{},"r":{"a":{"df":0,"docs":{},"f":{"df":0,"docs":{},"t":{"df":1,"docs":{"83":{"tf":1.0}}}}},"df":0,"docs":{}}},"df":4,"docs":{"114":{"tf":1.0},"117":{"tf":1.0},"52":{"tf":1.0},"56":{"tf":1.0}},"l":{"df":2,"docs":{"110":{"tf":1.0},"9":{"tf":1.0}}}},"df":0,"docs":{}},"p":{"df":0,"docs":{},"p":{"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"df":4,"docs":{"116":{"tf":1.0},"125":{"tf":1.0},"146":{"tf":1.0},"77":{"tf":1.0}}}},"i":{"df":1,"docs":{"35":{"tf":1.0}}}}},"r":{"d":{"df":2,"docs":{"14":{"tf":1.0},"20":{"tf":1.0}}},"df":0,"docs":{}},"s":{"df":0,"docs":{},"h":{"(":{"df":0,"docs":{},"m":{"df":0,"docs":{},"e":{"df":0,"docs":{},"s":{"df":0,"docs":{},"s":{"a":{"df":0,"docs":{},"g":{"df":0,"docs":{},"e":{"_":{"2":{"df":1,"docs":{"40":{"tf":1.7320508075688772}}},"3":{"df":1,"docs":{"40":{"tf":1.4142135623730951}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}}}}},"df":7,"docs":{"118":{"tf":1.0},"151":{"tf":1.0},"45":{"tf":1.0},"75":{"tf":1.7320508075688772},"88":{"tf":1.0},"91":{"tf":1.4142135623730951},"92":{"tf":1.0}},"e":{"df":0,"docs":{},"r":{"df":2,"docs":{"110":{"tf":1.0},"40":{"tf":2.6457513110645907}}}},"m":{"a":{"df":0,"docs":{},"p":{":":{":":{"df":0,"docs":{},"f":{"df":0,"docs":{},"r":{"df":0,"docs":{},"o":{"df":0,"docs":{},"m":{"(":{"[":{"(":{"df":0,"docs":{},"x":{"df":1,"docs":{"44":{"tf":1.0}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"v":{"df":0,"docs":{},"e":{"df":3,"docs":{"110":{"tf":1.0},"130":{"tf":1.0},"49":{"tf":1.0}},"n":{"'":{"df":0,"docs":{},"t":{"df":1,"docs":{"69":{"tf":1.0}}}},"df":0,"docs":{}}}}},"df":35,"docs":{"112":{"tf":1.4142135623730951},"113":{"tf":1.0},"114":{"tf":2.0},"116":{"tf":2.0},"117":{"tf":1.0},"118":{"tf":1.0},"125":{"tf":2.23606797749979},"134":{"tf":1.0},"14":{"tf":2.8284271247461903},"147":{"tf":1.4142135623730951},"20":{"tf":2.23606797749979},"24":{"tf":1.0},"25":{"tf":1.0},"26":{"tf":1.0},"32":{"tf":1.4142135623730951},"35":{"tf":1.0},"53":{"tf":1.0},"56":{"tf":2.449489742783178},"57":{"tf":2.23606797749979},"58":{"tf":2.0},"59":{"tf":1.7320508075688772},"60":{"tf":1.0},"62":{"tf":1.4142135623730951},"65":{"tf":1.0},"66":{"tf":1.0},"67":{"tf":1.7320508075688772},"69":{"tf":1.4142135623730951},"7":{"tf":1.4142135623730951},"73":{"tf":1.0},"75":{"tf":1.0},"82":{"tf":1.0},"84":{"tf":2.6457513110645907},"85":{"tf":2.0},"88":{"tf":1.0},"94":{"tf":1.4142135623730951}},"e":{"df":0,"docs":{},"l":{"df":0,"docs":{},"i":{"df":0,"docs":{},"o":{"df":1,"docs":{"42":{"tf":1.0}}}},"p":{"df":2,"docs":{"45":{"tf":1.0},"70":{"tf":1.0}},"e":{"df":0,"docs":{},"r":{"df":2,"docs":{"108":{"tf":1.0},"35":{"tf":1.0}}}}}},"n":{"c":{"df":2,"docs":{"125":{"tf":1.0},"130":{"tf":1.0}}},"df":0,"docs":{}},"r":{"df":0,"docs":{},"e":{"'":{"df":1,"docs":{"20":{"tf":1.0}}},"df":27,"docs":{"11":{"tf":1.0},"114":{"tf":1.0},"118":{"tf":1.0},"124":{"tf":1.0},"133":{"tf":1.4142135623730951},"135":{"tf":1.0},"137":{"tf":1.0},"14":{"tf":2.449489742783178},"142":{"tf":1.0},"151":{"tf":1.4142135623730951},"21":{"tf":1.0},"35":{"tf":1.4142135623730951},"4":{"tf":1.0},"40":{"tf":1.4142135623730951},"52":{"tf":1.0},"54":{"tf":1.0},"56":{"tf":1.0},"57":{"tf":1.0},"7":{"tf":1.4142135623730951},"70":{"tf":1.0},"72":{"tf":1.0},"75":{"tf":1.0},"88":{"tf":1.0},"9":{"tf":1.0},"90":{"tf":1.0},"94":{"tf":1.7320508075688772},"98":{"tf":1.0}}}},"u":{"df":0,"docs":{},"r":{"df":0,"docs":{},"i":{"df":0,"docs":{},"s":{"df":0,"docs":{},"t":{"df":4,"docs":{"118":{"tf":1.0},"40":{"tf":1.0},"42":{"tf":1.0},"89":{"tf":1.0}}}}}}}},"i":{"d":{"df":0,"docs":{},"e":{"df":1,"docs":{"19":{"tf":1.0}}}},"df":0,"docs":{},"g":{"df":0,"docs":{},"h":{"df":12,"docs":{"104":{"tf":1.7320508075688772},"105":{"tf":1.0},"106":{"tf":1.0},"107":{"tf":1.0},"108":{"tf":1.0},"109":{"tf":1.0},"110":{"tf":1.0},"35":{"tf":1.0},"46":{"tf":1.0},"59":{"tf":1.0},"69":{"tf":1.4142135623730951},"82":{"tf":1.4142135623730951}},"e":{"df":0,"docs":{},"r":{"df":4,"docs":{"125":{"tf":1.0},"146":{"tf":1.0},"20":{"tf":1.0},"68":{"tf":1.0}}}}}},"m":{"df":0,"docs":{},"s":{"df":0,"docs":{},"e":{"df":0,"docs":{},"l":{"df":0,"docs":{},"f":{"df":1,"docs":{"21":{"tf":1.4142135623730951}}}}}}}},"o":{"df":0,"docs":{},"l":{"d":{"df":20,"docs":{"10":{"tf":1.0},"107":{"tf":2.0},"108":{"tf":1.4142135623730951},"109":{"tf":1.7320508075688772},"11":{"tf":1.0},"123":{"tf":1.4142135623730951},"13":{"tf":1.0},"131":{"tf":1.0},"133":{"tf":1.0},"14":{"tf":2.23606797749979},"146":{"tf":1.4142135623730951},"16":{"tf":1.4142135623730951},"35":{"tf":1.0},"54":{"tf":1.0},"69":{"tf":1.0},"7":{"tf":1.0},"73":{"tf":2.23606797749979},"74":{"tf":2.0},"75":{"tf":1.0},"77":{"tf":1.0}}},"df":0,"docs":{},"e":{"df":3,"docs":{"130":{"tf":2.0},"134":{"tf":2.8284271247461903},"146":{"tf":2.6457513110645907}}}},"m":{"df":0,"docs":{},"o":{"df":0,"docs":{},"m":{"df":0,"docs":{},"o":{"df":0,"docs":{},"r":{"df":0,"docs":{},"p":{"df":0,"docs":{},"h":{"df":1,"docs":{"19":{"tf":1.0}}}}}}}}},"n":{"df":0,"docs":{},"e":{"df":0,"docs":{},"s":{"df":0,"docs":{},"t":{"df":4,"docs":{"118":{"tf":1.0},"20":{"tf":1.0},"77":{"tf":1.0},"78":{"tf":1.0}}}}}},"o":{"d":{"df":15,"docs":{"111":{"tf":1.7320508075688772},"112":{"tf":1.0},"113":{"tf":1.0},"114":{"tf":1.4142135623730951},"115":{"tf":1.0},"116":{"tf":1.0},"117":{"tf":1.0},"118":{"tf":1.0},"119":{"tf":1.0},"120":{"tf":1.0},"121":{"tf":1.0},"122":{"tf":1.0},"123":{"tf":1.0},"124":{"tf":1.0},"125":{"tf":1.0}}},"df":0,"docs":{}},"s":{"df":0,"docs":{},"t":{"df":1,"docs":{"0":{"tf":1.0}}}}},"u":{"df":0,"docs":{},"g":{"df":0,"docs":{},"e":{"df":2,"docs":{"123":{"tf":1.7320508075688772},"80":{"tf":1.0}}}}},"ω":{"df":0,"docs":{},"i":{":":{"0":{"df":0,"docs":{},"≤":{"df":0,"docs":{},"i":{"<":{"2":{"df":0,"docs":{},"n":{"df":1,"docs":{"125":{"tf":1.0}}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"∈":{"d":{"df":1,"docs":{"14":{"tf":1.4142135623730951}}},"df":0,"docs":{},"f":{"df":0,"docs":{},"∖":{"df":0,"docs":{},"{":{"df":0,"docs":{},"ω":{"df":0,"docs":{},"i":{"df":0,"docs":{},"}":{"df":0,"docs":{},"i":{"df":0,"docs":{},"≥":{"0":{"df":1,"docs":{"91":{"tf":1.0}}},"df":0,"docs":{}}}}}}}}},"h":{"df":2,"docs":{"14":{"tf":1.7320508075688772},"16":{"tf":1.0}}}}},"i":{"+":{"1":{"df":2,"docs":{"101":{"tf":1.0},"70":{"tf":1.4142135623730951}}},"2":{"df":0,"docs":{},"k":{"df":0,"docs":{},"−":{"1":{"df":1,"docs":{"101":{"tf":1.0}}},"df":0,"docs":{}}}},"df":0,"docs":{}},",":{"df":0,"docs":{},"j":{")":{",":{"df":0,"docs":{},"v":{"df":1,"docs":{"14":{"tf":1.4142135623730951}}}},"df":0,"docs":{},"∈":{"df":0,"docs":{},"i":{"df":1,"docs":{"14":{"tf":1.0}}}}},",":{"df":0,"docs":{},"k":{",":{"df":0,"docs":{},"l":{"df":4,"docs":{"11":{"tf":1.0},"13":{"tf":1.0},"14":{"tf":1.0},"16":{"tf":1.0}}}},"df":0,"docs":{}},"l":{"df":1,"docs":{"14":{"tf":1.0}}}},"df":1,"docs":{"14":{"tf":2.0}}}},".":{"df":6,"docs":{"107":{"tf":1.0},"114":{"tf":1.0},"125":{"tf":1.0},"52":{"tf":1.0},"59":{"tf":1.4142135623730951},"68":{"tf":1.0}}},":":{"0":{"df":0,"docs":{},"≤":{"df":0,"docs":{},"i":{"<":{"2":{"df":0,"docs":{},"n":{"+":{"1":{"df":1,"docs":{"125":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}},"=":{"0":{"df":0,"docs":{},"k":{"df":0,"docs":{},"−":{"1":{"df":0,"docs":{},"​":{"(":{"a":{"df":0,"docs":{},"i":{"df":1,"docs":{"14":{"tf":1.4142135623730951}},"​":{"+":{"df":0,"docs":{},"γ":{")":{"/":{"df":0,"docs":{},"∏":{"df":0,"docs":{},"i":{"=":{"0":{"df":0,"docs":{},"k":{"df":0,"docs":{},"−":{"1":{"df":0,"docs":{},"​":{"(":{"b":{"df":0,"docs":{},"i":{"df":1,"docs":{"14":{"tf":1.0}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"b":{"df":0,"docs":{},"i":{"df":1,"docs":{"14":{"tf":1.4142135623730951}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"∏":{"df":0,"docs":{},"k":{"df":0,"docs":{},"−":{"1":{"df":0,"docs":{},"​":{"(":{"a":{"df":0,"docs":{},"i":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"γ":{")":{"=":{"df":0,"docs":{},"i":{"=":{"0":{"df":0,"docs":{},"∏":{"df":0,"docs":{},"k":{"df":0,"docs":{},"−":{"1":{"df":0,"docs":{},"​":{"(":{"b":{"df":0,"docs":{},"i":{"df":1,"docs":{"14":{"tf":1.0}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"∑":{"df":0,"docs":{},"n":{"df":0,"docs":{},"​":{"df":0,"docs":{},"l":{"df":0,"docs":{},"i":{"df":0,"docs":{},"​":{"(":{"df":0,"docs":{},"ζ":{")":{"(":{"df":0,"docs":{},"p":{"df":0,"docs":{},"i":{")":{"df":0,"docs":{},"i":{"df":1,"docs":{"32":{"tf":1.0}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}}}}}},"1":{"df":1,"docs":{"101":{"tf":1.0}}},"df":0,"docs":{},"{":{"(":{"df":0,"docs":{},"i":{",":{"df":0,"docs":{},"j":{"df":1,"docs":{"14":{"tf":1.0}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"∣":{"df":0,"docs":{},"l":{"df":0,"docs":{},"j":{"df":1,"docs":{"142":{"tf":1.0}}}}}},">":{"df":0,"docs":{},"n":{"df":2,"docs":{"14":{"tf":1.0},"16":{"tf":1.0}}}},"b":{"df":0,"docs":{},"​":{"b":{"df":0,"docs":{},"i":{"df":0,"docs":{},"​":{"(":{"df":0,"docs":{},"x":{"df":1,"docs":{"114":{"tf":1.0}}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"d":{"df":0,"docs":{},"e":{"a":{"df":12,"docs":{"118":{"tf":1.0},"14":{"tf":1.0},"151":{"tf":1.0},"4":{"tf":1.0},"54":{"tf":1.0},"59":{"tf":1.0},"6":{"tf":1.4142135623730951},"65":{"tf":1.0},"66":{"tf":1.0},"67":{"tf":1.0},"7":{"tf":1.0},"79":{"tf":1.0}}},"df":0,"docs":{},"n":{"df":0,"docs":{},"t":{"df":0,"docs":{},"i":{"df":0,"docs":{},"f":{"df":0,"docs":{},"i":{"df":1,"docs":{"52":{"tf":1.0}}}}}}}}},"df":0,"docs":{},"l":{"df":0,"docs":{},"u":{"df":0,"docs":{},"s":{"df":0,"docs":{},"t":{"df":0,"docs":{},"r":{"df":1,"docs":{"111":{"tf":1.0}}}}}}},"m":{"a":{"df":0,"docs":{},"g":{"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"df":1,"docs":{"149":{"tf":1.0}}}}}},"df":0,"docs":{},"m":{"df":0,"docs":{},"e":{"d":{"df":0,"docs":{},"i":{"df":1,"docs":{"113":{"tf":1.0}}}},"df":0,"docs":{}}},"p":{"df":0,"docs":{},"l":{"df":0,"docs":{},"e":{"df":0,"docs":{},"m":{"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"df":0,"docs":{},"t":{"df":39,"docs":{"103":{"tf":2.23606797749979},"104":{"tf":1.0},"105":{"tf":1.4142135623730951},"106":{"tf":1.0},"107":{"tf":1.0},"108":{"tf":1.0},"109":{"tf":1.0},"110":{"tf":1.4142135623730951},"111":{"tf":1.4142135623730951},"112":{"tf":1.0},"113":{"tf":1.0},"114":{"tf":1.0},"115":{"tf":1.0},"116":{"tf":1.0},"117":{"tf":1.0},"118":{"tf":1.0},"119":{"tf":1.0},"120":{"tf":1.0},"121":{"tf":1.0},"122":{"tf":1.0},"123":{"tf":1.4142135623730951},"124":{"tf":1.4142135623730951},"125":{"tf":1.0},"127":{"tf":1.0},"19":{"tf":1.0},"34":{"tf":2.0},"35":{"tf":1.0},"36":{"tf":1.4142135623730951},"37":{"tf":2.0},"38":{"tf":1.0},"39":{"tf":1.0},"40":{"tf":1.4142135623730951},"41":{"tf":1.4142135623730951},"42":{"tf":1.0},"46":{"tf":1.0},"64":{"tf":1.0},"67":{"tf":1.0},"68":{"tf":1.0},"9":{"tf":1.0}}}}}}},"i":{"c":{"df":0,"docs":{},"i":{"df":0,"docs":{},"t":{"df":0,"docs":{},"l":{"df":0,"docs":{},"i":{"df":2,"docs":{"52":{"tf":1.0},"95":{"tf":1.0}}}}}}},"df":1,"docs":{"101":{"tf":1.0}}}},"o":{"df":0,"docs":{},"r":{"df":0,"docs":{},"t":{"df":10,"docs":{"10":{"tf":1.0},"105":{"tf":1.0},"124":{"tf":1.0},"134":{"tf":1.0},"14":{"tf":1.4142135623730951},"146":{"tf":1.7320508075688772},"16":{"tf":1.0},"68":{"tf":1.0},"73":{"tf":1.0},"92":{"tf":1.4142135623730951}}}}},"r":{"df":0,"docs":{},"o":{"df":0,"docs":{},"v":{"df":3,"docs":{"123":{"tf":1.0},"151":{"tf":1.0},"21":{"tf":1.0}}}}}}},"n":{"c":{"df":0,"docs":{},"l":{"df":0,"docs":{},"u":{"d":{"df":7,"docs":{"125":{"tf":1.7320508075688772},"13":{"tf":1.0},"146":{"tf":1.0},"149":{"tf":1.0},"23":{"tf":1.0},"66":{"tf":1.0},"84":{"tf":1.0}}},"df":0,"docs":{}}},"o":{"df":0,"docs":{},"r":{"df":0,"docs":{},"p":{"df":0,"docs":{},"o":{"df":0,"docs":{},"r":{"df":2,"docs":{"13":{"tf":1.4142135623730951},"147":{"tf":1.4142135623730951}}}}}}},"r":{"df":0,"docs":{},"e":{"a":{"df":0,"docs":{},"s":{"df":3,"docs":{"147":{"tf":1.4142135623730951},"88":{"tf":1.0},"94":{"tf":1.4142135623730951}}}},"d":{"df":1,"docs":{"50":{"tf":1.0}}},"df":0,"docs":{},"m":{"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"df":0,"docs":{},"t":{"df":1,"docs":{"142":{"tf":1.0}}}}}}}}},"d":{"df":0,"docs":{},"e":{"df":2,"docs":{"57":{"tf":1.0},"59":{"tf":1.4142135623730951}},"p":{"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"d":{"df":4,"docs":{"11":{"tf":1.0},"13":{"tf":1.4142135623730951},"150":{"tf":1.0},"80":{"tf":1.0}}},"df":0,"docs":{}}}},"x":{"df":11,"docs":{"101":{"tf":2.0},"109":{"tf":1.0},"11":{"tf":2.23606797749979},"13":{"tf":1.4142135623730951},"14":{"tf":1.4142135623730951},"149":{"tf":1.0},"36":{"tf":1.0},"72":{"tf":1.0},"80":{"tf":1.0},"94":{"tf":1.0},"95":{"tf":1.4142135623730951}}}},"i":{"c":{"df":4,"docs":{"11":{"tf":1.0},"14":{"tf":1.0},"28":{"tf":1.0},"95":{"tf":1.0}}},"df":0,"docs":{}},"u":{"c":{"df":2,"docs":{"14":{"tf":1.0},"90":{"tf":1.4142135623730951}}},"df":0,"docs":{}}},"df":2,"docs":{"146":{"tf":1.0},"151":{"tf":1.0}},"e":{"df":0,"docs":{},"f":{"df":0,"docs":{},"f":{"df":0,"docs":{},"i":{"c":{"df":0,"docs":{},"i":{"df":1,"docs":{"84":{"tf":1.0}}}},"df":0,"docs":{}}}}},"f":{"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"df":0,"docs":{},"i":{"df":0,"docs":{},"t":{"df":1,"docs":{"146":{"tf":1.4142135623730951}}}}}},"o":{"df":0,"docs":{},"r":{"df":0,"docs":{},"m":{"df":10,"docs":{"119":{"tf":1.0},"126":{"tf":1.0},"142":{"tf":1.0},"144":{"tf":1.7320508075688772},"145":{"tf":1.0},"146":{"tf":1.4142135623730951},"19":{"tf":1.0},"52":{"tf":1.0},"70":{"tf":1.0},"9":{"tf":1.0}}}}}},"g":{"df":0,"docs":{},"r":{"df":0,"docs":{},"e":{"d":{"df":0,"docs":{},"i":{"df":2,"docs":{"151":{"tf":1.0},"44":{"tf":1.0}}}},"df":0,"docs":{}}}},"i":{"df":0,"docs":{},"t":{"df":0,"docs":{},"i":{"df":5,"docs":{"146":{"tf":1.0},"151":{"tf":1.0},"31":{"tf":1.7320508075688772},"70":{"tf":1.0},"94":{"tf":1.0}}}}},"n":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":2,"docs":{"19":{"tf":1.0},"73":{"tf":1.0}}}}},"p":{"df":0,"docs":{},"u":{"df":0,"docs":{},"t":{"df":24,"docs":{"104":{"tf":2.449489742783178},"106":{"tf":1.4142135623730951},"11":{"tf":1.0},"110":{"tf":1.0},"121":{"tf":1.4142135623730951},"13":{"tf":2.6457513110645907},"14":{"tf":1.4142135623730951},"142":{"tf":1.0},"144":{"tf":1.0},"15":{"tf":1.7320508075688772},"151":{"tf":1.0},"16":{"tf":1.7320508075688772},"23":{"tf":1.7320508075688772},"26":{"tf":1.0},"3":{"tf":1.7320508075688772},"32":{"tf":1.4142135623730951},"34":{"tf":2.0},"35":{"tf":3.3166247903554},"36":{"tf":1.0},"42":{"tf":1.4142135623730951},"44":{"tf":2.449489742783178},"7":{"tf":3.1622776601683795},"9":{"tf":1.0},"95":{"tf":1.0}}}}},"s":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":0,"docs":{},"t":{"df":3,"docs":{"130":{"tf":1.0},"133":{"tf":1.4142135623730951},"134":{"tf":1.7320508075688772}}}}},"i":{"d":{"df":1,"docs":{"45":{"tf":1.0}}},"df":0,"docs":{},"g":{"df":0,"docs":{},"h":{"df":0,"docs":{},"t":{"df":1,"docs":{"103":{"tf":1.0}}}}}},"p":{"df":0,"docs":{},"e":{"c":{"df":0,"docs":{},"t":{"df":2,"docs":{"101":{"tf":1.0},"14":{"tf":1.0}}}},"df":0,"docs":{}}},"t":{"a":{"df":0,"docs":{},"n":{"c":{"df":5,"docs":{"13":{"tf":1.0},"14":{"tf":1.0},"16":{"tf":1.4142135623730951},"35":{"tf":2.6457513110645907},"8":{"tf":1.0}}},"df":0,"docs":{},"t":{"df":0,"docs":{},"i":{"df":4,"docs":{"106":{"tf":1.0},"109":{"tf":1.0},"110":{"tf":1.0},"35":{"tf":1.7320508075688772}}}}}},"df":3,"docs":{"142":{"tf":1.0},"146":{"tf":1.4142135623730951},"147":{"tf":1.4142135623730951}},"e":{"a":{"d":{"df":13,"docs":{"101":{"tf":1.0},"123":{"tf":1.0},"131":{"tf":1.0},"14":{"tf":1.0},"146":{"tf":1.0},"21":{"tf":1.0},"32":{"tf":1.0},"54":{"tf":1.0},"55":{"tf":1.0},"80":{"tf":1.4142135623730951},"84":{"tf":1.0},"9":{"tf":1.0},"92":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}},"r":{"df":0,"docs":{},"u":{"c":{"df":0,"docs":{},"t":{"df":8,"docs":{"130":{"tf":1.0},"131":{"tf":1.4142135623730951},"133":{"tf":2.449489742783178},"142":{"tf":1.4142135623730951},"146":{"tf":3.872983346207417},"147":{"tf":1.0},"151":{"tf":1.4142135623730951},"50":{"tf":1.0}}}},"df":0,"docs":{}}}}},"t":{"df":0,"docs":{},"e":{"df":0,"docs":{},"g":{"df":3,"docs":{"142":{"tf":1.0},"146":{"tf":1.0},"41":{"tf":1.4142135623730951}},"r":{"df":2,"docs":{"105":{"tf":1.0},"84":{"tf":1.0}}}},"n":{"df":0,"docs":{},"s":{"df":1,"docs":{"99":{"tf":1.0}}}},"r":{"a":{"c":{"df":0,"docs":{},"t":{"df":14,"docs":{"118":{"tf":1.4142135623730951},"127":{"tf":1.0},"128":{"tf":1.0},"137":{"tf":2.0},"145":{"tf":1.0},"146":{"tf":1.0},"70":{"tf":1.0},"71":{"tf":1.0},"73":{"tf":1.0},"75":{"tf":1.0},"77":{"tf":1.0},"82":{"tf":1.0},"91":{"tf":1.0},"95":{"tf":1.0}}}},"df":0,"docs":{}},"df":0,"docs":{},"e":{"df":0,"docs":{},"s":{"df":0,"docs":{},"t":{"df":1,"docs":{"14":{"tf":1.0}}}}},"l":{"df":0,"docs":{},"e":{"a":{"df":0,"docs":{},"v":{"df":2,"docs":{"149":{"tf":1.0},"150":{"tf":1.0}}}},"df":0,"docs":{}}},"m":{"df":0,"docs":{},"e":{"d":{"df":0,"docs":{},"i":{"df":2,"docs":{"11":{"tf":1.0},"48":{"tf":1.0}}}},"df":0,"docs":{}}},"n":{"df":4,"docs":{"118":{"tf":1.4142135623730951},"40":{"tf":2.23606797749979},"71":{"tf":1.0},"94":{"tf":1.0}}},"p":{"df":0,"docs":{},"o":{"df":0,"docs":{},"l":{"df":26,"docs":{"112":{"tf":1.0},"114":{"tf":2.6457513110645907},"121":{"tf":1.7320508075688772},"123":{"tf":2.0},"125":{"tf":1.0},"14":{"tf":3.0},"16":{"tf":1.0},"20":{"tf":1.4142135623730951},"24":{"tf":1.0},"25":{"tf":1.0},"26":{"tf":1.0},"3":{"tf":2.0},"32":{"tf":1.0},"35":{"tf":1.0},"36":{"tf":1.0},"5":{"tf":1.0},"52":{"tf":1.0},"54":{"tf":1.0},"62":{"tf":1.0},"68":{"tf":1.0},"73":{"tf":1.0},"82":{"tf":1.0},"83":{"tf":1.0},"84":{"tf":1.0},"91":{"tf":1.0},"94":{"tf":1.4142135623730951}}}},"r":{"df":0,"docs":{},"e":{"df":0,"docs":{},"t":{"df":2,"docs":{"41":{"tf":1.0},"70":{"tf":1.0}}}}}}}},"r":{"df":0,"docs":{},"o":{"d":{"df":0,"docs":{},"u":{"c":{"df":3,"docs":{"125":{"tf":1.0},"14":{"tf":1.0},"53":{"tf":1.0}},"t":{"df":2,"docs":{"0":{"tf":1.7320508075688772},"1":{"tf":1.0}}}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"v":{"a":{"df":0,"docs":{},"l":{"df":0,"docs":{},"i":{"d":{"df":2,"docs":{"79":{"tf":1.0},"9":{"tf":1.0}}},"df":0,"docs":{}}},"r":{"df":0,"docs":{},"i":{"df":1,"docs":{"20":{"tf":1.0}}}}},"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":0,"docs":{},"s":{"df":5,"docs":{"123":{"tf":1.0},"146":{"tf":1.0},"45":{"tf":1.0},"7":{"tf":1.0},"98":{"tf":2.0}}},"t":{"df":2,"docs":{"7":{"tf":1.0},"98":{"tf":1.0}}}}},"o":{"df":0,"docs":{},"l":{"df":0,"docs":{},"v":{"df":8,"docs":{"103":{"tf":1.0},"104":{"tf":1.0},"105":{"tf":1.0},"123":{"tf":1.4142135623730951},"149":{"tf":1.0},"21":{"tf":1.0},"53":{"tf":1.0},"70":{"tf":1.0}}}}}}},"o":{"df":1,"docs":{"151":{"tf":1.0}}},"s":{"c":{"df":0,"docs":{},"o":{"df":0,"docs":{},"m":{"df":0,"docs":{},"m":{"df":0,"docs":{},"i":{"df":0,"docs":{},"t":{"df":0,"docs":{},"m":{"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"df":0,"docs":{},"t":{"df":0,"docs":{},"s":{"c":{"df":0,"docs":{},"h":{"df":0,"docs":{},"e":{"df":0,"docs":{},"m":{"df":0,"docs":{},"e":{"<":{"df":0,"docs":{},"f":{"df":1,"docs":{"35":{"tf":1.7320508075688772}}}},"df":0,"docs":{}}}}}},"df":0,"docs":{}}}}}}}}}}}},"df":0,"docs":{},"f":{"df":0,"docs":{},"i":{"df":0,"docs":{},"e":{"df":0,"docs":{},"l":{"d":{"df":1,"docs":{"35":{"tf":2.23606797749979}}},"df":0,"docs":{}}}}},"n":{"'":{"df":0,"docs":{},"t":{"df":3,"docs":{"121":{"tf":1.0},"149":{"tf":1.0},"65":{"tf":1.0}}}},"df":0,"docs":{}},"r":{"a":{"df":0,"docs":{},"n":{"d":{"df":0,"docs":{},"o":{"df":0,"docs":{},"m":{"df":0,"docs":{},"f":{"df":0,"docs":{},"i":{"df":0,"docs":{},"e":{"df":0,"docs":{},"l":{"d":{"df":0,"docs":{},"e":{"df":0,"docs":{},"l":{"df":0,"docs":{},"e":{"df":0,"docs":{},"m":{"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"df":0,"docs":{},"t":{"df":0,"docs":{},"g":{"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"a":{"df":0,"docs":{},"t":{"df":0,"docs":{},"o":{"df":0,"docs":{},"r":{"<":{"df":0,"docs":{},"f":{"df":1,"docs":{"35":{"tf":1.0}}}},"df":0,"docs":{}}}}},"df":0,"docs":{}}}}}}}}}}}}}},"df":0,"docs":{}}}}}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"s":{"df":0,"docs":{},"u":{"df":2,"docs":{"146":{"tf":1.0},"151":{"tf":1.0}}}}},"t":{"'":{"df":15,"docs":{"107":{"tf":1.0},"11":{"tf":1.0},"124":{"tf":1.0},"125":{"tf":1.0},"14":{"tf":1.0},"146":{"tf":1.7320508075688772},"151":{"tf":1.0},"20":{"tf":1.0},"35":{"tf":1.0},"49":{"tf":1.4142135623730951},"50":{"tf":1.4142135623730951},"52":{"tf":1.0},"54":{"tf":1.0},"55":{"tf":1.0},"81":{"tf":1.0}}},"df":0,"docs":{},"e":{"df":0,"docs":{},"m":{"df":4,"docs":{"112":{"tf":1.0},"49":{"tf":1.0},"59":{"tf":1.4142135623730951},"62":{"tf":1.0}}},"r":{"df":2,"docs":{"102":{"tf":1.0},"114":{"tf":1.0}}}},"s":{"df":0,"docs":{},"e":{"df":0,"docs":{},"l":{"df":0,"docs":{},"f":{"df":3,"docs":{"10":{"tf":1.0},"14":{"tf":1.0},"58":{"tf":1.0}}}}}},"​":{"c":{"df":0,"docs":{},"i":{"df":0,"docs":{},"​":{"(":{"df":0,"docs":{},"x":{"df":1,"docs":{"114":{"tf":1.0}}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"∈":{"[":{"0":{",":{"2":{"df":0,"docs":{},"n":{"+":{"df":0,"docs":{},"k":{"df":1,"docs":{"92":{"tf":1.0}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"":{"=":{"df":0,"docs":{},"ω":{"df":0,"docs":{},"j":{"df":0,"docs":{},"k":{"1":{"df":0,"docs":{},"​":{"df":0,"docs":{},"":{"=":{"df":0,"docs":{},"ω":{"df":0,"docs":{},"l":{"df":0,"docs":{},"k":{"2":{"df":1,"docs":{"14":{"tf":1.0}}},"df":0,"docs":{}}}}},"df":0,"docs":{}}}},"df":0,"docs":{}}}}},"df":0,"docs":{}}},"j":{"=":{"1":{",":{"df":0,"docs":{},"…":{",":{"df":0,"docs":{},"m":{"df":4,"docs":{"91":{"tf":1.4142135623730951},"94":{"tf":1.4142135623730951},"95":{"tf":1.0},"97":{"tf":2.0}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{},"m":{"df":0,"docs":{},"′":{"+":{"1":{",":{"df":0,"docs":{},"…":{",":{"df":0,"docs":{},"m":{"df":0,"docs":{},"′":{"+":{"df":0,"docs":{},"m":{"df":2,"docs":{"94":{"tf":1.4142135623730951},"95":{"tf":1.0}}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}},"b":{"df":1,"docs":{"97":{"tf":1.4142135623730951}},"​":{":":{"=":{"df":0,"docs":{},"β":{"df":0,"docs":{},"j":{"+":{"df":0,"docs":{},"n":{"df":0,"docs":{},"t":{"df":0,"docs":{},"​":{"df":0,"docs":{},"−":{"1":{"df":1,"docs":{"97":{"tf":1.0}}},"df":0,"docs":{}}}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":9,"docs":{"106":{"tf":1.0},"14":{"tf":1.0},"147":{"tf":1.4142135623730951},"5":{"tf":1.0},"70":{"tf":1.4142135623730951},"83":{"tf":1.0},"85":{"tf":1.0},"92":{"tf":1.4142135623730951},"94":{"tf":1.4142135623730951}},"g":{"df":0,"docs":{},"z":{"df":1,"docs":{"95":{"tf":1.4142135623730951}}}},"o":{"b":{"df":3,"docs":{"114":{"tf":1.0},"118":{"tf":1.0},"20":{"tf":1.0}}},"df":0,"docs":{}},"u":{"df":0,"docs":{},"m":{"df":0,"docs":{},"p":{"df":2,"docs":{"146":{"tf":1.0},"50":{"tf":1.0}}}}},"z":{"df":1,"docs":{"95":{"tf":1.4142135623730951}}},"υ":{"df":0,"docs":{},"s":{"df":0,"docs":{},"​":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"t":{"df":0,"docs":{},"j":{"df":1,"docs":{"95":{"tf":1.0}}}}},"df":0,"docs":{}}}}},"​":{",":{"df":0,"docs":{},"γ":{"df":0,"docs":{},"j":{"df":1,"docs":{"97":{"tf":1.0}}}}},":":{"=":{"df":0,"docs":{},"α":{"df":0,"docs":{},"j":{"df":1,"docs":{"97":{"tf":1.0}}}}},"df":0,"docs":{}},"df":0,"docs":{}},"′":{"df":0,"docs":{},"​":{":":{"=":{"df":0,"docs":{},"α":{"df":0,"docs":{},"j":{"+":{"df":0,"docs":{},"m":{"df":0,"docs":{},"−":{"1":{"df":1,"docs":{"97":{"tf":1.0}}},"df":0,"docs":{}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"":{"=":{"df":0,"docs":{},"i":{"df":1,"docs":{"5":{"tf":1.0}}}},"df":0,"docs":{}}},"k":{"+":{"1":{"df":0,"docs":{},"υ":{"df":0,"docs":{},"s":{"2":{"df":0,"docs":{},"k":{"+":{"1":{"df":1,"docs":{"95":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"1":{"2":{"df":1,"docs":{"35":{"tf":1.0}}},"df":2,"docs":{"14":{"tf":1.0},"35":{"tf":1.4142135623730951}}},"2":{"df":2,"docs":{"14":{"tf":1.0},"35":{"tf":1.0}}},"<":{"df":0,"docs":{},"n":{"df":2,"docs":{"94":{"tf":1.0},"95":{"tf":1.0}},"−":{"1":{"df":1,"docs":{"95":{"tf":1.0}}},"df":0,"docs":{}}}},"=":{"0":{",":{"df":0,"docs":{},"…":{",":{"df":0,"docs":{},"n":{"df":0,"docs":{},"−":{"1":{"df":1,"docs":{"95":{"tf":1.0}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"1":{",":{"df":0,"docs":{},"…":{",":{"df":0,"docs":{},"n":{"df":2,"docs":{"94":{"tf":1.0},"95":{"tf":1.0}},"t":{"df":2,"docs":{"91":{"tf":1.4142135623730951},"97":{"tf":1.4142135623730951}}},"−":{"1":{"df":1,"docs":{"94":{"tf":1.0}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"3":{"df":1,"docs":{"101":{"tf":1.0}}},"df":0,"docs":{},"n":{"df":1,"docs":{"95":{"tf":1.0}}},"ω":{"0":{"df":1,"docs":{"14":{"tf":1.0}}},"df":0,"docs":{}}},">":{"1":{"df":1,"docs":{"102":{"tf":1.0}}},"df":0,"docs":{}},"a":{"df":0,"docs":{},"t":{"df":0,"docs":{},"e":{"df":3,"docs":{"19":{"tf":1.0},"35":{"tf":1.0},"38":{"tf":1.0}}}}},"df":6,"docs":{"101":{"tf":1.0},"124":{"tf":1.0},"14":{"tf":2.0},"147":{"tf":1.4142135623730951},"49":{"tf":1.0},"70":{"tf":1.4142135623730951}},"e":{"c":{"c":{"a":{"df":0,"docs":{},"k":{"2":{"5":{"6":{"(":{"df":0,"docs":{},"x":{"df":0,"docs":{},"∣":{"df":0,"docs":{},"∣":{"df":0,"docs":{},"i":{"df":2,"docs":{"94":{"tf":1.0},"95":{"tf":1.0}}}}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":1,"docs":{"151":{"tf":1.0}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{},"e":{"df":0,"docs":{},"p":{"df":5,"docs":{"118":{"tf":1.0},"124":{"tf":1.0},"20":{"tf":1.0},"45":{"tf":1.0},"50":{"tf":1.0}}}},"y":{"df":3,"docs":{"146":{"tf":1.0},"151":{"tf":1.0},"44":{"tf":1.0}}}},"i":{"df":0,"docs":{},"n":{"d":{"df":1,"docs":{"74":{"tf":1.0}}},"df":0,"docs":{}}},"n":{"df":0,"docs":{},"o":{"df":0,"docs":{},"w":{"df":14,"docs":{"114":{"tf":1.0},"119":{"tf":1.0},"146":{"tf":1.7320508075688772},"151":{"tf":1.0},"44":{"tf":1.0},"45":{"tf":1.0},"57":{"tf":1.0},"58":{"tf":1.0},"7":{"tf":1.0},"74":{"tf":1.0},"75":{"tf":1.0},"79":{"tf":1.0},"80":{"tf":1.0},"83":{"tf":1.0}},"l":{"df":0,"docs":{},"e":{"d":{"df":0,"docs":{},"g":{"df":3,"docs":{"20":{"tf":1.0},"4":{"tf":1.0},"7":{"tf":1.4142135623730951}}}},"df":0,"docs":{}}},"n":{"df":6,"docs":{"151":{"tf":1.0},"21":{"tf":1.0},"42":{"tf":1.0},"73":{"tf":1.0},"91":{"tf":1.0},"98":{"tf":1.0}}}}}},"t":{"df":1,"docs":{"97":{"tf":1.4142135623730951}},"​":{":":{"=":{"df":0,"docs":{},"β":{"df":0,"docs":{},"k":{"df":0,"docs":{},"−":{"1":{"df":1,"docs":{"97":{"tf":1.0}}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"z":{"df":0,"docs":{},"g":{":":{":":{"df":0,"docs":{},"n":{"df":0,"docs":{},"e":{"df":0,"docs":{},"w":{"(":{"df":0,"docs":{},"s":{"df":0,"docs":{},"r":{"df":2,"docs":{"35":{"tf":1.4142135623730951},"44":{"tf":1.0}}}}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":2,"docs":{"35":{"tf":2.23606797749979},"44":{"tf":1.4142135623730951}}}},"υ":{"df":0,"docs":{},"s":{"2":{"df":0,"docs":{},"k":{"df":1,"docs":{"102":{"tf":1.0}},"​":{"df":0,"docs":{},"​":{"df":0,"docs":{},"π":{"df":0,"docs":{},"k":{"df":0,"docs":{},"−":{"df":0,"docs":{},"υ":{"df":0,"docs":{},"s":{"2":{"df":0,"docs":{},"k":{"df":0,"docs":{},"​":{"df":0,"docs":{},"​":{"df":0,"docs":{},"​":{"=":{"df":0,"docs":{},"g":{"+":{"df":0,"docs":{},"υ":{"df":0,"docs":{},"s":{"2":{"df":0,"docs":{},"k":{"df":0,"docs":{},"​":{"df":0,"docs":{},"h":{"=":{"df":0,"docs":{},"g":{"df":0,"docs":{},"−":{"df":0,"docs":{},"υ":{"df":0,"docs":{},"s":{"2":{"df":0,"docs":{},"k":{"df":0,"docs":{},"​":{"df":0,"docs":{},"h":{"df":1,"docs":{"95":{"tf":1.0}}}}}},"df":0,"docs":{}}}}}},"df":0,"docs":{}}}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}}},"df":0,"docs":{}}}}}}}}}},"df":0,"docs":{}}},"−":{"1":{"df":3,"docs":{"14":{"tf":1.0},"94":{"tf":1.0},"95":{"tf":1.0}}},"df":0,"docs":{}},"≥":{"0":{"df":1,"docs":{"20":{"tf":1.0}}},"df":0,"docs":{}}},"l":{",":{"df":0,"docs":{},"r":{",":{"df":0,"docs":{},"o":{"df":2,"docs":{"10":{"tf":1.0},"9":{"tf":1.0}}}},"df":0,"docs":{}}},"=":{"df":0,"docs":{},"∣":{"d":{"df":0,"docs":{},"k":{"df":0,"docs":{},"−":{"1":{"df":0,"docs":{},"​":{"df":0,"docs":{},"∣":{"/":{"2":{"df":1,"docs":{"94":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"a":{"df":0,"docs":{},"g":{"df":0,"docs":{},"r":{"a":{"df":0,"docs":{},"n":{"df":0,"docs":{},"g":{"df":7,"docs":{"123":{"tf":1.0},"3":{"tf":1.7320508075688772},"32":{"tf":1.0},"35":{"tf":1.0},"5":{"tf":1.0},"73":{"tf":1.0},"99":{"tf":1.0}}}}},"df":0,"docs":{}}},"m":{"b":{"d":{"a":{"df":0,"docs":{},"w":{"df":0,"docs":{},"o":{"df":0,"docs":{},"r":{"df":0,"docs":{},"k":{"df":6,"docs":{"0":{"tf":1.0},"1":{"tf":1.7320508075688772},"103":{"tf":1.4142135623730951},"19":{"tf":1.0},"45":{"tf":1.7320508075688772},"87":{"tf":1.0}},"s":{"_":{"c":{"df":0,"docs":{},"r":{"df":0,"docs":{},"y":{"df":0,"docs":{},"p":{"df":0,"docs":{},"t":{"df":0,"docs":{},"o":{"df":1,"docs":{"38":{"tf":1.0}}}}}}}},"df":0,"docs":{},"m":{"a":{"df":0,"docs":{},"t":{"df":0,"docs":{},"h":{":":{":":{"df":0,"docs":{},"e":{"df":0,"docs":{},"l":{"df":0,"docs":{},"l":{"df":0,"docs":{},"i":{"df":0,"docs":{},"p":{"df":0,"docs":{},"t":{"df":0,"docs":{},"i":{"c":{"_":{"c":{"df":0,"docs":{},"u":{"df":0,"docs":{},"r":{"df":0,"docs":{},"v":{"df":0,"docs":{},"e":{":":{":":{"df":0,"docs":{},"s":{"df":0,"docs":{},"h":{"df":0,"docs":{},"o":{"df":0,"docs":{},"r":{"df":0,"docs":{},"t":{"_":{"df":0,"docs":{},"w":{"df":0,"docs":{},"e":{"df":0,"docs":{},"i":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":0,"docs":{},"s":{"df":0,"docs":{},"t":{"df":0,"docs":{},"r":{"a":{"df":0,"docs":{},"s":{"df":0,"docs":{},"s":{":":{":":{"c":{"df":0,"docs":{},"u":{"df":0,"docs":{},"r":{"df":0,"docs":{},"v":{"df":0,"docs":{},"e":{"df":0,"docs":{},"s":{":":{":":{"b":{"df":0,"docs":{},"l":{"df":0,"docs":{},"s":{"1":{"2":{"_":{"3":{"8":{"1":{":":{":":{"d":{"df":0,"docs":{},"e":{"df":0,"docs":{},"f":{"a":{"df":0,"docs":{},"u":{"df":0,"docs":{},"l":{"df":0,"docs":{},"t":{"_":{"df":0,"docs":{},"t":{"df":0,"docs":{},"y":{"df":0,"docs":{},"p":{"df":0,"docs":{},"e":{"df":0,"docs":{},"s":{":":{":":{"df":0,"docs":{},"f":{"df":0,"docs":{},"r":{"df":0,"docs":{},"f":{"df":0,"docs":{},"i":{"df":0,"docs":{},"e":{"df":0,"docs":{},"l":{"d":{"df":2,"docs":{"44":{"tf":1.0},"45":{"tf":1.0}}},"df":0,"docs":{}}}}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}}}}},"df":0,"docs":{}}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}}}}}}}}},"df":0,"docs":{}}}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}}}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}},"p":{"df":0,"docs":{},"l":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"df":0,"docs":{},"k":{":":{":":{"c":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"df":0,"docs":{},"s":{"df":0,"docs":{},"t":{"df":0,"docs":{},"r":{"a":{"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"df":0,"docs":{},"t":{"_":{"df":0,"docs":{},"s":{"df":0,"docs":{},"y":{"df":0,"docs":{},"s":{"df":0,"docs":{},"t":{"df":0,"docs":{},"e":{"df":0,"docs":{},"m":{":":{":":{"c":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"df":0,"docs":{},"s":{"df":0,"docs":{},"t":{"df":0,"docs":{},"r":{"a":{"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"df":0,"docs":{},"t":{"df":0,"docs":{},"s":{"df":0,"docs":{},"y":{"df":0,"docs":{},"s":{"df":0,"docs":{},"t":{"df":0,"docs":{},"e":{"df":0,"docs":{},"m":{"df":2,"docs":{"44":{"tf":1.0},"45":{"tf":1.0}}}}}}}}}}}},"df":0,"docs":{}}}}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}}}}}},"df":0,"docs":{}}}}},"df":0,"docs":{}}}}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}}}}},"df":0,"docs":{}}}}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"n":{"d":{"df":1,"docs":{"14":{"tf":1.4142135623730951}}},"df":0,"docs":{},"g":{"df":0,"docs":{},"u":{"a":{"df":0,"docs":{},"g":{"df":1,"docs":{"50":{"tf":1.0}}}},"df":0,"docs":{}}}},"s":{"df":0,"docs":{},"t":{"df":14,"docs":{"10":{"tf":1.0},"107":{"tf":1.0},"109":{"tf":1.0},"119":{"tf":1.0},"13":{"tf":1.4142135623730951},"134":{"tf":1.0},"14":{"tf":2.0},"142":{"tf":1.4142135623730951},"146":{"tf":2.0},"147":{"tf":1.0},"151":{"tf":1.0},"16":{"tf":1.0},"40":{"tf":1.0},"9":{"tf":1.0}}}},"t":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":3,"docs":{"102":{"tf":1.0},"19":{"tf":1.4142135623730951},"87":{"tf":1.0}}}},"t":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":2,"docs":{"19":{"tf":1.0},"73":{"tf":1.0}}}}}},"y":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":2,"docs":{"102":{"tf":1.4142135623730951},"119":{"tf":1.4142135623730951}}}},"o":{"df":0,"docs":{},"u":{"df":0,"docs":{},"t":{"df":18,"docs":{"127":{"tf":2.23606797749979},"128":{"tf":1.0},"129":{"tf":1.0},"130":{"tf":1.0},"131":{"tf":1.0},"132":{"tf":1.0},"133":{"tf":1.0},"134":{"tf":1.0},"135":{"tf":1.0},"136":{"tf":1.0},"137":{"tf":1.0},"138":{"tf":1.0},"139":{"tf":1.0},"142":{"tf":1.0},"146":{"tf":1.4142135623730951},"147":{"tf":1.0},"149":{"tf":1.0},"151":{"tf":1.0}}}}}}},"d":{"df":0,"docs":{},"e":{"_":{"d":{"df":0,"docs":{},"o":{"df":0,"docs":{},"m":{"a":{"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{".":{"df":0,"docs":{},"i":{"df":0,"docs":{},"t":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"(":{")":{".":{"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"df":0,"docs":{},"u":{"df":0,"docs":{},"m":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":1,"docs":{"114":{"tf":1.0}}}}}}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}}}},"df":0,"docs":{}}}},"df":0,"docs":{}}}},"df":0,"docs":{},"r":{"df":0,"docs":{},"o":{"df":0,"docs":{},"o":{"df":0,"docs":{},"t":{"df":0,"docs":{},"s":{"_":{"df":0,"docs":{},"o":{"df":0,"docs":{},"f":{"_":{"df":0,"docs":{},"u":{"df":0,"docs":{},"n":{"df":0,"docs":{},"i":{"df":0,"docs":{},"t":{"df":0,"docs":{},"y":{"_":{"c":{"df":0,"docs":{},"o":{"df":0,"docs":{},"s":{"df":0,"docs":{},"e":{"df":0,"docs":{},"t":{"df":1,"docs":{"114":{"tf":1.4142135623730951}}}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}}}}},"df":0,"docs":{}}}},"df":0,"docs":{}}}}}},"t":{"df":0,"docs":{},"r":{"a":{"c":{"df":1,"docs":{"114":{"tf":2.0}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":6,"docs":{"109":{"tf":1.4142135623730951},"114":{"tf":1.4142135623730951},"119":{"tf":1.0},"125":{"tf":1.7320508075688772},"149":{"tf":1.0},"68":{"tf":2.0}}}},"df":7,"docs":{"11":{"tf":1.0},"12":{"tf":1.0},"13":{"tf":1.0},"14":{"tf":1.0},"142":{"tf":1.0},"147":{"tf":1.4142135623730951},"80":{"tf":1.4142135623730951}},"e":{"a":{"d":{"df":3,"docs":{"151":{"tf":1.0},"94":{"tf":1.0},"95":{"tf":1.0}}},"df":0,"docs":{},"f":{"df":2,"docs":{"101":{"tf":1.4142135623730951},"92":{"tf":1.0}}},"r":{"df":0,"docs":{},"n":{"df":1,"docs":{"42":{"tf":1.0}}}},"v":{"df":7,"docs":{"101":{"tf":1.7320508075688772},"14":{"tf":1.0},"144":{"tf":1.0},"16":{"tf":1.0},"20":{"tf":1.0},"80":{"tf":1.0},"92":{"tf":1.0}}}},"df":0,"docs":{},"f":{"df":0,"docs":{},"t":{"df":8,"docs":{"109":{"tf":1.0},"11":{"tf":1.0},"134":{"tf":1.0},"16":{"tf":1.0},"20":{"tf":1.0},"59":{"tf":1.0},"88":{"tf":1.0},"9":{"tf":2.23606797749979}}}},"m":{"df":0,"docs":{},"m":{"a":{"df":1,"docs":{"14":{"tf":1.4142135623730951}}},"df":0,"docs":{}}},"n":{"df":0,"docs":{},"g":{"df":0,"docs":{},"h":{"df":0,"docs":{},"t":{"df":1,"docs":{"131":{"tf":1.0}}}},"t":{"df":0,"docs":{},"h":{"df":7,"docs":{"119":{"tf":1.0},"142":{"tf":1.4142135623730951},"146":{"tf":1.0},"147":{"tf":1.7320508075688772},"36":{"tf":1.0},"51":{"tf":1.0},"72":{"tf":1.0}}}}}},"q":{"df":3,"docs":{"52":{"tf":1.4142135623730951},"62":{"tf":1.0},"68":{"tf":2.0}}},"s":{"df":0,"docs":{},"s":{"df":6,"docs":{"14":{"tf":1.0},"146":{"tf":1.0},"151":{"tf":1.4142135623730951},"48":{"tf":1.0},"73":{"tf":2.6457513110645907},"82":{"tf":1.0}}}},"t":{"'":{"df":24,"docs":{"104":{"tf":1.0},"107":{"tf":1.0},"109":{"tf":1.0},"114":{"tf":1.0},"12":{"tf":1.0},"125":{"tf":1.0},"14":{"tf":2.23606797749979},"149":{"tf":2.23606797749979},"150":{"tf":1.4142135623730951},"151":{"tf":1.7320508075688772},"16":{"tf":1.0},"20":{"tf":1.0},"34":{"tf":1.0},"35":{"tf":1.0},"44":{"tf":1.0},"48":{"tf":1.0},"49":{"tf":1.0},"52":{"tf":1.0},"54":{"tf":1.0},"71":{"tf":1.0},"75":{"tf":1.0},"77":{"tf":1.0},"79":{"tf":1.0},"9":{"tf":1.0}}},"df":0,"docs":{},"t":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":4,"docs":{"146":{"tf":1.0},"149":{"tf":1.0},"5":{"tf":1.0},"95":{"tf":1.7320508075688772}}}}}},"v":{"df":0,"docs":{},"e":{"df":0,"docs":{},"l":{"df":12,"docs":{"104":{"tf":1.7320508075688772},"105":{"tf":1.0},"106":{"tf":1.0},"107":{"tf":1.0},"108":{"tf":1.0},"109":{"tf":1.0},"110":{"tf":1.0},"46":{"tf":1.0},"59":{"tf":1.0},"67":{"tf":1.0},"69":{"tf":1.7320508075688772},"82":{"tf":1.4142135623730951}}},"r":{"a":{"df":0,"docs":{},"g":{"df":1,"docs":{"83":{"tf":1.0}}}},"df":0,"docs":{}}}}},"i":{",":{"df":0,"docs":{},"j":{"df":0,"docs":{},"′":{"df":0,"docs":{},"′":{"df":0,"docs":{},"​":{"<":{"df":0,"docs":{},"m":{"a":{"d":{"d":{"df":0,"docs":{},"r":{",":{"df":0,"docs":{},"i":{"+":{"1":{",":{"df":0,"docs":{},"j":{"df":1,"docs":{"142":{"tf":1.0}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}}}},"b":{"df":0,"docs":{},"r":{"a":{"df":0,"docs":{},"r":{"df":0,"docs":{},"i":{"df":3,"docs":{"121":{"tf":1.0},"2":{"tf":1.0},"3":{"tf":1.0}}}}},"df":0,"docs":{}}},"df":3,"docs":{"146":{"tf":1.0},"32":{"tf":1.0},"5":{"tf":1.4142135623730951}},"m":{"b":{"df":1,"docs":{"151":{"tf":1.4142135623730951}}},"df":0,"docs":{}},"n":{"df":0,"docs":{},"e":{"a":{"df":0,"docs":{},"r":{"df":6,"docs":{"14":{"tf":1.0},"21":{"tf":2.0},"28":{"tf":1.0},"32":{"tf":1.0},"84":{"tf":1.0},"85":{"tf":1.4142135623730951}}}},"df":1,"docs":{"35":{"tf":1.0}}},"k":{"df":1,"docs":{"151":{"tf":1.0}}}},"o":{"df":0,"docs":{},"r":{"df":1,"docs":{"103":{"tf":1.0}}}},"s":{"df":0,"docs":{},"t":{"df":4,"docs":{"114":{"tf":1.0},"119":{"tf":1.0},"146":{"tf":1.0},"59":{"tf":1.0}}}},"t":{"df":0,"docs":{},"t":{"df":0,"docs":{},"l":{"df":2,"docs":{"107":{"tf":1.0},"14":{"tf":1.0}}}}},"​":{"(":{"df":0,"docs":{},"h":{"df":0,"docs":{},"i":{"df":0,"docs":{},"​":{")":{"=":{"1":{"df":1,"docs":{"5":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"j":{"df":0,"docs":{},"​":{")":{"=":{"0":{"df":1,"docs":{"5":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}}},"o":{"c":{"a":{"df":0,"docs":{},"l":{"df":2,"docs":{"70":{"tf":2.0},"83":{"tf":1.0}}}},"df":0,"docs":{}},"df":0,"docs":{},"g":{"df":0,"docs":{},"i":{"c":{"df":5,"docs":{"146":{"tf":1.0},"151":{"tf":1.0},"16":{"tf":1.0},"36":{"tf":1.0},"4":{"tf":1.0}}},"df":0,"docs":{}}},"n":{"df":0,"docs":{},"g":{"df":2,"docs":{"114":{"tf":1.0},"73":{"tf":1.0}},"e":{"df":0,"docs":{},"r":{"df":2,"docs":{"13":{"tf":1.0},"146":{"tf":1.4142135623730951}}}}}},"o":{"df":0,"docs":{},"k":{"df":8,"docs":{"106":{"tf":1.0},"107":{"tf":1.0},"108":{"tf":1.0},"114":{"tf":1.0},"14":{"tf":1.0},"149":{"tf":2.23606797749979},"21":{"tf":1.0},"51":{"tf":1.0}}},"p":{"df":2,"docs":{"102":{"tf":1.0},"146":{"tf":2.0}}},"s":{"df":1,"docs":{"16":{"tf":1.0}}}},"s":{"df":0,"docs":{},"t":{"df":1,"docs":{"49":{"tf":1.0}}}},"t":{"df":2,"docs":{"50":{"tf":1.0},"53":{"tf":1.0}}},"w":{"df":5,"docs":{"125":{"tf":1.0},"35":{"tf":1.0},"68":{"tf":2.0},"81":{"tf":1.0},"91":{"tf":1.0}},"e":{"df":0,"docs":{},"s":{"df":0,"docs":{},"t":{"df":1,"docs":{"3":{"tf":1.0}}}}}}},"p":{"df":0,"docs":{},"u":{"b":{"df":2,"docs":{"142":{"tf":1.0},"147":{"tf":1.0}}},"df":0,"docs":{}}},"u":{"c":{"df":0,"docs":{},"k":{"df":0,"docs":{},"i":{"df":0,"docs":{},"l":{"df":0,"docs":{},"i":{"df":1,"docs":{"146":{"tf":1.0}}}}}}},"df":0,"docs":{}},"×":{"3":{"df":1,"docs":{"141":{"tf":1.0}}},"df":0,"docs":{}},"′":{":":{"=":{"df":0,"docs":{},"⌈":{"df":0,"docs":{},"l":{"df":0,"docs":{},"p":{"df":0,"docs":{},"u":{"b":{"df":0,"docs":{},"​":{"/":{"4":{"df":1,"docs":{"142":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}}}},"df":0,"docs":{}},"df":0,"docs":{},"′":{"=":{"(":{"df":0,"docs":{},"l":{"0":{"df":0,"docs":{},"′":{"df":0,"docs":{},"′":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"l":{"1":{"df":0,"docs":{},"′":{"df":0,"docs":{},"′":{"df":0,"docs":{},"​":{",":{".":{".":{".":{",":{"df":0,"docs":{},"l":{"df":0,"docs":{},"j":{"df":0,"docs":{},"′":{"df":0,"docs":{},"′":{"df":0,"docs":{},"​":{")":{"df":0,"docs":{},"t":{"df":1,"docs":{"142":{"tf":1.0}}}},"df":0,"docs":{}}}}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"m":{",":{"df":0,"docs":{},"v":{")":{"=":{"(":{"0":{",":{"0":{"df":1,"docs":{"146":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":1,"docs":{"146":{"tf":1.0}}}},"/":{"2":{"df":1,"docs":{"133":{"tf":1.0}}},"df":0,"docs":{}},"0":{"df":0,"docs":{},"​":{"=":{"df":0,"docs":{},"r":{"0":{"df":1,"docs":{"151":{"tf":1.0}}},"df":0,"docs":{}}},"df":0,"docs":{}}},":":{"=":{"df":0,"docs":{},"m":{"df":0,"docs":{},"′":{"+":{"df":0,"docs":{},"m":{"df":1,"docs":{"91":{"tf":1.0}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"=":{"2":{"df":0,"docs":{},"m":{"df":3,"docs":{"73":{"tf":1.0},"75":{"tf":1.0},"80":{"tf":1.0}}}},"5":{"2":{"df":1,"docs":{"147":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{},"n":{"+":{"df":0,"docs":{},"k":{"df":1,"docs":{"20":{"tf":1.0}}}},"df":0,"docs":{}}},"^":{"df":0,"docs":{},"j":{"df":1,"docs":{"94":{"tf":1.0}}}},"a":{"c":{"df":0,"docs":{},"h":{"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"df":2,"docs":{"151":{"tf":1.7320508075688772},"50":{"tf":1.4142135623730951}}}}}},"d":{"d":{"df":0,"docs":{},"r":{",":{"df":0,"docs":{},"i":{"+":{"1":{",":{"df":0,"docs":{},"j":{"df":0,"docs":{},"​":{"df":0,"docs":{},"−":{"df":0,"docs":{},"m":{"a":{"d":{"d":{"df":0,"docs":{},"r":{",":{"df":0,"docs":{},"i":{",":{"df":0,"docs":{},"j":{"df":0,"docs":{},"​":{">":{"1":{"df":1,"docs":{"142":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}}}},"df":0,"docs":{}},"df":0,"docs":{}},",":{"df":0,"docs":{},"j":{"df":0,"docs":{},"​":{"<":{"df":0,"docs":{},"l":{"0":{",":{"df":0,"docs":{},"j":{"df":1,"docs":{"142":{"tf":1.0}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":1,"docs":{"142":{"tf":1.0}}}},"df":0,"docs":{},"e":{"df":1,"docs":{"130":{"tf":1.0}}}},"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"df":14,"docs":{"0":{"tf":1.0},"104":{"tf":1.4142135623730951},"111":{"tf":1.0},"113":{"tf":1.0},"128":{"tf":1.0},"129":{"tf":1.4142135623730951},"133":{"tf":1.0},"145":{"tf":1.4142135623730951},"146":{"tf":2.0},"21":{"tf":1.0},"4":{"tf":1.0},"44":{"tf":1.0},"45":{"tf":1.0},"94":{"tf":1.0}},"l":{"df":0,"docs":{},"i":{"df":1,"docs":{"34":{"tf":1.0}}}},"t":{"a":{"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"df":1,"docs":{"13":{"tf":1.0}}}}},"df":0,"docs":{}}}},"j":{"df":0,"docs":{},"o":{"df":0,"docs":{},"r":{"df":3,"docs":{"108":{"tf":1.0},"147":{"tf":1.4142135623730951},"69":{"tf":1.0}}}}},"k":{"df":0,"docs":{},"e":{"df":15,"docs":{"125":{"tf":1.0},"146":{"tf":1.7320508075688772},"149":{"tf":1.0},"151":{"tf":1.0},"20":{"tf":1.0},"45":{"tf":1.0},"48":{"tf":1.0},"56":{"tf":1.0},"70":{"tf":1.0},"71":{"tf":1.0},"77":{"tf":1.0},"80":{"tf":1.0},"83":{"tf":1.0},"89":{"tf":1.0},"95":{"tf":1.0}}}},"l":{"df":0,"docs":{},"f":{"df":0,"docs":{},"o":{"df":0,"docs":{},"r":{"df":0,"docs":{},"m":{"df":1,"docs":{"11":{"tf":1.0}}}}}},"i":{"c":{"df":0,"docs":{},"i":{"df":1,"docs":{"77":{"tf":1.0}}}},"df":0,"docs":{}}},"n":{"df":0,"docs":{},"i":{"df":6,"docs":{"108":{"tf":1.0},"109":{"tf":1.0},"146":{"tf":1.7320508075688772},"20":{"tf":1.0},"70":{"tf":1.0},"98":{"tf":1.0}},"p":{"df":0,"docs":{},"u":{"df":0,"docs":{},"l":{"df":1,"docs":{"118":{"tf":1.0}}}}}}},"p":{"df":3,"docs":{"101":{"tf":1.0},"113":{"tf":1.4142135623730951},"14":{"tf":2.23606797749979}}},"r":{"df":0,"docs":{},"k":{"df":1,"docs":{"3":{"tf":1.0}}}},"t":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":0,"docs":{},"i":{"df":1,"docs":{"144":{"tf":1.4142135623730951}}}}},"h":{"/":{"df":0,"docs":{},"s":{"df":0,"docs":{},"r":{"c":{"/":{"df":0,"docs":{},"p":{"df":0,"docs":{},"o":{"df":0,"docs":{},"l":{"df":0,"docs":{},"y":{"df":0,"docs":{},"n":{"df":0,"docs":{},"o":{"df":0,"docs":{},"m":{"df":0,"docs":{},"i":{"a":{"df":0,"docs":{},"l":{".":{"df":0,"docs":{},"r":{"df":0,"docs":{},"s":{":":{"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"df":0,"docs":{},"t":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":0,"docs":{},"p":{"df":0,"docs":{},"o":{"df":0,"docs":{},"l":{"a":{"df":0,"docs":{},"t":{"df":0,"docs":{},"e":{"_":{"df":0,"docs":{},"f":{"df":0,"docs":{},"f":{"df":0,"docs":{},"t":{"df":1,"docs":{"3":{"tf":1.0}}}}}},"df":0,"docs":{}}}},"df":1,"docs":{"3":{"tf":1.0}}}}}}}}}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}}}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":2,"docs":{"1":{"tf":1.0},"146":{"tf":1.0}},"e":{"df":0,"docs":{},"m":{"a":{"df":0,"docs":{},"t":{"df":4,"docs":{"131":{"tf":1.0},"46":{"tf":1.0},"70":{"tf":1.0},"8":{"tf":1.0}}}},"df":0,"docs":{}}}},"r":{"df":0,"docs":{},"i":{"c":{"df":9,"docs":{"12":{"tf":1.0},"13":{"tf":2.0},"14":{"tf":1.7320508075688772},"147":{"tf":1.0},"16":{"tf":2.23606797749979},"34":{"tf":1.4142135623730951},"36":{"tf":1.4142135623730951},"8":{"tf":1.4142135623730951},"9":{"tf":1.4142135623730951}}},"df":0,"docs":{},"x":{"df":24,"docs":{"10":{"tf":2.23606797749979},"11":{"tf":2.449489742783178},"12":{"tf":1.7320508075688772},"13":{"tf":2.6457513110645907},"14":{"tf":2.0},"141":{"tf":1.0},"142":{"tf":2.23606797749979},"147":{"tf":2.8284271247461903},"16":{"tf":2.449489742783178},"20":{"tf":1.7320508075688772},"23":{"tf":1.4142135623730951},"26":{"tf":1.0},"32":{"tf":1.0},"34":{"tf":1.0},"35":{"tf":1.0},"36":{"tf":2.0},"5":{"tf":1.0},"69":{"tf":1.7320508075688772},"70":{"tf":1.0},"8":{"tf":1.4142135623730951},"83":{"tf":1.0},"9":{"tf":1.7320508075688772},"91":{"tf":2.0},"94":{"tf":1.4142135623730951}}}}},"t":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":3,"docs":{"14":{"tf":1.0},"146":{"tf":1.0},"16":{"tf":1.0}}}}}},"x":{"df":5,"docs":{"103":{"tf":1.0},"119":{"tf":1.0},"130":{"tf":1.0},"134":{"tf":1.0},"146":{"tf":1.0}},"i":{"df":0,"docs":{},"m":{"df":0,"docs":{},"u":{"df":0,"docs":{},"m":{"df":3,"docs":{"142":{"tf":1.0},"36":{"tf":1.0},"38":{"tf":1.4142135623730951}}}}}}}},"df":12,"docs":{"133":{"tf":1.4142135623730951},"14":{"tf":1.0},"141":{"tf":1.0},"142":{"tf":2.449489742783178},"146":{"tf":2.449489742783178},"20":{"tf":1.0},"5":{"tf":1.0},"70":{"tf":1.4142135623730951},"72":{"tf":1.0},"73":{"tf":1.7320508075688772},"75":{"tf":1.0},"91":{"tf":1.7320508075688772}},"e":{"a":{"df":0,"docs":{},"n":{"df":32,"docs":{"101":{"tf":1.0},"105":{"tf":1.0},"106":{"tf":1.0},"108":{"tf":1.0},"114":{"tf":1.0},"123":{"tf":1.0},"124":{"tf":1.4142135623730951},"127":{"tf":1.0},"131":{"tf":1.0},"133":{"tf":1.4142135623730951},"134":{"tf":1.0},"14":{"tf":1.7320508075688772},"142":{"tf":1.0},"146":{"tf":1.4142135623730951},"147":{"tf":1.0},"21":{"tf":1.0},"23":{"tf":1.0},"28":{"tf":1.0},"35":{"tf":1.0},"36":{"tf":1.0},"52":{"tf":1.0},"65":{"tf":1.4142135623730951},"66":{"tf":1.0},"68":{"tf":1.4142135623730951},"70":{"tf":1.0},"72":{"tf":1.0},"73":{"tf":1.0},"75":{"tf":1.0},"78":{"tf":1.0},"9":{"tf":1.0},"92":{"tf":1.0},"95":{"tf":1.0}}}},"c":{"df":0,"docs":{},"h":{"a":{"df":0,"docs":{},"n":{"df":1,"docs":{"4":{"tf":1.0}}}},"df":0,"docs":{}}},"df":0,"docs":{},"m":{"_":{"a":{"df":3,"docs":{"142":{"tf":2.0},"146":{"tf":1.0},"147":{"tf":1.4142135623730951}}},"df":0,"docs":{},"p":{"df":1,"docs":{"147":{"tf":1.0}}},"v":{"df":3,"docs":{"142":{"tf":1.7320508075688772},"146":{"tf":1.0},"147":{"tf":1.4142135623730951}}}},"df":3,"docs":{"133":{"tf":1.4142135623730951},"134":{"tf":1.7320508075688772},"135":{"tf":1.4142135623730951}},"o":{"df":0,"docs":{},"r":{"df":0,"docs":{},"i":{"df":11,"docs":{"127":{"tf":1.0},"133":{"tf":3.7416573867739413},"134":{"tf":2.8284271247461903},"135":{"tf":2.0},"141":{"tf":1.0},"142":{"tf":1.7320508075688772},"144":{"tf":2.23606797749979},"146":{"tf":5.5677643628300215},"147":{"tf":2.8284271247461903},"149":{"tf":1.0},"151":{"tf":3.1622776601683795}}},"y":{"_":{"c":{"df":0,"docs":{},"e":{"df":0,"docs":{},"l":{"df":1,"docs":{"134":{"tf":1.0}},"l":{".":{"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"df":0,"docs":{},"l":{"df":1,"docs":{"134":{"tf":1.0}}}}}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"n":{"df":0,"docs":{},"t":{"a":{"df":0,"docs":{},"l":{"df":1,"docs":{"50":{"tf":1.0}}}},"df":0,"docs":{},"i":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"df":14,"docs":{"142":{"tf":1.0},"146":{"tf":1.4142135623730951},"15":{"tf":1.0},"35":{"tf":1.0},"37":{"tf":1.0},"51":{"tf":1.0},"55":{"tf":1.0},"58":{"tf":1.0},"7":{"tf":1.0},"70":{"tf":1.0},"77":{"tf":1.0},"83":{"tf":1.0},"84":{"tf":1.0},"92":{"tf":1.0}}}}}}},"r":{"df":0,"docs":{},"g":{"df":1,"docs":{"12":{"tf":1.0}}},"k":{"df":0,"docs":{},"l":{"df":13,"docs":{"101":{"tf":1.7320508075688772},"110":{"tf":1.0},"112":{"tf":1.4142135623730951},"113":{"tf":1.4142135623730951},"119":{"tf":1.0},"60":{"tf":1.4142135623730951},"62":{"tf":1.4142135623730951},"63":{"tf":1.4142135623730951},"72":{"tf":1.4142135623730951},"76":{"tf":1.0},"80":{"tf":2.449489742783178},"91":{"tf":1.0},"92":{"tf":2.23606797749979}}}}},"s":{"df":0,"docs":{},"s":{"a":{"df":0,"docs":{},"g":{"df":2,"docs":{"40":{"tf":1.4142135623730951},"42":{"tf":1.0}},"e":{"_":{"1":{"df":1,"docs":{"40":{"tf":2.23606797749979}}},"2":{"df":1,"docs":{"40":{"tf":1.4142135623730951}}},"3":{"df":1,"docs":{"40":{"tf":1.0}}},"4":{"df":1,"docs":{"40":{"tf":1.4142135623730951}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"t":{"df":1,"docs":{"121":{"tf":1.0}},"h":{"df":0,"docs":{},"o":{"d":{"df":12,"docs":{"105":{"tf":1.0},"106":{"tf":1.4142135623730951},"107":{"tf":1.7320508075688772},"113":{"tf":1.0},"114":{"tf":2.23606797749979},"117":{"tf":1.0},"123":{"tf":1.0},"14":{"tf":1.4142135623730951},"3":{"tf":1.7320508075688772},"35":{"tf":1.4142135623730951},"40":{"tf":1.7320508075688772},"70":{"tf":1.0}}},"df":0,"docs":{}}}}},"i":{"+":{"1":{"df":0,"docs":{},"​":{"=":{"df":0,"docs":{},"m":{"df":0,"docs":{},"i":{"df":0,"docs":{},"​":{"+":{"1":{"df":1,"docs":{"151":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}}},"df":0,"docs":{}},",":{"df":0,"docs":{},"j":{"df":1,"docs":{"5":{"tf":1.0}}}},"d":{"d":{"df":0,"docs":{},"l":{"df":1,"docs":{"35":{"tf":1.0}}}},"df":0,"docs":{}},"df":1,"docs":{"151":{"tf":1.0}},"l":{"df":0,"docs":{},"l":{"df":0,"docs":{},"i":{"df":0,"docs":{},"s":{"df":0,"docs":{},"e":{"c":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"d":{"df":1,"docs":{"3":{"tf":1.0}}},"df":0,"docs":{}}}},"df":0,"docs":{}}}}}},"n":{"d":{"df":4,"docs":{"124":{"tf":1.0},"146":{"tf":1.0},"49":{"tf":1.0},"50":{"tf":1.0}}},"df":0,"docs":{},"i":{"df":0,"docs":{},"m":{"df":0,"docs":{},"u":{"df":0,"docs":{},"m":{"df":1,"docs":{"142":{"tf":1.0}}}}}},"u":{"df":0,"docs":{},"t":{"df":1,"docs":{"14":{"tf":1.0}}}}},"s":{"df":0,"docs":{},"s":{"df":4,"docs":{"128":{"tf":1.0},"130":{"tf":1.0},"146":{"tf":1.0},"151":{"tf":1.0}}}},"x":{"df":2,"docs":{"150":{"tf":1.0},"66":{"tf":1.0}}}},"j":{"df":1,"docs":{"94":{"tf":1.0}}},"m":{"df":0,"docs":{},"e":{"df":0,"docs":{},"m":{"df":1,"docs":{"147":{"tf":1.0}},"r":{"a":{"df":0,"docs":{},"p":{"2":{"df":1,"docs":{"147":{"tf":1.4142135623730951}}},"df":0,"docs":{}}},"df":0,"docs":{},"e":{"df":0,"docs":{},"p":{"df":0,"docs":{},"l":{"df":1,"docs":{"147":{"tf":1.0}},"s":{"df":0,"docs":{},"o":{"df":0,"docs":{},"r":{"df":0,"docs":{},"t":{"df":1,"docs":{"147":{"tf":1.4142135623730951}}}}}},"​":{"df":0,"docs":{},"∈":{"df":0,"docs":{},"f":{"2":{"df":0,"docs":{},"n":{"+":{"2":{"df":0,"docs":{},"×":{"2":{"df":1,"docs":{"147":{"tf":1.0}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}}}}}},"​":{"df":0,"docs":{},"∈":{"df":0,"docs":{},"f":{"2":{"df":0,"docs":{},"n":{"+":{"2":{"df":0,"docs":{},"×":{"2":{"df":1,"docs":{"147":{"tf":1.0}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}}}}},"o":{"d":{"df":0,"docs":{},"e":{"df":1,"docs":{"146":{"tf":1.4142135623730951}},"l":{"df":1,"docs":{"50":{"tf":1.0}}},"r":{"df":0,"docs":{},"n":{"df":1,"docs":{"42":{"tf":1.0}}}}},"i":{"df":0,"docs":{},"f":{"df":0,"docs":{},"i":{"df":4,"docs":{"102":{"tf":1.0},"146":{"tf":1.0},"45":{"tf":1.0},"59":{"tf":1.4142135623730951}}}}},"u":{"df":0,"docs":{},"l":{"df":1,"docs":{"38":{"tf":1.0}},"o":{"df":1,"docs":{"41":{"tf":1.0}}}}}},"df":0,"docs":{},"f":{"df":0,"docs":{},"f":{"df":0,"docs":{},"s":{"df":0,"docs":{},"e":{"df":0,"docs":{},"t":{"df":1,"docs":{"142":{"tf":1.4142135623730951}}}}}}},"m":{"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"df":0,"docs":{},"t":{"df":5,"docs":{"10":{"tf":1.0},"19":{"tf":1.0},"34":{"tf":1.0},"70":{"tf":1.0},"79":{"tf":1.0}}}}}},"r":{"df":0,"docs":{},"e":{"df":27,"docs":{"103":{"tf":1.0},"114":{"tf":1.7320508075688772},"118":{"tf":1.0},"12":{"tf":1.0},"130":{"tf":1.0},"133":{"tf":1.0},"135":{"tf":1.0},"14":{"tf":2.0},"146":{"tf":1.7320508075688772},"150":{"tf":1.0},"151":{"tf":2.0},"16":{"tf":1.0},"21":{"tf":1.0},"34":{"tf":1.0},"41":{"tf":1.4142135623730951},"42":{"tf":1.0},"45":{"tf":1.0},"64":{"tf":1.0},"66":{"tf":1.0},"68":{"tf":1.0},"7":{"tf":1.0},"73":{"tf":1.4142135623730951},"74":{"tf":1.0},"80":{"tf":1.0},"82":{"tf":1.0},"83":{"tf":1.0},"84":{"tf":1.4142135623730951}},"o":{"df":0,"docs":{},"v":{"df":1,"docs":{"14":{"tf":1.0}}}}}},"v":{"df":0,"docs":{},"e":{"df":4,"docs":{"128":{"tf":1.4142135623730951},"149":{"tf":1.0},"50":{"tf":1.0},"53":{"tf":1.0}},"m":{"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"df":0,"docs":{},"t":{"df":1,"docs":{"149":{"tf":1.0}}}}}}}}},"r":{"a":{"df":0,"docs":{},"n":{"df":0,"docs":{},"g":{"df":0,"docs":{},"e":{"c":{"df":0,"docs":{},"h":{"df":0,"docs":{},"e":{"c":{"df":0,"docs":{},"k":{"df":0,"docs":{},"r":{"a":{"df":0,"docs":{},"p":{"2":{"df":1,"docs":{"147":{"tf":1.4142135623730951}}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}}}},"df":0,"docs":{}}}},"p":{"2":{"df":3,"docs":{"147":{"tf":1.0},"83":{"tf":1.0},"94":{"tf":1.0}},"​":{"df":0,"docs":{},"∈":{"df":0,"docs":{},"f":{"2":{"df":0,"docs":{},"n":{"df":0,"docs":{},"×":{"df":0,"docs":{},"m":{"df":1,"docs":{"94":{"tf":1.0}}}}}},"df":0,"docs":{}}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"s":{"df":1,"docs":{"3":{"tf":4.0}}},"u":{"c":{"df":0,"docs":{},"h":{"df":6,"docs":{"116":{"tf":1.0},"123":{"tf":1.4142135623730951},"125":{"tf":1.0},"21":{"tf":1.0},"37":{"tf":1.0},"66":{"tf":1.0}}}},"df":0,"docs":{},"l":{"df":3,"docs":{"142":{"tf":1.0},"146":{"tf":1.7320508075688772},"147":{"tf":1.0}},"t":{"df":0,"docs":{},"i":{"df":1,"docs":{"139":{"tf":1.4142135623730951}},"p":{"df":0,"docs":{},"l":{"df":16,"docs":{"10":{"tf":1.4142135623730951},"101":{"tf":1.0},"106":{"tf":1.0},"107":{"tf":1.0},"12":{"tf":1.0},"14":{"tf":1.0},"142":{"tf":1.4142135623730951},"149":{"tf":1.4142135623730951},"151":{"tf":1.0},"44":{"tf":1.0},"65":{"tf":2.0},"66":{"tf":2.0},"7":{"tf":1.0},"9":{"tf":1.0},"92":{"tf":1.0},"98":{"tf":1.0}},"i":{"df":4,"docs":{"125":{"tf":1.0},"14":{"tf":1.0},"45":{"tf":1.0},"52":{"tf":1.0}}}}},"v":{"a":{"df":0,"docs":{},"r":{"df":0,"docs":{},"i":{"df":2,"docs":{"70":{"tf":1.0},"84":{"tf":1.0}}}}},"df":0,"docs":{}}}}},"t":{"a":{"df":0,"docs":{},"t":{"df":1,"docs":{"118":{"tf":1.4142135623730951}}}},"df":3,"docs":{"114":{"tf":1.0},"44":{"tf":1.0},"45":{"tf":1.7320508075688772}}}},"′":{",":{"df":0,"docs":{},"v":{"df":1,"docs":{"146":{"tf":1.0}}}},"=":{"3":{"3":{"df":1,"docs":{"147":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{},"′":{"=":{"1":{"8":{"df":1,"docs":{"147":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"∈":{"df":0,"docs":{},"f":{"(":{"df":0,"docs":{},"l":{"+":{"df":0,"docs":{},"l":{"df":0,"docs":{},"′":{")":{"df":0,"docs":{},"×":{"3":{"3":{"df":1,"docs":{"142":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}}},"+":{"df":0,"docs":{},"l":{"df":0,"docs":{},"′":{"df":0,"docs":{},"′":{")":{"df":0,"docs":{},"×":{"3":{"3":{"df":1,"docs":{"142":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{},"l":{"df":0,"docs":{},"×":{"3":{"3":{"df":1,"docs":{"142":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"−":{"1":{"df":2,"docs":{"73":{"tf":2.23606797749979},"79":{"tf":1.0}}},"df":0,"docs":{}},"≥":{"df":0,"docs":{},"n":{"df":1,"docs":{"20":{"tf":1.0}}}}},"n":{"+":{"1":{"df":1,"docs":{"26":{"tf":1.0}}},"df":0,"docs":{},"m":{"+":{"1":{")":{"df":0,"docs":{},"×":{"3":{"df":1,"docs":{"14":{"tf":1.0}}},"5":{"df":1,"docs":{"14":{"tf":1.0}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"<":{"df":0,"docs":{},"m":{"df":3,"docs":{"73":{"tf":1.0},"75":{"tf":1.0},"80":{"tf":1.0}}}},"=":{"2":{"df":0,"docs":{},"n":{"df":3,"docs":{"73":{"tf":1.0},"75":{"tf":1.0},"80":{"tf":1.0}}}},"3":{"df":1,"docs":{"51":{"tf":1.0}}},"df":0,"docs":{},"n":{"+":{"df":0,"docs":{},"m":{"+":{"1":{"df":1,"docs":{"14":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"a":{"df":0,"docs":{},"i":{"df":0,"docs":{},"v":{"df":3,"docs":{"101":{"tf":1.0},"123":{"tf":1.0},"3":{"tf":1.0}}}},"m":{"df":0,"docs":{},"e":{"df":3,"docs":{"102":{"tf":1.0},"13":{"tf":1.0},"130":{"tf":1.0}}}},"r":{"df":0,"docs":{},"r":{"df":0,"docs":{},"o":{"df":0,"docs":{},"w":{"df":1,"docs":{"50":{"tf":1.0}}}}}},"t":{"df":0,"docs":{},"u":{"df":0,"docs":{},"r":{"df":2,"docs":{"101":{"tf":1.0},"84":{"tf":1.0}}}}}},"c":{"df":1,"docs":{"28":{"tf":1.0}}},"df":14,"docs":{"124":{"tf":2.23606797749979},"127":{"tf":1.0},"14":{"tf":2.449489742783178},"151":{"tf":1.0},"16":{"tf":1.0},"20":{"tf":1.0},"23":{"tf":1.4142135623730951},"32":{"tf":1.0},"35":{"tf":1.7320508075688772},"68":{"tf":1.4142135623730951},"75":{"tf":1.4142135623730951},"78":{"tf":1.4142135623730951},"79":{"tf":1.0},"91":{"tf":1.4142135623730951}},"e":{"a":{"df":0,"docs":{},"r":{"df":0,"docs":{},"e":{"df":0,"docs":{},"s":{"df":0,"docs":{},"t":{"df":1,"docs":{"142":{"tf":1.0}}}}}}},"c":{"df":0,"docs":{},"e":{"df":0,"docs":{},"s":{"df":0,"docs":{},"s":{"a":{"df":0,"docs":{},"r":{"df":0,"docs":{},"i":{"df":6,"docs":{"103":{"tf":1.0},"110":{"tf":1.0},"112":{"tf":1.0},"13":{"tf":1.0},"133":{"tf":1.0},"33":{"tf":1.0}},"l":{"df":0,"docs":{},"i":{"df":1,"docs":{"14":{"tf":1.0}}}}}}},"df":0,"docs":{}}}}},"df":0,"docs":{},"e":{"d":{"df":42,"docs":{"102":{"tf":1.4142135623730951},"105":{"tf":1.0},"107":{"tf":2.0},"109":{"tf":1.0},"111":{"tf":1.4142135623730951},"114":{"tf":2.0},"117":{"tf":1.4142135623730951},"118":{"tf":1.4142135623730951},"119":{"tf":1.4142135623730951},"123":{"tf":1.0},"125":{"tf":1.0},"130":{"tf":1.0},"133":{"tf":1.0},"14":{"tf":1.4142135623730951},"144":{"tf":1.0},"146":{"tf":3.3166247903554},"149":{"tf":1.0},"151":{"tf":1.7320508075688772},"19":{"tf":1.0},"20":{"tf":1.0},"21":{"tf":1.0},"32":{"tf":2.0},"34":{"tf":1.0},"35":{"tf":1.4142135623730951},"36":{"tf":1.4142135623730951},"44":{"tf":1.4142135623730951},"48":{"tf":1.0},"5":{"tf":1.0},"57":{"tf":1.0},"58":{"tf":1.4142135623730951},"59":{"tf":1.0},"60":{"tf":1.7320508075688772},"68":{"tf":1.7320508075688772},"69":{"tf":1.0},"70":{"tf":1.7320508075688772},"74":{"tf":1.4142135623730951},"75":{"tf":1.0},"8":{"tf":1.4142135623730951},"80":{"tf":1.7320508075688772},"82":{"tf":1.0},"88":{"tf":1.0},"9":{"tf":1.0}}},"df":0,"docs":{}},"g":{"df":0,"docs":{},"l":{"df":0,"docs":{},"i":{"df":0,"docs":{},"g":{"df":1,"docs":{"73":{"tf":1.0}}}}}},"v":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":1,"docs":{"95":{"tf":1.0}}}}},"w":{"_":{"df":0,"docs":{},"s":{"df":0,"docs":{},"i":{"df":0,"docs":{},"m":{"df":0,"docs":{},"p":{"df":0,"docs":{},"l":{"df":1,"docs":{"106":{"tf":1.0}}}}}}}},"df":10,"docs":{"106":{"tf":1.0},"13":{"tf":1.0},"14":{"tf":1.0},"142":{"tf":1.0},"146":{"tf":1.7320508075688772},"20":{"tf":1.4142135623730951},"59":{"tf":1.0},"68":{"tf":1.0},"83":{"tf":1.4142135623730951},"94":{"tf":1.0}}},"x":{"df":0,"docs":{},"t":{"df":15,"docs":{"10":{"tf":1.0},"102":{"tf":1.4142135623730951},"118":{"tf":1.0},"128":{"tf":1.0},"134":{"tf":1.4142135623730951},"14":{"tf":1.4142135623730951},"144":{"tf":1.0},"149":{"tf":1.0},"23":{"tf":1.0},"35":{"tf":1.0},"49":{"tf":1.0},"66":{"tf":1.0},"74":{"tf":1.0},"77":{"tf":1.0},"9":{"tf":1.0}}}}},"i":{"c":{"df":0,"docs":{},"e":{"df":1,"docs":{"14":{"tf":1.0}}}},"df":0,"docs":{}},"o":{"df":0,"docs":{},"n":{"c":{"df":2,"docs":{"119":{"tf":1.0},"88":{"tf":1.4142135623730951}}},"df":1,"docs":{"28":{"tf":1.4142135623730951}},"i":{"df":0,"docs":{},"n":{"df":0,"docs":{},"t":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"a":{"c":{"df":0,"docs":{},"t":{"df":1,"docs":{"89":{"tf":1.0}}}},"df":0,"docs":{}},"df":0,"docs":{}}}}}},"z":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":0,"docs":{},"o":{"df":2,"docs":{"73":{"tf":1.0},"75":{"tf":1.0}}}}}}},"t":{"a":{"df":0,"docs":{},"t":{"df":11,"docs":{"142":{"tf":1.0},"147":{"tf":1.0},"34":{"tf":1.0},"35":{"tf":1.0},"5":{"tf":1.7320508075688772},"70":{"tf":1.4142135623730951},"87":{"tf":1.0},"9":{"tf":1.0},"90":{"tf":1.7320508075688772},"92":{"tf":2.0},"95":{"tf":1.4142135623730951}}}},"df":0,"docs":{},"e":{"df":14,"docs":{"104":{"tf":1.0},"125":{"tf":1.7320508075688772},"128":{"tf":1.0},"131":{"tf":1.0},"146":{"tf":1.0},"44":{"tf":1.0},"68":{"tf":1.0},"74":{"tf":1.0},"79":{"tf":1.0},"80":{"tf":1.4142135623730951},"84":{"tf":1.0},"92":{"tf":1.0},"95":{"tf":1.0},"96":{"tf":1.4142135623730951}}},"h":{"df":1,"docs":{"114":{"tf":1.0}}},"i":{"c":{"df":8,"docs":{"10":{"tf":1.0},"114":{"tf":1.0},"118":{"tf":1.0},"13":{"tf":1.0},"14":{"tf":1.4142135623730951},"21":{"tf":1.0},"68":{"tf":1.0},"7":{"tf":1.0}}},"df":0,"docs":{}}},"w":{"df":17,"docs":{"11":{"tf":1.4142135623730951},"12":{"tf":1.0},"121":{"tf":1.0},"123":{"tf":1.0},"13":{"tf":1.4142135623730951},"14":{"tf":2.23606797749979},"146":{"tf":1.0},"149":{"tf":1.7320508075688772},"150":{"tf":1.0},"33":{"tf":1.0},"44":{"tf":1.0},"58":{"tf":1.0},"65":{"tf":1.0},"66":{"tf":1.0},"7":{"tf":1.0},"77":{"tf":1.0},"83":{"tf":1.0}}}},"u":{"df":0,"docs":{},"m":{"_":{"c":{"df":0,"docs":{},"o":{"df":0,"docs":{},"l":{"df":1,"docs":{"108":{"tf":1.7320508075688772}}}}},"df":0,"docs":{},"t":{"df":0,"docs":{},"r":{"a":{"df":0,"docs":{},"n":{"df":0,"docs":{},"s":{"df":0,"docs":{},"i":{"df":0,"docs":{},"t":{"df":0,"docs":{},"i":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"_":{"c":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"df":0,"docs":{},"s":{"df":0,"docs":{},"t":{"df":0,"docs":{},"r":{"a":{"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"df":0,"docs":{},"t":{"df":1,"docs":{"109":{"tf":1.4142135623730951}}}}}},"df":0,"docs":{}}}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}}}}}}},"df":0,"docs":{}}}},"b":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":25,"docs":{"106":{"tf":1.0},"109":{"tf":2.0},"118":{"tf":1.7320508075688772},"119":{"tf":1.0},"124":{"tf":1.4142135623730951},"125":{"tf":1.0},"128":{"tf":1.4142135623730951},"133":{"tf":1.0},"142":{"tf":1.0},"144":{"tf":1.0},"146":{"tf":1.4142135623730951},"147":{"tf":1.0},"149":{"tf":1.0},"151":{"tf":1.7320508075688772},"21":{"tf":1.0},"32":{"tf":1.0},"35":{"tf":1.0},"49":{"tf":2.449489742783178},"50":{"tf":1.0},"56":{"tf":1.0},"59":{"tf":1.0},"69":{"tf":1.0},"73":{"tf":1.4142135623730951},"88":{"tf":1.0},"91":{"tf":2.6457513110645907}}}}},"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":4,"docs":{"114":{"tf":1.0},"123":{"tf":1.0},"125":{"tf":1.4142135623730951},"55":{"tf":1.0}}}}}},"×":{"1":{"df":2,"docs":{"14":{"tf":1.0},"16":{"tf":1.4142135623730951}}},"3":{"df":3,"docs":{"14":{"tf":1.0},"16":{"tf":1.4142135623730951},"23":{"tf":1.0}}},"df":0,"docs":{}},"−":{"1":{")":{"df":0,"docs":{},"t":{"df":1,"docs":{"33":{"tf":1.0}}}},"df":7,"docs":{"14":{"tf":1.0},"20":{"tf":1.0},"73":{"tf":1.4142135623730951},"74":{"tf":1.4142135623730951},"78":{"tf":1.4142135623730951},"79":{"tf":1.0},"80":{"tf":1.0}}},"df":0,"docs":{}}},"o":{"b":{"df":0,"docs":{},"j":{"df":0,"docs":{},"e":{"c":{"df":0,"docs":{},"t":{"df":2,"docs":{"87":{"tf":1.0},"89":{"tf":1.0}}}},"df":0,"docs":{}}},"s":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":0,"docs":{},"v":{"df":3,"docs":{"13":{"tf":1.0},"151":{"tf":1.0},"7":{"tf":1.0}}}}}},"t":{"a":{"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"df":14,"docs":{"114":{"tf":1.4142135623730951},"124":{"tf":1.0},"14":{"tf":1.7320508075688772},"142":{"tf":1.7320508075688772},"146":{"tf":1.7320508075688772},"32":{"tf":1.0},"34":{"tf":1.0},"44":{"tf":1.4142135623730951},"7":{"tf":1.0},"83":{"tf":1.0},"84":{"tf":1.4142135623730951},"91":{"tf":1.0},"94":{"tf":1.7320508075688772},"95":{"tf":1.0}}}}},"df":0,"docs":{}},"v":{"df":0,"docs":{},"i":{"df":0,"docs":{},"o":{"df":0,"docs":{},"u":{"df":3,"docs":{"107":{"tf":1.0},"14":{"tf":1.0},"16":{"tf":1.0}}}}}}},"c":{"c":{"df":0,"docs":{},"u":{"df":0,"docs":{},"r":{"df":1,"docs":{"41":{"tf":1.0}},"r":{"df":1,"docs":{"14":{"tf":1.7320508075688772}}}}}},"df":0,"docs":{}},"d":{"d":{"df":4,"docs":{"119":{"tf":1.0},"67":{"tf":1.0},"7":{"tf":1.0},"94":{"tf":1.0}}},"df":0,"docs":{}},"df":4,"docs":{"11":{"tf":1.0},"12":{"tf":1.0},"13":{"tf":1.0},"14":{"tf":1.0}},"f":{"df":0,"docs":{},"f":{"0":{"df":1,"docs":{"130":{"tf":1.0}}},"1":{"df":1,"docs":{"130":{"tf":1.0}}},"2":{"df":1,"docs":{"130":{"tf":1.0}}},"_":{"d":{"df":0,"docs":{},"s":{"df":0,"docs":{},"t":{"df":4,"docs":{"130":{"tf":1.4142135623730951},"142":{"tf":1.0},"146":{"tf":1.7320508075688772},"147":{"tf":1.0}}}}},"df":0,"docs":{},"o":{"df":0,"docs":{},"p":{"0":{"df":4,"docs":{"130":{"tf":1.4142135623730951},"142":{"tf":1.0},"146":{"tf":1.7320508075688772},"147":{"tf":1.0}}},"1":{"df":4,"docs":{"130":{"tf":1.4142135623730951},"142":{"tf":1.0},"146":{"tf":1.7320508075688772},"147":{"tf":1.0}}},"df":0,"docs":{}}}},"df":0,"docs":{},"s":{"df":0,"docs":{},"e":{"df":0,"docs":{},"t":{"df":12,"docs":{"109":{"tf":1.0},"125":{"tf":2.23606797749979},"128":{"tf":1.0},"130":{"tf":2.23606797749979},"133":{"tf":3.4641016151377544},"134":{"tf":1.0},"136":{"tf":2.449489742783178},"139":{"tf":1.4142135623730951},"142":{"tf":1.4142135623730951},"146":{"tf":2.23606797749979},"147":{"tf":1.7320508075688772},"151":{"tf":1.0}},"s":{"/":{"df":0,"docs":{},"r":{"a":{"df":0,"docs":{},"n":{"df":0,"docs":{},"g":{"df":1,"docs":{"146":{"tf":1.0}}}}},"df":0,"docs":{}}},"_":{"b":{"df":1,"docs":{"147":{"tf":1.0}}},"df":0,"docs":{},"p":{"df":1,"docs":{"147":{"tf":1.0}}}},"df":0,"docs":{}}}}}}},"m":{"df":0,"docs":{},"e":{"df":0,"docs":{},"g":{"a":{"^":{"2":{"df":1,"docs":{"68":{"tf":1.4142135623730951}}},"df":0,"docs":{},"i":{"df":1,"docs":{"68":{"tf":1.4142135623730951}}},"{":{"1":{"6":{"df":1,"docs":{"68":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":2,"docs":{"35":{"tf":1.0},"68":{"tf":1.7320508075688772}}},"df":0,"docs":{}}},"i":{"df":0,"docs":{},"s":{"df":0,"docs":{},"s":{"df":1,"docs":{"64":{"tf":1.4142135623730951}}}}}},"n":{"c":{"df":7,"docs":{"111":{"tf":1.0},"134":{"tf":1.0},"44":{"tf":1.0},"67":{"tf":1.0},"8":{"tf":1.0},"80":{"tf":1.4142135623730951},"98":{"tf":1.0}}},"df":47,"docs":{"10":{"tf":1.0},"101":{"tf":1.0},"102":{"tf":1.0},"105":{"tf":1.0},"106":{"tf":1.0},"107":{"tf":2.0},"109":{"tf":1.0},"11":{"tf":1.4142135623730951},"114":{"tf":2.0},"118":{"tf":1.0},"123":{"tf":1.0},"124":{"tf":1.0},"125":{"tf":1.4142135623730951},"13":{"tf":2.0},"130":{"tf":1.0},"131":{"tf":1.0},"132":{"tf":1.0},"133":{"tf":1.0},"135":{"tf":1.0},"136":{"tf":1.0},"14":{"tf":2.449489742783178},"142":{"tf":1.0},"145":{"tf":1.0},"146":{"tf":3.3166247903554},"149":{"tf":2.23606797749979},"151":{"tf":1.7320508075688772},"19":{"tf":1.0},"21":{"tf":1.0},"22":{"tf":1.0},"34":{"tf":1.0},"49":{"tf":2.23606797749979},"5":{"tf":1.0},"50":{"tf":1.0},"51":{"tf":1.0},"52":{"tf":1.4142135623730951},"57":{"tf":1.0},"59":{"tf":1.4142135623730951},"65":{"tf":2.0},"66":{"tf":1.7320508075688772},"68":{"tf":1.0},"70":{"tf":1.0},"73":{"tf":1.4142135623730951},"80":{"tf":2.0},"84":{"tf":2.0},"9":{"tf":1.7320508075688772},"92":{"tf":1.0},"99":{"tf":1.0}},"w":{"a":{"df":0,"docs":{},"r":{"d":{"df":1,"docs":{"52":{"tf":1.0}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"o":{"d":{"df":2,"docs":{"113":{"tf":1.0},"117":{"tf":1.4142135623730951}}},"df":0,"docs":{}},"p":{"0":{"_":{"a":{"d":{"d":{"df":0,"docs":{},"r":{"[":{"0":{"df":1,"docs":{"147":{"tf":1.0}}},"df":0,"docs":{}},"df":3,"docs":{"142":{"tf":1.0},"146":{"tf":2.0},"147":{"tf":1.4142135623730951}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{},"r":{"df":0,"docs":{},"e":{"df":0,"docs":{},"g":{"df":1,"docs":{"146":{"tf":1.0}}}}}},"df":4,"docs":{"133":{"tf":2.0},"142":{"tf":1.0},"146":{"tf":2.0},"147":{"tf":1.4142135623730951}},"r":{"df":0,"docs":{},"e":{"df":0,"docs":{},"g":{"df":1,"docs":{"149":{"tf":1.0}}}}}},"1":{"_":{"a":{"d":{"d":{"df":0,"docs":{},"r":{"[":{"0":{"df":1,"docs":{"147":{"tf":1.0}}},"df":0,"docs":{}},"df":3,"docs":{"142":{"tf":1.0},"146":{"tf":1.7320508075688772},"147":{"tf":1.4142135623730951}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{},"s":{"df":0,"docs":{},"r":{"c":{"df":1,"docs":{"146":{"tf":1.0}}},"df":0,"docs":{}}}},"df":4,"docs":{"133":{"tf":2.0},"142":{"tf":1.0},"146":{"tf":2.0},"147":{"tf":1.4142135623730951}}},"c":{"df":0,"docs":{},"o":{"d":{"df":2,"docs":{"146":{"tf":1.7320508075688772},"149":{"tf":1.0}}},"df":0,"docs":{}}},"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"(":{"a":{",":{"a":{"b":{"df":0,"docs":{},"i":{"df":1,"docs":{"92":{"tf":1.0}}}},"df":0,"docs":{}},"df":0,"docs":{},"i":{"df":2,"docs":{"92":{"tf":1.4142135623730951},"95":{"tf":1.0}}}},"df":0,"docs":{}},"df":0,"docs":{},"f":{"df":1,"docs":{"19":{"tf":1.0}}},"h":{"1":{"df":0,"docs":{},"​":{"(":{"d":{"df":0,"docs":{},"l":{"d":{"df":0,"docs":{},"e":{"df":0,"docs":{},"​":{")":{",":{"df":0,"docs":{},"υ":{"df":1,"docs":{"94":{"tf":1.0}}},"−":{"df":0,"docs":{},"υ":{"df":1,"docs":{"94":{"tf":1.0}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"2":{"df":0,"docs":{},"​":{"(":{"d":{"df":0,"docs":{},"l":{"d":{"df":0,"docs":{},"e":{"df":0,"docs":{},"​":{")":{",":{"df":0,"docs":{},"υ":{"df":1,"docs":{"94":{"tf":1.0}}},"−":{"df":0,"docs":{},"υ":{"df":1,"docs":{"94":{"tf":1.0}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"p":{"(":{"d":{")":{",":{"df":0,"docs":{},"h":{"df":0,"docs":{},"ω":{"df":0,"docs":{},"i":{"df":1,"docs":{"101":{"tf":1.0}}}}},"−":{"df":0,"docs":{},"h":{"df":0,"docs":{},"ω":{"df":0,"docs":{},"i":{"df":1,"docs":{"101":{"tf":1.0}}}}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{},"k":{"df":0,"docs":{},"​":{"(":{"d":{"df":0,"docs":{},"k":{"df":0,"docs":{},"​":{")":{",":{"df":0,"docs":{},"υ":{"df":0,"docs":{},"s":{"2":{"df":0,"docs":{},"k":{"df":1,"docs":{"94":{"tf":1.0}}}},"df":0,"docs":{}}},"−":{"df":0,"docs":{},"υ":{"df":0,"docs":{},"s":{"2":{"df":0,"docs":{},"k":{"df":1,"docs":{"94":{"tf":1.0}}}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"t":{"df":0,"docs":{},"j":{"df":0,"docs":{},"​":{"(":{"d":{"df":0,"docs":{},"l":{"d":{"df":0,"docs":{},"e":{"df":0,"docs":{},"​":{")":{",":{"df":0,"docs":{},"υ":{"df":2,"docs":{"94":{"tf":1.0},"95":{"tf":1.0}}},"−":{"df":0,"docs":{},"υ":{"df":1,"docs":{"94":{"tf":1.0}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":17,"docs":{"101":{"tf":1.4142135623730951},"102":{"tf":1.0},"119":{"tf":1.0},"19":{"tf":1.0},"21":{"tf":2.8284271247461903},"28":{"tf":1.4142135623730951},"33":{"tf":1.4142135623730951},"35":{"tf":1.4142135623730951},"69":{"tf":1.0},"71":{"tf":1.0},"72":{"tf":1.0},"75":{"tf":2.0},"77":{"tf":1.4142135623730951},"80":{"tf":2.23606797749979},"86":{"tf":1.7320508075688772},"94":{"tf":1.0},"95":{"tf":1.7320508075688772}}},"r":{"a":{"df":0,"docs":{},"n":{"d":{"df":3,"docs":{"11":{"tf":1.0},"20":{"tf":1.0},"9":{"tf":1.4142135623730951}}},"df":0,"docs":{}}},"df":5,"docs":{"13":{"tf":1.0},"45":{"tf":2.0},"50":{"tf":1.0},"92":{"tf":2.23606797749979},"99":{"tf":1.0}}}},"s":{"_":{"df":0,"docs":{},"m":{"df":0,"docs":{},"u":{"df":0,"docs":{},"l":{"df":1,"docs":{"136":{"tf":1.4142135623730951}}}}}},"df":0,"docs":{}},"t":{"df":0,"docs":{},"i":{"df":0,"docs":{},"m":{"df":9,"docs":{"12":{"tf":1.0},"136":{"tf":1.0},"146":{"tf":1.4142135623730951},"21":{"tf":1.0},"67":{"tf":1.0},"80":{"tf":1.0},"94":{"tf":1.7320508075688772},"96":{"tf":1.4142135623730951},"99":{"tf":1.0}}},"o":{"df":0,"docs":{},"n":{"df":4,"docs":{"104":{"tf":2.0},"109":{"tf":1.4142135623730951},"110":{"tf":1.0},"91":{"tf":1.0}}}}}}},"r":{"d":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"_":{"df":0,"docs":{},"r":{"_":{"df":0,"docs":{},"m":{"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"df":0,"docs":{},"u":{"df":0,"docs":{},"s":{"_":{"1":{"_":{"df":0,"docs":{},"r":{"df":0,"docs":{},"o":{"df":0,"docs":{},"o":{"df":0,"docs":{},"t":{"_":{"df":0,"docs":{},"u":{"df":0,"docs":{},"n":{"df":1,"docs":{"44":{"tf":1.0}}}}},"df":0,"docs":{}}}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}}}}},"df":0,"docs":{}}},"df":16,"docs":{"101":{"tf":2.23606797749979},"118":{"tf":1.0},"130":{"tf":1.0},"132":{"tf":1.0},"134":{"tf":1.0},"135":{"tf":1.0},"142":{"tf":1.4142135623730951},"146":{"tf":2.449489742783178},"147":{"tf":1.4142135623730951},"20":{"tf":1.0},"36":{"tf":1.0},"38":{"tf":1.7320508075688772},"41":{"tf":1.7320508075688772},"45":{"tf":1.0},"94":{"tf":1.4142135623730951},"95":{"tf":1.0}}}}},"df":0,"docs":{},"g":{"a":{"df":0,"docs":{},"n":{"df":2,"docs":{"149":{"tf":1.0},"69":{"tf":1.0}}}},"df":0,"docs":{}},"i":{"df":0,"docs":{},"g":{"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"df":6,"docs":{"13":{"tf":1.0},"146":{"tf":1.0},"19":{"tf":1.4142135623730951},"74":{"tf":1.0},"83":{"tf":1.0},"84":{"tf":1.0}}}}}}},"t":{"df":0,"docs":{},"h":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":4,"docs":{"107":{"tf":1.0},"109":{"tf":1.0},"149":{"tf":1.0},"41":{"tf":1.0}},"w":{"df":0,"docs":{},"i":{"df":0,"docs":{},"s":{"df":5,"docs":{"104":{"tf":1.0},"124":{"tf":1.0},"19":{"tf":1.0},"33":{"tf":1.0},"7":{"tf":1.0}}}}}}}}},"u":{"df":0,"docs":{},"t":{"c":{"df":0,"docs":{},"o":{"df":0,"docs":{},"m":{"df":1,"docs":{"118":{"tf":1.0}}}}},"df":16,"docs":{"101":{"tf":1.0},"112":{"tf":1.0},"115":{"tf":1.0},"117":{"tf":1.7320508075688772},"119":{"tf":1.0},"16":{"tf":1.0},"41":{"tf":1.0},"42":{"tf":1.0},"50":{"tf":1.0},"59":{"tf":1.4142135623730951},"60":{"tf":1.0},"62":{"tf":1.0},"66":{"tf":1.0},"7":{"tf":1.0},"72":{"tf":1.0},"79":{"tf":1.0}},"p":{"df":0,"docs":{},"u":{"df":0,"docs":{},"t":{"df":16,"docs":{"104":{"tf":1.0},"11":{"tf":1.0},"13":{"tf":1.7320508075688772},"146":{"tf":1.0},"151":{"tf":1.0},"16":{"tf":1.0},"19":{"tf":1.0},"32":{"tf":1.0},"33":{"tf":2.0},"34":{"tf":1.4142135623730951},"35":{"tf":2.0},"4":{"tf":1.0},"40":{"tf":1.4142135623730951},"7":{"tf":2.8284271247461903},"74":{"tf":1.0},"9":{"tf":2.449489742783178}}}}},"s":{"df":0,"docs":{},"i":{"d":{"df":1,"docs":{"20":{"tf":1.0}}},"df":0,"docs":{}}}}},"v":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":16,"docs":{"103":{"tf":1.0},"104":{"tf":1.4142135623730951},"109":{"tf":1.0},"111":{"tf":1.0},"112":{"tf":1.0},"125":{"tf":2.0},"131":{"tf":1.4142135623730951},"149":{"tf":1.4142135623730951},"44":{"tf":1.0},"62":{"tf":1.0},"68":{"tf":1.4142135623730951},"70":{"tf":1.0},"80":{"tf":1.0},"82":{"tf":1.4142135623730951},"83":{"tf":1.0},"84":{"tf":1.0}},"v":{"df":0,"docs":{},"i":{"df":0,"docs":{},"e":{"df":0,"docs":{},"w":{"df":19,"docs":{"69":{"tf":1.7320508075688772},"70":{"tf":1.0},"71":{"tf":1.0},"72":{"tf":1.0},"73":{"tf":1.0},"74":{"tf":1.0},"75":{"tf":1.0},"76":{"tf":1.0},"77":{"tf":1.0},"78":{"tf":1.0},"79":{"tf":1.0},"80":{"tf":1.0},"81":{"tf":1.0},"82":{"tf":1.0},"83":{"tf":1.0},"84":{"tf":1.0},"85":{"tf":1.0},"86":{"tf":1.0},"92":{"tf":1.0}}}}}},"w":{"df":0,"docs":{},"h":{"df":0,"docs":{},"e":{"df":0,"docs":{},"l":{"df":0,"docs":{},"m":{"df":3,"docs":{"14":{"tf":2.6457513110645907},"19":{"tf":1.0},"85":{"tf":1.0}}}}}}}}}}},"p":{"(":{"1":{")":{"=":{"1":{"df":0,"docs":{},"p":{"(":{"df":0,"docs":{},"g":{")":{"=":{"1":{"df":1,"docs":{"54":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"d":{"1":{"df":0,"docs":{},"​":{")":{",":{"df":0,"docs":{},"…":{",":{"df":0,"docs":{},"p":{"(":{"d":{"df":0,"docs":{},"m":{"df":3,"docs":{"73":{"tf":1.4142135623730951},"76":{"tf":1.0},"80":{"tf":1.0}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{},"i":{"df":0,"docs":{},"​":{")":{"=":{"df":0,"docs":{},"y":{"df":0,"docs":{},"i":{"df":1,"docs":{"73":{"tf":1.0}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{},"h":{")":{",":{"df":0,"docs":{},"p":{"(":{"df":0,"docs":{},"h":{"df":0,"docs":{},"ω":{")":{",":{"df":0,"docs":{},"p":{"(":{"df":0,"docs":{},"h":{"df":0,"docs":{},"ω":{"2":{")":{",":{"df":0,"docs":{},"…":{",":{"df":0,"docs":{},"p":{"(":{"df":0,"docs":{},"h":{"df":0,"docs":{},"ω":{"2":{"df":0,"docs":{},"k":{"df":0,"docs":{},"−":{"1":{"df":1,"docs":{"101":{"tf":1.0}}},"df":0,"docs":{}}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{},"ω":{"df":0,"docs":{},"i":{"df":1,"docs":{"101":{"tf":1.4142135623730951}}},"σ":{"(":{"df":0,"docs":{},"i":{"df":1,"docs":{"101":{"tf":1.4142135623730951}}}},"df":0,"docs":{}}}},"x":{"df":1,"docs":{"54":{"tf":1.0}}},"z":{"df":2,"docs":{"77":{"tf":1.0},"78":{"tf":1.0}}},"ζ":{")":{"=":{"a":{"(":{"df":0,"docs":{},"ζ":{")":{"df":0,"docs":{},"q":{"df":0,"docs":{},"l":{"df":0,"docs":{},"​":{"(":{"df":0,"docs":{},"ζ":{")":{"+":{"b":{"(":{"df":0,"docs":{},"ζ":{")":{"df":0,"docs":{},"q":{"df":0,"docs":{},"r":{"df":1,"docs":{"21":{"tf":1.0}}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{},"p":{"df":0,"docs":{},"ˉ":{"df":0,"docs":{},"​":{"c":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"p":{"df":0,"docs":{},"ˉ":{"df":0,"docs":{},"​":{"df":0,"docs":{},"n":{"c":{"df":1,"docs":{"32":{"tf":1.0}}},"df":0,"docs":{}}}}}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"z":{"df":0,"docs":{},"h":{"df":0,"docs":{},"​":{"(":{"df":0,"docs":{},"ζ":{")":{"df":0,"docs":{},"t":{"df":1,"docs":{"21":{"tf":1.0}}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}},"−":{"df":0,"docs":{},"h":{"df":0,"docs":{},"ω":{"df":0,"docs":{},"i":{"df":1,"docs":{"101":{"tf":1.0}}},"σ":{"(":{"df":0,"docs":{},"i":{"df":1,"docs":{"101":{"tf":1.0}}}},"df":0,"docs":{}}}}}},",":{"a":{",":{"b":{",":{"df":0,"docs":{},"z":{"df":1,"docs":{"5":{"tf":1.0}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{},"q":{"df":0,"docs":{},"∈":{"df":0,"docs":{},"f":{"[":{"df":0,"docs":{},"x":{"df":1,"docs":{"90":{"tf":1.0}}}},"df":0,"docs":{}}}}},".":{"df":0,"docs":{},"i":{"df":1,"docs":{"32":{"tf":1.0}}}},"/":{"df":0,"docs":{},"q":{"df":1,"docs":{"90":{"tf":1.0}}}},"0":{"df":1,"docs":{"94":{"tf":1.0}}},"1":{"df":0,"docs":{},"​":{"(":{"d":{"1":{"df":0,"docs":{},"​":{")":{"df":0,"docs":{},"∣":{"df":0,"docs":{},"∣":{"df":0,"docs":{},"…":{"df":0,"docs":{},"∣":{"df":0,"docs":{},"∣":{"df":0,"docs":{},"p":{"df":0,"docs":{},"l":{"df":0,"docs":{},"​":{"(":{"d":{"1":{"df":0,"docs":{},"​":{")":{",":{"df":0,"docs":{},"…":{",":{"df":0,"docs":{},"p":{"df":0,"docs":{},"l":{"df":0,"docs":{},"​":{"(":{"d":{"df":0,"docs":{},"m":{"df":0,"docs":{},"​":{")":{"df":0,"docs":{},"∣":{"df":0,"docs":{},"∣":{"df":0,"docs":{},"…":{"df":0,"docs":{},"∣":{"df":0,"docs":{},"∣":{"df":0,"docs":{},"p":{"df":0,"docs":{},"n":{"df":0,"docs":{},"​":{"(":{"d":{"df":0,"docs":{},"m":{"df":1,"docs":{"80":{"tf":1.0}}}},"df":0,"docs":{}},"df":0,"docs":{}}}}}}}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}}}}}}}},"df":0,"docs":{}}},"df":0,"docs":{},"i":{"df":0,"docs":{},"​":{")":{",":{"df":0,"docs":{},"…":{",":{"df":0,"docs":{},"p":{"df":0,"docs":{},"l":{"df":0,"docs":{},"​":{"(":{"d":{"df":0,"docs":{},"i":{"df":1,"docs":{"80":{"tf":1.0}}}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}}},"df":0,"docs":{},"∣":{"df":0,"docs":{},"∣":{"df":0,"docs":{},"…":{"df":0,"docs":{},"∣":{"df":0,"docs":{},"∣":{"df":0,"docs":{},"p":{"df":0,"docs":{},"l":{"df":0,"docs":{},"​":{"(":{"d":{"df":0,"docs":{},"i":{"df":1,"docs":{"80":{"tf":1.0}}}},"df":0,"docs":{}},"df":0,"docs":{}}}}}}}}}},"df":0,"docs":{}}}},"df":0,"docs":{},"z":{"1":{"df":0,"docs":{},"​":{")":{",":{"df":0,"docs":{},"…":{",":{"df":0,"docs":{},"p":{"df":0,"docs":{},"k":{"df":0,"docs":{},"​":{"(":{"df":0,"docs":{},"z":{"df":0,"docs":{},"l":{"df":1,"docs":{"80":{"tf":1.0}}}}},"df":0,"docs":{}}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{},"p":{"2":{"df":0,"docs":{},"​":{"df":0,"docs":{},"p":{"3":{"df":0,"docs":{},"​":{"df":0,"docs":{},"​":{"=":{"a":{"df":0,"docs":{},"q":{"df":0,"docs":{},"l":{"df":0,"docs":{},"​":{"+":{"b":{"df":0,"docs":{},"q":{"df":0,"docs":{},"r":{"df":0,"docs":{},"​":{"+":{"a":{"b":{"df":0,"docs":{},"q":{"df":0,"docs":{},"m":{"df":0,"docs":{},"​":{"+":{"c":{"df":0,"docs":{},"q":{"df":0,"docs":{},"o":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"q":{"c":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"p":{"df":0,"docs":{},"i":{"=":{"(":{"a":{"+":{"df":0,"docs":{},"β":{"df":0,"docs":{},"x":{"+":{"df":0,"docs":{},"γ":{")":{"(":{"b":{"+":{"df":0,"docs":{},"β":{"df":0,"docs":{},"k":{"1":{"df":0,"docs":{},"​":{"df":0,"docs":{},"x":{"+":{"df":0,"docs":{},"γ":{")":{"(":{"c":{"+":{"df":0,"docs":{},"β":{"df":0,"docs":{},"k":{"2":{"df":0,"docs":{},"​":{"df":0,"docs":{},"x":{"+":{"df":0,"docs":{},"γ":{")":{"df":0,"docs":{},"z":{"df":0,"docs":{},"−":{"(":{"a":{"+":{"df":0,"docs":{},"β":{"df":0,"docs":{},"s":{"df":0,"docs":{},"σ":{"1":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"γ":{")":{"(":{"b":{"+":{"df":0,"docs":{},"β":{"df":0,"docs":{},"s":{"df":0,"docs":{},"σ":{"2":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"γ":{")":{"(":{"c":{"+":{"df":0,"docs":{},"β":{"df":0,"docs":{},"s":{"df":0,"docs":{},"σ":{"3":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"γ":{")":{"df":0,"docs":{},"z":{"(":{"df":0,"docs":{},"ω":{"df":0,"docs":{},"x":{")":{"=":{"(":{"df":0,"docs":{},"z":{"df":0,"docs":{},"−":{"1":{")":{"df":0,"docs":{},"l":{"1":{"df":1,"docs":{"26":{"tf":1.0}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}}},"df":0,"docs":{}}}},"=":{"1":{"df":1,"docs":{"73":{"tf":1.0}}},"a":{"df":0,"docs":{},"q":{"df":0,"docs":{},"l":{"df":0,"docs":{},"​":{"+":{"b":{"df":0,"docs":{},"q":{"df":0,"docs":{},"r":{"df":0,"docs":{},"​":{"+":{"a":{"b":{"df":0,"docs":{},"q":{"df":0,"docs":{},"m":{"df":1,"docs":{"21":{"tf":1.0}}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{},"p":{"1":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"α":{"df":0,"docs":{},"p":{"2":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"α":{"2":{"df":0,"docs":{},"p":{"3":{"df":1,"docs":{"26":{"tf":1.0}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"t":{"df":0,"docs":{},"z":{"df":0,"docs":{},"h":{"df":1,"docs":{"26":{"tf":1.0}}}}},"{":{"df":0,"docs":{},"p":{"1":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"…":{",":{"df":0,"docs":{},"p":{"df":0,"docs":{},"l":{"df":1,"docs":{"80":{"tf":1.0}}},"n":{"df":1,"docs":{"80":{"tf":1.0}}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"^":{"df":0,"docs":{},"​":{"df":0,"docs":{},"n":{"c":{"1":{"df":0,"docs":{},"​":{"]":{"1":{"df":0,"docs":{},"​":{"[":{"df":0,"docs":{},"p":{"^":{"df":0,"docs":{},"​":{"df":0,"docs":{},"n":{"c":{"2":{"df":0,"docs":{},"​":{"]":{"1":{"df":0,"docs":{},"​":{"[":{"df":0,"docs":{},"p":{"^":{"df":0,"docs":{},"​":{"df":0,"docs":{},"n":{"c":{"3":{"df":0,"docs":{},"​":{"]":{"1":{"df":0,"docs":{},"​":{"df":0,"docs":{},"​":{"=":{"a":{"df":0,"docs":{},"ˉ":{"[":{"df":0,"docs":{},"q":{"df":0,"docs":{},"l":{"df":0,"docs":{},"​":{"]":{"1":{"df":0,"docs":{},"​":{"+":{"b":{"df":0,"docs":{},"ˉ":{"[":{"df":0,"docs":{},"q":{"df":0,"docs":{},"r":{"df":0,"docs":{},"​":{"]":{"1":{"df":0,"docs":{},"​":{"+":{"(":{"a":{"df":0,"docs":{},"ˉ":{"b":{"df":0,"docs":{},"ˉ":{")":{"[":{"df":0,"docs":{},"q":{"df":0,"docs":{},"m":{"df":0,"docs":{},"​":{"]":{"1":{"df":0,"docs":{},"​":{"+":{"c":{"df":0,"docs":{},"ˉ":{"[":{"df":0,"docs":{},"q":{"df":0,"docs":{},"o":{"df":0,"docs":{},"​":{"]":{"1":{"df":0,"docs":{},"​":{"+":{"[":{"df":0,"docs":{},"q":{"c":{"df":0,"docs":{},"​":{"]":{"1":{"df":0,"docs":{},"​":{"=":{"(":{"a":{"df":0,"docs":{},"ˉ":{"+":{"df":0,"docs":{},"β":{"df":0,"docs":{},"ζ":{"+":{"df":0,"docs":{},"γ":{")":{"(":{"b":{"df":0,"docs":{},"ˉ":{"+":{"df":0,"docs":{},"β":{"df":0,"docs":{},"k":{"1":{"df":0,"docs":{},"​":{"df":0,"docs":{},"ζ":{"+":{"df":0,"docs":{},"γ":{")":{"(":{"c":{"df":0,"docs":{},"ˉ":{"+":{"df":0,"docs":{},"β":{"df":0,"docs":{},"k":{"2":{"df":0,"docs":{},"​":{"df":0,"docs":{},"ζ":{"+":{"df":0,"docs":{},"γ":{")":{"[":{"df":0,"docs":{},"z":{"]":{"1":{"df":0,"docs":{},"​":{"df":0,"docs":{},"−":{"(":{"a":{"df":0,"docs":{},"ˉ":{"+":{"df":0,"docs":{},"β":{"df":0,"docs":{},"s":{"df":0,"docs":{},"ˉ":{"df":0,"docs":{},"σ":{"1":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"γ":{")":{"(":{"b":{"df":0,"docs":{},"ˉ":{"+":{"df":0,"docs":{},"β":{"df":0,"docs":{},"s":{"df":0,"docs":{},"ˉ":{"df":0,"docs":{},"σ":{"2":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"γ":{")":{"df":0,"docs":{},"β":{"df":0,"docs":{},"z":{"df":0,"docs":{},"ˉ":{"df":0,"docs":{},"ω":{"df":0,"docs":{},"​":{"[":{"df":0,"docs":{},"s":{"df":0,"docs":{},"σ":{"3":{"df":0,"docs":{},"​":{"]":{"1":{"df":0,"docs":{},"​":{"=":{"df":0,"docs":{},"l":{"1":{"df":0,"docs":{},"​":{"(":{"df":0,"docs":{},"ζ":{")":{"[":{"df":0,"docs":{},"z":{"]":{"1":{"df":1,"docs":{"32":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}}}}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{},"p":{"^":{"df":0,"docs":{},"​":{"df":0,"docs":{},"n":{"c":{"2":{"df":0,"docs":{},"​":{"df":0,"docs":{},"p":{"^":{"df":0,"docs":{},"​":{"df":0,"docs":{},"n":{"c":{"3":{"df":0,"docs":{},"​":{"df":0,"docs":{},"​":{"=":{"a":{"df":0,"docs":{},"ˉ":{"df":0,"docs":{},"q":{"df":0,"docs":{},"l":{"df":0,"docs":{},"​":{"+":{"b":{"df":0,"docs":{},"ˉ":{"df":0,"docs":{},"q":{"df":0,"docs":{},"r":{"df":0,"docs":{},"​":{"+":{"a":{"df":0,"docs":{},"ˉ":{"b":{"df":0,"docs":{},"ˉ":{"df":0,"docs":{},"q":{"df":0,"docs":{},"m":{"df":0,"docs":{},"​":{"+":{"c":{"df":0,"docs":{},"ˉ":{"df":0,"docs":{},"q":{"df":0,"docs":{},"o":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"q":{"c":{"df":0,"docs":{},"​":{"=":{"(":{"a":{"df":0,"docs":{},"ˉ":{"+":{"df":0,"docs":{},"β":{"df":0,"docs":{},"ζ":{"+":{"df":0,"docs":{},"γ":{")":{"(":{"b":{"df":0,"docs":{},"ˉ":{"+":{"df":0,"docs":{},"β":{"df":0,"docs":{},"k":{"1":{"df":0,"docs":{},"​":{"df":0,"docs":{},"ζ":{"+":{"df":0,"docs":{},"γ":{")":{"(":{"c":{"df":0,"docs":{},"ˉ":{"+":{"df":0,"docs":{},"β":{"df":0,"docs":{},"k":{"2":{"df":0,"docs":{},"​":{"df":0,"docs":{},"ζ":{"+":{"df":0,"docs":{},"γ":{")":{"df":0,"docs":{},"z":{"df":0,"docs":{},"−":{"(":{"a":{"df":0,"docs":{},"ˉ":{"+":{"df":0,"docs":{},"β":{"df":0,"docs":{},"s":{"df":0,"docs":{},"ˉ":{"df":0,"docs":{},"σ":{"1":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"γ":{")":{"(":{"b":{"df":0,"docs":{},"ˉ":{"+":{"df":0,"docs":{},"β":{"df":0,"docs":{},"s":{"df":0,"docs":{},"ˉ":{"df":0,"docs":{},"σ":{"2":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"γ":{")":{"df":0,"docs":{},"β":{"df":0,"docs":{},"z":{"df":0,"docs":{},"ˉ":{"df":0,"docs":{},"ω":{"df":0,"docs":{},"​":{"df":0,"docs":{},"s":{"df":0,"docs":{},"σ":{"3":{"df":0,"docs":{},"​":{"=":{"df":0,"docs":{},"l":{"1":{"df":0,"docs":{},"​":{"(":{"df":0,"docs":{},"ζ":{")":{"df":0,"docs":{},"z":{"df":1,"docs":{"28":{"tf":1.0}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}}}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"_":{"df":0,"docs":{},"n":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"_":{"c":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"df":0,"docs":{},"s":{"df":0,"docs":{},"t":{"a":{"df":0,"docs":{},"n":{"df":0,"docs":{},"t":{"_":{"df":0,"docs":{},"z":{"df":0,"docs":{},"e":{"df":0,"docs":{},"t":{"a":{"df":1,"docs":{"35":{"tf":1.0}}},"df":0,"docs":{}}}}},"df":0,"docs":{}}}},"df":1,"docs":{"35":{"tf":1.0}}}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"a":{"c":{"df":0,"docs":{},"k":{"a":{"df":0,"docs":{},"g":{"df":1,"docs":{"38":{"tf":1.0}}}},"df":0,"docs":{}}},"d":{"df":5,"docs":{"101":{"tf":1.0},"121":{"tf":1.0},"142":{"tf":1.0},"146":{"tf":2.0},"36":{"tf":2.0}}},"df":0,"docs":{},"i":{"df":0,"docs":{},"r":{"df":9,"docs":{"102":{"tf":1.0},"135":{"tf":1.0},"14":{"tf":3.4641016151377544},"146":{"tf":2.23606797749979},"151":{"tf":1.0},"3":{"tf":1.0},"38":{"tf":1.0},"92":{"tf":1.0},"95":{"tf":1.0}}}},"p":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":3,"docs":{"146":{"tf":1.4142135623730951},"16":{"tf":1.0},"4":{"tf":1.0}}}},"i":{"df":0,"docs":{},"n":{"df":0,"docs":{},"i":{"df":1,"docs":{"103":{"tf":1.0}}}}}},"r":{"a":{"df":0,"docs":{},"m":{"df":0,"docs":{},"e":{"df":0,"docs":{},"t":{"df":7,"docs":{"109":{"tf":1.7320508075688772},"128":{"tf":1.0},"68":{"tf":1.0},"73":{"tf":1.4142135623730951},"75":{"tf":1.0},"80":{"tf":1.4142135623730951},"91":{"tf":1.0}}}}}},"df":0,"docs":{},"t":{"df":22,"docs":{"114":{"tf":1.0},"117":{"tf":1.0},"123":{"tf":1.0},"13":{"tf":1.0},"14":{"tf":1.0},"146":{"tf":1.0},"147":{"tf":1.0},"149":{"tf":1.0},"151":{"tf":1.0},"16":{"tf":1.0},"28":{"tf":1.0},"32":{"tf":1.0},"35":{"tf":1.7320508075688772},"50":{"tf":1.0},"53":{"tf":1.0},"58":{"tf":1.0},"69":{"tf":1.0},"7":{"tf":1.0},"70":{"tf":1.0},"73":{"tf":1.0},"9":{"tf":1.0},"94":{"tf":1.0}},"i":{"a":{"df":0,"docs":{},"l":{"df":1,"docs":{"28":{"tf":1.7320508075688772}}}},"c":{"df":0,"docs":{},"u":{"df":0,"docs":{},"l":{"a":{"df":0,"docs":{},"r":{"df":14,"docs":{"10":{"tf":1.4142135623730951},"11":{"tf":1.0},"13":{"tf":1.4142135623730951},"134":{"tf":1.0},"146":{"tf":1.4142135623730951},"21":{"tf":1.0},"22":{"tf":1.0},"28":{"tf":1.0},"35":{"tf":1.4142135623730951},"36":{"tf":1.0},"43":{"tf":1.0},"69":{"tf":1.0},"70":{"tf":1.4142135623730951},"8":{"tf":1.4142135623730951}}}},"df":0,"docs":{}}}},"df":5,"docs":{"19":{"tf":1.4142135623730951},"4":{"tf":1.0},"7":{"tf":1.0},"73":{"tf":1.4142135623730951},"74":{"tf":1.0}}}}},"s":{"df":0,"docs":{},"s":{"df":10,"docs":{"11":{"tf":1.0},"110":{"tf":1.4142135623730951},"114":{"tf":1.4142135623730951},"117":{"tf":1.0},"144":{"tf":1.0},"33":{"tf":1.0},"40":{"tf":2.0},"50":{"tf":1.4142135623730951},"57":{"tf":1.0},"75":{"tf":1.0}}}},"t":{"df":0,"docs":{},"h":{"df":7,"docs":{"101":{"tf":1.7320508075688772},"102":{"tf":1.0},"35":{"tf":1.0},"72":{"tf":1.4142135623730951},"80":{"tf":1.4142135623730951},"92":{"tf":1.4142135623730951},"95":{"tf":1.7320508075688772}}}}},"c":{"[":{"0":{"df":1,"docs":{"147":{"tf":1.0}}},"1":{"df":1,"docs":{"147":{"tf":1.0}}},"df":0,"docs":{}},"_":{"df":0,"docs":{},"j":{"df":0,"docs":{},"n":{"df":0,"docs":{},"z":{"df":1,"docs":{"146":{"tf":1.0}}}}},"u":{"df":0,"docs":{},"p":{"d":{"a":{"df":0,"docs":{},"t":{"df":1,"docs":{"146":{"tf":1.0}}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":8,"docs":{"133":{"tf":1.0},"136":{"tf":1.7320508075688772},"141":{"tf":1.0},"142":{"tf":1.0},"144":{"tf":1.0},"146":{"tf":2.23606797749979},"147":{"tf":1.4142135623730951},"19":{"tf":1.4142135623730951}},"i":{"df":1,"docs":{"142":{"tf":1.0}},"​":{",":{"a":{"df":0,"docs":{},"p":{"df":0,"docs":{},"i":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"f":{"df":0,"docs":{},"p":{"df":0,"docs":{},"i":{"df":1,"docs":{"142":{"tf":1.0}}}}}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":14,"docs":{"101":{"tf":1.0},"147":{"tf":2.0},"21":{"tf":2.6457513110645907},"28":{"tf":1.0},"32":{"tf":1.4142135623730951},"33":{"tf":1.0},"54":{"tf":1.4142135623730951},"73":{"tf":2.6457513110645907},"75":{"tf":2.8284271247461903},"76":{"tf":2.0},"77":{"tf":1.4142135623730951},"78":{"tf":1.4142135623730951},"79":{"tf":1.7320508075688772},"80":{"tf":2.449489742783178}},"e":{"df":0,"docs":{},"r":{"df":3,"docs":{"107":{"tf":1.0},"65":{"tf":1.0},"9":{"tf":1.0}},"f":{"df":0,"docs":{},"o":{"df":0,"docs":{},"r":{"df":0,"docs":{},"m":{"df":10,"docs":{"100":{"tf":1.0},"109":{"tf":1.0},"123":{"tf":1.0},"125":{"tf":1.4142135623730951},"151":{"tf":1.0},"35":{"tf":1.0},"66":{"tf":1.0},"7":{"tf":1.0},"82":{"tf":1.0},"99":{"tf":1.0}}}}}},"m":{"df":0,"docs":{},"u":{"df":0,"docs":{},"t":{"df":6,"docs":{"101":{"tf":1.4142135623730951},"138":{"tf":1.0},"139":{"tf":1.4142135623730951},"14":{"tf":2.0},"146":{"tf":2.0},"147":{"tf":1.4142135623730951}}}}}}},"h":{"a":{"df":0,"docs":{},"n":{"df":0,"docs":{},"t":{"df":0,"docs":{},"o":{"df":0,"docs":{},"m":{"d":{"a":{"df":0,"docs":{},"t":{"a":{"<":{"df":0,"docs":{},"f":{"df":1,"docs":{"35":{"tf":1.4142135623730951}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":1,"docs":{"35":{"tf":1.4142135623730951}}}}}},"s":{"df":0,"docs":{},"e":{"df":4,"docs":{"127":{"tf":1.0},"22":{"tf":1.0},"35":{"tf":1.0},"94":{"tf":1.4142135623730951}}}}},"df":0,"docs":{}},"i":{")":{"df":0,"docs":{},"i":{"df":0,"docs":{},"​":{"=":{"0":{"df":2,"docs":{"14":{"tf":1.0},"16":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":7,"docs":{"13":{"tf":2.0},"14":{"tf":1.4142135623730951},"16":{"tf":2.449489742783178},"26":{"tf":1.4142135623730951},"32":{"tf":2.449489742783178},"36":{"tf":1.0},"80":{"tf":1.0}},"e":{"c":{"df":2,"docs":{"114":{"tf":1.0},"116":{"tf":1.0}}},"df":0,"docs":{}},"t":{"df":0,"docs":{},"f":{"a":{"df":0,"docs":{},"l":{"df":1,"docs":{"42":{"tf":1.0}}}},"df":0,"docs":{}}},"​":{":":{"=":{"df":0,"docs":{},"j":{"=":{"0":{"df":0,"docs":{},"∏":{"df":0,"docs":{},"i":{"df":0,"docs":{},"​":{"df":0,"docs":{},"z":{"df":0,"docs":{},"−":{"(":{"a":{"df":0,"docs":{},"i":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"α":{"df":0,"docs":{},"v":{"df":0,"docs":{},"i":{"df":0,"docs":{},"​":{")":{"df":0,"docs":{},"z":{"df":0,"docs":{},"−":{"(":{"a":{"df":0,"docs":{},"i":{"df":0,"docs":{},"′":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"α":{"df":0,"docs":{},"v":{"df":0,"docs":{},"i":{"df":1,"docs":{"147":{"tf":1.0}}}}}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}}}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"′":{"df":0,"docs":{},"​":{":":{"=":{"df":0,"docs":{},"j":{"=":{"0":{"df":0,"docs":{},"∏":{"df":0,"docs":{},"i":{"df":0,"docs":{},"​":{"df":0,"docs":{},"z":{"df":0,"docs":{},"′":{"df":0,"docs":{},"−":{"b":{"df":0,"docs":{},"i":{"df":0,"docs":{},"​":{"df":0,"docs":{},"z":{"df":0,"docs":{},"′":{"df":0,"docs":{},"−":{"b":{"df":0,"docs":{},"i":{"df":1,"docs":{"147":{"tf":1.0}}}},"df":0,"docs":{}}}}}}},"df":0,"docs":{}}}}}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"j":{"b":{"df":2,"docs":{"70":{"tf":1.0},"91":{"tf":1.0}}},"df":0,"docs":{}},"k":{"+":{"1":{"df":0,"docs":{},"​":{"(":{"df":0,"docs":{},"υ":{"2":{"df":0,"docs":{},"k":{"+":{"1":{"df":1,"docs":{"102":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":3,"docs":{"102":{"tf":1.4142135623730951},"94":{"tf":1.0},"95":{"tf":1.0}},"t":{"df":2,"docs":{"70":{"tf":1.0},"91":{"tf":1.0}},"​":{"(":{"df":0,"docs":{},"t":{"df":0,"docs":{},"i":{",":{"0":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"…":{",":{"df":0,"docs":{},"t":{"df":0,"docs":{},"i":{",":{"df":0,"docs":{},"m":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"t":{"df":0,"docs":{},"i":{"+":{"1":{",":{"0":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"…":{",":{"df":0,"docs":{},"t":{"df":0,"docs":{},"i":{"+":{"1":{",":{"df":0,"docs":{},"m":{"df":0,"docs":{},"​":{")":{"=":{"0":{"df":1,"docs":{"70":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"x":{"1":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"…":{",":{"df":0,"docs":{},"x":{"df":0,"docs":{},"m":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"y":{"1":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"…":{",":{"df":0,"docs":{},"y":{"df":0,"docs":{},"m":{"df":1,"docs":{"70":{"tf":1.0}}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"​":{"(":{"df":0,"docs":{},"υ":{"2":{"df":0,"docs":{},"k":{"+":{"1":{"df":1,"docs":{"102":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},":":{"=":{"df":0,"docs":{},"p":{"df":0,"docs":{},"k":{"df":0,"docs":{},"−":{"1":{"df":0,"docs":{},"o":{"d":{"d":{"df":0,"docs":{},"​":{"(":{"df":0,"docs":{},"x":{")":{"+":{"df":0,"docs":{},"ζ":{"df":0,"docs":{},"k":{"df":0,"docs":{},"−":{"1":{"df":0,"docs":{},"​":{"df":0,"docs":{},"p":{"df":0,"docs":{},"k":{"df":0,"docs":{},"−":{"1":{"df":0,"docs":{},"e":{"df":0,"docs":{},"v":{"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"df":0,"docs":{},"​":{"(":{"df":0,"docs":{},"x":{"df":1,"docs":{"94":{"tf":1.0}}}},"df":0,"docs":{}}}}}}},"df":0,"docs":{}}}}}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"]":{":":{"=":{"c":{"df":0,"docs":{},"o":{"df":0,"docs":{},"m":{"df":0,"docs":{},"m":{"df":0,"docs":{},"i":{"df":0,"docs":{},"t":{"(":{"df":0,"docs":{},"p":{"df":0,"docs":{},"k":{"df":0,"docs":{},"​":{"(":{"d":{"df":0,"docs":{},"k":{"df":1,"docs":{"94":{"tf":1.0}}}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}}}}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"−":{"1":{"df":1,"docs":{"94":{"tf":1.0}},"​":{"=":{"df":0,"docs":{},"p":{"df":0,"docs":{},"k":{"df":0,"docs":{},"−":{"1":{"df":0,"docs":{},"o":{"d":{"d":{"df":0,"docs":{},"​":{"(":{"df":0,"docs":{},"x":{"2":{")":{"+":{"df":0,"docs":{},"x":{"df":0,"docs":{},"p":{"df":0,"docs":{},"k":{"df":0,"docs":{},"−":{"1":{"df":0,"docs":{},"e":{"df":0,"docs":{},"v":{"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"df":0,"docs":{},"​":{"(":{"df":0,"docs":{},"x":{"2":{"df":1,"docs":{"94":{"tf":1.0}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}}}},"df":0,"docs":{}}}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"l":{"a":{"c":{"df":0,"docs":{},"e":{"df":3,"docs":{"123":{"tf":1.4142135623730951},"146":{"tf":1.0},"151":{"tf":1.0}}}},"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"df":14,"docs":{"127":{"tf":2.0},"128":{"tf":1.0},"129":{"tf":1.0},"130":{"tf":1.0},"131":{"tf":1.0},"132":{"tf":1.0},"133":{"tf":1.0},"134":{"tf":1.0},"135":{"tf":1.0},"136":{"tf":1.0},"137":{"tf":1.0},"138":{"tf":1.0},"139":{"tf":1.0},"149":{"tf":1.0}}}},"y":{"df":1,"docs":{"16":{"tf":1.0}}}},"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"df":0,"docs":{},"t":{"df":0,"docs":{},"i":{"df":1,"docs":{"130":{"tf":1.0}}}}}},"o":{"df":0,"docs":{},"n":{"df":0,"docs":{},"k":{"df":42,"docs":{"10":{"tf":1.0},"11":{"tf":1.0},"12":{"tf":1.7320508075688772},"13":{"tf":1.0},"14":{"tf":1.7320508075688772},"15":{"tf":1.0},"16":{"tf":1.0},"17":{"tf":1.0},"18":{"tf":1.0},"19":{"tf":1.4142135623730951},"20":{"tf":1.0},"21":{"tf":1.7320508075688772},"22":{"tf":1.0},"23":{"tf":1.0},"24":{"tf":1.0},"25":{"tf":1.0},"26":{"tf":1.0},"27":{"tf":1.0},"28":{"tf":1.0},"29":{"tf":1.0},"30":{"tf":1.0},"31":{"tf":1.0},"32":{"tf":1.0},"33":{"tf":1.0},"34":{"tf":1.7320508075688772},"35":{"tf":1.0},"36":{"tf":1.0},"37":{"tf":1.0},"38":{"tf":1.0},"39":{"tf":1.0},"4":{"tf":2.23606797749979},"40":{"tf":1.0},"41":{"tf":1.0},"42":{"tf":1.0},"43":{"tf":1.0},"44":{"tf":1.4142135623730951},"45":{"tf":1.0},"5":{"tf":1.0},"6":{"tf":1.0},"7":{"tf":2.0},"8":{"tf":1.7320508075688772},"9":{"tf":1.0}}}}},"u":{"df":0,"docs":{},"g":{"df":1,"docs":{"70":{"tf":1.0}}}}},"n":{"c":{"df":0,"docs":{},"​":{"]":{"1":{"df":1,"docs":{"32":{"tf":1.4142135623730951}},"​":{"=":{"[":{"df":0,"docs":{},"p":{"df":0,"docs":{},"n":{"c":{"1":{"df":0,"docs":{},"​":{"]":{"1":{"df":0,"docs":{},"​":{"+":{"[":{"df":0,"docs":{},"p":{"df":0,"docs":{},"n":{"c":{"2":{"df":0,"docs":{},"​":{"]":{"1":{"df":0,"docs":{},"​":{"+":{"[":{"df":0,"docs":{},"p":{"df":0,"docs":{},"n":{"c":{"3":{"df":0,"docs":{},"​":{"]":{"1":{"df":1,"docs":{"32":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{},"t":{"df":0,"docs":{},"p":{"a":{"df":0,"docs":{},"r":{"df":0,"docs":{},"t":{"df":0,"docs":{},"i":{"a":{"df":0,"docs":{},"l":{"df":0,"docs":{},"​":{"df":0,"docs":{},"​":{"=":{"df":0,"docs":{},"p":{"df":0,"docs":{},"n":{"c":{"1":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"α":{"df":0,"docs":{},"p":{"df":0,"docs":{},"n":{"c":{"2":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"α":{"2":{"df":0,"docs":{},"p":{"df":0,"docs":{},"n":{"c":{"3":{"df":0,"docs":{},"​":{"=":{"df":0,"docs":{},"t":{"df":0,"docs":{},"l":{"df":0,"docs":{},"o":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"ζ":{"df":0,"docs":{},"n":{"+":{"2":{"df":0,"docs":{},"t":{"df":0,"docs":{},"m":{"df":0,"docs":{},"i":{"d":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"ζ":{"2":{"(":{"df":0,"docs":{},"n":{"+":{"2":{")":{"df":0,"docs":{},"t":{"df":0,"docs":{},"h":{"df":0,"docs":{},"i":{"df":1,"docs":{"28":{"tf":1.0}}}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}}}},"df":0,"docs":{}}}}},"df":0,"docs":{}}}}},"df":1,"docs":{"94":{"tf":1.4142135623730951}},"​":{"df":0,"docs":{},"∈":{"df":0,"docs":{},"f":{"df":1,"docs":{"94":{"tf":1.0}}}}},"−":{"1":{"df":1,"docs":{"146":{"tf":1.0}},"​":{"=":{"1":{"df":1,"docs":{"146":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"o":{"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"df":0,"docs":{},"t":{"df":23,"docs":{"101":{"tf":1.0},"112":{"tf":1.4142135623730951},"114":{"tf":2.8284271247461903},"115":{"tf":1.0},"117":{"tf":1.0},"123":{"tf":1.7320508075688772},"133":{"tf":1.0},"142":{"tf":1.0},"149":{"tf":1.0},"19":{"tf":1.0},"3":{"tf":1.0},"40":{"tf":1.0},"49":{"tf":1.0},"58":{"tf":1.4142135623730951},"59":{"tf":1.0},"60":{"tf":1.7320508075688772},"62":{"tf":1.4142135623730951},"68":{"tf":1.7320508075688772},"69":{"tf":1.4142135623730951},"80":{"tf":1.0},"85":{"tf":1.0},"88":{"tf":1.0},"95":{"tf":1.0}},"e":{"df":0,"docs":{},"r":{"df":3,"docs":{"142":{"tf":1.4142135623730951},"146":{"tf":2.449489742783178},"147":{"tf":1.4142135623730951}}}},"l":{"df":0,"docs":{},"e":{"df":0,"docs":{},"s":{"df":0,"docs":{},"s":{"df":1,"docs":{"7":{"tf":1.0}}}}}}}}},"l":{"df":0,"docs":{},"i":{"df":1,"docs":{"119":{"tf":1.0}}},"y":{"df":0,"docs":{},"l":{"df":0,"docs":{},"o":{"df":0,"docs":{},"g":{"a":{"df":0,"docs":{},"r":{"df":0,"docs":{},"i":{"df":0,"docs":{},"t":{"df":0,"docs":{},"h":{"df":0,"docs":{},"m":{"df":1,"docs":{"81":{"tf":1.0}}}}}}}},"df":0,"docs":{}}}},"n":{"df":0,"docs":{},"o":{"df":0,"docs":{},"m":{"df":0,"docs":{},"i":{"a":{"df":0,"docs":{},"l":{"'":{"df":3,"docs":{"19":{"tf":1.0},"3":{"tf":1.0},"68":{"tf":1.0}}},"<":{"df":0,"docs":{},"f":{"df":0,"docs":{},"i":{"df":0,"docs":{},"e":{"df":0,"docs":{},"l":{"d":{"df":0,"docs":{},"e":{"df":0,"docs":{},"l":{"df":0,"docs":{},"e":{"df":0,"docs":{},"m":{"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"df":0,"docs":{},"t":{"<":{"df":0,"docs":{},"f":{"df":1,"docs":{"35":{"tf":2.8284271247461903}}}},"df":0,"docs":{}}}}}}}}},"df":0,"docs":{}}}}}},"df":0,"docs":{}}},"df":70,"docs":{"100":{"tf":1.4142135623730951},"101":{"tf":1.0},"102":{"tf":1.0},"112":{"tf":2.23606797749979},"113":{"tf":1.7320508075688772},"114":{"tf":3.4641016151377544},"116":{"tf":1.0},"117":{"tf":1.4142135623730951},"119":{"tf":1.7320508075688772},"121":{"tf":1.0},"123":{"tf":3.4641016151377544},"124":{"tf":1.0},"125":{"tf":2.6457513110645907},"14":{"tf":5.916079783099616},"149":{"tf":1.0},"15":{"tf":1.0},"16":{"tf":3.0},"19":{"tf":3.3166247903554},"20":{"tf":3.1622776601683795},"21":{"tf":2.0},"23":{"tf":1.0},"24":{"tf":1.4142135623730951},"25":{"tf":1.4142135623730951},"26":{"tf":1.0},"28":{"tf":1.4142135623730951},"3":{"tf":2.449489742783178},"32":{"tf":1.0},"35":{"tf":3.605551275463989},"4":{"tf":1.4142135623730951},"42":{"tf":1.0},"48":{"tf":2.0},"5":{"tf":2.6457513110645907},"52":{"tf":2.449489742783178},"53":{"tf":2.449489742783178},"54":{"tf":2.6457513110645907},"55":{"tf":2.23606797749979},"56":{"tf":1.7320508075688772},"57":{"tf":1.7320508075688772},"58":{"tf":1.0},"59":{"tf":3.1622776601683795},"60":{"tf":1.0},"62":{"tf":2.23606797749979},"65":{"tf":2.23606797749979},"66":{"tf":2.0},"67":{"tf":1.4142135623730951},"68":{"tf":1.7320508075688772},"69":{"tf":2.8284271247461903},"70":{"tf":1.7320508075688772},"71":{"tf":2.449489742783178},"73":{"tf":2.6457513110645907},"74":{"tf":1.7320508075688772},"75":{"tf":3.1622776601683795},"76":{"tf":1.0},"77":{"tf":1.0},"78":{"tf":1.4142135623730951},"79":{"tf":1.4142135623730951},"8":{"tf":1.4142135623730951},"80":{"tf":3.7416573867739413},"81":{"tf":1.0},"82":{"tf":1.0},"83":{"tf":1.0},"84":{"tf":4.242640687119285},"85":{"tf":1.4142135623730951},"86":{"tf":1.0},"90":{"tf":1.4142135623730951},"91":{"tf":1.4142135623730951},"94":{"tf":2.449489742783178},"95":{"tf":2.0},"97":{"tf":1.0},"99":{"tf":1.0}}}}}}}},"o":{"df":0,"docs":{},"l":{"df":5,"docs":{"130":{"tf":1.4142135623730951},"133":{"tf":1.4142135623730951},"134":{"tf":1.7320508075688772},"135":{"tf":1.4142135623730951},"138":{"tf":1.4142135623730951}}}},"p":{"df":0,"docs":{},"u":{"df":0,"docs":{},"l":{"a":{"df":0,"docs":{},"r":{"df":2,"docs":{"19":{"tf":1.0},"4":{"tf":1.0}}}},"df":0,"docs":{}}}},"s":{"df":0,"docs":{},"e":{"df":0,"docs":{},"i":{"d":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"df":1,"docs":{"151":{"tf":1.0}}}}},"df":0,"docs":{}}},"i":{"df":0,"docs":{},"t":{"df":4,"docs":{"101":{"tf":1.4142135623730951},"14":{"tf":1.4142135623730951},"151":{"tf":1.7320508075688772},"72":{"tf":1.0}}}},"s":{"df":0,"docs":{},"i":{"b":{"df":0,"docs":{},"l":{"df":6,"docs":{"114":{"tf":1.0},"149":{"tf":1.4142135623730951},"36":{"tf":1.0},"38":{"tf":1.0},"50":{"tf":1.0},"71":{"tf":1.0}}}},"df":0,"docs":{}}}},"w":{"(":{"df":0,"docs":{},"s":{"df":0,"docs":{},"y":{"df":0,"docs":{},"s":{"df":0,"docs":{},"t":{"df":0,"docs":{},"e":{"df":0,"docs":{},"m":{"df":1,"docs":{"45":{"tf":1.0}}}}}}}},"x":{"df":1,"docs":{"45":{"tf":1.0}}}},"df":1,"docs":{"45":{"tf":1.0}},"e":{"df":0,"docs":{},"r":{"df":15,"docs":{"121":{"tf":1.0},"124":{"tf":1.4142135623730951},"125":{"tf":1.0},"142":{"tf":1.0},"146":{"tf":1.0},"149":{"tf":1.0},"28":{"tf":1.4142135623730951},"36":{"tf":1.0},"38":{"tf":1.4142135623730951},"52":{"tf":1.0},"68":{"tf":1.7320508075688772},"73":{"tf":1.0},"75":{"tf":1.0},"95":{"tf":1.0},"97":{"tf":1.0}}}}}},"r":{"a":{"c":{"df":0,"docs":{},"t":{"df":0,"docs":{},"i":{"c":{"df":1,"docs":{"149":{"tf":1.0}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{},"e":{"c":{"df":0,"docs":{},"e":{"d":{"df":4,"docs":{"49":{"tf":1.0},"52":{"tf":1.0},"70":{"tf":1.0},"84":{"tf":1.0}}},"df":0,"docs":{}},"i":{"df":0,"docs":{},"s":{"df":6,"docs":{"14":{"tf":1.0},"22":{"tf":1.0},"34":{"tf":1.0},"73":{"tf":1.4142135623730951},"74":{"tf":1.0},"87":{"tf":1.0}}}},"o":{"df":0,"docs":{},"m":{"df":0,"docs":{},"p":{"df":0,"docs":{},"u":{"df":0,"docs":{},"t":{"df":2,"docs":{"34":{"tf":1.0},"56":{"tf":1.0}}}}}}}},"d":{"df":0,"docs":{},"e":{"df":0,"docs":{},"f":{"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"df":1,"docs":{"70":{"tf":1.0}}}}}},"i":{"c":{"df":0,"docs":{},"t":{"df":1,"docs":{"118":{"tf":1.0}}}},"df":0,"docs":{}}},"df":0,"docs":{},"f":{"df":0,"docs":{},"i":{"df":0,"docs":{},"x":{"df":1,"docs":{"88":{"tf":1.0}}}}},"i":{"df":0,"docs":{},"m":{"a":{"df":0,"docs":{},"g":{"df":1,"docs":{"7":{"tf":1.0}}}},"df":0,"docs":{}}},"p":{"df":0,"docs":{},"r":{"df":0,"docs":{},"o":{"c":{"df":0,"docs":{},"e":{"df":0,"docs":{},"s":{"df":0,"docs":{},"s":{"df":6,"docs":{"15":{"tf":1.7320508075688772},"23":{"tf":1.0},"35":{"tf":1.7320508075688772},"42":{"tf":1.0},"70":{"tf":1.0},"83":{"tf":1.0}}}}}},"df":0,"docs":{}}}},"t":{"df":0,"docs":{},"t":{"df":0,"docs":{},"i":{"df":5,"docs":{"113":{"tf":1.0},"132":{"tf":1.0},"14":{"tf":1.0},"151":{"tf":1.0},"37":{"tf":1.0}}}}},"v":{"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"df":0,"docs":{},"t":{"df":2,"docs":{"42":{"tf":1.0},"71":{"tf":1.0}}}}},"i":{"df":0,"docs":{},"o":{"df":0,"docs":{},"u":{"df":10,"docs":{"107":{"tf":1.7320508075688772},"109":{"tf":1.0},"11":{"tf":1.0},"14":{"tf":2.6457513110645907},"145":{"tf":1.0},"149":{"tf":1.4142135623730951},"45":{"tf":1.0},"70":{"tf":1.0},"84":{"tf":1.0},"86":{"tf":1.0}}}}}}},"i":{"df":0,"docs":{},"m":{"a":{"df":0,"docs":{},"r":{"df":0,"docs":{},"i":{"df":1,"docs":{"70":{"tf":1.0}}}}},"df":0,"docs":{},"e":{"df":2,"docs":{"14":{"tf":1.0},"41":{"tf":1.0}}},"i":{"df":0,"docs":{},"t":{"df":17,"docs":{"114":{"tf":1.0},"124":{"tf":3.872983346207417},"125":{"tf":1.7320508075688772},"14":{"tf":2.23606797749979},"16":{"tf":1.0},"23":{"tf":1.0},"34":{"tf":1.0},"35":{"tf":1.0},"36":{"tf":1.0},"38":{"tf":1.0},"5":{"tf":1.0},"52":{"tf":1.4142135623730951},"68":{"tf":1.4142135623730951},"71":{"tf":1.0},"73":{"tf":1.0},"75":{"tf":1.0},"91":{"tf":1.0}}}}},"o":{"df":0,"docs":{},"r":{"df":1,"docs":{"91":{"tf":1.0}},"i":{"df":1,"docs":{"101":{"tf":1.0}}}}},"v":{"a":{"df":0,"docs":{},"t":{"df":5,"docs":{"34":{"tf":1.0},"35":{"tf":1.7320508075688772},"44":{"tf":1.7320508075688772},"69":{"tf":1.0},"7":{"tf":2.0}}}},"df":0,"docs":{}}},"o":{"b":{"a":{"b":{"df":0,"docs":{},"l":{"df":5,"docs":{"111":{"tf":1.0},"14":{"tf":2.6457513110645907},"19":{"tf":1.0},"41":{"tf":1.0},"85":{"tf":1.0}}}},"df":0,"docs":{}},"df":0,"docs":{},"l":{"df":0,"docs":{},"e":{"df":0,"docs":{},"m":{"df":4,"docs":{"125":{"tf":1.4142135623730951},"149":{"tf":1.0},"21":{"tf":1.0},"50":{"tf":1.0}}}}}},"c":{"df":0,"docs":{},"e":{"df":2,"docs":{"33":{"tf":1.0},"80":{"tf":1.0}},"s":{"df":0,"docs":{},"s":{"df":9,"docs":{"14":{"tf":1.0},"151":{"tf":1.0},"20":{"tf":1.4142135623730951},"4":{"tf":1.0},"40":{"tf":1.0},"69":{"tf":1.0},"77":{"tf":1.0},"8":{"tf":1.0},"94":{"tf":1.0}}}}}},"d":{"df":0,"docs":{},"u":{"c":{"df":5,"docs":{"141":{"tf":1.0},"19":{"tf":1.4142135623730951},"20":{"tf":1.0},"75":{"tf":1.0},"8":{"tf":1.0}},"t":{"df":6,"docs":{"123":{"tf":1.4142135623730951},"138":{"tf":1.7320508075688772},"139":{"tf":1.7320508075688772},"14":{"tf":1.4142135623730951},"144":{"tf":1.0},"146":{"tf":2.449489742783178}}}},"df":0,"docs":{}}},"df":0,"docs":{},"g":{"df":0,"docs":{},"r":{"a":{"df":0,"docs":{},"m":{"df":25,"docs":{"10":{"tf":1.0},"12":{"tf":1.4142135623730951},"127":{"tf":1.0},"13":{"tf":2.23606797749979},"133":{"tf":1.4142135623730951},"14":{"tf":1.0},"141":{"tf":1.0},"142":{"tf":1.0},"144":{"tf":1.7320508075688772},"146":{"tf":2.0},"147":{"tf":1.0},"16":{"tf":1.0},"23":{"tf":1.0},"34":{"tf":1.4142135623730951},"35":{"tf":1.4142135623730951},"4":{"tf":1.0},"43":{"tf":1.0},"44":{"tf":1.0},"45":{"tf":1.4142135623730951},"50":{"tf":2.0},"69":{"tf":1.0},"7":{"tf":3.605551275463989},"8":{"tf":1.4142135623730951},"9":{"tf":1.7320508075688772},"91":{"tf":1.4142135623730951}}}},"df":0,"docs":{},"e":{"df":0,"docs":{},"s":{"df":0,"docs":{},"s":{"df":1,"docs":{"0":{"tf":1.0}}}}}}},"o":{"df":0,"docs":{},"f":{"<":{"df":0,"docs":{},"f":{"df":1,"docs":{"35":{"tf":1.0}}}},"_":{"df":0,"docs":{},"o":{"df":0,"docs":{},"p":{"df":0,"docs":{},"t":{"df":2,"docs":{"104":{"tf":1.4142135623730951},"110":{"tf":1.4142135623730951}},"i":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"df":0,"docs":{},"s":{")":{".":{"df":0,"docs":{},"u":{"df":0,"docs":{},"n":{"df":0,"docs":{},"w":{"df":0,"docs":{},"r":{"a":{"df":0,"docs":{},"p":{"df":1,"docs":{"104":{"tf":1.0}}}},"df":0,"docs":{}}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}}}}}}},"df":39,"docs":{"102":{"tf":1.7320508075688772},"104":{"tf":3.0},"109":{"tf":1.4142135623730951},"110":{"tf":2.0},"112":{"tf":1.0},"113":{"tf":1.0},"119":{"tf":2.23606797749979},"13":{"tf":1.0},"14":{"tf":1.0},"144":{"tf":1.0},"146":{"tf":1.7320508075688772},"16":{"tf":1.0},"19":{"tf":1.4142135623730951},"21":{"tf":2.449489742783178},"22":{"tf":1.0},"28":{"tf":1.4142135623730951},"29":{"tf":1.7320508075688772},"32":{"tf":1.0},"33":{"tf":1.7320508075688772},"34":{"tf":1.4142135623730951},"35":{"tf":2.8284271247461903},"4":{"tf":1.0},"42":{"tf":1.0},"44":{"tf":1.7320508075688772},"50":{"tf":2.0},"57":{"tf":1.0},"59":{"tf":1.0},"60":{"tf":1.0},"61":{"tf":1.0},"62":{"tf":1.0},"63":{"tf":1.0},"7":{"tf":2.0},"77":{"tf":1.0},"8":{"tf":1.0},"80":{"tf":1.4142135623730951},"88":{"tf":1.0},"91":{"tf":1.0},"94":{"tf":1.4142135623730951},"95":{"tf":1.4142135623730951}},"o":{"df":0,"docs":{},"p":{"df":0,"docs":{},"t":{"df":1,"docs":{"109":{"tf":1.4142135623730951}},"i":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"df":0,"docs":{},"s":{":":{":":{"d":{"df":0,"docs":{},"e":{"df":0,"docs":{},"f":{"a":{"df":0,"docs":{},"u":{"df":0,"docs":{},"l":{"df":0,"docs":{},"t":{"_":{"df":0,"docs":{},"t":{"df":0,"docs":{},"e":{"df":0,"docs":{},"s":{"df":0,"docs":{},"t":{"_":{"df":0,"docs":{},"o":{"df":0,"docs":{},"p":{"df":0,"docs":{},"t":{"df":1,"docs":{"104":{"tf":1.0}}}}}},"df":0,"docs":{}}}}}},"df":0,"docs":{}}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}}}}}}}},"p":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":0,"docs":{},"l":{"df":0,"docs":{},"i":{"df":2,"docs":{"146":{"tf":1.0},"85":{"tf":1.0}}}},"t":{"df":0,"docs":{},"i":{"df":8,"docs":{"14":{"tf":1.0},"146":{"tf":1.0},"19":{"tf":1.4142135623730951},"20":{"tf":1.0},"52":{"tf":1.0},"70":{"tf":2.0},"73":{"tf":1.0},"84":{"tf":1.0}}}}}},"o":{"df":0,"docs":{},"r":{"df":0,"docs":{},"t":{"df":1,"docs":{"73":{"tf":1.0}}}}}},"t":{"df":0,"docs":{},"o":{"c":{"df":0,"docs":{},"o":{"df":0,"docs":{},"l":{"df":64,"docs":{"100":{"tf":1.0},"101":{"tf":1.7320508075688772},"102":{"tf":1.4142135623730951},"109":{"tf":1.7320508075688772},"118":{"tf":1.4142135623730951},"123":{"tf":1.0},"14":{"tf":1.0},"142":{"tf":1.0},"16":{"tf":1.7320508075688772},"17":{"tf":1.7320508075688772},"18":{"tf":1.0},"19":{"tf":1.0},"20":{"tf":1.7320508075688772},"21":{"tf":1.0},"22":{"tf":1.0},"23":{"tf":1.0},"24":{"tf":1.0},"25":{"tf":1.0},"26":{"tf":1.0},"27":{"tf":1.0},"28":{"tf":1.0},"29":{"tf":1.0},"30":{"tf":1.0},"31":{"tf":1.0},"32":{"tf":1.0},"33":{"tf":1.0},"34":{"tf":1.0},"37":{"tf":1.0},"4":{"tf":1.0},"57":{"tf":1.4142135623730951},"58":{"tf":1.0},"59":{"tf":1.0},"68":{"tf":1.0},"69":{"tf":2.449489742783178},"70":{"tf":1.7320508075688772},"71":{"tf":1.7320508075688772},"72":{"tf":1.0},"73":{"tf":2.6457513110645907},"74":{"tf":1.4142135623730951},"75":{"tf":2.449489742783178},"76":{"tf":1.0},"77":{"tf":1.7320508075688772},"78":{"tf":1.4142135623730951},"79":{"tf":1.0},"80":{"tf":2.0},"81":{"tf":1.0},"82":{"tf":2.0},"83":{"tf":1.4142135623730951},"84":{"tf":1.7320508075688772},"85":{"tf":1.4142135623730951},"86":{"tf":2.0},"87":{"tf":2.0},"88":{"tf":1.7320508075688772},"89":{"tf":1.4142135623730951},"90":{"tf":1.0},"91":{"tf":1.0},"92":{"tf":1.4142135623730951},"93":{"tf":1.7320508075688772},"94":{"tf":1.4142135623730951},"95":{"tf":1.0},"96":{"tf":1.0},"97":{"tf":1.0},"98":{"tf":1.0},"99":{"tf":1.0}}}}},"df":0,"docs":{}}},"v":{"a":{"b":{"df":0,"docs":{},"l":{"df":1,"docs":{"149":{"tf":1.4142135623730951}}}},"df":0,"docs":{}},"df":0,"docs":{},"e":{"(":{"&":{"df":0,"docs":{},"t":{"df":0,"docs":{},"r":{"a":{"c":{"df":0,"docs":{},"e":{"_":{"df":0,"docs":{},"t":{"df":1,"docs":{"110":{"tf":1.0}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}},":":{":":{"<":{"df":0,"docs":{},"f":{"df":1,"docs":{"104":{"tf":1.0}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":34,"docs":{"102":{"tf":1.0},"103":{"tf":1.0},"104":{"tf":1.4142135623730951},"105":{"tf":1.0},"108":{"tf":1.0},"109":{"tf":1.0},"110":{"tf":2.0},"111":{"tf":1.0},"113":{"tf":1.0},"114":{"tf":1.4142135623730951},"118":{"tf":1.0},"138":{"tf":1.0},"14":{"tf":1.0},"144":{"tf":1.0},"146":{"tf":1.7320508075688772},"151":{"tf":1.7320508075688772},"16":{"tf":1.0},"19":{"tf":1.4142135623730951},"23":{"tf":1.7320508075688772},"34":{"tf":1.0},"35":{"tf":1.0},"4":{"tf":1.0},"42":{"tf":1.0},"43":{"tf":1.0},"44":{"tf":1.0},"46":{"tf":1.0},"48":{"tf":1.0},"49":{"tf":1.0},"50":{"tf":1.0},"70":{"tf":1.0},"73":{"tf":1.4142135623730951},"74":{"tf":1.0},"84":{"tf":1.0},"85":{"tf":1.0}},"r":{"'":{"df":2,"docs":{"35":{"tf":1.0},"7":{"tf":1.0}}},".":{"df":0,"docs":{},"p":{"df":0,"docs":{},"r":{"df":0,"docs":{},"o":{"df":0,"docs":{},"v":{"df":1,"docs":{"35":{"tf":1.4142135623730951}},"e":{"(":{"&":{"df":0,"docs":{},"w":{"df":0,"docs":{},"i":{"df":0,"docs":{},"t":{"df":1,"docs":{"44":{"tf":1.0}}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}}}}},":":{":":{"df":0,"docs":{},"n":{"df":0,"docs":{},"e":{"df":0,"docs":{},"w":{"(":{"df":0,"docs":{},"k":{"df":0,"docs":{},"z":{"df":0,"docs":{},"g":{".":{"c":{"df":0,"docs":{},"l":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"df":2,"docs":{"35":{"tf":1.4142135623730951},"44":{"tf":1.0}}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"<":{"df":0,"docs":{},"f":{"df":1,"docs":{"35":{"tf":1.0}}}},"df":71,"docs":{"101":{"tf":1.0},"102":{"tf":1.4142135623730951},"103":{"tf":1.4142135623730951},"104":{"tf":1.4142135623730951},"111":{"tf":1.4142135623730951},"112":{"tf":1.4142135623730951},"113":{"tf":2.0},"117":{"tf":1.4142135623730951},"118":{"tf":2.8284271247461903},"126":{"tf":2.0},"127":{"tf":2.0},"128":{"tf":1.0},"129":{"tf":1.0},"13":{"tf":1.4142135623730951},"130":{"tf":1.0},"131":{"tf":1.4142135623730951},"132":{"tf":1.0},"133":{"tf":1.0},"134":{"tf":1.0},"135":{"tf":1.0},"136":{"tf":1.0},"137":{"tf":1.0},"138":{"tf":1.0},"139":{"tf":1.0},"144":{"tf":1.4142135623730951},"146":{"tf":1.7320508075688772},"147":{"tf":1.4142135623730951},"151":{"tf":1.4142135623730951},"20":{"tf":2.0},"21":{"tf":2.449489742783178},"23":{"tf":1.0},"31":{"tf":1.0},"32":{"tf":1.0},"34":{"tf":1.0},"35":{"tf":2.23606797749979},"44":{"tf":1.4142135623730951},"45":{"tf":1.0},"46":{"tf":1.7320508075688772},"50":{"tf":1.4142135623730951},"51":{"tf":1.0},"53":{"tf":1.0},"56":{"tf":1.0},"57":{"tf":1.0},"58":{"tf":2.0},"59":{"tf":2.0},"60":{"tf":2.0},"61":{"tf":1.0},"62":{"tf":1.4142135623730951},"63":{"tf":2.0},"66":{"tf":1.4142135623730951},"69":{"tf":1.7320508075688772},"7":{"tf":1.4142135623730951},"71":{"tf":1.0},"72":{"tf":1.7320508075688772},"73":{"tf":1.4142135623730951},"74":{"tf":1.0},"75":{"tf":2.449489742783178},"77":{"tf":2.23606797749979},"78":{"tf":1.0},"79":{"tf":2.0},"80":{"tf":1.7320508075688772},"82":{"tf":1.0},"83":{"tf":2.23606797749979},"84":{"tf":2.0},"85":{"tf":1.0},"86":{"tf":1.0},"88":{"tf":1.0},"91":{"tf":2.23606797749979},"94":{"tf":1.4142135623730951},"95":{"tf":1.0},"97":{"tf":1.0}}}},"i":{"d":{"df":19,"docs":{"103":{"tf":1.0},"109":{"tf":1.0},"112":{"tf":2.0},"113":{"tf":1.7320508075688772},"117":{"tf":1.0},"118":{"tf":1.0},"125":{"tf":1.0},"34":{"tf":1.0},"48":{"tf":1.0},"50":{"tf":1.4142135623730951},"57":{"tf":1.0},"58":{"tf":1.0},"59":{"tf":1.7320508075688772},"60":{"tf":1.4142135623730951},"62":{"tf":2.0},"63":{"tf":1.7320508075688772},"66":{"tf":1.4142135623730951},"68":{"tf":1.0},"80":{"tf":1.0}}},"df":0,"docs":{}}}}},"u":{"b":{"_":{"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"df":0,"docs":{},"p":{"df":0,"docs":{},"u":{"df":0,"docs":{},"t":{"df":2,"docs":{"104":{"tf":1.7320508075688772},"110":{"tf":1.4142135623730951}}}}}}}},"df":4,"docs":{"133":{"tf":1.4142135623730951},"135":{"tf":1.4142135623730951},"35":{"tf":6.928203230275509},"45":{"tf":1.0}},"l":{"c":{"df":1,"docs":{"135":{"tf":1.0}}},"df":0,"docs":{},"i":{"c":{"_":{"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"df":0,"docs":{},"p":{"df":0,"docs":{},"u":{"df":0,"docs":{},"t":{"df":2,"docs":{"35":{"tf":2.449489742783178},"44":{"tf":1.7320508075688772}}}}}}}},"df":26,"docs":{"104":{"tf":2.0},"106":{"tf":1.4142135623730951},"110":{"tf":1.0},"13":{"tf":2.23606797749979},"133":{"tf":2.23606797749979},"135":{"tf":1.4142135623730951},"14":{"tf":1.4142135623730951},"142":{"tf":1.0},"144":{"tf":1.4142135623730951},"146":{"tf":2.8284271247461903},"147":{"tf":1.0},"151":{"tf":1.0},"16":{"tf":1.7320508075688772},"23":{"tf":1.4142135623730951},"26":{"tf":1.0},"32":{"tf":1.4142135623730951},"34":{"tf":1.4142135623730951},"35":{"tf":1.4142135623730951},"36":{"tf":1.0},"42":{"tf":1.0},"44":{"tf":2.0},"58":{"tf":1.0},"69":{"tf":1.0},"7":{"tf":1.0},"94":{"tf":1.0},"95":{"tf":1.0}},"l":{"df":0,"docs":{},"i":{"df":2,"docs":{"144":{"tf":1.0},"19":{"tf":1.0}}}}},"df":0,"docs":{},"s":{"df":0,"docs":{},"h":{"df":1,"docs":{"22":{"tf":1.0}}}}}}},"df":0,"docs":{},"r":{"df":0,"docs":{},"p":{"df":0,"docs":{},"o":{"df":0,"docs":{},"s":{"df":4,"docs":{"149":{"tf":1.0},"34":{"tf":1.0},"50":{"tf":1.4142135623730951},"56":{"tf":1.0}}}}}},"t":{"df":7,"docs":{"10":{"tf":1.0},"13":{"tf":1.0},"14":{"tf":1.4142135623730951},"147":{"tf":1.0},"151":{"tf":1.0},"45":{"tf":1.0},"9":{"tf":1.4142135623730951}}}},"ˉ":{"df":0,"docs":{},"​":{"c":{"df":0,"docs":{},"​":{":":{"=":{"df":0,"docs":{},"p":{"df":0,"docs":{},"i":{"(":{"df":0,"docs":{},"ζ":{")":{"+":{"df":0,"docs":{},"α":{"df":0,"docs":{},"z":{"df":0,"docs":{},"ˉ":{"df":0,"docs":{},"ω":{"df":0,"docs":{},"​":{"(":{"c":{"df":0,"docs":{},"ˉ":{"+":{"df":0,"docs":{},"γ":{")":{"(":{"a":{"df":0,"docs":{},"ˉ":{"+":{"df":0,"docs":{},"β":{"df":0,"docs":{},"s":{"df":0,"docs":{},"ˉ":{"df":0,"docs":{},"σ":{"1":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"γ":{")":{"(":{"b":{"df":0,"docs":{},"ˉ":{"+":{"df":0,"docs":{},"β":{"df":0,"docs":{},"s":{"df":0,"docs":{},"ˉ":{"df":0,"docs":{},"σ":{"2":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"γ":{")":{"df":0,"docs":{},"−":{"df":0,"docs":{},"α":{"2":{"df":0,"docs":{},"l":{"1":{"df":1,"docs":{"32":{"tf":1.0}}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{},"n":{"c":{"df":1,"docs":{"32":{"tf":1.0}},"​":{":":{"=":{"df":0,"docs":{},"p":{"df":0,"docs":{},"n":{"c":{"df":1,"docs":{"28":{"tf":1.0}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"∈":{"df":0,"docs":{},"f":{"[":{"df":0,"docs":{},"x":{"df":1,"docs":{"90":{"tf":1.0}}}},"df":0,"docs":{}}},"−":{"df":0,"docs":{},"y":{"=":{"(":{"df":0,"docs":{},"x":{"df":0,"docs":{},"−":{"df":0,"docs":{},"z":{")":{"df":0,"docs":{},"q":{"df":1,"docs":{"78":{"tf":1.0}}}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"q":{"(":{"d":{"1":{"df":0,"docs":{},"​":{")":{",":{"df":0,"docs":{},"…":{",":{"df":0,"docs":{},"q":{"(":{"d":{"df":0,"docs":{},"m":{"df":1,"docs":{"78":{"tf":1.0}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},",":{"df":0,"docs":{},"v":{",":{"df":0,"docs":{},"t":{",":{"df":0,"docs":{},"p":{"df":0,"docs":{},"i":{"df":1,"docs":{"36":{"tf":1.0}}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"c":{"_":{"1":{"df":1,"docs":{"35":{"tf":1.0}}},"df":0,"docs":{}},"df":4,"docs":{"10":{"tf":2.23606797749979},"12":{"tf":1.0},"13":{"tf":2.0},"35":{"tf":1.0}}},"df":15,"docs":{"10":{"tf":1.7320508075688772},"11":{"tf":1.0},"12":{"tf":1.0},"13":{"tf":1.7320508075688772},"14":{"tf":1.0},"16":{"tf":1.4142135623730951},"34":{"tf":1.4142135623730951},"36":{"tf":1.0},"74":{"tf":1.0},"78":{"tf":1.4142135623730951},"8":{"tf":1.0},"80":{"tf":1.4142135623730951},"9":{"tf":1.0},"90":{"tf":1.0},"91":{"tf":1.0}},"k":{"df":0,"docs":{},"t":{"df":0,"docs":{},"​":{"(":{"df":0,"docs":{},"x":{")":{"=":{"df":0,"docs":{},"p":{"df":0,"docs":{},"k":{"df":0,"docs":{},"t":{"df":0,"docs":{},"​":{"(":{"df":0,"docs":{},"t":{"1":{"df":0,"docs":{},"​":{"(":{"df":0,"docs":{},"x":{")":{",":{"df":0,"docs":{},"…":{",":{"df":0,"docs":{},"t":{"df":0,"docs":{},"m":{"df":0,"docs":{},"​":{"(":{"df":0,"docs":{},"x":{")":{",":{"df":0,"docs":{},"t":{"1":{"df":0,"docs":{},"​":{"(":{"df":0,"docs":{},"g":{"df":0,"docs":{},"x":{")":{",":{"df":0,"docs":{},"…":{",":{"df":0,"docs":{},"t":{"df":0,"docs":{},"m":{"df":0,"docs":{},"​":{"(":{"df":0,"docs":{},"ω":{"df":0,"docs":{},"x":{"df":1,"docs":{"84":{"tf":1.0}}}}},"df":0,"docs":{}}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"l":{"_":{"1":{"df":1,"docs":{"35":{"tf":1.0}}},"df":0,"docs":{}},"df":5,"docs":{"10":{"tf":2.23606797749979},"12":{"tf":1.0},"13":{"tf":1.4142135623730951},"21":{"tf":2.0},"35":{"tf":1.0}},"​":{"(":{"df":0,"docs":{},"ζ":{")":{",":{"df":0,"docs":{},"q":{"df":0,"docs":{},"r":{"df":1,"docs":{"21":{"tf":1.0}}}}},"df":0,"docs":{}},"df":0,"docs":{}}},",":{"df":0,"docs":{},"q":{"df":0,"docs":{},"r":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"q":{"df":0,"docs":{},"m":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"q":{"df":0,"docs":{},"o":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"q":{"c":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"s":{"df":0,"docs":{},"σ":{"1":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"s":{"df":0,"docs":{},"σ":{"2":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"s":{"df":0,"docs":{},"σ":{"3":{"df":2,"docs":{"15":{"tf":1.0},"16":{"tf":1.0}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}},"df":0,"docs":{}}},"o":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"q":{"df":0,"docs":{},"m":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"q":{"c":{"df":1,"docs":{"10":{"tf":1.0}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}},"df":0,"docs":{}}}}},"df":0,"docs":{}}}}},"]":{"1":{"df":1,"docs":{"21":{"tf":1.0}},"​":{",":{"[":{"df":0,"docs":{},"q":{"df":0,"docs":{},"r":{"df":0,"docs":{},"​":{"]":{"1":{"df":0,"docs":{},"​":{",":{"[":{"df":0,"docs":{},"q":{"df":0,"docs":{},"m":{"df":0,"docs":{},"​":{"]":{"1":{"df":0,"docs":{},"​":{",":{"[":{"df":0,"docs":{},"q":{"df":0,"docs":{},"o":{"df":0,"docs":{},"​":{"]":{"1":{"df":0,"docs":{},"​":{",":{"[":{"df":0,"docs":{},"q":{"c":{"df":0,"docs":{},"​":{"]":{"1":{"df":0,"docs":{},"​":{",":{"[":{"df":0,"docs":{},"s":{"df":0,"docs":{},"σ":{"1":{"df":0,"docs":{},"​":{"]":{"1":{"df":0,"docs":{},"​":{",":{"[":{"df":0,"docs":{},"s":{"df":0,"docs":{},"σ":{"2":{"df":0,"docs":{},"​":{"]":{"1":{"df":0,"docs":{},"​":{",":{"[":{"df":0,"docs":{},"s":{"df":0,"docs":{},"σ":{"3":{"df":0,"docs":{},"​":{"]":{"1":{"df":1,"docs":{"22":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"m":{"_":{"1":{"df":1,"docs":{"35":{"tf":1.0}}},"df":0,"docs":{}},"df":4,"docs":{"10":{"tf":2.23606797749979},"12":{"tf":1.0},"13":{"tf":1.4142135623730951},"35":{"tf":1.0}}},"o":{"_":{"1":{"df":1,"docs":{"35":{"tf":1.0}}},"df":0,"docs":{}},"df":4,"docs":{"10":{"tf":2.23606797749979},"12":{"tf":1.0},"13":{"tf":1.4142135623730951},"35":{"tf":1.0}}},"r":{"_":{"1":{"df":1,"docs":{"35":{"tf":1.0}}},"df":0,"docs":{}},"df":5,"docs":{"10":{"tf":2.23606797749979},"12":{"tf":1.0},"13":{"tf":1.4142135623730951},"21":{"tf":2.0},"35":{"tf":1.0}},"​":{"]":{"1":{"df":1,"docs":{"21":{"tf":1.4142135623730951}}},"df":0,"docs":{}},"df":0,"docs":{}}},"u":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":0,"docs":{},"i":{"df":4,"docs":{"109":{"tf":1.0},"119":{"tf":1.0},"91":{"tf":1.0},"94":{"tf":1.0}}}},"s":{"df":0,"docs":{},"t":{"df":0,"docs":{},"i":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"df":1,"docs":{"14":{"tf":1.0}}}}}}}},"i":{"c":{"df":0,"docs":{},"k":{"df":1,"docs":{"131":{"tf":1.0}}}},"df":0,"docs":{},"t":{"df":1,"docs":{"146":{"tf":1.0}}}},"o":{"df":0,"docs":{},"t":{"df":0,"docs":{},"i":{"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"df":0,"docs":{},"t":{"df":2,"docs":{"35":{"tf":1.7320508075688772},"69":{"tf":1.0}}}}}}}}}},"r":{"0":{"df":1,"docs":{"151":{"tf":1.4142135623730951}}},"a":{"d":{"df":0,"docs":{},"i":{"df":0,"docs":{},"x":{"df":1,"docs":{"121":{"tf":1.0}}}}},"df":0,"docs":{},"n":{"d":{"df":0,"docs":{},"o":{"df":0,"docs":{},"m":{"_":{"df":0,"docs":{},"g":{"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":1,"docs":{"35":{"tf":2.23606797749979}}}}}}}},"df":19,"docs":{"118":{"tf":1.4142135623730951},"14":{"tf":2.8284271247461903},"16":{"tf":2.0},"20":{"tf":1.7320508075688772},"24":{"tf":1.0},"25":{"tf":1.0},"26":{"tf":1.0},"3":{"tf":1.0},"35":{"tf":1.0},"58":{"tf":1.0},"60":{"tf":1.4142135623730951},"69":{"tf":1.4142135623730951},"70":{"tf":1.4142135623730951},"79":{"tf":1.0},"83":{"tf":1.0},"84":{"tf":1.0},"85":{"tf":1.0},"94":{"tf":1.4142135623730951},"95":{"tf":1.4142135623730951}},"l":{"df":0,"docs":{},"i":{"df":3,"docs":{"118":{"tf":1.0},"59":{"tf":1.0},"85":{"tf":1.0}}}}}}},"df":0,"docs":{},"g":{"df":9,"docs":{"130":{"tf":1.4142135623730951},"134":{"tf":1.0},"138":{"tf":1.4142135623730951},"14":{"tf":1.4142135623730951},"144":{"tf":1.0},"146":{"tf":2.23606797749979},"147":{"tf":1.4142135623730951},"151":{"tf":2.449489742783178},"70":{"tf":1.0}},"e":{"_":{"c":{"df":0,"docs":{},"h":{"df":0,"docs":{},"e":{"c":{"df":0,"docs":{},"k":{"_":{"c":{"df":0,"docs":{},"e":{"df":0,"docs":{},"l":{"df":0,"docs":{},"l":{".":{"df":0,"docs":{},"h":{"df":1,"docs":{"130":{"tf":1.0}}},"i":{"df":0,"docs":{},"n":{"df":0,"docs":{},"l":{"df":1,"docs":{"130":{"tf":1.0}}}}}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"p":{"2":{"df":1,"docs":{"83":{"tf":1.0}}},"df":3,"docs":{"147":{"tf":1.4142135623730951},"91":{"tf":1.7320508075688772},"94":{"tf":1.0}}},"t":{"df":0,"docs":{},"i":{"df":0,"docs":{},"o":{"df":2,"docs":{"149":{"tf":1.0},"151":{"tf":1.0}},"n":{"df":3,"docs":{"54":{"tf":1.0},"55":{"tf":1.0},"56":{"tf":1.0}}}}}},"w":{"df":2,"docs":{"144":{"tf":1.4142135623730951},"40":{"tf":1.0}}}},"c":{"1":{"6":{"/":{"df":0,"docs":{},"s":{"df":0,"docs":{},"o":{"df":0,"docs":{},"r":{"df":0,"docs":{},"t":{"df":1,"docs":{"132":{"tf":1.4142135623730951}}}}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":5,"docs":{"130":{"tf":1.7320508075688772},"131":{"tf":1.4142135623730951},"132":{"tf":1.4142135623730951},"138":{"tf":1.4142135623730951},"146":{"tf":1.0}},"m":{"a":{"df":0,"docs":{},"x":{"df":1,"docs":{"146":{"tf":1.0}}}},"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"df":1,"docs":{"146":{"tf":1.0}}}}}},"df":10,"docs":{"11":{"tf":1.0},"12":{"tf":1.0},"13":{"tf":1.0},"14":{"tf":1.0},"142":{"tf":2.6457513110645907},"35":{"tf":1.7320508075688772},"38":{"tf":1.0},"9":{"tf":1.0},"91":{"tf":1.0},"92":{"tf":1.4142135623730951}},"e":{"a":{"c":{"df":0,"docs":{},"h":{"df":1,"docs":{"146":{"tf":1.0}}}},"d":{"df":2,"docs":{"111":{"tf":1.4142135623730951},"69":{"tf":1.0}},"e":{"df":0,"docs":{},"r":{"df":1,"docs":{"125":{"tf":1.0}}}},"i":{"df":1,"docs":{"146":{"tf":1.0}}}},"df":0,"docs":{},"l":{"df":4,"docs":{"128":{"tf":1.7320508075688772},"135":{"tf":1.0},"146":{"tf":1.0},"149":{"tf":1.0}},"i":{"df":0,"docs":{},"t":{"df":0,"docs":{},"i":{"df":1,"docs":{"146":{"tf":1.0}}}}},"l":{"df":0,"docs":{},"i":{"df":4,"docs":{"116":{"tf":1.0},"123":{"tf":1.0},"14":{"tf":1.0},"50":{"tf":1.0}}}}},"r":{"df":0,"docs":{},"r":{"a":{"df":0,"docs":{},"n":{"df":0,"docs":{},"g":{"df":1,"docs":{"14":{"tf":1.0}}}}},"df":0,"docs":{}}},"s":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"df":6,"docs":{"117":{"tf":1.0},"123":{"tf":1.0},"146":{"tf":1.0},"20":{"tf":1.0},"50":{"tf":1.0},"56":{"tf":1.0}}}}}},"c":{"a":{"df":0,"docs":{},"l":{"c":{"df":0,"docs":{},"u":{"df":0,"docs":{},"l":{"df":1,"docs":{"146":{"tf":1.0}}}}},"df":7,"docs":{"111":{"tf":1.0},"125":{"tf":1.0},"14":{"tf":2.0},"16":{"tf":1.0},"54":{"tf":1.0},"91":{"tf":1.0},"95":{"tf":1.0}}},"p":{"df":46,"docs":{"10":{"tf":1.0},"103":{"tf":1.0},"105":{"tf":1.0},"11":{"tf":1.0},"111":{"tf":1.0},"116":{"tf":1.0},"12":{"tf":1.0},"13":{"tf":1.0},"14":{"tf":1.0},"15":{"tf":1.0},"16":{"tf":1.0},"34":{"tf":1.4142135623730951},"35":{"tf":1.0},"36":{"tf":1.0},"4":{"tf":1.0},"46":{"tf":1.0},"47":{"tf":1.7320508075688772},"48":{"tf":1.0},"49":{"tf":1.0},"5":{"tf":1.0},"50":{"tf":1.0},"51":{"tf":1.0},"52":{"tf":1.0},"53":{"tf":1.0},"54":{"tf":1.0},"55":{"tf":1.0},"56":{"tf":1.0},"57":{"tf":1.0},"58":{"tf":1.0},"59":{"tf":1.0},"6":{"tf":1.0},"60":{"tf":1.0},"61":{"tf":1.0},"62":{"tf":1.0},"63":{"tf":1.0},"64":{"tf":1.0},"65":{"tf":1.0},"66":{"tf":1.0},"67":{"tf":1.0},"68":{"tf":1.0},"69":{"tf":1.0},"7":{"tf":1.4142135623730951},"70":{"tf":1.0},"8":{"tf":1.0},"84":{"tf":1.0},"9":{"tf":1.0}}}},"df":0,"docs":{},"e":{"df":0,"docs":{},"i":{"df":0,"docs":{},"v":{"df":5,"docs":{"114":{"tf":1.0},"21":{"tf":1.0},"77":{"tf":1.0},"79":{"tf":1.0},"95":{"tf":1.0}}}}},"o":{"df":0,"docs":{},"g":{"df":0,"docs":{},"n":{"df":1,"docs":{"16":{"tf":1.0}}}},"m":{"df":0,"docs":{},"m":{"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"d":{"df":1,"docs":{"69":{"tf":1.0}}},"df":0,"docs":{}}}}},"n":{"df":0,"docs":{},"s":{"df":0,"docs":{},"t":{"df":0,"docs":{},"r":{"df":0,"docs":{},"u":{"c":{"df":0,"docs":{},"t":{"df":8,"docs":{"102":{"tf":1.4142135623730951},"112":{"tf":1.0},"113":{"tf":1.0},"114":{"tf":1.7320508075688772},"115":{"tf":1.0},"117":{"tf":1.0},"63":{"tf":1.0},"95":{"tf":1.0}}}},"df":0,"docs":{}}}}}},"v":{"df":3,"docs":{"114":{"tf":1.7320508075688772},"124":{"tf":1.0},"95":{"tf":1.0}}}},"u":{"df":0,"docs":{},"r":{"df":0,"docs":{},"s":{"df":2,"docs":{"14":{"tf":1.0},"25":{"tf":1.0}}}}}},"d":{"df":0,"docs":{},"u":{"c":{"df":6,"docs":{"101":{"tf":1.0},"12":{"tf":1.0},"14":{"tf":1.7320508075688772},"144":{"tf":1.0},"21":{"tf":1.4142135623730951},"80":{"tf":1.7320508075688772}}},"df":0,"docs":{},"n":{"d":{"df":2,"docs":{"102":{"tf":1.4142135623730951},"70":{"tf":1.0}}},"df":0,"docs":{}}}},"df":4,"docs":{"136":{"tf":1.4142135623730951},"142":{"tf":1.4142135623730951},"146":{"tf":2.449489742783178},"147":{"tf":1.4142135623730951}},"f":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":11,"docs":{"126":{"tf":1.0},"130":{"tf":1.0},"134":{"tf":1.0},"139":{"tf":1.0},"146":{"tf":1.0},"3":{"tf":1.0},"70":{"tf":1.0},"81":{"tf":1.4142135623730951},"83":{"tf":1.0},"87":{"tf":1.0},"95":{"tf":2.0}}}},"l":{"df":0,"docs":{},"e":{"c":{"df":0,"docs":{},"t":{"df":1,"docs":{"133":{"tf":1.0}}}},"df":0,"docs":{}}},"o":{"df":0,"docs":{},"r":{"df":0,"docs":{},"m":{"df":0,"docs":{},"u":{"df":0,"docs":{},"l":{"df":1,"docs":{"16":{"tf":1.0}}}}}}}},"g":{"a":{"df":0,"docs":{},"r":{"d":{"df":1,"docs":{"126":{"tf":1.0}}},"df":0,"docs":{}}},"df":1,"docs":{"146":{"tf":1.4142135623730951}},"i":{"df":0,"docs":{},"s":{"df":0,"docs":{},"t":{"df":8,"docs":{"127":{"tf":1.0},"133":{"tf":1.0},"136":{"tf":2.0},"141":{"tf":1.0},"144":{"tf":1.4142135623730951},"146":{"tf":1.4142135623730951},"149":{"tf":1.0},"50":{"tf":1.0}},"e":{"df":0,"docs":{},"r":{"df":0,"docs":{},"s":{"df":0,"docs":{},"t":{"df":1,"docs":{"146":{"tf":1.0}}}}}}}}}},"j":{"df":0,"docs":{},"e":{"c":{"df":0,"docs":{},"t":{"df":4,"docs":{"19":{"tf":1.0},"33":{"tf":1.0},"7":{"tf":1.0},"92":{"tf":1.0}}}},"df":0,"docs":{}}},"l":{"a":{"df":0,"docs":{},"t":{"df":6,"docs":{"109":{"tf":2.0},"13":{"tf":1.0},"136":{"tf":1.0},"146":{"tf":1.0},"68":{"tf":1.0},"69":{"tf":1.0}},"i":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"df":0,"docs":{},"s":{"df":0,"docs":{},"h":{"df":0,"docs":{},"i":{"df":0,"docs":{},"p":{"df":4,"docs":{"107":{"tf":1.0},"151":{"tf":1.0},"53":{"tf":1.0},"66":{"tf":1.0}}}}}}}}}}},"df":2,"docs":{"146":{"tf":1.0},"70":{"tf":1.0}},"e":{"df":0,"docs":{},"v":{"df":2,"docs":{"139":{"tf":1.0},"87":{"tf":1.0}}}},"i":{"df":2,"docs":{"34":{"tf":1.0},"4":{"tf":1.0}}},"o":{"c":{"df":1,"docs":{"144":{"tf":1.0}}},"df":0,"docs":{}}},"m":{"a":{"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"df":6,"docs":{"125":{"tf":1.0},"130":{"tf":1.0},"59":{"tf":1.0},"65":{"tf":1.0},"66":{"tf":1.0},"67":{"tf":1.0}}}}},"df":0,"docs":{},"e":{"df":0,"docs":{},"m":{"b":{"df":1,"docs":{"114":{"tf":1.4142135623730951}}},"df":0,"docs":{}}},"i":{"df":0,"docs":{},"n":{"d":{"df":1,"docs":{"68":{"tf":1.0}}},"df":0,"docs":{}}},"o":{"df":0,"docs":{},"v":{"df":2,"docs":{"118":{"tf":1.0},"13":{"tf":1.0}}}}},"p":{"df":0,"docs":{},"e":{"a":{"df":0,"docs":{},"t":{"df":2,"docs":{"135":{"tf":1.0},"83":{"tf":1.0}}}},"df":0,"docs":{}},"h":{"df":0,"docs":{},"r":{"a":{"df":0,"docs":{},"s":{"df":1,"docs":{"14":{"tf":1.0}}}},"df":0,"docs":{}}},"l":{"a":{"c":{"df":3,"docs":{"135":{"tf":1.4142135623730951},"28":{"tf":1.0},"98":{"tf":1.0}}},"df":0,"docs":{},"y":{"df":1,"docs":{"95":{"tf":1.0}}}},"df":0,"docs":{}},"r":{"df":0,"docs":{},"e":{"df":0,"docs":{},"s":{"df":7,"docs":{"10":{"tf":2.23606797749979},"106":{"tf":1.0},"127":{"tf":1.4142135623730951},"146":{"tf":1.7320508075688772},"41":{"tf":1.0},"44":{"tf":1.0},"82":{"tf":1.0}},"e":{"df":0,"docs":{},"n":{"df":0,"docs":{},"t":{"df":6,"docs":{"101":{"tf":1.7320508075688772},"12":{"tf":1.0},"128":{"tf":1.0},"146":{"tf":1.7320508075688772},"4":{"tf":1.4142135623730951},"48":{"tf":1.4142135623730951}}}}}}}}},"q":{"df":0,"docs":{},"u":{"df":0,"docs":{},"i":{"df":0,"docs":{},"r":{"df":8,"docs":{"105":{"tf":1.0},"109":{"tf":1.0},"114":{"tf":1.0},"121":{"tf":1.0},"134":{"tf":1.0},"151":{"tf":1.0},"53":{"tf":1.0},"61":{"tf":1.0}}}}}},"s":{"_":{"df":0,"docs":{},"l":{"df":0,"docs":{},"o":{"df":0,"docs":{},"g":{"df":1,"docs":{"146":{"tf":1.4142135623730951}}}}}},"c":{"df":0,"docs":{},"u":{"df":1,"docs":{"14":{"tf":1.0}}}},"df":0,"docs":{},"e":{"df":0,"docs":{},"t":{"df":1,"docs":{"40":{"tf":1.0}}}},"h":{"a":{"df":0,"docs":{},"p":{"df":1,"docs":{"147":{"tf":1.7320508075688772}}}},"df":0,"docs":{}},"o":{"df":0,"docs":{},"u":{"df":0,"docs":{},"r":{"c":{"df":1,"docs":{"42":{"tf":1.0}}},"df":0,"docs":{}}}},"p":{"df":0,"docs":{},"e":{"c":{"df":0,"docs":{},"t":{"df":9,"docs":{"109":{"tf":1.0},"14":{"tf":2.449489742783178},"142":{"tf":1.0},"144":{"tf":1.0},"146":{"tf":1.4142135623730951},"147":{"tf":1.0},"16":{"tf":1.0},"3":{"tf":1.0},"32":{"tf":1.0}}}},"df":0,"docs":{}},"o":{"df":0,"docs":{},"n":{"df":0,"docs":{},"s":{"df":1,"docs":{"13":{"tf":1.0}}}}}},"t":{"df":2,"docs":{"114":{"tf":1.4142135623730951},"84":{"tf":1.0}}},"u":{"df":0,"docs":{},"l":{"df":0,"docs":{},"t":{"_":{"df":0,"docs":{},"t":{"df":0,"docs":{},"i":{"df":0,"docs":{},"m":{"df":0,"docs":{},"e":{"df":0,"docs":{},"s":{"_":{"b":{"a":{"df":0,"docs":{},"s":{"df":1,"docs":{"45":{"tf":1.4142135623730951}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}}}}},"df":23,"docs":{"11":{"tf":1.0},"112":{"tf":1.0},"13":{"tf":1.0},"135":{"tf":1.0},"14":{"tf":1.4142135623730951},"142":{"tf":1.4142135623730951},"146":{"tf":1.0},"147":{"tf":1.7320508075688772},"151":{"tf":1.4142135623730951},"16":{"tf":1.4142135623730951},"3":{"tf":1.0},"40":{"tf":1.4142135623730951},"41":{"tf":1.0},"45":{"tf":2.449489742783178},"5":{"tf":1.0},"56":{"tf":1.0},"62":{"tf":1.0},"73":{"tf":1.0},"75":{"tf":1.0},"77":{"tf":1.4142135623730951},"8":{"tf":1.0},"84":{"tf":1.4142135623730951},"9":{"tf":1.4142135623730951}}}}}},"t":{"df":0,"docs":{},"u":{"df":0,"docs":{},"r":{"df":0,"docs":{},"n":{"df":9,"docs":{"106":{"tf":1.0},"107":{"tf":1.4142135623730951},"114":{"tf":1.7320508075688772},"146":{"tf":1.0},"35":{"tf":1.4142135623730951},"40":{"tf":1.0},"74":{"tf":1.0},"80":{"tf":1.0},"92":{"tf":1.7320508075688772}}}}}},"u":{"df":0,"docs":{},"s":{"df":1,"docs":{"151":{"tf":1.0}}}},"v":{"df":0,"docs":{},"e":{"a":{"df":0,"docs":{},"l":{"df":6,"docs":{"19":{"tf":1.0},"20":{"tf":1.0},"69":{"tf":1.0},"7":{"tf":1.4142135623730951},"72":{"tf":1.0},"80":{"tf":1.0}}}},"df":0,"docs":{},"r":{"df":0,"docs":{},"s":{"df":1,"docs":{"101":{"tf":2.449489742783178}}}}},"o":{"df":0,"docs":{},"l":{"df":0,"docs":{},"v":{"df":1,"docs":{"104":{"tf":1.0}}}}}}},"i":{"d":{"df":1,"docs":{"14":{"tf":1.0}}},"df":0,"docs":{},"g":{"df":0,"docs":{},"h":{"df":0,"docs":{},"t":{"df":6,"docs":{"11":{"tf":1.0},"118":{"tf":1.0},"121":{"tf":1.0},"14":{"tf":1.0},"16":{"tf":1.0},"9":{"tf":2.23606797749979}}}}}},"m":{"a":{"df":0,"docs":{},"x":{"df":1,"docs":{"142":{"tf":1.4142135623730951}}}},"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"df":1,"docs":{"142":{"tf":1.4142135623730951}}}}},"n":{"df":0,"docs":{},"g":{"df":1,"docs":{"118":{"tf":1.0}}}},"o":{"df":0,"docs":{},"o":{"df":0,"docs":{},"t":{"df":29,"docs":{"112":{"tf":1.4142135623730951},"113":{"tf":1.0},"114":{"tf":1.4142135623730951},"119":{"tf":1.4142135623730951},"123":{"tf":2.6457513110645907},"124":{"tf":4.123105625617661},"125":{"tf":2.8284271247461903},"14":{"tf":3.0},"16":{"tf":1.0},"23":{"tf":1.0},"34":{"tf":1.0},"36":{"tf":1.0},"38":{"tf":1.0},"5":{"tf":1.0},"52":{"tf":2.0},"54":{"tf":1.7320508075688772},"60":{"tf":1.0},"62":{"tf":1.4142135623730951},"63":{"tf":1.0},"68":{"tf":3.0},"72":{"tf":1.7320508075688772},"73":{"tf":1.0},"75":{"tf":1.0},"76":{"tf":1.0},"80":{"tf":1.0},"82":{"tf":1.4142135623730951},"90":{"tf":1.0},"91":{"tf":1.0},"92":{"tf":2.0}}}},"u":{"df":0,"docs":{},"n":{"d":{"df":14,"docs":{"24":{"tf":1.4142135623730951},"25":{"tf":1.4142135623730951},"26":{"tf":1.4142135623730951},"27":{"tf":1.4142135623730951},"28":{"tf":1.4142135623730951},"35":{"tf":2.23606797749979},"37":{"tf":1.0},"82":{"tf":1.7320508075688772},"83":{"tf":2.0},"84":{"tf":2.0},"85":{"tf":1.7320508075688772},"86":{"tf":2.0},"91":{"tf":1.0},"94":{"tf":3.1622776601683795}}},"df":0,"docs":{}}},"w":{"df":32,"docs":{"10":{"tf":3.1622776601683795},"104":{"tf":1.0},"106":{"tf":1.7320508075688772},"107":{"tf":2.23606797749979},"108":{"tf":1.4142135623730951},"109":{"tf":1.7320508075688772},"119":{"tf":1.0},"12":{"tf":1.0},"125":{"tf":1.0},"127":{"tf":1.7320508075688772},"128":{"tf":2.0},"13":{"tf":3.1622776601683795},"130":{"tf":2.23606797749979},"133":{"tf":3.605551275463989},"134":{"tf":2.449489742783178},"136":{"tf":2.449489742783178},"139":{"tf":1.4142135623730951},"142":{"tf":2.449489742783178},"147":{"tf":2.6457513110645907},"149":{"tf":2.449489742783178},"20":{"tf":1.0},"23":{"tf":1.0},"36":{"tf":2.449489742783178},"49":{"tf":1.7320508075688772},"5":{"tf":1.0},"52":{"tf":2.0},"66":{"tf":1.7320508075688772},"69":{"tf":1.0},"70":{"tf":1.7320508075688772},"84":{"tf":1.4142135623730951},"9":{"tf":1.0},"91":{"tf":1.4142135623730951}}}},"u":{"df":0,"docs":{},"f":{"df":0,"docs":{},"f":{"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"df":0,"docs":{},"i":{"'":{"df":1,"docs":{"100":{"tf":1.7320508075688772}}},"df":0,"docs":{}}}}}},"g":{"df":1,"docs":{"64":{"tf":1.0}}},"l":{"df":0,"docs":{},"e":{"df":4,"docs":{"100":{"tf":1.7320508075688772},"48":{"tf":1.4142135623730951},"70":{"tf":1.0},"85":{"tf":1.0}}}},"n":{"df":5,"docs":{"144":{"tf":1.0},"44":{"tf":1.0},"80":{"tf":1.7320508075688772},"86":{"tf":1.4142135623730951},"94":{"tf":1.0}},"n":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":1,"docs":{"104":{"tf":1.0}}}}}}},"−":{"1":{"df":1,"docs":{"38":{"tf":1.0}}},"df":0,"docs":{}}},"s":{"1":{"_":{"1":{"df":1,"docs":{"35":{"tf":1.0}}},"df":0,"docs":{},"l":{"a":{"df":0,"docs":{},"g":{"df":0,"docs":{},"r":{"a":{"df":0,"docs":{},"n":{"df":0,"docs":{},"g":{"df":1,"docs":{"35":{"tf":1.0}}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"z":{"df":0,"docs":{},"e":{"df":0,"docs":{},"t":{"a":{"df":1,"docs":{"35":{"tf":1.0}}},"df":0,"docs":{}}}}},"df":1,"docs":{"35":{"tf":1.0}}},"2":{"_":{"1":{"df":1,"docs":{"35":{"tf":1.0}}},"df":0,"docs":{},"l":{"a":{"df":0,"docs":{},"g":{"df":0,"docs":{},"r":{"a":{"df":0,"docs":{},"n":{"df":0,"docs":{},"g":{"df":1,"docs":{"35":{"tf":1.0}}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"z":{"df":0,"docs":{},"e":{"df":0,"docs":{},"t":{"a":{"df":1,"docs":{"35":{"tf":1.0}}},"df":0,"docs":{}}}}},"df":1,"docs":{"35":{"tf":1.0}},"k":{"df":1,"docs":{"102":{"tf":1.4142135623730951}}}},"3":{"_":{"1":{"df":1,"docs":{"35":{"tf":1.0}}},"df":0,"docs":{},"l":{"a":{"df":0,"docs":{},"g":{"df":0,"docs":{},"r":{"a":{"df":0,"docs":{},"n":{"df":0,"docs":{},"g":{"df":1,"docs":{"35":{"tf":1.0}}}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":1,"docs":{"35":{"tf":1.0}}},"=":{"0":{",":{"df":0,"docs":{},"…":{",":{"df":0,"docs":{},"q":{"df":0,"docs":{},"−":{"1":{"df":2,"docs":{"94":{"tf":1.0},"95":{"tf":1.7320508075688772}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"_":{"df":0,"docs":{},"σ":{"1":{"df":1,"docs":{"35":{"tf":1.0}}},"2":{"df":1,"docs":{"35":{"tf":1.0}}},"df":0,"docs":{}}},"a":{"df":0,"docs":{},"k":{"df":0,"docs":{},"e":{"df":1,"docs":{"21":{"tf":1.0}}}},"m":{"df":0,"docs":{},"e":{"df":33,"docs":{"10":{"tf":1.0},"101":{"tf":1.0},"104":{"tf":1.0},"110":{"tf":1.0},"115":{"tf":1.0},"118":{"tf":2.0},"12":{"tf":1.0},"124":{"tf":1.0},"125":{"tf":1.0},"132":{"tf":1.0},"133":{"tf":1.0},"135":{"tf":1.0},"14":{"tf":1.7320508075688772},"146":{"tf":1.7320508075688772},"149":{"tf":2.23606797749979},"151":{"tf":1.0},"31":{"tf":1.0},"36":{"tf":1.0},"52":{"tf":1.0},"54":{"tf":1.0},"55":{"tf":1.0},"59":{"tf":1.0},"60":{"tf":1.0},"65":{"tf":1.0},"66":{"tf":1.4142135623730951},"67":{"tf":1.0},"68":{"tf":1.0},"69":{"tf":1.0},"75":{"tf":1.0},"80":{"tf":1.0},"84":{"tf":1.7320508075688772},"95":{"tf":1.0},"97":{"tf":1.0}}},"p":{"df":0,"docs":{},"l":{"df":19,"docs":{"112":{"tf":1.4142135623730951},"118":{"tf":1.7320508075688772},"147":{"tf":1.0},"20":{"tf":1.0},"24":{"tf":1.0},"25":{"tf":1.4142135623730951},"26":{"tf":1.4142135623730951},"27":{"tf":1.0},"28":{"tf":1.0},"32":{"tf":2.0},"56":{"tf":1.0},"59":{"tf":1.0},"62":{"tf":1.4142135623730951},"83":{"tf":1.4142135623730951},"84":{"tf":1.0},"89":{"tf":1.0},"94":{"tf":2.6457513110645907},"95":{"tf":2.6457513110645907},"97":{"tf":2.23606797749979}}}}},"t":{"df":0,"docs":{},"i":{"df":0,"docs":{},"s":{"df":0,"docs":{},"f":{"df":0,"docs":{},"i":{"df":28,"docs":{"10":{"tf":1.0},"11":{"tf":1.0},"113":{"tf":1.4142135623730951},"114":{"tf":1.0},"116":{"tf":1.0},"12":{"tf":1.0},"124":{"tf":1.0},"130":{"tf":1.0},"14":{"tf":1.0},"146":{"tf":1.0},"19":{"tf":1.0},"20":{"tf":1.4142135623730951},"48":{"tf":1.0},"49":{"tf":1.4142135623730951},"52":{"tf":1.7320508075688772},"53":{"tf":1.4142135623730951},"54":{"tf":1.7320508075688772},"55":{"tf":1.4142135623730951},"56":{"tf":1.0},"57":{"tf":1.0},"59":{"tf":1.0},"63":{"tf":1.4142135623730951},"66":{"tf":1.0},"67":{"tf":1.0},"68":{"tf":1.0},"69":{"tf":1.0},"75":{"tf":1.0},"84":{"tf":1.4142135623730951}}}}}}},"v":{"df":0,"docs":{},"e":{"df":2,"docs":{"151":{"tf":1.0},"21":{"tf":1.0}}}},"w":{"df":3,"docs":{"10":{"tf":1.0},"14":{"tf":1.0},"16":{"tf":1.0}}},"y":{"df":1,"docs":{"56":{"tf":1.0}}}},"c":{"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"a":{"df":0,"docs":{},"r":{"df":0,"docs":{},"i":{"df":0,"docs":{},"o":{"df":1,"docs":{"100":{"tf":1.0}}}}}},"df":0,"docs":{}}},"h":{"df":0,"docs":{},"e":{"df":0,"docs":{},"m":{"df":0,"docs":{},"e":{"df":11,"docs":{"19":{"tf":2.0},"35":{"tf":1.7320508075688772},"38":{"tf":2.0},"44":{"tf":1.0},"71":{"tf":2.6457513110645907},"74":{"tf":1.0},"75":{"tf":1.4142135623730951},"81":{"tf":1.0},"82":{"tf":1.0},"86":{"tf":1.0},"92":{"tf":1.0}}}}},"w":{"a":{"df":0,"docs":{},"r":{"df":0,"docs":{},"t":{"df":0,"docs":{},"z":{"df":1,"docs":{"14":{"tf":1.4142135623730951}}}}}},"df":0,"docs":{}}}},"df":2,"docs":{"92":{"tf":1.4142135623730951},"95":{"tf":1.7320508075688772}},"e":{"c":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"d":{"_":{"df":0,"docs":{},"r":{"df":0,"docs":{},"o":{"df":0,"docs":{},"w":{"[":{"0":{"df":2,"docs":{"107":{"tf":1.4142135623730951},"114":{"tf":1.0}}},"df":0,"docs":{}},"df":2,"docs":{"107":{"tf":1.0},"114":{"tf":1.0}}}}}},"df":13,"docs":{"11":{"tf":1.0},"13":{"tf":1.0},"14":{"tf":1.7320508075688772},"145":{"tf":1.0},"147":{"tf":1.0},"149":{"tf":1.4142135623730951},"3":{"tf":1.0},"49":{"tf":1.0},"59":{"tf":1.0},"66":{"tf":1.0},"70":{"tf":1.0},"9":{"tf":1.0},"91":{"tf":1.0}}},"df":0,"docs":{}}},"r":{"df":0,"docs":{},"e":{"df":0,"docs":{},"t":{"df":2,"docs":{"20":{"tf":1.0},"21":{"tf":1.0}}}}},"t":{"df":0,"docs":{},"i":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"df":34,"docs":{"103":{"tf":1.0},"11":{"tf":1.0},"111":{"tf":1.0},"117":{"tf":1.0},"126":{"tf":1.0},"130":{"tf":1.0},"131":{"tf":1.0},"138":{"tf":1.0},"139":{"tf":1.0},"14":{"tf":2.6457513110645907},"142":{"tf":1.0},"144":{"tf":1.0},"145":{"tf":1.0},"146":{"tf":4.0},"19":{"tf":1.0},"21":{"tf":1.0},"23":{"tf":1.0},"34":{"tf":1.4142135623730951},"37":{"tf":1.0},"4":{"tf":1.0},"43":{"tf":1.0},"45":{"tf":1.0},"46":{"tf":1.0},"49":{"tf":1.0},"69":{"tf":1.4142135623730951},"70":{"tf":1.0},"74":{"tf":1.0},"77":{"tf":1.0},"80":{"tf":1.0},"82":{"tf":1.0},"84":{"tf":1.0},"87":{"tf":1.0},"9":{"tf":1.0},"95":{"tf":1.0}}}}}},"u":{"df":0,"docs":{},"r":{"df":5,"docs":{"109":{"tf":1.4142135623730951},"151":{"tf":1.0},"68":{"tf":1.0},"73":{"tf":1.0},"91":{"tf":1.0}}}}},"df":0,"docs":{},"e":{"df":15,"docs":{"101":{"tf":1.0},"134":{"tf":1.0},"14":{"tf":2.0},"149":{"tf":1.0},"151":{"tf":1.0},"16":{"tf":1.4142135623730951},"20":{"tf":1.0},"3":{"tf":1.4142135623730951},"34":{"tf":1.0},"71":{"tf":1.0},"75":{"tf":1.0},"77":{"tf":1.0},"80":{"tf":1.0},"9":{"tf":1.7320508075688772},"98":{"tf":1.0}},"m":{"df":2,"docs":{"20":{"tf":1.0},"66":{"tf":1.0}},"i":{"df":0,"docs":{},"n":{"df":0,"docs":{},"g":{"df":0,"docs":{},"l":{"df":0,"docs":{},"i":{"df":1,"docs":{"20":{"tf":1.0}}}}}}}},"n":{"df":7,"docs":{"10":{"tf":1.0},"130":{"tf":1.0},"131":{"tf":1.0},"133":{"tf":1.0},"14":{"tf":1.0},"149":{"tf":1.0},"16":{"tf":1.0}}}},"l":{"df":0,"docs":{},"f":{".":{"a":{"df":0,"docs":{},"i":{"df":0,"docs":{},"r":{".":{"c":{"df":0,"docs":{},"o":{"df":0,"docs":{},"m":{"df":0,"docs":{},"p":{"df":0,"docs":{},"u":{"df":0,"docs":{},"t":{"df":0,"docs":{},"e":{"_":{"df":0,"docs":{},"t":{"df":0,"docs":{},"r":{"a":{"df":0,"docs":{},"n":{"df":0,"docs":{},"s":{"df":0,"docs":{},"i":{"df":0,"docs":{},"t":{"df":0,"docs":{},"i":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"(":{"&":{"df":0,"docs":{},"f":{"df":0,"docs":{},"r":{"a":{"df":0,"docs":{},"m":{"df":1,"docs":{"114":{"tf":1.0}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}}}}}}}},"df":0,"docs":{}}}},"df":0,"docs":{}}}}}},"n":{"df":0,"docs":{},"t":{"df":0,"docs":{},"e":{"df":0,"docs":{},"x":{"df":0,"docs":{},"t":{"(":{")":{".":{"df":0,"docs":{},"t":{"df":0,"docs":{},"r":{"a":{"df":0,"docs":{},"n":{"df":0,"docs":{},"s":{"df":0,"docs":{},"i":{"df":0,"docs":{},"t":{"df":0,"docs":{},"i":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"_":{"df":0,"docs":{},"o":{"df":0,"docs":{},"f":{"df":0,"docs":{},"f":{"df":0,"docs":{},"s":{"df":0,"docs":{},"e":{"df":0,"docs":{},"t":{"df":1,"docs":{"114":{"tf":1.0}}}}}}}}},"df":0,"docs":{}}}}}}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{},"p":{"df":0,"docs":{},"u":{"b":{"_":{"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"df":0,"docs":{},"p":{"df":0,"docs":{},"u":{"df":0,"docs":{},"t":{"df":0,"docs":{},"s":{".":{"a":{"0":{".":{"c":{"df":0,"docs":{},"l":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"df":1,"docs":{"106":{"tf":1.0}}}}}},"df":0,"docs":{}},"df":0,"docs":{}},"1":{".":{"c":{"df":0,"docs":{},"l":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"df":1,"docs":{"106":{"tf":1.0}}}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}},":":{":":{"df":0,"docs":{},"r":{"a":{"df":0,"docs":{},"p":{"c":{"df":0,"docs":{},"h":{"a":{"df":0,"docs":{},"l":{"df":0,"docs":{},"l":{"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"df":0,"docs":{},"g":{"df":2,"docs":{"106":{"tf":1.0},"107":{"tf":1.0}}}}}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":3,"docs":{"106":{"tf":1.0},"107":{"tf":1.0},"114":{"tf":1.0}}}},"n":{"d":{"df":13,"docs":{"101":{"tf":1.4142135623730951},"102":{"tf":1.0},"147":{"tf":1.4142135623730951},"21":{"tf":2.0},"7":{"tf":1.4142135623730951},"72":{"tf":1.4142135623730951},"75":{"tf":1.7320508075688772},"77":{"tf":1.0},"79":{"tf":1.4142135623730951},"80":{"tf":1.4142135623730951},"83":{"tf":1.0},"84":{"tf":1.0},"94":{"tf":1.0}}},"df":0,"docs":{},"s":{"df":4,"docs":{"19":{"tf":1.0},"45":{"tf":1.0},"70":{"tf":1.0},"77":{"tf":1.0}},"i":{"b":{"df":0,"docs":{},"l":{"df":1,"docs":{"7":{"tf":1.0}}}},"df":0,"docs":{}}},"t":{"df":1,"docs":{"91":{"tf":1.0}}}},"q":{"df":0,"docs":{},"u":{"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"c":{"df":5,"docs":{"105":{"tf":1.0},"16":{"tf":1.0},"49":{"tf":1.7320508075688772},"51":{"tf":1.0},"9":{"tf":1.0}}},"df":0,"docs":{}}}}},"r":{"df":0,"docs":{},"v":{"df":4,"docs":{"101":{"tf":1.0},"7":{"tf":1.0},"83":{"tf":1.0},"95":{"tf":1.0}}}},"t":{"(":{"d":{"df":1,"docs":{"90":{"tf":1.0}}},"df":0,"docs":{}},"df":24,"docs":{"106":{"tf":1.4142135623730951},"112":{"tf":1.0},"119":{"tf":1.0},"124":{"tf":1.4142135623730951},"125":{"tf":2.0},"14":{"tf":6.48074069840786},"142":{"tf":1.7320508075688772},"149":{"tf":1.0},"150":{"tf":1.4142135623730951},"151":{"tf":1.0},"3":{"tf":1.0},"4":{"tf":1.0},"45":{"tf":1.0},"62":{"tf":1.0},"65":{"tf":1.0},"68":{"tf":2.0},"69":{"tf":1.0},"73":{"tf":1.0},"8":{"tf":1.4142135623730951},"80":{"tf":1.4142135623730951},"82":{"tf":1.7320508075688772},"83":{"tf":1.0},"90":{"tf":1.4142135623730951},"92":{"tf":1.0}},"u":{"df":0,"docs":{},"p":{"(":{"&":{"c":{"df":0,"docs":{},"o":{"df":0,"docs":{},"m":{"df":0,"docs":{},"m":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"_":{"df":0,"docs":{},"p":{"df":0,"docs":{},"r":{"df":0,"docs":{},"e":{"df":0,"docs":{},"p":{"df":0,"docs":{},"r":{"df":0,"docs":{},"o":{"c":{"df":0,"docs":{},"e":{"df":0,"docs":{},"s":{"df":0,"docs":{},"s":{"df":0,"docs":{},"e":{"d":{"_":{"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"df":0,"docs":{},"p":{"df":0,"docs":{},"u":{"df":0,"docs":{},"t":{"df":1,"docs":{"35":{"tf":1.4142135623730951}}}}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}}}},"df":0,"docs":{}}}}}}}},"df":1,"docs":{"44":{"tf":1.0}}}}}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":4,"docs":{"22":{"tf":1.7320508075688772},"34":{"tf":1.0},"35":{"tf":1.4142135623730951},"44":{"tf":1.0}}}}},"v":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":7,"docs":{"10":{"tf":1.0},"124":{"tf":1.0},"45":{"tf":1.0},"65":{"tf":1.4142135623730951},"66":{"tf":1.0},"70":{"tf":1.4142135623730951},"80":{"tf":1.4142135623730951}}}}}},"h":{"a":{"2":{"5":{"6":{"(":{"df":1,"docs":{"7":{"tf":1.0}}},"df":1,"docs":{"7":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{},"h":{"a":{"df":0,"docs":{},"r":{"df":1,"docs":{"103":{"tf":1.0}}}},"df":0,"docs":{}},"m":{"df":0,"docs":{},"i":{"df":0,"docs":{},"r":{"df":7,"docs":{"118":{"tf":1.4142135623730951},"39":{"tf":1.4142135623730951},"40":{"tf":1.0},"42":{"tf":2.23606797749979},"89":{"tf":1.0},"94":{"tf":1.0},"95":{"tf":1.0}}}}},"p":{"df":0,"docs":{},"e":{"df":2,"docs":{"73":{"tf":1.0},"9":{"tf":1.0}}}},"r":{"df":0,"docs":{},"e":{"df":4,"docs":{"104":{"tf":1.0},"150":{"tf":2.0},"151":{"tf":1.4142135623730951},"19":{"tf":1.0}}}}},"df":0,"docs":{},"o":{"df":0,"docs":{},"w":{"c":{"a":{"df":0,"docs":{},"s":{"df":1,"docs":{"12":{"tf":1.0}}}},"df":0,"docs":{}},"df":11,"docs":{"114":{"tf":1.0},"14":{"tf":1.0},"53":{"tf":1.0},"54":{"tf":1.7320508075688772},"55":{"tf":1.0},"56":{"tf":1.0},"57":{"tf":1.0},"58":{"tf":1.0},"59":{"tf":2.0},"60":{"tf":1.4142135623730951},"69":{"tf":1.0}},"n":{"df":1,"docs":{"149":{"tf":1.0}}}}},"u":{"df":0,"docs":{},"f":{"df":0,"docs":{},"f":{"df":0,"docs":{},"l":{"df":1,"docs":{"79":{"tf":1.0}}}}}}},"i":{"d":{"df":0,"docs":{},"e":{"df":9,"docs":{"112":{"tf":1.4142135623730951},"113":{"tf":1.4142135623730951},"146":{"tf":1.0},"58":{"tf":1.0},"60":{"tf":1.0},"62":{"tf":1.4142135623730951},"63":{"tf":1.4142135623730951},"64":{"tf":1.0},"80":{"tf":1.0}}}},"df":0,"docs":{},"g":{"df":0,"docs":{},"n":{"a":{"df":0,"docs":{},"t":{"df":0,"docs":{},"u":{"df":0,"docs":{},"r":{"df":1,"docs":{"151":{"tf":1.0}}}}}},"df":0,"docs":{}}},"m":{"df":0,"docs":{},"i":{"df":0,"docs":{},"l":{"a":{"df":0,"docs":{},"r":{"df":5,"docs":{"114":{"tf":1.0},"14":{"tf":1.4142135623730951},"146":{"tf":1.0},"151":{"tf":1.0},"85":{"tf":1.0}},"l":{"df":0,"docs":{},"i":{"df":5,"docs":{"13":{"tf":1.0},"139":{"tf":1.0},"14":{"tf":1.4142135623730951},"80":{"tf":1.0},"92":{"tf":1.0}}}}}},"df":0,"docs":{}}},"p":{"df":0,"docs":{},"l":{"df":8,"docs":{"110":{"tf":1.0},"117":{"tf":1.0},"118":{"tf":1.0},"128":{"tf":1.0},"20":{"tf":1.0},"44":{"tf":1.7320508075688772},"45":{"tf":1.0},"49":{"tf":1.0}},"e":{"_":{"df":0,"docs":{},"f":{"df":0,"docs":{},"i":{"b":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"a":{"c":{"c":{"df":0,"docs":{},"i":{":":{":":{"df":0,"docs":{},"f":{"df":0,"docs":{},"i":{"b":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"a":{"c":{"c":{"df":0,"docs":{},"i":{"_":{"df":0,"docs":{},"t":{"df":0,"docs":{},"r":{"a":{"c":{"df":0,"docs":{},"e":{"(":{"[":{"df":0,"docs":{},"f":{"df":0,"docs":{},"e":{":":{":":{"df":0,"docs":{},"f":{"df":0,"docs":{},"r":{"df":0,"docs":{},"o":{"df":0,"docs":{},"m":{"(":{"1":{"df":1,"docs":{"104":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}}},"df":0,"docs":{},"r":{"df":2,"docs":{"14":{"tf":1.0},"149":{"tf":1.0}}}},"i":{"c":{"df":2,"docs":{"21":{"tf":1.0},"64":{"tf":1.0}}},"df":4,"docs":{"106":{"tf":1.0},"109":{"tf":1.0},"114":{"tf":1.0},"125":{"tf":1.0}},"f":{"df":2,"docs":{"149":{"tf":1.0},"64":{"tf":1.4142135623730951}},"i":{"df":1,"docs":{"56":{"tf":1.0}}}}}}},"u":{"df":0,"docs":{},"l":{"df":0,"docs":{},"t":{"a":{"df":0,"docs":{},"n":{"df":3,"docs":{"101":{"tf":1.0},"59":{"tf":1.0},"80":{"tf":1.0}}}},"df":0,"docs":{}}}}},"n":{"df":0,"docs":{},"g":{"df":0,"docs":{},"l":{"df":14,"docs":{"101":{"tf":1.4142135623730951},"12":{"tf":1.7320508075688772},"128":{"tf":1.0},"146":{"tf":1.0},"16":{"tf":1.0},"28":{"tf":1.0},"33":{"tf":1.0},"35":{"tf":1.0},"5":{"tf":1.0},"69":{"tf":1.0},"80":{"tf":1.4142135623730951},"84":{"tf":1.0},"92":{"tf":1.0},"97":{"tf":1.4142135623730951}}}}},"t":{"df":1,"docs":{"13":{"tf":1.0}},"e":{"df":1,"docs":{"0":{"tf":1.0}}},"u":{"a":{"df":0,"docs":{},"t":{"df":2,"docs":{"14":{"tf":1.0},"70":{"tf":1.0}}}},"df":0,"docs":{}}},"z":{"df":0,"docs":{},"e":{"df":9,"docs":{"12":{"tf":1.0},"121":{"tf":1.4142135623730951},"14":{"tf":1.0},"141":{"tf":1.0},"147":{"tf":2.6457513110645907},"23":{"tf":1.0},"3":{"tf":2.0},"36":{"tf":1.0},"80":{"tf":1.0}}}}},"k":{"df":0,"docs":{},"e":{"df":0,"docs":{},"t":{"c":{"df":0,"docs":{},"h":{"df":1,"docs":{"79":{"tf":1.0}}}},"df":0,"docs":{}}},"i":{"df":0,"docs":{},"p":{"df":1,"docs":{"102":{"tf":1.0}}}}},"l":{"df":0,"docs":{},"i":{"df":0,"docs":{},"g":{"df":0,"docs":{},"h":{"df":0,"docs":{},"t":{"df":0,"docs":{},"l":{"df":0,"docs":{},"i":{"df":1,"docs":{"59":{"tf":1.0}}}}}}}},"o":{"df":0,"docs":{},"w":{"df":2,"docs":{"3":{"tf":1.0},"98":{"tf":1.0}}}}},"m":{"a":{"df":0,"docs":{},"l":{"df":0,"docs":{},"l":{"df":2,"docs":{"151":{"tf":1.0},"74":{"tf":1.0}},"e":{"df":0,"docs":{},"r":{"df":2,"docs":{"123":{"tf":1.0},"142":{"tf":1.0}}},"s":{"df":0,"docs":{},"t":{"df":1,"docs":{"142":{"tf":1.0}}}}}}}},"df":0,"docs":{}},"o":{"df":0,"docs":{},"l":{"df":0,"docs":{},"u":{"df":0,"docs":{},"t":{"df":4,"docs":{"101":{"tf":1.0},"14":{"tf":1.0},"4":{"tf":1.0},"44":{"tf":1.0}}}},"v":{"df":2,"docs":{"146":{"tf":1.0},"95":{"tf":1.0}}}},"m":{"df":0,"docs":{},"e":{"df":0,"docs":{},"h":{"df":0,"docs":{},"o":{"df":0,"docs":{},"w":{"df":2,"docs":{"114":{"tf":1.0},"70":{"tf":1.0}}}}},"o":{"df":0,"docs":{},"n":{"df":1,"docs":{"114":{"tf":1.0}}}},"t":{"df":0,"docs":{},"h":{"df":6,"docs":{"130":{"tf":1.0},"14":{"tf":1.4142135623730951},"146":{"tf":2.23606797749979},"45":{"tf":1.0},"50":{"tf":1.0},"95":{"tf":1.0}}},"i":{"df":0,"docs":{},"m":{"df":2,"docs":{"48":{"tf":1.0},"52":{"tf":1.0}}}}},"w":{"df":0,"docs":{},"h":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":1,"docs":{"21":{"tf":1.0}}}}}}}},"p":{"df":0,"docs":{},"h":{"df":0,"docs":{},"i":{"df":0,"docs":{},"s":{"df":0,"docs":{},"t":{"df":1,"docs":{"12":{"tf":1.0}}}}}}},"r":{"df":0,"docs":{},"t":{"df":6,"docs":{"132":{"tf":1.7320508075688772},"135":{"tf":2.0},"138":{"tf":1.0},"14":{"tf":1.0},"147":{"tf":2.23606797749979},"75":{"tf":1.0}}}},"u":{"df":0,"docs":{},"n":{"d":{"df":3,"docs":{"7":{"tf":1.0},"79":{"tf":1.7320508075688772},"88":{"tf":1.0}}},"df":0,"docs":{}},"r":{"c":{"df":3,"docs":{"118":{"tf":1.0},"127":{"tf":1.0},"34":{"tf":1.0}}},"df":0,"docs":{}}}},"p":{"a":{"c":{"df":0,"docs":{},"e":{"df":1,"docs":{"149":{"tf":1.0}}}},"df":0,"docs":{}},"df":0,"docs":{},"e":{"a":{"df":0,"docs":{},"k":{"df":1,"docs":{"16":{"tf":1.0}}}},"c":{"df":0,"docs":{},"i":{"a":{"df":0,"docs":{},"l":{"df":2,"docs":{"120":{"tf":1.4142135623730951},"151":{"tf":1.0}}}},"df":0,"docs":{},"f":{"df":14,"docs":{"100":{"tf":1.0},"106":{"tf":1.0},"108":{"tf":1.0},"109":{"tf":1.0},"114":{"tf":1.0},"13":{"tf":1.4142135623730951},"146":{"tf":1.0},"151":{"tf":1.7320508075688772},"19":{"tf":1.0},"49":{"tf":1.0},"70":{"tf":1.4142135623730951},"73":{"tf":1.0},"91":{"tf":1.0},"94":{"tf":1.0}},"i":{"df":7,"docs":{"105":{"tf":1.0},"106":{"tf":1.0},"107":{"tf":1.4142135623730951},"109":{"tf":1.4142135623730951},"128":{"tf":1.0},"146":{"tf":1.0},"44":{"tf":1.0}}}}}},"df":0,"docs":{}},"l":{"df":0,"docs":{},"i":{"df":0,"docs":{},"t":{"df":2,"docs":{"151":{"tf":1.0},"82":{"tf":1.0}}}}}},"q":{"df":0,"docs":{},"u":{"a":{"df":0,"docs":{},"r":{"df":1,"docs":{"45":{"tf":1.0}}},"s":{"df":0,"docs":{},"h":{"df":2,"docs":{"12":{"tf":1.0},"14":{"tf":1.0}}}}},"df":0,"docs":{}}},"r":{"c":{"df":1,"docs":{"146":{"tf":1.0}}},"df":2,"docs":{"35":{"tf":1.4142135623730951},"44":{"tf":1.0}}},"t":{"a":{"c":{"df":0,"docs":{},"k":{"df":2,"docs":{"14":{"tf":1.0},"147":{"tf":1.7320508075688772}}}},"df":0,"docs":{},"g":{"df":0,"docs":{},"e":{"df":1,"docs":{"145":{"tf":1.4142135623730951}}}},"n":{"d":{"df":2,"docs":{"13":{"tf":1.0},"28":{"tf":1.0}}},"df":0,"docs":{}},"r":{"df":0,"docs":{},"k":{"df":95,"docs":{"100":{"tf":1.0},"101":{"tf":1.0},"102":{"tf":1.0},"103":{"tf":1.7320508075688772},"104":{"tf":1.4142135623730951},"105":{"tf":1.0},"106":{"tf":1.0},"107":{"tf":1.0},"108":{"tf":1.0},"109":{"tf":2.0},"110":{"tf":1.0},"111":{"tf":1.0},"112":{"tf":1.0},"113":{"tf":1.0},"114":{"tf":1.0},"115":{"tf":1.0},"116":{"tf":1.0},"117":{"tf":1.0},"118":{"tf":1.0},"119":{"tf":1.0},"120":{"tf":1.0},"121":{"tf":1.0},"122":{"tf":1.0},"123":{"tf":1.4142135623730951},"124":{"tf":1.0},"125":{"tf":1.0},"126":{"tf":1.0},"127":{"tf":1.0},"128":{"tf":1.0},"129":{"tf":1.0},"130":{"tf":1.0},"131":{"tf":1.0},"132":{"tf":1.0},"133":{"tf":1.0},"134":{"tf":1.0},"135":{"tf":1.0},"136":{"tf":1.0},"137":{"tf":1.0},"138":{"tf":1.0},"139":{"tf":1.0},"142":{"tf":1.0},"46":{"tf":2.0},"47":{"tf":1.7320508075688772},"48":{"tf":1.0},"49":{"tf":1.4142135623730951},"50":{"tf":1.7320508075688772},"51":{"tf":1.4142135623730951},"52":{"tf":1.0},"53":{"tf":1.0},"54":{"tf":1.0},"55":{"tf":1.0},"56":{"tf":1.0},"57":{"tf":1.0},"58":{"tf":1.0},"59":{"tf":1.0},"60":{"tf":1.0},"61":{"tf":1.4142135623730951},"62":{"tf":1.0},"63":{"tf":1.0},"64":{"tf":1.0},"65":{"tf":1.0},"66":{"tf":1.0},"67":{"tf":1.0},"68":{"tf":1.0},"69":{"tf":1.4142135623730951},"70":{"tf":1.0},"71":{"tf":1.4142135623730951},"72":{"tf":1.0},"73":{"tf":1.4142135623730951},"74":{"tf":2.0},"75":{"tf":1.4142135623730951},"76":{"tf":1.0},"77":{"tf":1.0},"78":{"tf":1.0},"79":{"tf":1.0},"80":{"tf":1.4142135623730951},"81":{"tf":1.0},"82":{"tf":1.0},"83":{"tf":1.0},"84":{"tf":1.4142135623730951},"85":{"tf":1.0},"86":{"tf":1.0},"87":{"tf":2.0},"88":{"tf":1.0},"89":{"tf":1.0},"90":{"tf":1.0},"91":{"tf":1.0},"92":{"tf":1.0},"93":{"tf":1.0},"94":{"tf":1.0},"95":{"tf":1.0},"96":{"tf":1.0},"97":{"tf":1.0},"98":{"tf":1.0},"99":{"tf":1.0}},"w":{"a":{"df":0,"docs":{},"r":{"df":1,"docs":{"103":{"tf":1.0}},"e":{"'":{"df":1,"docs":{"126":{"tf":1.0}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"t":{"df":12,"docs":{"109":{"tf":1.0},"14":{"tf":1.4142135623730951},"149":{"tf":1.0},"151":{"tf":1.0},"16":{"tf":1.0},"40":{"tf":1.0},"50":{"tf":1.0},"69":{"tf":1.4142135623730951},"8":{"tf":1.0},"82":{"tf":1.0},"94":{"tf":1.0},"95":{"tf":1.0}}}},"t":{"df":0,"docs":{},"e":{"df":12,"docs":{"118":{"tf":1.7320508075688772},"127":{"tf":2.0},"133":{"tf":1.0},"144":{"tf":1.4142135623730951},"146":{"tf":1.7320508075688772},"149":{"tf":1.0},"40":{"tf":2.23606797749979},"48":{"tf":1.0},"70":{"tf":3.0},"84":{"tf":1.0},"88":{"tf":1.7320508075688772},"94":{"tf":1.0}},"m":{"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"df":0,"docs":{},"t":{"df":5,"docs":{"11":{"tf":1.4142135623730951},"14":{"tf":1.0},"146":{"tf":1.4142135623730951},"34":{"tf":1.0},"84":{"tf":1.0}}}}}}}}},"df":0,"docs":{},"e":{"df":0,"docs":{},"p":{"df":27,"docs":{"102":{"tf":1.0},"107":{"tf":1.7320508075688772},"111":{"tf":1.0},"113":{"tf":1.0},"127":{"tf":2.0},"128":{"tf":1.4142135623730951},"130":{"tf":1.7320508075688772},"131":{"tf":1.0},"133":{"tf":4.123105625617661},"136":{"tf":2.449489742783178},"139":{"tf":1.4142135623730951},"144":{"tf":1.0},"146":{"tf":2.449489742783178},"147":{"tf":1.4142135623730951},"149":{"tf":1.4142135623730951},"16":{"tf":1.0},"31":{"tf":1.0},"32":{"tf":1.0},"48":{"tf":1.0},"49":{"tf":1.0},"51":{"tf":2.449489742783178},"52":{"tf":1.0},"53":{"tf":1.4142135623730951},"57":{"tf":1.0},"61":{"tf":1.0},"70":{"tf":1.0},"95":{"tf":2.0}}}},"i":{"c":{"df":0,"docs":{},"k":{"df":1,"docs":{"7":{"tf":1.0}}}},"df":0,"docs":{},"l":{"df":0,"docs":{},"l":{"df":3,"docs":{"0":{"tf":1.0},"125":{"tf":1.0},"58":{"tf":1.0}}}}},"o":{"df":0,"docs":{},"n":{"df":0,"docs":{},"e":{"df":14,"docs":{"126":{"tf":2.0},"127":{"tf":2.23606797749979},"128":{"tf":1.0},"129":{"tf":1.0},"130":{"tf":1.4142135623730951},"131":{"tf":1.4142135623730951},"132":{"tf":1.0},"133":{"tf":1.0},"134":{"tf":1.0},"135":{"tf":1.0},"136":{"tf":1.0},"137":{"tf":1.0},"138":{"tf":1.0},"139":{"tf":1.0}}}},"r":{"df":0,"docs":{},"e":{"df":5,"docs":{"136":{"tf":1.0},"146":{"tf":1.4142135623730951},"151":{"tf":1.0},"35":{"tf":2.0},"70":{"tf":1.0}}}}},"r":{"a":{"df":0,"docs":{},"i":{"df":0,"docs":{},"g":{"df":0,"docs":{},"h":{"df":0,"docs":{},"t":{"df":0,"docs":{},"f":{"df":0,"docs":{},"o":{"df":0,"docs":{},"r":{"df":0,"docs":{},"w":{"a":{"df":0,"docs":{},"r":{"d":{"df":4,"docs":{"132":{"tf":1.0},"14":{"tf":1.4142135623730951},"146":{"tf":1.0},"151":{"tf":1.0}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}}}}}},"t":{"df":0,"docs":{},"e":{"df":0,"docs":{},"g":{"df":0,"docs":{},"i":{"df":1,"docs":{"40":{"tf":1.4142135623730951}}}}}}},"df":0,"docs":{},"i":{"c":{"df":0,"docs":{},"t":{"df":0,"docs":{},"l":{"df":0,"docs":{},"i":{"df":1,"docs":{"73":{"tf":1.4142135623730951}}}}}},"df":0,"docs":{},"n":{"df":0,"docs":{},"g":{"df":3,"docs":{"41":{"tf":1.0},"88":{"tf":1.7320508075688772},"91":{"tf":1.0}}}}},"o":{"df":0,"docs":{},"n":{"df":0,"docs":{},"g":{"df":3,"docs":{"42":{"tf":1.4142135623730951},"94":{"tf":1.0},"95":{"tf":1.0}}}}},"u":{"c":{"df":0,"docs":{},"t":{"df":8,"docs":{"106":{"tf":1.4142135623730951},"107":{"tf":1.0},"108":{"tf":1.0},"109":{"tf":1.4142135623730951},"110":{"tf":1.0},"114":{"tf":1.4142135623730951},"118":{"tf":1.0},"35":{"tf":3.3166247903554}},"u":{"df":0,"docs":{},"r":{"df":2,"docs":{"146":{"tf":1.0},"83":{"tf":1.0}}}}}},"df":0,"docs":{}}},"u":{"df":0,"docs":{},"f":{"df":0,"docs":{},"f":{"df":1,"docs":{"56":{"tf":1.0}}}}}},"u":{"b":{"c":{"df":0,"docs":{},"o":{"df":0,"docs":{},"l":{"df":0,"docs":{},"u":{"df":0,"docs":{},"m":{"df":0,"docs":{},"n":{"df":4,"docs":{"146":{"tf":1.4142135623730951},"148":{"tf":1.4142135623730951},"150":{"tf":2.0},"151":{"tf":1.0}}}}}}}},"df":2,"docs":{"133":{"tf":1.7320508075688772},"134":{"tf":1.0}},"g":{"df":0,"docs":{},"r":{"df":0,"docs":{},"o":{"df":0,"docs":{},"u":{"df":0,"docs":{},"p":{"df":1,"docs":{"38":{"tf":1.0}}}}}}},"m":{"a":{"df":0,"docs":{},"t":{"df":0,"docs":{},"r":{"df":0,"docs":{},"i":{"df":0,"docs":{},"x":{"df":2,"docs":{"142":{"tf":1.7320508075688772},"147":{"tf":1.7320508075688772}}}}}}},"df":0,"docs":{}},"p":{"df":0,"docs":{},"r":{"df":0,"docs":{},"o":{"df":0,"docs":{},"g":{"df":0,"docs":{},"r":{"a":{"df":0,"docs":{},"m":{"df":1,"docs":{"151":{"tf":1.4142135623730951}}}},"df":0,"docs":{}}}}}},"s":{"c":{"df":0,"docs":{},"r":{"df":0,"docs":{},"i":{"df":0,"docs":{},"p":{"df":0,"docs":{},"t":{"df":1,"docs":{"28":{"tf":1.4142135623730951}}}}}}},"df":0,"docs":{},"e":{"c":{"df":0,"docs":{},"t":{"df":1,"docs":{"146":{"tf":1.4142135623730951}}}},"df":0,"docs":{},"q":{"df":0,"docs":{},"u":{"df":3,"docs":{"11":{"tf":1.0},"114":{"tf":1.0},"40":{"tf":1.0}}}},"t":{"df":3,"docs":{"128":{"tf":1.4142135623730951},"150":{"tf":1.0},"90":{"tf":1.4142135623730951}}}}},"t":{"a":{"b":{"df":0,"docs":{},"l":{"df":1,"docs":{"127":{"tf":1.0}}}},"df":0,"docs":{}},"df":0,"docs":{},"r":{"a":{"c":{"df":0,"docs":{},"t":{"df":1,"docs":{"9":{"tf":1.4142135623730951}}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"c":{"c":{"df":0,"docs":{},"e":{"df":1,"docs":{"78":{"tf":1.0}}}},"df":0,"docs":{},"h":{"df":23,"docs":{"100":{"tf":1.0},"109":{"tf":1.0},"11":{"tf":1.0},"124":{"tf":1.4142135623730951},"13":{"tf":1.0},"14":{"tf":3.3166247903554},"144":{"tf":1.0},"151":{"tf":1.0},"16":{"tf":2.23606797749979},"19":{"tf":1.0},"20":{"tf":1.7320508075688772},"26":{"tf":1.0},"44":{"tf":1.0},"45":{"tf":1.4142135623730951},"5":{"tf":1.0},"66":{"tf":1.0},"70":{"tf":1.0},"71":{"tf":1.0},"73":{"tf":1.7320508075688772},"79":{"tf":1.0},"88":{"tf":1.0},"94":{"tf":1.7320508075688772},"95":{"tf":1.0}}}},"df":0,"docs":{},"f":{"df":0,"docs":{},"f":{"df":0,"docs":{},"i":{"df":0,"docs":{},"x":{"df":1,"docs":{"35":{"tf":1.0}}}}}},"m":{"df":6,"docs":{"114":{"tf":1.0},"125":{"tf":1.0},"19":{"tf":1.0},"49":{"tf":1.0},"52":{"tf":1.0},"65":{"tf":1.0}},"m":{"a":{"df":0,"docs":{},"n":{"d":{"df":1,"docs":{"59":{"tf":1.4142135623730951}}},"df":0,"docs":{}},"r":{"df":1,"docs":{"61":{"tf":1.0}},"i":{"df":8,"docs":{"131":{"tf":1.0},"138":{"tf":1.0},"14":{"tf":1.0},"146":{"tf":1.0},"150":{"tf":1.0},"61":{"tf":1.4142135623730951},"69":{"tf":1.0},"81":{"tf":1.0}}}}},"df":0,"docs":{}}},"p":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":0,"docs":{},"s":{"c":{"df":0,"docs":{},"r":{"df":0,"docs":{},"i":{"df":0,"docs":{},"p":{"df":0,"docs":{},"t":{"df":1,"docs":{"95":{"tf":1.4142135623730951}}}}}}},"df":0,"docs":{}}}},"p":{"df":0,"docs":{},"o":{"df":0,"docs":{},"r":{"df":0,"docs":{},"t":{"df":3,"docs":{"107":{"tf":1.0},"121":{"tf":1.0},"34":{"tf":1.0}}}},"s":{"df":5,"docs":{"14":{"tf":2.23606797749979},"45":{"tf":1.0},"73":{"tf":1.4142135623730951},"74":{"tf":1.4142135623730951},"79":{"tf":1.0}}}}}},"r":{"df":0,"docs":{},"e":{"df":2,"docs":{"125":{"tf":1.0},"151":{"tf":1.0}}}}},"w":{"df":0,"docs":{},"e":{"df":0,"docs":{},"e":{"df":0,"docs":{},"p":{"df":1,"docs":{"64":{"tf":1.0}}}}}},"y":{"df":0,"docs":{},"m":{"b":{"df":0,"docs":{},"o":{"df":0,"docs":{},"l":{"df":3,"docs":{"36":{"tf":1.0},"5":{"tf":2.0},"9":{"tf":1.0}}}}},"df":0,"docs":{},"m":{"df":0,"docs":{},"e":{"df":0,"docs":{},"t":{"df":0,"docs":{},"r":{"df":1,"docs":{"73":{"tf":1.0}}}}}}},"s":{"df":0,"docs":{},"t":{"df":0,"docs":{},"e":{"df":0,"docs":{},"m":{"'":{"df":1,"docs":{"70":{"tf":1.0}}},".":{"a":{"df":0,"docs":{},"s":{"df":0,"docs":{},"s":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":0,"docs":{},"t":{"_":{"df":0,"docs":{},"e":{"df":0,"docs":{},"q":{"(":{"&":{"df":0,"docs":{},"i":{"df":2,"docs":{"44":{"tf":1.0},"45":{"tf":1.0}}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}}}}}},"df":0,"docs":{},"i":{"df":0,"docs":{},"f":{"_":{"df":0,"docs":{},"e":{"df":0,"docs":{},"l":{"df":0,"docs":{},"s":{"df":0,"docs":{},"e":{"(":{"&":{"df":0,"docs":{},"e":{"df":0,"docs":{},"x":{"df":0,"docs":{},"p":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"df":0,"docs":{},"t":{"_":{"b":{"df":0,"docs":{},"i":{"df":0,"docs":{},"t":{"df":0,"docs":{},"s":{"[":{"df":0,"docs":{},"i":{"df":1,"docs":{"45":{"tf":1.0}}}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}}}}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}}}},"df":0,"docs":{}}},"m":{"df":0,"docs":{},"u":{"df":0,"docs":{},"l":{"(":{"&":{"df":0,"docs":{},"r":{"df":0,"docs":{},"e":{"df":0,"docs":{},"s":{"df":0,"docs":{},"u":{"df":0,"docs":{},"l":{"df":0,"docs":{},"t":{"df":1,"docs":{"45":{"tf":1.4142135623730951}}}}}}}},"x":{"df":1,"docs":{"44":{"tf":1.0}}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"n":{"df":0,"docs":{},"e":{"df":0,"docs":{},"w":{"_":{"c":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"df":0,"docs":{},"s":{"df":0,"docs":{},"t":{"a":{"df":0,"docs":{},"n":{"df":0,"docs":{},"t":{"(":{"df":0,"docs":{},"f":{"df":0,"docs":{},"i":{"df":0,"docs":{},"e":{"df":0,"docs":{},"l":{"d":{"df":0,"docs":{},"e":{"df":0,"docs":{},"l":{"df":0,"docs":{},"e":{"df":0,"docs":{},"m":{"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"df":0,"docs":{},"t":{":":{":":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"df":1,"docs":{"45":{"tf":1.0}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}}}}}}},"df":0,"docs":{}}}}}},"df":0,"docs":{}}}},"df":0,"docs":{}}}}}},"df":0,"docs":{},"p":{"df":0,"docs":{},"u":{"b":{"df":0,"docs":{},"l":{"df":0,"docs":{},"i":{"c":{"_":{"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"df":0,"docs":{},"p":{"df":0,"docs":{},"u":{"df":0,"docs":{},"t":{"df":2,"docs":{"44":{"tf":1.4142135623730951},"45":{"tf":1.4142135623730951}}}}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"u":{"3":{"2":{"(":{"&":{"df":0,"docs":{},"e":{"df":0,"docs":{},"x":{"df":0,"docs":{},"p":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"df":1,"docs":{"45":{"tf":1.0}}}}}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"v":{"a":{"df":0,"docs":{},"r":{"df":0,"docs":{},"i":{"df":2,"docs":{"44":{"tf":1.0},"45":{"tf":1.0}}}}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"p":{"df":0,"docs":{},"u":{"b":{"df":0,"docs":{},"l":{"df":0,"docs":{},"i":{"c":{"_":{"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"df":0,"docs":{},"p":{"df":0,"docs":{},"u":{"df":0,"docs":{},"t":{"_":{"df":0,"docs":{},"v":{"a":{"df":0,"docs":{},"l":{"df":0,"docs":{},"u":{"df":0,"docs":{},"e":{"df":0,"docs":{},"s":{"(":{"&":{"a":{"df":0,"docs":{},"s":{"df":0,"docs":{},"s":{"df":0,"docs":{},"i":{"df":0,"docs":{},"g":{"df":0,"docs":{},"n":{"df":1,"docs":{"44":{"tf":1.0}}}}}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"s":{"df":0,"docs":{},"o":{"df":0,"docs":{},"l":{"df":0,"docs":{},"v":{"df":0,"docs":{},"e":{"(":{"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"df":0,"docs":{},"p":{"df":0,"docs":{},"u":{"df":0,"docs":{},"t":{"df":0,"docs":{},"s":{")":{".":{"df":0,"docs":{},"u":{"df":0,"docs":{},"n":{"df":0,"docs":{},"w":{"df":0,"docs":{},"r":{"a":{"df":0,"docs":{},"p":{"df":1,"docs":{"44":{"tf":1.0}}}},"df":0,"docs":{}}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}}}}}},"df":0,"docs":{}}}}}}},"df":17,"docs":{"103":{"tf":1.0},"104":{"tf":1.0},"118":{"tf":1.0},"119":{"tf":1.0},"144":{"tf":1.0},"146":{"tf":1.0},"151":{"tf":1.0},"4":{"tf":1.4142135623730951},"42":{"tf":1.0},"43":{"tf":1.0},"44":{"tf":2.8284271247461903},"45":{"tf":2.449489742783178},"46":{"tf":1.0},"48":{"tf":1.4142135623730951},"49":{"tf":1.0},"70":{"tf":1.0},"95":{"tf":1.0}}}}}}},"σ":{"1":{"df":1,"docs":{"14":{"tf":1.0}},"​":{"]":{"1":{"df":0,"docs":{},"​":{",":{"[":{"df":0,"docs":{},"s":{"df":0,"docs":{},"σ":{"2":{"df":0,"docs":{},"​":{"]":{"1":{"df":0,"docs":{},"​":{",":{"[":{"df":0,"docs":{},"s":{"df":0,"docs":{},"σ":{"3":{"]":{"1":{"df":0,"docs":{},"​":{",":{"[":{"df":0,"docs":{},"q":{"df":0,"docs":{},"l":{"df":0,"docs":{},"​":{"]":{"1":{"df":0,"docs":{},"​":{",":{"[":{"df":0,"docs":{},"q":{"df":0,"docs":{},"r":{"df":0,"docs":{},"​":{"]":{"1":{"df":0,"docs":{},"​":{",":{"[":{"df":0,"docs":{},"q":{"df":0,"docs":{},"m":{"df":0,"docs":{},"​":{"]":{"1":{"df":0,"docs":{},"​":{",":{"[":{"df":0,"docs":{},"q":{"df":0,"docs":{},"o":{"df":0,"docs":{},"​":{"]":{"1":{"df":0,"docs":{},"​":{",":{"[":{"df":0,"docs":{},"q":{"c":{"df":0,"docs":{},"​":{"]":{"1":{"df":1,"docs":{"24":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{},"​":{"]":{"1":{"df":0,"docs":{},"​":{",":{"[":{"df":0,"docs":{},"q":{"df":0,"docs":{},"l":{"df":0,"docs":{},"​":{"]":{"1":{"df":0,"docs":{},"​":{",":{"[":{"df":0,"docs":{},"q":{"df":0,"docs":{},"r":{"df":0,"docs":{},"​":{"]":{"1":{"df":0,"docs":{},"​":{",":{"[":{"df":0,"docs":{},"q":{"df":0,"docs":{},"m":{"df":0,"docs":{},"​":{"]":{"1":{"df":0,"docs":{},"​":{",":{"[":{"df":0,"docs":{},"q":{"df":0,"docs":{},"o":{"df":0,"docs":{},"​":{"]":{"1":{"df":0,"docs":{},"​":{",":{"[":{"df":0,"docs":{},"q":{"c":{"df":0,"docs":{},"​":{"]":{"1":{"df":1,"docs":{"31":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"2":{"df":1,"docs":{"14":{"tf":1.0}}},"3":{"df":1,"docs":{"14":{"tf":1.0}}},"df":0,"docs":{},"i":{"df":1,"docs":{"35":{"tf":1.0}}}},"​":{":":{"=":{"df":0,"docs":{},"h":{"df":0,"docs":{},"ω":{"df":0,"docs":{},"ι":{"df":2,"docs":{"94":{"tf":1.0},"95":{"tf":1.0}}}}}},"df":0,"docs":{}},"df":0,"docs":{},"∈":{"[":{"0":{",":{"2":{"df":0,"docs":{},"n":{"+":{"df":0,"docs":{},"l":{"df":0,"docs":{},"−":{"1":{"df":2,"docs":{"94":{"tf":1.0},"95":{"tf":1.0}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}},"t":{"(":{"1":{")":{"df":0,"docs":{},"−":{"df":0,"docs":{},"p":{"(":{"1":{")":{"=":{"1":{"df":0,"docs":{},"−":{"1":{"=":{"0":{"df":1,"docs":{"54":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{},"g":{")":{"df":0,"docs":{},"−":{"df":0,"docs":{},"p":{"(":{"df":0,"docs":{},"g":{")":{"=":{"1":{"df":0,"docs":{},"−":{"1":{"=":{"0":{"df":1,"docs":{"54":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"^":{"0":{"df":2,"docs":{"52":{"tf":1.0},"54":{"tf":1.0}}},"df":0,"docs":{}},"df":2,"docs":{"52":{"tf":1.0},"54":{"tf":1.0}},"i":{")":{"=":{"a":{"df":0,"docs":{},"i":{"df":1,"docs":{"52":{"tf":1.0}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"x":{")":{"'":{"df":1,"docs":{"114":{"tf":1.0}}},"df":0,"docs":{}},"0":{"df":2,"docs":{"112":{"tf":1.4142135623730951},"113":{"tf":1.4142135623730951}},"​":{")":{"df":0,"docs":{},"t":{"(":{"df":0,"docs":{},"x":{"0":{"df":0,"docs":{},"​":{"df":0,"docs":{},"g":{")":{"df":0,"docs":{},"t":{"(":{"df":0,"docs":{},"x":{"0":{"df":0,"docs":{},"​":{"df":0,"docs":{},"g":{"2":{"df":1,"docs":{"114":{"tf":1.0}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{},"g":{"2":{")":{"df":0,"docs":{},"−":{"df":0,"docs":{},"t":{"(":{"df":0,"docs":{},"x":{"0":{"df":0,"docs":{},"​":{"df":0,"docs":{},"g":{")":{"df":0,"docs":{},"−":{"df":0,"docs":{},"t":{"(":{"df":0,"docs":{},"x":{"0":{"df":1,"docs":{"114":{"tf":1.0}}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"_":{"0":{"df":3,"docs":{"60":{"tf":1.7320508075688772},"62":{"tf":1.4142135623730951},"63":{"tf":1.4142135623730951}}},"df":0,"docs":{}},"df":3,"docs":{"35":{"tf":1.7320508075688772},"52":{"tf":1.4142135623730951},"54":{"tf":1.0}},"g":{"2":{")":{"df":0,"docs":{},"−":{"df":0,"docs":{},"t":{"(":{"df":0,"docs":{},"x":{"df":0,"docs":{},"g":{")":{"df":0,"docs":{},"−":{"df":0,"docs":{},"t":{"(":{"df":0,"docs":{},"x":{"df":1,"docs":{"114":{"tf":1.0}}}},"df":0,"docs":{}}}},"df":0,"docs":{}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":1,"docs":{"52":{"tf":1.0}}}},"z":{"df":7,"docs":{"112":{"tf":1.0},"117":{"tf":1.0},"58":{"tf":1.0},"59":{"tf":1.0},"60":{"tf":1.0},"62":{"tf":1.0},"63":{"tf":1.0}},"g":{"2":{"df":2,"docs":{"112":{"tf":1.0},"117":{"tf":1.0}}},"^":{"2":{"df":5,"docs":{"58":{"tf":1.0},"59":{"tf":1.0},"60":{"tf":1.0},"62":{"tf":1.0},"63":{"tf":1.0}}},"df":0,"docs":{}},"df":7,"docs":{"112":{"tf":1.0},"117":{"tf":1.0},"58":{"tf":1.0},"59":{"tf":1.0},"60":{"tf":1.0},"62":{"tf":1.0},"63":{"tf":1.0}}}},"ω":{"0":{")":{"df":0,"docs":{},"t":{"(":{"df":0,"docs":{},"ω":{"1":{")":{"df":0,"docs":{},"…":{"df":0,"docs":{},"t":{"(":{"df":0,"docs":{},"ω":{"1":{"5":{"df":1,"docs":{"114":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"2":{")":{"df":0,"docs":{},"t":{"(":{"df":0,"docs":{},"ω":{"4":{"df":1,"docs":{"114":{"tf":1.0}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{},"g":{"2":{")":{"df":0,"docs":{},"−":{"df":0,"docs":{},"t":{"(":{"df":0,"docs":{},"ω":{"0":{"df":0,"docs":{},"g":{")":{"df":0,"docs":{},"−":{"df":0,"docs":{},"t":{"(":{"df":0,"docs":{},"ω":{"0":{")":{"df":0,"docs":{},"t":{"(":{"df":0,"docs":{},"ω":{"df":0,"docs":{},"g":{"2":{")":{"df":0,"docs":{},"−":{"df":0,"docs":{},"t":{"(":{"df":0,"docs":{},"ω":{"df":0,"docs":{},"g":{")":{"df":0,"docs":{},"−":{"df":0,"docs":{},"t":{"(":{"df":0,"docs":{},"ω":{")":{"df":0,"docs":{},"t":{"(":{"df":0,"docs":{},"ω":{"2":{"df":0,"docs":{},"g":{"2":{")":{"df":0,"docs":{},"−":{"df":0,"docs":{},"t":{"(":{"df":0,"docs":{},"ω":{"2":{"df":0,"docs":{},"g":{")":{"df":0,"docs":{},"−":{"df":0,"docs":{},"t":{"(":{"df":0,"docs":{},"ω":{"2":{")":{"df":0,"docs":{},"⋮":{"df":0,"docs":{},"t":{"(":{"df":0,"docs":{},"ω":{"1":{"5":{"df":0,"docs":{},"g":{"2":{")":{"df":0,"docs":{},"−":{"df":0,"docs":{},"t":{"(":{"df":0,"docs":{},"ω":{"1":{"5":{"df":0,"docs":{},"g":{")":{"df":0,"docs":{},"−":{"df":0,"docs":{},"t":{"(":{"df":0,"docs":{},"ω":{"1":{"5":{"df":1,"docs":{"114":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"2":{"df":0,"docs":{},"i":{")":{":":{"0":{"df":0,"docs":{},"≤":{"df":0,"docs":{},"i":{"df":0,"docs":{},"≤":{"1":{"5":{"df":0,"docs":{},"}":{"=":{"df":0,"docs":{},"{":{"df":0,"docs":{},"t":{"(":{"df":0,"docs":{},"g":{"df":0,"docs":{},"i":{")":{":":{"0":{"df":0,"docs":{},"≤":{"df":0,"docs":{},"i":{"df":0,"docs":{},"≤":{"7":{"df":1,"docs":{"68":{"tf":1.0}}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"4":{")":{"df":0,"docs":{},"−":{"df":0,"docs":{},"t":{"(":{"df":0,"docs":{},"ω":{"2":{")":{"df":0,"docs":{},"−":{"df":0,"docs":{},"t":{"(":{"df":0,"docs":{},"ω":{"0":{")":{"df":0,"docs":{},"t":{"(":{"df":0,"docs":{},"ω":{"5":{")":{"df":0,"docs":{},"−":{"df":0,"docs":{},"t":{"(":{"df":0,"docs":{},"ω":{"3":{")":{"df":0,"docs":{},"−":{"df":0,"docs":{},"t":{"(":{"df":0,"docs":{},"ω":{")":{"df":0,"docs":{},"t":{"(":{"df":0,"docs":{},"ω":{"6":{")":{"df":0,"docs":{},"−":{"df":0,"docs":{},"t":{"(":{"df":0,"docs":{},"ω":{"4":{")":{"df":0,"docs":{},"−":{"df":0,"docs":{},"t":{"(":{"df":0,"docs":{},"ω":{"2":{")":{"df":0,"docs":{},"⋮":{"df":0,"docs":{},"t":{"(":{"df":0,"docs":{},"ω":{"3":{")":{"df":0,"docs":{},"−":{"df":0,"docs":{},"t":{"(":{"df":0,"docs":{},"ω":{")":{"df":0,"docs":{},"−":{"df":0,"docs":{},"t":{"(":{"df":0,"docs":{},"ω":{"1":{"5":{"df":1,"docs":{"114":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"0":{",":{"df":0,"docs":{},"i":{"df":1,"docs":{"20":{"tf":1.0}}},"j":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"s":{"df":0,"docs":{},"σ":{"1":{"df":0,"docs":{},"​":{"(":{"df":0,"docs":{},"ω":{"df":0,"docs":{},"i":{")":{"df":0,"docs":{},"β":{"+":{"df":0,"docs":{},"γ":{")":{"(":{"df":0,"docs":{},"t":{"0":{",":{"df":0,"docs":{},"j":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"s":{"df":0,"docs":{},"σ":{"2":{"df":0,"docs":{},"​":{"(":{"df":0,"docs":{},"ω":{"df":0,"docs":{},"i":{")":{"df":0,"docs":{},"β":{"+":{"df":0,"docs":{},"γ":{")":{"(":{"df":0,"docs":{},"t":{"0":{",":{"df":0,"docs":{},"j":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"s":{"df":0,"docs":{},"σ":{"3":{"df":0,"docs":{},"​":{"(":{"df":0,"docs":{},"ω":{"df":0,"docs":{},"i":{")":{"df":0,"docs":{},"β":{"+":{"df":0,"docs":{},"γ":{")":{":":{"0":{"df":0,"docs":{},"≤":{"df":0,"docs":{},"i":{"<":{"df":0,"docs":{},"n":{"df":1,"docs":{"14":{"tf":1.0}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"ω":{"df":0,"docs":{},"i":{"df":0,"docs":{},"β":{"+":{"df":0,"docs":{},"γ":{")":{"(":{"df":0,"docs":{},"t":{"1":{",":{"df":0,"docs":{},"j":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"ω":{"df":0,"docs":{},"i":{"df":0,"docs":{},"k":{"1":{"df":0,"docs":{},"​":{"df":0,"docs":{},"β":{"+":{"df":0,"docs":{},"γ":{")":{"(":{"df":0,"docs":{},"t":{"2":{",":{"df":0,"docs":{},"j":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"ω":{"df":0,"docs":{},"i":{"df":0,"docs":{},"k":{"2":{"df":0,"docs":{},"​":{"df":0,"docs":{},"β":{"+":{"df":0,"docs":{},"γ":{")":{":":{"0":{"df":0,"docs":{},"≤":{"df":0,"docs":{},"i":{"<":{"df":0,"docs":{},"n":{"df":1,"docs":{"14":{"tf":1.0}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}}},"df":0,"docs":{}}}},"df":4,"docs":{"136":{"tf":1.0},"142":{"tf":1.0},"146":{"tf":2.0},"147":{"tf":1.0}}},"1":{"df":5,"docs":{"136":{"tf":1.0},"142":{"tf":1.0},"146":{"tf":1.7320508075688772},"147":{"tf":1.0},"16":{"tf":1.0}}},"2":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"t":{"3":{"df":1,"docs":{"16":{"tf":1.0}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"=":{"df":0,"docs":{},"t":{"df":0,"docs":{},"l":{"df":0,"docs":{},"o":{"df":0,"docs":{},"′":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"x":{"df":0,"docs":{},"n":{"+":{"2":{"df":0,"docs":{},"t":{"df":0,"docs":{},"m":{"df":0,"docs":{},"i":{"d":{"df":0,"docs":{},"′":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"x":{"2":{"(":{"df":0,"docs":{},"n":{"+":{"2":{")":{"df":0,"docs":{},"t":{"df":0,"docs":{},"h":{"df":0,"docs":{},"i":{"df":1,"docs":{"26":{"tf":1.0}}}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}}}}}},"_":{"1":{"(":{"df":0,"docs":{},"z":{"df":0,"docs":{},"g":{"df":1,"docs":{"66":{"tf":1.0}}}}},"df":0,"docs":{}},"2":{"(":{"df":0,"docs":{},"z":{"df":1,"docs":{"66":{"tf":1.0}}}},"df":0,"docs":{}},"df":0,"docs":{},"h":{"df":0,"docs":{},"i":{"_":{"1":{"df":1,"docs":{"35":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}}},"i":{"(":{"df":0,"docs":{},"x":{"df":1,"docs":{"65":{"tf":1.0}}}},"df":0,"docs":{}},"l":{"df":0,"docs":{},"o":{"_":{"1":{"df":1,"docs":{"35":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}}},"m":{"df":0,"docs":{},"i":{"d":{"_":{"1":{"df":1,"docs":{"35":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"z":{"df":0,"docs":{},"e":{"df":0,"docs":{},"t":{"a":{"df":1,"docs":{"35":{"tf":1.0}}},"df":0,"docs":{}}}}},"a":{"b":{"df":0,"docs":{},"l":{"df":9,"docs":{"108":{"tf":1.7320508075688772},"119":{"tf":1.0},"127":{"tf":1.0},"137":{"tf":1.0},"145":{"tf":1.0},"48":{"tf":1.0},"49":{"tf":1.4142135623730951},"70":{"tf":1.0},"9":{"tf":1.0}}}},"c":{"df":0,"docs":{},"k":{"df":0,"docs":{},"l":{"df":1,"docs":{"64":{"tf":1.0}}}}},"df":0,"docs":{},"k":{"df":0,"docs":{},"e":{"df":23,"docs":{"104":{"tf":1.4142135623730951},"113":{"tf":2.0},"123":{"tf":1.0},"13":{"tf":1.4142135623730951},"14":{"tf":2.0},"149":{"tf":1.0},"151":{"tf":1.4142135623730951},"19":{"tf":1.7320508075688772},"20":{"tf":1.7320508075688772},"34":{"tf":1.0},"41":{"tf":1.0},"44":{"tf":1.0},"51":{"tf":1.0},"52":{"tf":1.4142135623730951},"56":{"tf":1.0},"63":{"tf":1.4142135623730951},"68":{"tf":1.4142135623730951},"7":{"tf":1.4142135623730951},"70":{"tf":1.4142135623730951},"74":{"tf":1.0},"8":{"tf":1.0},"80":{"tf":1.0},"9":{"tf":1.0}},"n":{"df":1,"docs":{"20":{"tf":1.0}}}}},"l":{"df":0,"docs":{},"k":{"df":4,"docs":{"105":{"tf":1.0},"107":{"tf":1.0},"116":{"tf":1.0},"52":{"tf":1.4142135623730951}}}}},"df":27,"docs":{"11":{"tf":1.0},"112":{"tf":1.7320508075688772},"114":{"tf":1.4142135623730951},"13":{"tf":1.0},"14":{"tf":1.7320508075688772},"142":{"tf":1.7320508075688772},"147":{"tf":1.4142135623730951},"16":{"tf":2.449489742783178},"20":{"tf":1.4142135623730951},"23":{"tf":1.0},"24":{"tf":1.0},"26":{"tf":1.0},"28":{"tf":1.0},"32":{"tf":1.7320508075688772},"34":{"tf":1.4142135623730951},"35":{"tf":1.4142135623730951},"36":{"tf":1.4142135623730951},"52":{"tf":1.7320508075688772},"60":{"tf":1.0},"62":{"tf":1.7320508075688772},"68":{"tf":2.23606797749979},"69":{"tf":1.4142135623730951},"70":{"tf":2.0},"83":{"tf":1.4142135623730951},"84":{"tf":1.7320508075688772},"91":{"tf":1.7320508075688772},"94":{"tf":1.0}},"e":{"c":{"df":0,"docs":{},"h":{"df":0,"docs":{},"n":{"df":0,"docs":{},"i":{"c":{"df":1,"docs":{"52":{"tf":1.0}}},"df":0,"docs":{},"q":{"df":0,"docs":{},"u":{"df":1,"docs":{"88":{"tf":1.0}}}}}}}},"df":0,"docs":{},"l":{"df":0,"docs":{},"l":{"df":3,"docs":{"109":{"tf":1.0},"125":{"tf":1.0},"49":{"tf":1.0}}}},"m":{"df":0,"docs":{},"p":{"df":0,"docs":{},"o":{"df":0,"docs":{},"r":{"a":{"df":0,"docs":{},"r":{"df":0,"docs":{},"i":{"df":3,"docs":{"142":{"tf":1.0},"146":{"tf":1.4142135623730951},"147":{"tf":1.0}},"l":{"df":0,"docs":{},"i":{"df":1,"docs":{"34":{"tf":1.4142135623730951}}}}}}},"df":0,"docs":{}}}}},"r":{"df":0,"docs":{},"m":{"df":14,"docs":{"114":{"tf":1.4142135623730951},"123":{"tf":1.0},"125":{"tf":1.0},"128":{"tf":1.0},"14":{"tf":1.0},"144":{"tf":1.4142135623730951},"16":{"tf":1.0},"20":{"tf":1.0},"52":{"tf":1.4142135623730951},"65":{"tf":1.0},"66":{"tf":1.0},"67":{"tf":1.4142135623730951},"80":{"tf":1.0},"85":{"tf":1.0}},"i":{"df":0,"docs":{},"n":{"df":0,"docs":{},"o":{"df":0,"docs":{},"l":{"df":0,"docs":{},"o":{"df":0,"docs":{},"g":{"df":1,"docs":{"34":{"tf":1.0}}}}}}}}}},"s":{"df":0,"docs":{},"t":{"_":{"c":{"df":0,"docs":{},"o":{"df":0,"docs":{},"m":{"df":0,"docs":{},"m":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"_":{"df":0,"docs":{},"p":{"df":0,"docs":{},"r":{"df":0,"docs":{},"e":{"df":0,"docs":{},"p":{"df":0,"docs":{},"r":{"df":0,"docs":{},"o":{"c":{"df":0,"docs":{},"e":{"df":0,"docs":{},"s":{"df":0,"docs":{},"s":{"df":0,"docs":{},"e":{"d":{"_":{"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"df":0,"docs":{},"p":{"df":0,"docs":{},"u":{"df":0,"docs":{},"t":{"_":{"2":{"df":1,"docs":{"35":{"tf":1.4142135623730951}}},"df":0,"docs":{}},"df":0,"docs":{}}}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}}}},"df":0,"docs":{}}}}}}}},"df":0,"docs":{}}}}}}},"df":0,"docs":{},"p":{"df":0,"docs":{},"r":{"df":0,"docs":{},"o":{"df":0,"docs":{},"v":{"df":0,"docs":{},"e":{"_":{"df":0,"docs":{},"f":{"df":0,"docs":{},"i":{"b":{"df":1,"docs":{"104":{"tf":1.0}}},"df":0,"docs":{}}}},"df":0,"docs":{}}}}}},"s":{"df":0,"docs":{},"r":{"df":0,"docs":{},"s":{"(":{"c":{"df":0,"docs":{},"o":{"df":0,"docs":{},"m":{"df":0,"docs":{},"m":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{".":{"df":0,"docs":{},"n":{"df":1,"docs":{"44":{"tf":1.0}}}},"_":{"df":0,"docs":{},"p":{"df":0,"docs":{},"r":{"df":0,"docs":{},"e":{"df":0,"docs":{},"p":{"df":0,"docs":{},"r":{"df":0,"docs":{},"o":{"c":{"df":0,"docs":{},"e":{"df":0,"docs":{},"s":{"df":0,"docs":{},"s":{"df":0,"docs":{},"e":{"d":{"_":{"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"df":0,"docs":{},"p":{"df":0,"docs":{},"u":{"df":0,"docs":{},"t":{".":{"df":0,"docs":{},"n":{"df":1,"docs":{"35":{"tf":1.4142135623730951}}}},"df":0,"docs":{}}}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}}}},"df":0,"docs":{}}}}}}}},"df":0,"docs":{}}}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"w":{"df":0,"docs":{},"i":{"df":0,"docs":{},"t":{"df":0,"docs":{},"n":{"df":0,"docs":{},"e":{"df":0,"docs":{},"s":{"df":0,"docs":{},"s":{"_":{"2":{"(":{"df":0,"docs":{},"x":{"df":1,"docs":{"35":{"tf":1.7320508075688772}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}}}}}}},"df":5,"docs":{"104":{"tf":1.0},"114":{"tf":1.0},"34":{"tf":1.0},"35":{"tf":1.4142135623730951},"81":{"tf":1.0}},"h":{"a":{"df":0,"docs":{},"s":{"df":0,"docs":{},"h":{"df":1,"docs":{"110":{"tf":1.0}}}}},"df":0,"docs":{}},"r":{"a":{"df":0,"docs":{},"n":{"d":{"df":0,"docs":{},"o":{"df":0,"docs":{},"m":{"df":0,"docs":{},"f":{"df":0,"docs":{},"i":{"df":0,"docs":{},"e":{"df":0,"docs":{},"l":{"d":{"df":0,"docs":{},"g":{"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":2,"docs":{"35":{"tf":1.4142135623730951},"44":{"tf":1.0}}}}}}}},"df":0,"docs":{}}}}}}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}},"h":{"a":{"df":0,"docs":{},"n":{"df":0,"docs":{},"k":{"df":2,"docs":{"103":{"tf":1.0},"81":{"tf":1.0}}}},"t":{"'":{"df":10,"docs":{"142":{"tf":1.4142135623730951},"146":{"tf":1.0},"147":{"tf":1.0},"151":{"tf":1.0},"16":{"tf":1.0},"46":{"tf":1.0},"54":{"tf":1.0},"79":{"tf":1.0},"9":{"tf":1.0},"95":{"tf":1.0}}},"df":0,"docs":{}}},"df":17,"docs":{"112":{"tf":1.0},"123":{"tf":1.0},"124":{"tf":2.8284271247461903},"125":{"tf":1.7320508075688772},"14":{"tf":3.0},"142":{"tf":1.0},"16":{"tf":1.0},"23":{"tf":1.0},"35":{"tf":1.0},"49":{"tf":1.4142135623730951},"52":{"tf":1.7320508075688772},"62":{"tf":1.0},"65":{"tf":1.0},"68":{"tf":2.23606797749979},"70":{"tf":1.7320508075688772},"91":{"tf":1.0},"92":{"tf":2.0}},"e":{"df":0,"docs":{},"o":{"df":0,"docs":{},"r":{"df":0,"docs":{},"e":{"df":0,"docs":{},"m":{"df":1,"docs":{"73":{"tf":1.0}}}}}},"r":{"df":0,"docs":{},"e":{"'":{"df":6,"docs":{"102":{"tf":1.0},"116":{"tf":1.0},"14":{"tf":1.0},"146":{"tf":1.0},"22":{"tf":1.0},"7":{"tf":1.0}}},"df":0,"docs":{},"f":{"df":0,"docs":{},"o":{"df":0,"docs":{},"r":{"df":8,"docs":{"101":{"tf":1.0},"12":{"tf":1.0},"20":{"tf":1.0},"36":{"tf":1.0},"38":{"tf":1.0},"41":{"tf":1.0},"66":{"tf":1.0},"94":{"tf":1.0}}}}}}},"y":{"'":{"df":0,"docs":{},"r":{"df":2,"docs":{"3":{"tf":1.0},"68":{"tf":1.0}}}},"df":0,"docs":{}}},"i":{"df":1,"docs":{"26":{"tf":1.0}},"n":{"df":0,"docs":{},"g":{"df":16,"docs":{"104":{"tf":1.0},"109":{"tf":1.4142135623730951},"111":{"tf":1.0},"113":{"tf":1.0},"124":{"tf":1.0},"14":{"tf":1.7320508075688772},"16":{"tf":1.4142135623730951},"21":{"tf":1.0},"49":{"tf":1.4142135623730951},"50":{"tf":1.0},"52":{"tf":1.0},"53":{"tf":1.0},"56":{"tf":1.0},"58":{"tf":1.0},"59":{"tf":1.0},"68":{"tf":1.0}}},"k":{"df":4,"docs":{"118":{"tf":1.4142135623730951},"151":{"tf":1.4142135623730951},"52":{"tf":1.0},"70":{"tf":1.0}}}},"r":{"d":{"_":{"df":0,"docs":{},"r":{"df":0,"docs":{},"o":{"df":0,"docs":{},"w":{"[":{"0":{"df":1,"docs":{"107":{"tf":1.0}}},"df":0,"docs":{}},"df":2,"docs":{"107":{"tf":1.0},"114":{"tf":1.0}}}}}},"df":1,"docs":{"13":{"tf":1.0}}},"df":0,"docs":{}}},"o":{"df":0,"docs":{},"s":{"df":0,"docs":{},"e":{"df":3,"docs":{"102":{"tf":1.0},"14":{"tf":1.0},"3":{"tf":1.0}}}},"u":{"df":0,"docs":{},"g":{"df":0,"docs":{},"h":{"df":6,"docs":{"114":{"tf":1.0},"118":{"tf":1.0},"49":{"tf":1.0},"52":{"tf":1.0},"57":{"tf":1.0},"66":{"tf":1.0}}}}}},"r":{"df":0,"docs":{},"e":{"df":0,"docs":{},"e":{"df":16,"docs":{"107":{"tf":1.0},"12":{"tf":1.0},"128":{"tf":1.0},"130":{"tf":1.4142135623730951},"141":{"tf":1.0},"142":{"tf":1.4142135623730951},"144":{"tf":1.4142135623730951},"149":{"tf":1.0},"150":{"tf":1.0},"21":{"tf":1.4142135623730951},"3":{"tf":1.0},"44":{"tf":1.0},"58":{"tf":1.0},"69":{"tf":1.0},"84":{"tf":1.0},"9":{"tf":1.0}}}},"o":{"df":0,"docs":{},"u":{"df":0,"docs":{},"g":{"df":0,"docs":{},"h":{"df":8,"docs":{"105":{"tf":1.0},"106":{"tf":1.0},"111":{"tf":1.0},"113":{"tf":1.0},"4":{"tf":1.0},"46":{"tf":1.0},"48":{"tf":1.7320508075688772},"51":{"tf":1.0}},"o":{"df":0,"docs":{},"u":{"df":0,"docs":{},"t":{"df":4,"docs":{"118":{"tf":1.7320508075688772},"124":{"tf":1.4142135623730951},"49":{"tf":1.0},"7":{"tf":1.0}}}}}}}}}},"u":{"df":3,"docs":{"57":{"tf":1.0},"58":{"tf":1.0},"83":{"tf":1.0}}}},"i":{",":{"df":0,"docs":{},"j":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"η":{"3":{"df":0,"docs":{},"i":{"+":{"df":0,"docs":{},"j":{"df":0,"docs":{},"β":{"+":{"df":0,"docs":{},"γ":{":":{"(":{"df":0,"docs":{},"i":{",":{"df":0,"docs":{},"j":{")":{"df":0,"docs":{},"∈":{"df":0,"docs":{},"i":{"df":1,"docs":{"14":{"tf":1.0}}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"k":{"+":{"df":0,"docs":{},"l":{"df":0,"docs":{},"β":{"+":{"df":0,"docs":{},"γ":{":":{"(":{"df":0,"docs":{},"i":{",":{"df":0,"docs":{},"j":{")":{"df":0,"docs":{},"∈":{"df":0,"docs":{},"i":{",":{"df":0,"docs":{},"σ":{"(":{"(":{"df":0,"docs":{},"i":{",":{"df":0,"docs":{},"j":{")":{")":{"=":{"(":{"df":0,"docs":{},"k":{",":{"df":0,"docs":{},"l":{"df":1,"docs":{"14":{"tf":1.0}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"=":{"df":0,"docs":{},"t":{"df":0,"docs":{},"j":{"df":0,"docs":{},"​":{"(":{"df":0,"docs":{},"g":{"df":0,"docs":{},"i":{"df":2,"docs":{"83":{"tf":1.0},"84":{"tf":1.0}}}}},"df":0,"docs":{}}},"k":{",":{"df":0,"docs":{},"l":{"df":4,"docs":{"11":{"tf":1.0},"13":{"tf":1.0},"14":{"tf":1.0},"16":{"tf":1.0}}}},"df":0,"docs":{}},"σ":{"(":{"(":{"df":0,"docs":{},"i":{",":{"df":0,"docs":{},"j":{"df":1,"docs":{"14":{"tf":1.0}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}}},"df":1,"docs":{"16":{"tf":1.0}},"j":{"df":1,"docs":{"70":{"tf":1.0}}},"m":{"df":0,"docs":{},"e":{"df":10,"docs":{"101":{"tf":1.0},"118":{"tf":2.0},"133":{"tf":1.0},"146":{"tf":1.4142135623730951},"149":{"tf":1.0},"22":{"tf":1.0},"3":{"tf":1.4142135623730951},"45":{"tf":1.0},"59":{"tf":1.0},"80":{"tf":2.0}}}}},"j":{"df":5,"docs":{"70":{"tf":1.7320508075688772},"83":{"tf":1.4142135623730951},"84":{"tf":1.0},"94":{"tf":1.7320508075688772},"95":{"tf":2.6457513110645907}},"​":{"(":{"df":0,"docs":{},"g":{"df":0,"docs":{},"i":{")":{"=":{"df":0,"docs":{},"m":{"^":{"df":0,"docs":{},"i":{",":{"df":0,"docs":{},"j":{"df":1,"docs":{"94":{"tf":1.0}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"t":{"df":0,"docs":{},"i":{",":{"df":0,"docs":{},"j":{"df":1,"docs":{"94":{"tf":1.0}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}},"z":{"df":3,"docs":{"85":{"tf":1.0},"94":{"tf":1.4142135623730951},"95":{"tf":1.0}}}},"z":{"df":3,"docs":{"85":{"tf":1.0},"94":{"tf":1.4142135623730951},"95":{"tf":1.4142135623730951}}}},",":{"df":0,"docs":{},"τ":{"df":0,"docs":{},"j":{"df":0,"docs":{},"z":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"τ":{"df":0,"docs":{},"j":{"df":0,"docs":{},"g":{"df":0,"docs":{},"z":{"df":0,"docs":{},"​":{":":{"0":{"df":0,"docs":{},"≤":{"df":0,"docs":{},"j":{"<":{"df":0,"docs":{},"m":{"df":0,"docs":{},"}":{",":{"df":0,"docs":{},"h":{"1":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"η":{"1":{"df":0,"docs":{},"z":{"2":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"h":{"2":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"η":{"2":{"df":0,"docs":{},"z":{"2":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"{":{"df":0,"docs":{},"p":{"df":0,"docs":{},"k":{"df":0,"docs":{},"​":{":":{"1":{"df":0,"docs":{},"≤":{"df":0,"docs":{},"k":{"<":{"df":0,"docs":{},"n":{"df":0,"docs":{},"}":{",":{"df":0,"docs":{},"π":{",":{"df":0,"docs":{},"y":{",":{"df":0,"docs":{},"{":{"(":{"df":0,"docs":{},"τ":{"df":0,"docs":{},"j":{"df":0,"docs":{},"υ":{"df":0,"docs":{},"s":{"df":0,"docs":{},"​":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"t":{"df":0,"docs":{},"j":{"df":0,"docs":{},"​":{")":{":":{"0":{"df":0,"docs":{},"≤":{"df":0,"docs":{},"j":{"<":{"df":0,"docs":{},"m":{",":{"0":{"df":0,"docs":{},"≤":{"df":0,"docs":{},"s":{"<":{"df":0,"docs":{},"q":{"df":0,"docs":{},"}":{",":{"df":0,"docs":{},"{":{"(":{"df":0,"docs":{},"η":{"1":{"df":0,"docs":{},"υ":{"df":0,"docs":{},"s":{"df":0,"docs":{},"​":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"h":{"1":{"df":0,"docs":{},"​":{")":{":":{"0":{"df":0,"docs":{},"≤":{"df":0,"docs":{},"s":{"<":{"df":0,"docs":{},"q":{"df":0,"docs":{},"}":{"df":0,"docs":{},"{":{"(":{"df":0,"docs":{},"η":{"2":{"df":0,"docs":{},"υ":{"df":0,"docs":{},"s":{"df":0,"docs":{},"​":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"h":{"2":{"df":0,"docs":{},"​":{")":{":":{"0":{"df":0,"docs":{},"≤":{"df":0,"docs":{},"s":{"<":{"df":0,"docs":{},"q":{"df":0,"docs":{},"}":{",":{"df":0,"docs":{},"{":{"(":{"df":0,"docs":{},"π":{"df":0,"docs":{},"k":{"df":0,"docs":{},"υ":{"df":0,"docs":{},"s":{"2":{"df":0,"docs":{},"k":{"df":0,"docs":{},"​":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"p":{"df":0,"docs":{},"k":{"df":0,"docs":{},"​":{")":{":":{"1":{"df":0,"docs":{},"≤":{"df":0,"docs":{},"k":{"<":{"df":0,"docs":{},"n":{",":{"0":{"df":0,"docs":{},"≤":{"df":0,"docs":{},"s":{"<":{"df":0,"docs":{},"q":{"df":0,"docs":{},"}":{",":{"df":0,"docs":{},"{":{"(":{"df":0,"docs":{},"τ":{"df":0,"docs":{},"j":{"df":0,"docs":{},"−":{"df":0,"docs":{},"υ":{"df":0,"docs":{},"s":{"df":0,"docs":{},"​":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"t":{"df":0,"docs":{},"j":{"df":0,"docs":{},"′":{"df":0,"docs":{},"​":{")":{":":{"0":{"df":0,"docs":{},"≤":{"df":0,"docs":{},"j":{"<":{"df":0,"docs":{},"m":{",":{"0":{"df":0,"docs":{},"≤":{"df":0,"docs":{},"s":{"<":{"df":0,"docs":{},"q":{"df":0,"docs":{},"}":{",":{"df":0,"docs":{},"{":{"(":{"df":0,"docs":{},"η":{"1":{"df":0,"docs":{},"−":{"df":0,"docs":{},"υ":{"df":0,"docs":{},"s":{"df":0,"docs":{},"​":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"h":{"1":{"df":0,"docs":{},"′":{"df":0,"docs":{},"​":{")":{":":{"0":{"df":0,"docs":{},"≤":{"df":0,"docs":{},"s":{"<":{"df":0,"docs":{},"q":{"df":0,"docs":{},"}":{"df":0,"docs":{},"{":{"(":{"df":0,"docs":{},"η":{"2":{"df":0,"docs":{},"−":{"df":0,"docs":{},"υ":{"df":0,"docs":{},"s":{"df":0,"docs":{},"​":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"h":{"2":{"df":0,"docs":{},"′":{"df":0,"docs":{},"​":{")":{":":{"0":{"df":0,"docs":{},"≤":{"df":0,"docs":{},"s":{"<":{"df":0,"docs":{},"q":{"df":0,"docs":{},"}":{",":{"df":0,"docs":{},"{":{"(":{"df":0,"docs":{},"π":{"df":0,"docs":{},"k":{"df":0,"docs":{},"−":{"df":0,"docs":{},"υ":{"df":0,"docs":{},"s":{"2":{"df":0,"docs":{},"k":{"df":0,"docs":{},"​":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"p":{"df":0,"docs":{},"k":{"df":0,"docs":{},"′":{"df":0,"docs":{},"​":{")":{":":{"1":{"df":0,"docs":{},"≤":{"df":0,"docs":{},"k":{"<":{"df":0,"docs":{},"n":{",":{"0":{"df":0,"docs":{},"≤":{"df":0,"docs":{},"s":{"<":{"df":0,"docs":{},"q":{"df":1,"docs":{"95":{"tf":1.0}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}}}},"df":0,"docs":{}}}}},"df":0,"docs":{}}}}}}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}}}},"df":0,"docs":{}}}}}}}}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}}}}},"df":0,"docs":{}}}}}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}}}}}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}}}}}},"df":0,"docs":{}}}}}},"]":{",":{"df":0,"docs":{},"t":{"df":0,"docs":{},"j":{"df":0,"docs":{},"​":{"(":{"df":0,"docs":{},"z":{")":{",":{"df":0,"docs":{},"t":{"df":0,"docs":{},"j":{"df":0,"docs":{},"​":{"(":{"df":0,"docs":{},"g":{"df":0,"docs":{},"z":{")":{":":{"0":{"df":0,"docs":{},"≤":{"df":0,"docs":{},"j":{"<":{"df":0,"docs":{},"m":{"df":0,"docs":{},"}":{",":{"[":{"df":0,"docs":{},"h":{"1":{"df":0,"docs":{},"​":{"]":{",":{"df":0,"docs":{},"h":{"1":{"df":0,"docs":{},"​":{"(":{"df":0,"docs":{},"z":{"2":{")":{",":{"[":{"df":0,"docs":{},"h":{"2":{"df":0,"docs":{},"​":{"]":{",":{"df":0,"docs":{},"h":{"2":{"df":0,"docs":{},"​":{"(":{"df":0,"docs":{},"z":{"2":{")":{",":{"df":0,"docs":{},"{":{"[":{"df":0,"docs":{},"p":{"df":0,"docs":{},"k":{"df":0,"docs":{},"​":{"]":{":":{"1":{"df":0,"docs":{},"≤":{"df":0,"docs":{},"k":{"<":{"df":0,"docs":{},"n":{"df":0,"docs":{},"}":{",":{"df":0,"docs":{},"p":{"df":0,"docs":{},"n":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"y":{",":{"df":0,"docs":{},"{":{"df":0,"docs":{},"o":{"df":0,"docs":{},"p":{"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"(":{"df":0,"docs":{},"t":{"df":0,"docs":{},"j":{"df":0,"docs":{},"​":{"(":{"d":{"df":0,"docs":{},"l":{"d":{"df":0,"docs":{},"e":{"df":0,"docs":{},"​":{")":{",":{"df":0,"docs":{},"υ":{"df":0,"docs":{},"s":{"df":0,"docs":{},"​":{")":{":":{"0":{"df":0,"docs":{},"≤":{"df":0,"docs":{},"j":{"<":{"df":0,"docs":{},"m":{",":{"0":{"df":0,"docs":{},"≤":{"df":0,"docs":{},"s":{"<":{"df":0,"docs":{},"q":{"df":0,"docs":{},"}":{",":{"df":0,"docs":{},"{":{"df":0,"docs":{},"o":{"df":0,"docs":{},"p":{"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"(":{"df":0,"docs":{},"h":{"1":{"df":0,"docs":{},"​":{"(":{"d":{"df":0,"docs":{},"l":{"d":{"df":0,"docs":{},"e":{"df":0,"docs":{},"​":{")":{",":{"df":0,"docs":{},"υ":{"df":0,"docs":{},"s":{"df":0,"docs":{},"​":{")":{":":{"0":{"df":0,"docs":{},"≤":{"df":0,"docs":{},"s":{"<":{"df":0,"docs":{},"q":{"df":0,"docs":{},"}":{",":{"df":0,"docs":{},"{":{"df":0,"docs":{},"o":{"df":0,"docs":{},"p":{"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"(":{"df":0,"docs":{},"h":{"2":{"df":0,"docs":{},"​":{"(":{"d":{"df":0,"docs":{},"l":{"d":{"df":0,"docs":{},"e":{"df":0,"docs":{},"​":{")":{",":{"df":0,"docs":{},"υ":{"df":0,"docs":{},"s":{"df":0,"docs":{},"​":{")":{":":{"0":{"df":0,"docs":{},"≤":{"df":0,"docs":{},"s":{"<":{"df":0,"docs":{},"q":{"df":0,"docs":{},"}":{",":{"df":0,"docs":{},"{":{"df":0,"docs":{},"o":{"df":0,"docs":{},"p":{"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"(":{"df":0,"docs":{},"p":{"df":0,"docs":{},"k":{"df":0,"docs":{},"​":{"(":{"d":{"df":0,"docs":{},"k":{"df":0,"docs":{},"​":{")":{",":{"df":0,"docs":{},"υ":{"df":0,"docs":{},"s":{"2":{"df":0,"docs":{},"k":{"df":0,"docs":{},"​":{")":{":":{"1":{"df":0,"docs":{},"≤":{"df":0,"docs":{},"k":{"<":{"df":0,"docs":{},"n":{",":{"0":{"df":0,"docs":{},"≤":{"df":0,"docs":{},"s":{"<":{"df":0,"docs":{},"q":{"df":0,"docs":{},"}":{",":{"df":0,"docs":{},"{":{"df":0,"docs":{},"o":{"df":0,"docs":{},"p":{"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"(":{"df":0,"docs":{},"t":{"df":0,"docs":{},"j":{"df":0,"docs":{},"​":{"(":{"d":{"df":0,"docs":{},"l":{"d":{"df":0,"docs":{},"e":{"df":0,"docs":{},"​":{")":{",":{"df":0,"docs":{},"−":{"df":0,"docs":{},"υ":{"df":0,"docs":{},"s":{"df":0,"docs":{},"​":{")":{":":{"0":{"df":0,"docs":{},"≤":{"df":0,"docs":{},"j":{"<":{"df":0,"docs":{},"m":{",":{"0":{"df":0,"docs":{},"≤":{"df":0,"docs":{},"s":{"<":{"df":0,"docs":{},"q":{"df":0,"docs":{},"}":{",":{"df":0,"docs":{},"{":{"df":0,"docs":{},"o":{"df":0,"docs":{},"p":{"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"(":{"df":0,"docs":{},"h":{"1":{"df":0,"docs":{},"​":{"(":{"d":{"df":0,"docs":{},"l":{"d":{"df":0,"docs":{},"e":{"df":0,"docs":{},"​":{")":{",":{"df":0,"docs":{},"−":{"df":0,"docs":{},"υ":{"df":0,"docs":{},"s":{"df":0,"docs":{},"​":{")":{":":{"0":{"df":0,"docs":{},"≤":{"df":0,"docs":{},"s":{"<":{"df":0,"docs":{},"q":{"df":0,"docs":{},"}":{",":{"df":0,"docs":{},"{":{"df":0,"docs":{},"o":{"df":0,"docs":{},"p":{"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"(":{"df":0,"docs":{},"h":{"2":{"df":0,"docs":{},"​":{"(":{"d":{"df":0,"docs":{},"l":{"d":{"df":0,"docs":{},"e":{"df":0,"docs":{},"​":{")":{",":{"df":0,"docs":{},"−":{"df":0,"docs":{},"υ":{"df":0,"docs":{},"s":{"df":0,"docs":{},"​":{")":{":":{"0":{"df":0,"docs":{},"≤":{"df":0,"docs":{},"s":{"<":{"df":0,"docs":{},"q":{"df":0,"docs":{},"}":{",":{"df":0,"docs":{},"{":{"df":0,"docs":{},"o":{"df":0,"docs":{},"p":{"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"(":{"df":0,"docs":{},"p":{"df":0,"docs":{},"k":{"df":0,"docs":{},"​":{"(":{"d":{"df":0,"docs":{},"k":{"df":0,"docs":{},"​":{")":{",":{"df":0,"docs":{},"−":{"df":0,"docs":{},"υ":{"df":0,"docs":{},"s":{"2":{"df":0,"docs":{},"k":{"df":0,"docs":{},"​":{")":{":":{"1":{"df":0,"docs":{},"≤":{"df":0,"docs":{},"k":{"<":{"df":0,"docs":{},"n":{",":{"0":{"df":0,"docs":{},"≤":{"df":0,"docs":{},"s":{"<":{"df":0,"docs":{},"q":{"df":1,"docs":{"94":{"tf":1.0}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}}}}}}},"df":0,"docs":{}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}}}},"df":0,"docs":{}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}}}},"df":0,"docs":{}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}}}}}}},"df":0,"docs":{}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}}}}}}},"df":0,"docs":{}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}}}},"df":0,"docs":{}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}}}},"df":0,"docs":{}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}}}}}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}},"df":0,"docs":{}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}}},":":{"=":{"c":{"df":0,"docs":{},"o":{"df":0,"docs":{},"m":{"df":0,"docs":{},"m":{"df":0,"docs":{},"i":{"df":0,"docs":{},"t":{"(":{"df":0,"docs":{},"t":{"df":0,"docs":{},"j":{"df":0,"docs":{},"​":{"(":{"d":{"df":0,"docs":{},"l":{"df":1,"docs":{"94":{"tf":1.4142135623730951}}}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}}}}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"l":{"df":0,"docs":{},"o":{"df":0,"docs":{},"​":{"]":{"1":{"df":0,"docs":{},"​":{",":{"[":{"df":0,"docs":{},"t":{"df":0,"docs":{},"m":{"df":0,"docs":{},"i":{"d":{"df":0,"docs":{},"​":{"]":{"1":{"df":0,"docs":{},"​":{",":{"[":{"df":0,"docs":{},"t":{"df":0,"docs":{},"h":{"df":0,"docs":{},"i":{"df":0,"docs":{},"​":{"]":{"1":{"df":2,"docs":{"26":{"tf":1.0},"32":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}}}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{},"t":{"df":0,"docs":{},"m":{"df":0,"docs":{},"i":{"d":{"df":0,"docs":{},"​":{"df":0,"docs":{},"t":{"df":0,"docs":{},"h":{"df":0,"docs":{},"i":{"df":0,"docs":{},"​":{"df":0,"docs":{},"​":{"=":{"df":0,"docs":{},"t":{"df":0,"docs":{},"l":{"df":0,"docs":{},"o":{"df":0,"docs":{},"′":{"df":0,"docs":{},"​":{"+":{"b":{"1":{"0":{"df":0,"docs":{},"​":{"df":0,"docs":{},"x":{"df":0,"docs":{},"n":{"+":{"2":{"=":{"df":0,"docs":{},"t":{"df":0,"docs":{},"m":{"df":0,"docs":{},"i":{"d":{"df":0,"docs":{},"′":{"df":0,"docs":{},"​":{"df":0,"docs":{},"−":{"b":{"1":{"0":{"df":0,"docs":{},"​":{"+":{"b":{"1":{"1":{"df":0,"docs":{},"​":{"df":0,"docs":{},"x":{"df":0,"docs":{},"n":{"+":{"2":{"=":{"df":0,"docs":{},"t":{"df":0,"docs":{},"h":{"df":0,"docs":{},"i":{"df":0,"docs":{},"′":{"df":0,"docs":{},"​":{"df":0,"docs":{},"−":{"b":{"1":{"1":{"df":1,"docs":{"26":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}}}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}}}}},"df":0,"docs":{}}}}}}}},"df":0,"docs":{}}}}},"′":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"t":{"df":0,"docs":{},"m":{"df":0,"docs":{},"i":{"d":{"df":1,"docs":{"26":{"tf":1.0}}},"df":0,"docs":{}}}}},"df":0,"docs":{}}}}},"m":{"df":0,"docs":{},"p":{"0":{"df":1,"docs":{"136":{"tf":1.0}}},"1":{"df":1,"docs":{"136":{"tf":1.0}}},"df":0,"docs":{}},"′":{"+":{"1":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"…":{",":{"df":0,"docs":{},"t":{"df":0,"docs":{},"m":{"df":0,"docs":{},"′":{"+":{"df":0,"docs":{},"m":{"df":1,"docs":{"94":{"tf":1.0}}}},"df":0,"docs":{}}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"o":{"df":0,"docs":{},"g":{"df":0,"docs":{},"e":{"df":0,"docs":{},"t":{"df":0,"docs":{},"h":{"df":1,"docs":{"147":{"tf":1.0}}}}}},"o":{"df":0,"docs":{},"l":{"df":5,"docs":{"19":{"tf":1.0},"50":{"tf":1.0},"70":{"tf":1.0},"8":{"tf":1.0},"9":{"tf":1.0}}}},"p":{"df":1,"docs":{"151":{"tf":1.0}}},"t":{"a":{"df":0,"docs":{},"l":{"df":6,"docs":{"133":{"tf":1.0},"135":{"tf":1.4142135623730951},"144":{"tf":1.0},"147":{"tf":1.0},"73":{"tf":1.0},"91":{"tf":1.0}}}},"df":0,"docs":{}},"w":{"a":{"df":0,"docs":{},"r":{"d":{"df":1,"docs":{"50":{"tf":1.0}}},"df":0,"docs":{}}},"df":0,"docs":{}},"y":{"df":3,"docs":{"34":{"tf":1.0},"7":{"tf":2.449489742783178},"9":{"tf":1.0}}}},"p":{"a":{"df":0,"docs":{},"r":{"df":0,"docs":{},"t":{"df":0,"docs":{},"i":{"a":{"df":0,"docs":{},"l":{"df":0,"docs":{},"​":{"(":{"df":0,"docs":{},"ζ":{")":{"=":{"df":0,"docs":{},"t":{"df":1,"docs":{"28":{"tf":1.0}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"+":{"df":0,"docs":{},"υ":{"df":0,"docs":{},"p":{"df":0,"docs":{},"n":{"c":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"υ":{"2":{"a":{"+":{"df":0,"docs":{},"υ":{"3":{"b":{"+":{"df":0,"docs":{},"υ":{"4":{"c":{"+":{"df":0,"docs":{},"υ":{"5":{"df":0,"docs":{},"s":{"df":0,"docs":{},"σ":{"1":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"υ":{"6":{"df":0,"docs":{},"s":{"df":0,"docs":{},"σ":{"2":{"df":1,"docs":{"28":{"tf":1.0}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}},"]":{"1":{"df":1,"docs":{"32":{"tf":1.0}},"​":{"=":{"[":{"df":0,"docs":{},"t":{"df":0,"docs":{},"l":{"df":0,"docs":{},"o":{"df":0,"docs":{},"​":{"]":{"1":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"ζ":{"df":0,"docs":{},"n":{"+":{"2":{"[":{"df":0,"docs":{},"t":{"df":0,"docs":{},"m":{"df":0,"docs":{},"i":{"d":{"df":0,"docs":{},"​":{"]":{"1":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"ζ":{"2":{"(":{"df":0,"docs":{},"n":{"+":{"2":{")":{"[":{"df":0,"docs":{},"t":{"df":0,"docs":{},"h":{"df":0,"docs":{},"i":{"df":0,"docs":{},"​":{"]":{"1":{"df":1,"docs":{"32":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}}}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"r":{"a":{"c":{"df":0,"docs":{},"e":{"'":{"df":2,"docs":{"68":{"tf":1.0},"70":{"tf":1.0}}},".":{"c":{"df":0,"docs":{},"l":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"df":1,"docs":{"108":{"tf":1.0}}}}}},"df":0,"docs":{}},"_":{"c":{"df":0,"docs":{},"o":{"df":0,"docs":{},"l":{"df":0,"docs":{},"u":{"df":0,"docs":{},"m":{"df":0,"docs":{},"n":{"df":1,"docs":{"109":{"tf":1.4142135623730951}}}}}}}},"df":0,"docs":{},"t":{"a":{"b":{"df":0,"docs":{},"l":{"df":0,"docs":{},"e":{".":{"df":0,"docs":{},"n":{"_":{"c":{"df":0,"docs":{},"o":{"df":0,"docs":{},"l":{"df":1,"docs":{"109":{"tf":1.0}}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":1,"docs":{"108":{"tf":1.4142135623730951}}}},"df":68,"docs":{"10":{"tf":1.7320508075688772},"104":{"tf":1.7320508075688772},"105":{"tf":1.4142135623730951},"107":{"tf":1.0},"108":{"tf":2.6457513110645907},"109":{"tf":2.449489742783178},"11":{"tf":1.0},"110":{"tf":1.0},"112":{"tf":1.7320508075688772},"113":{"tf":1.7320508075688772},"114":{"tf":1.4142135623730951},"117":{"tf":1.0},"119":{"tf":1.7320508075688772},"12":{"tf":1.0},"123":{"tf":1.4142135623730951},"125":{"tf":2.0},"127":{"tf":2.8284271247461903},"128":{"tf":1.4142135623730951},"129":{"tf":1.7320508075688772},"13":{"tf":1.0},"130":{"tf":2.0},"131":{"tf":1.4142135623730951},"132":{"tf":1.0},"133":{"tf":2.0},"134":{"tf":1.4142135623730951},"135":{"tf":1.0},"136":{"tf":1.0},"137":{"tf":2.0},"138":{"tf":1.0},"139":{"tf":1.0},"14":{"tf":1.0},"141":{"tf":1.7320508075688772},"142":{"tf":2.0},"143":{"tf":1.7320508075688772},"144":{"tf":2.23606797749979},"145":{"tf":2.0},"146":{"tf":3.3166247903554},"147":{"tf":1.0},"149":{"tf":2.0},"16":{"tf":1.0},"20":{"tf":1.4142135623730951},"23":{"tf":1.0},"34":{"tf":1.0},"35":{"tf":1.0},"48":{"tf":1.4142135623730951},"49":{"tf":1.7320508075688772},"50":{"tf":1.7320508075688772},"51":{"tf":1.4142135623730951},"52":{"tf":3.1622776601683795},"53":{"tf":1.0},"54":{"tf":1.4142135623730951},"58":{"tf":1.7320508075688772},"59":{"tf":1.4142135623730951},"60":{"tf":2.0},"62":{"tf":1.4142135623730951},"63":{"tf":1.4142135623730951},"65":{"tf":2.23606797749979},"66":{"tf":1.7320508075688772},"68":{"tf":1.4142135623730951},"69":{"tf":1.7320508075688772},"70":{"tf":2.8284271247461903},"8":{"tf":1.0},"83":{"tf":2.6457513110645907},"84":{"tf":2.23606797749979},"9":{"tf":2.23606797749979},"91":{"tf":2.23606797749979},"94":{"tf":2.0},"95":{"tf":1.0}},"t":{"df":1,"docs":{"108":{"tf":2.449489742783178}}}}},"d":{"df":0,"docs":{},"i":{"df":0,"docs":{},"t":{"df":1,"docs":{"149":{"tf":1.0}}}},"u":{"c":{"df":1,"docs":{"14":{"tf":1.0}}},"df":0,"docs":{}}},"df":0,"docs":{},"i":{"df":0,"docs":{},"t":{"df":1,"docs":{"105":{"tf":1.0}}}},"n":{"df":0,"docs":{},"s":{"c":{"df":0,"docs":{},"r":{"df":0,"docs":{},"i":{"df":0,"docs":{},"p":{"df":0,"docs":{},"t":{"df":16,"docs":{"113":{"tf":1.0},"118":{"tf":2.0},"147":{"tf":1.0},"24":{"tf":1.4142135623730951},"25":{"tf":1.4142135623730951},"26":{"tf":1.4142135623730951},"27":{"tf":1.4142135623730951},"28":{"tf":1.0},"31":{"tf":1.7320508075688772},"32":{"tf":2.0},"40":{"tf":2.449489742783178},"42":{"tf":1.0},"88":{"tf":1.0},"89":{"tf":1.7320508075688772},"94":{"tf":4.123105625617661},"95":{"tf":3.872983346207417}}}}}}},"df":0,"docs":{},"f":{"df":0,"docs":{},"o":{"df":0,"docs":{},"r":{"df":0,"docs":{},"m":{"df":7,"docs":{"123":{"tf":1.0},"3":{"tf":1.0},"4":{"tf":1.0},"69":{"tf":1.0},"74":{"tf":1.4142135623730951},"79":{"tf":1.4142135623730951},"99":{"tf":1.0}}}}}},"i":{"df":0,"docs":{},"t":{"df":20,"docs":{"105":{"tf":1.4142135623730951},"107":{"tf":2.23606797749979},"109":{"tf":2.23606797749979},"112":{"tf":1.0},"113":{"tf":1.0},"114":{"tf":2.0},"125":{"tf":1.0},"146":{"tf":1.0},"149":{"tf":1.0},"49":{"tf":1.0},"50":{"tf":1.0},"53":{"tf":1.0},"55":{"tf":2.23606797749979},"56":{"tf":1.0},"58":{"tf":1.0},"62":{"tf":1.0},"66":{"tf":2.23606797749979},"70":{"tf":2.0},"84":{"tf":1.7320508075688772},"91":{"tf":1.4142135623730951}},"i":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"_":{"d":{"df":0,"docs":{},"e":{"df":0,"docs":{},"g":{"df":0,"docs":{},"r":{"df":0,"docs":{},"e":{"df":1,"docs":{"109":{"tf":1.4142135623730951}}}}}},"i":{"df":0,"docs":{},"v":{"df":0,"docs":{},"i":{"df":0,"docs":{},"s":{"df":0,"docs":{},"o":{"df":0,"docs":{},"r":{"df":1,"docs":{"114":{"tf":1.0}}}}}}}}},"df":0,"docs":{},"e":{"df":0,"docs":{},"x":{"df":0,"docs":{},"e":{"df":0,"docs":{},"m":{"df":0,"docs":{},"p":{"df":0,"docs":{},"t":{"df":2,"docs":{"109":{"tf":1.4142135623730951},"114":{"tf":1.0}}}}}}}},"o":{"df":0,"docs":{},"f":{"df":0,"docs":{},"f":{"df":0,"docs":{},"s":{"df":0,"docs":{},"e":{"df":0,"docs":{},"t":{"df":1,"docs":{"109":{"tf":1.4142135623730951}}}}}}}}},"df":0,"docs":{}}}}}},"l":{"a":{"df":0,"docs":{},"t":{"df":1,"docs":{"52":{"tf":1.0}}}},"df":0,"docs":{}},"p":{"a":{"df":0,"docs":{},"r":{"df":1,"docs":{"81":{"tf":1.0}}}},"df":0,"docs":{}}}},"v":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":0,"docs":{},"s":{"df":1,"docs":{"80":{"tf":1.0}}}}}}},"df":0,"docs":{},"e":{"a":{"df":0,"docs":{},"t":{"df":3,"docs":{"151":{"tf":1.7320508075688772},"57":{"tf":1.0},"68":{"tf":1.0}}}},"df":0,"docs":{},"e":{"'":{"df":1,"docs":{"72":{"tf":1.0}}},"df":6,"docs":{"101":{"tf":1.7320508075688772},"72":{"tf":1.4142135623730951},"76":{"tf":1.0},"80":{"tf":2.449489742783178},"91":{"tf":1.0},"92":{"tf":2.23606797749979}}}},"i":{"c":{"df":0,"docs":{},"k":{"df":8,"docs":{"115":{"tf":1.0},"123":{"tf":1.0},"130":{"tf":1.0},"14":{"tf":1.7320508075688772},"146":{"tf":1.0},"18":{"tf":1.4142135623730951},"21":{"tf":1.7320508075688772},"98":{"tf":1.0}}}},"df":5,"docs":{"111":{"tf":1.0},"14":{"tf":1.0},"16":{"tf":1.0},"79":{"tf":1.0},"80":{"tf":1.0}},"v":{"df":0,"docs":{},"i":{"a":{"df":0,"docs":{},"l":{"df":2,"docs":{"14":{"tf":1.0},"146":{"tf":1.4142135623730951}}}},"df":0,"docs":{}}}},"u":{"df":0,"docs":{},"e":{"df":6,"docs":{"10":{"tf":1.0},"104":{"tf":1.0},"14":{"tf":1.4142135623730951},"146":{"tf":1.0},"21":{"tf":1.0},"84":{"tf":1.0}}}}},"u":{"df":0,"docs":{},"p":{"df":0,"docs":{},"l":{"df":2,"docs":{"14":{"tf":1.7320508075688772},"7":{"tf":1.4142135623730951}}}},"r":{"df":0,"docs":{},"n":{"df":4,"docs":{"14":{"tf":1.0},"65":{"tf":1.0},"70":{"tf":1.0},"79":{"tf":1.0}}}}},"w":{"df":0,"docs":{},"i":{"d":{"d":{"df":0,"docs":{},"l":{"df":1,"docs":{"3":{"tf":1.0}}}},"df":0,"docs":{}},"df":0,"docs":{}},"o":{"df":35,"docs":{"101":{"tf":1.0},"104":{"tf":1.0},"105":{"tf":1.0},"107":{"tf":1.0},"108":{"tf":1.0},"109":{"tf":1.4142135623730951},"116":{"tf":1.0},"121":{"tf":1.0},"123":{"tf":1.0},"128":{"tf":1.0},"13":{"tf":2.23606797749979},"133":{"tf":1.0},"14":{"tf":3.0},"142":{"tf":1.0},"145":{"tf":1.0},"146":{"tf":2.23606797749979},"16":{"tf":1.0},"32":{"tf":1.0},"36":{"tf":1.0},"38":{"tf":1.4142135623730951},"40":{"tf":1.0},"44":{"tf":1.4142135623730951},"48":{"tf":1.0},"49":{"tf":1.7320508075688772},"52":{"tf":2.0},"59":{"tf":1.0},"68":{"tf":1.4142135623730951},"71":{"tf":1.0},"73":{"tf":1.0},"74":{"tf":1.0},"75":{"tf":1.0},"8":{"tf":1.0},"84":{"tf":1.4142135623730951},"9":{"tf":1.4142135623730951},"90":{"tf":1.0}}}},"y":{"df":0,"docs":{},"p":{"df":0,"docs":{},"e":{"df":3,"docs":{"10":{"tf":1.0},"52":{"tf":1.0},"70":{"tf":1.0}}},"i":{"c":{"df":1,"docs":{"106":{"tf":1.4142135623730951}}},"df":0,"docs":{}}}},"ˉ":{"=":{"df":0,"docs":{},"t":{"df":1,"docs":{"28":{"tf":1.0}}}},"df":0,"docs":{}},"∈":{"df":0,"docs":{},"f":{"2":{"df":0,"docs":{},"n":{"df":0,"docs":{},"×":{"3":{"3":{"df":1,"docs":{"142":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}}},"u":{"=":{"6":{"df":1,"docs":{"9":{"tf":1.0}}},"df":0,"docs":{}},"df":2,"docs":{"11":{"tf":1.0},"9":{"tf":1.4142135623730951}},"l":{"df":0,"docs":{},"t":{"df":0,"docs":{},"i":{"df":0,"docs":{},"m":{"df":1,"docs":{"50":{"tf":1.0}}}}}},"n":{"b":{"a":{"df":0,"docs":{},"l":{"df":1,"docs":{"146":{"tf":1.4142135623730951}}}},"df":0,"docs":{}},"c":{"df":0,"docs":{},"h":{"a":{"df":0,"docs":{},"n":{"df":0,"docs":{},"g":{"df":1,"docs":{"20":{"tf":1.0}}}}},"df":0,"docs":{}}},"d":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":16,"docs":{"111":{"tf":1.7320508075688772},"112":{"tf":1.0},"113":{"tf":1.0},"114":{"tf":1.4142135623730951},"115":{"tf":1.0},"116":{"tf":1.0},"117":{"tf":1.0},"118":{"tf":1.0},"119":{"tf":1.0},"120":{"tf":1.0},"121":{"tf":1.0},"122":{"tf":1.0},"123":{"tf":1.0},"124":{"tf":1.0},"125":{"tf":1.0},"64":{"tf":1.0}},"l":{"df":0,"docs":{},"i":{"df":2,"docs":{"40":{"tf":1.0},"90":{"tf":1.0}}}},"s":{"df":0,"docs":{},"t":{"a":{"df":0,"docs":{},"n":{"d":{"df":7,"docs":{"111":{"tf":1.0},"14":{"tf":1.0},"151":{"tf":1.0},"46":{"tf":1.0},"50":{"tf":1.0},"68":{"tf":1.0},"8":{"tf":1.0}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}}},"df":0,"docs":{},"i":{"df":0,"docs":{},"q":{"df":0,"docs":{},"u":{"df":4,"docs":{"14":{"tf":1.4142135623730951},"20":{"tf":1.0},"73":{"tf":1.7320508075688772},"75":{"tf":1.0}}}},"t":{"df":0,"docs":{},"i":{"df":20,"docs":{"112":{"tf":1.0},"114":{"tf":1.4142135623730951},"123":{"tf":2.449489742783178},"124":{"tf":3.4641016151377544},"125":{"tf":2.23606797749979},"14":{"tf":3.0},"16":{"tf":1.0},"23":{"tf":1.0},"34":{"tf":1.0},"35":{"tf":1.0},"36":{"tf":1.0},"38":{"tf":1.0},"5":{"tf":1.0},"52":{"tf":1.7320508075688772},"62":{"tf":1.0},"68":{"tf":3.0},"73":{"tf":1.0},"75":{"tf":1.0},"82":{"tf":1.4142135623730951},"91":{"tf":1.0}}}},"v":{"a":{"df":0,"docs":{},"r":{"df":0,"docs":{},"i":{"df":4,"docs":{"71":{"tf":1.0},"75":{"tf":1.0},"83":{"tf":1.0},"84":{"tf":1.4142135623730951}}}}},"df":0,"docs":{}}},"l":{"df":0,"docs":{},"i":{"df":0,"docs":{},"k":{"df":1,"docs":{"79":{"tf":1.0}}}}},"o":{"df":0,"docs":{},"r":{"d":{"df":1,"docs":{"146":{"tf":1.0}}},"df":0,"docs":{}}},"p":{"a":{"c":{"df":0,"docs":{},"k":{"df":1,"docs":{"142":{"tf":1.0}}}},"df":0,"docs":{}},"df":0,"docs":{},"r":{"df":0,"docs":{},"e":{"d":{"df":0,"docs":{},"i":{"c":{"df":0,"docs":{},"t":{"df":2,"docs":{"56":{"tf":1.0},"79":{"tf":1.0}}}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"t":{"df":0,"docs":{},"i":{"df":0,"docs":{},"l":{"df":7,"docs":{"121":{"tf":1.0},"130":{"tf":1.0},"134":{"tf":1.0},"135":{"tf":1.0},"142":{"tf":1.0},"146":{"tf":1.0},"94":{"tf":1.0}}}},"r":{"df":0,"docs":{},"u":{"df":0,"docs":{},"s":{"df":0,"docs":{},"t":{"df":2,"docs":{"4":{"tf":1.0},"7":{"tf":1.0}}}}}}},"u":{"df":0,"docs":{},"s":{"df":4,"docs":{"130":{"tf":2.23606797749979},"134":{"tf":2.449489742783178},"139":{"tf":1.0},"146":{"tf":1.7320508075688772}}}}},"p":{"d":{"a":{"df":0,"docs":{},"t":{"df":5,"docs":{"11":{"tf":1.0},"13":{"tf":1.4142135623730951},"136":{"tf":1.7320508075688772},"146":{"tf":1.4142135623730951},"40":{"tf":1.4142135623730951}}}},"df":0,"docs":{}},"df":8,"docs":{"10":{"tf":1.0},"12":{"tf":1.0},"121":{"tf":1.0},"13":{"tf":1.4142135623730951},"130":{"tf":1.0},"16":{"tf":1.7320508075688772},"71":{"tf":1.0},"84":{"tf":1.0}},"p":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":1,"docs":{"73":{"tf":1.0}}}}}},"s":{"a":{"df":0,"docs":{},"g":{"df":1,"docs":{"35":{"tf":1.4142135623730951}}}},"df":65,"docs":{"101":{"tf":1.4142135623730951},"102":{"tf":1.4142135623730951},"104":{"tf":1.0},"106":{"tf":1.0},"107":{"tf":1.0},"108":{"tf":1.0},"109":{"tf":2.0},"110":{"tf":1.0},"111":{"tf":1.4142135623730951},"113":{"tf":1.0},"114":{"tf":1.7320508075688772},"116":{"tf":1.0},"118":{"tf":2.0},"119":{"tf":1.0},"121":{"tf":1.0},"123":{"tf":2.6457513110645907},"124":{"tf":1.4142135623730951},"125":{"tf":2.23606797749979},"128":{"tf":1.0},"13":{"tf":1.0},"130":{"tf":1.0},"134":{"tf":1.4142135623730951},"136":{"tf":1.4142135623730951},"14":{"tf":2.8284271247461903},"142":{"tf":1.0},"144":{"tf":1.4142135623730951},"146":{"tf":4.0},"147":{"tf":1.0},"149":{"tf":1.4142135623730951},"150":{"tf":1.0},"151":{"tf":2.23606797749979},"19":{"tf":1.0},"21":{"tf":1.0},"33":{"tf":1.0},"34":{"tf":1.4142135623730951},"35":{"tf":1.0},"36":{"tf":1.0},"38":{"tf":1.0},"4":{"tf":1.0},"40":{"tf":1.7320508075688772},"41":{"tf":1.0},"44":{"tf":1.4142135623730951},"45":{"tf":2.0},"48":{"tf":1.4142135623730951},"49":{"tf":1.4142135623730951},"5":{"tf":1.4142135623730951},"50":{"tf":2.23606797749979},"57":{"tf":1.0},"59":{"tf":1.0},"60":{"tf":1.0},"63":{"tf":1.0},"68":{"tf":1.0},"7":{"tf":2.23606797749979},"71":{"tf":1.0},"74":{"tf":2.0},"75":{"tf":1.0},"8":{"tf":1.7320508075688772},"87":{"tf":1.0},"89":{"tf":1.0},"9":{"tf":1.0},"91":{"tf":1.4142135623730951},"92":{"tf":1.0},"94":{"tf":1.0},"95":{"tf":1.4142135623730951},"97":{"tf":1.0}},"e":{"df":0,"docs":{},"r":{"df":1,"docs":{"50":{"tf":1.0}}}},"i":{"df":0,"docs":{},"z":{"df":1,"docs":{"35":{"tf":1.0}}}},"u":{"a":{"df":0,"docs":{},"l":{"df":9,"docs":{"123":{"tf":1.0},"133":{"tf":1.0},"14":{"tf":1.0},"151":{"tf":1.0},"5":{"tf":1.0},"53":{"tf":1.0},"70":{"tf":1.0},"74":{"tf":1.0},"75":{"tf":1.0}}}},"df":0,"docs":{}}},"t":{"df":0,"docs":{},"i":{"df":0,"docs":{},"l":{"df":1,"docs":{"99":{"tf":1.0}}}}}},"v":{"=":{"9":{"df":1,"docs":{"9":{"tf":1.0}}},"df":0,"docs":{}},"a":{"df":0,"docs":{},"l":{"df":0,"docs":{},"i":{"d":{"df":23,"docs":{"10":{"tf":1.0},"101":{"tf":1.0},"105":{"tf":1.0},"11":{"tf":1.4142135623730951},"125":{"tf":1.0},"13":{"tf":1.0},"14":{"tf":1.0},"146":{"tf":1.4142135623730951},"16":{"tf":1.7320508075688772},"21":{"tf":1.0},"33":{"tf":1.0},"34":{"tf":1.0},"48":{"tf":1.0},"49":{"tf":1.0},"50":{"tf":1.0},"52":{"tf":1.4142135623730951},"69":{"tf":1.4142135623730951},"70":{"tf":1.0},"77":{"tf":1.0},"80":{"tf":1.0},"84":{"tf":1.7320508075688772},"86":{"tf":1.0},"9":{"tf":1.4142135623730951}}},"df":0,"docs":{}},"u":{"a":{"b":{"df":0,"docs":{},"l":{"df":1,"docs":{"103":{"tf":1.0}}}},"df":0,"docs":{}},"df":58,"docs":{"101":{"tf":2.23606797749979},"102":{"tf":2.0},"107":{"tf":1.7320508075688772},"108":{"tf":1.4142135623730951},"114":{"tf":1.4142135623730951},"118":{"tf":2.0},"119":{"tf":1.0},"128":{"tf":1.0},"13":{"tf":2.23606797749979},"130":{"tf":3.0},"131":{"tf":1.7320508075688772},"133":{"tf":3.3166247903554},"134":{"tf":1.7320508075688772},"135":{"tf":2.0},"136":{"tf":1.7320508075688772},"137":{"tf":1.0},"139":{"tf":1.0},"14":{"tf":3.3166247903554},"142":{"tf":2.449489742783178},"146":{"tf":5.196152422706632},"147":{"tf":2.23606797749979},"151":{"tf":2.449489742783178},"16":{"tf":1.4142135623730951},"19":{"tf":1.0},"20":{"tf":2.23606797749979},"21":{"tf":2.8284271247461903},"22":{"tf":1.0},"25":{"tf":1.4142135623730951},"3":{"tf":1.0},"32":{"tf":2.23606797749979},"33":{"tf":1.4142135623730951},"34":{"tf":1.7320508075688772},"35":{"tf":3.0},"36":{"tf":1.7320508075688772},"42":{"tf":1.0},"49":{"tf":1.4142135623730951},"5":{"tf":1.0},"52":{"tf":1.4142135623730951},"54":{"tf":1.0},"58":{"tf":1.0},"60":{"tf":1.0},"66":{"tf":1.0},"69":{"tf":1.0},"7":{"tf":2.23606797749979},"70":{"tf":1.4142135623730951},"72":{"tf":1.7320508075688772},"73":{"tf":1.0},"75":{"tf":2.0},"77":{"tf":1.4142135623730951},"79":{"tf":1.0},"80":{"tf":1.7320508075688772},"83":{"tf":1.0},"87":{"tf":1.0},"89":{"tf":1.0},"9":{"tf":2.23606797749979},"91":{"tf":2.23606797749979},"94":{"tf":2.0},"95":{"tf":2.8284271247461903}}}},"n":{"df":0,"docs":{},"i":{"df":0,"docs":{},"s":{"df":0,"docs":{},"h":{"df":5,"docs":{"123":{"tf":2.6457513110645907},"125":{"tf":1.7320508075688772},"20":{"tf":1.0},"55":{"tf":1.0},"84":{"tf":1.0}}}}}},"r":{"df":0,"docs":{},"i":{"a":{"b":{"df":0,"docs":{},"l":{"df":11,"docs":{"11":{"tf":1.0},"13":{"tf":1.4142135623730951},"14":{"tf":1.0},"151":{"tf":1.0},"36":{"tf":1.0},"44":{"tf":1.7320508075688772},"45":{"tf":1.7320508075688772},"5":{"tf":1.0},"70":{"tf":1.7320508075688772},"9":{"tf":1.4142135623730951},"95":{"tf":1.0}}}},"df":0,"docs":{},"n":{"df":0,"docs":{},"t":{"df":5,"docs":{"74":{"tf":2.0},"77":{"tf":1.0},"80":{"tf":1.0},"96":{"tf":1.4142135623730951},"97":{"tf":2.23606797749979}}}}},"df":0,"docs":{}}}},"df":13,"docs":{"11":{"tf":2.0},"12":{"tf":1.0},"13":{"tf":1.4142135623730951},"14":{"tf":2.23606797749979},"141":{"tf":1.0},"142":{"tf":2.0},"146":{"tf":2.449489742783178},"147":{"tf":1.7320508075688772},"16":{"tf":1.4142135623730951},"34":{"tf":1.4142135623730951},"36":{"tf":1.7320508075688772},"8":{"tf":1.0},"9":{"tf":2.0}},"e":{"c":{"!":{"[":{"0":{"df":1,"docs":{"109":{"tf":1.0}}},"1":{"df":2,"docs":{"108":{"tf":1.0},"109":{"tf":1.0}}},"2":{"df":1,"docs":{"109":{"tf":1.0}}},"df":0,"docs":{},"t":{"df":0,"docs":{},"h":{"df":0,"docs":{},"i":{"df":0,"docs":{},"r":{"d":{"_":{"df":0,"docs":{},"r":{"df":0,"docs":{},"o":{"df":0,"docs":{},"w":{"[":{"0":{"df":2,"docs":{"107":{"tf":1.0},"114":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"x":{".":{"c":{"df":0,"docs":{},"l":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"df":1,"docs":{"35":{"tf":1.4142135623730951}}}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"<":{"df":0,"docs":{},"f":{"df":0,"docs":{},"i":{"df":0,"docs":{},"e":{"df":0,"docs":{},"l":{"d":{"df":0,"docs":{},"e":{"df":0,"docs":{},"l":{"df":0,"docs":{},"e":{"df":0,"docs":{},"m":{"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"df":0,"docs":{},"t":{"<":{"df":0,"docs":{},"f":{"df":1,"docs":{"35":{"tf":2.6457513110645907}}},"s":{"df":0,"docs":{},"e":{"df":0,"docs":{},"l":{"df":0,"docs":{},"f":{":":{":":{"df":0,"docs":{},"f":{"df":0,"docs":{},"i":{"df":0,"docs":{},"e":{"df":0,"docs":{},"l":{"d":{"df":2,"docs":{"107":{"tf":1.0},"114":{"tf":1.0}}},"df":0,"docs":{}}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}}}},"df":0,"docs":{}}}}}}}}},"df":0,"docs":{}}}}}},"df":3,"docs":{"108":{"tf":1.0},"109":{"tf":1.0},"116":{"tf":1.0}},"t":{"df":0,"docs":{},"o":{"df":0,"docs":{},"r":{"'":{"df":1,"docs":{"72":{"tf":1.0}}},"df":18,"docs":{"101":{"tf":1.0},"107":{"tf":1.0},"141":{"tf":1.0},"142":{"tf":1.7320508075688772},"147":{"tf":2.449489742783178},"71":{"tf":1.0},"72":{"tf":2.0},"73":{"tf":3.1622776601683795},"74":{"tf":2.449489742783178},"75":{"tf":1.0},"76":{"tf":1.4142135623730951},"78":{"tf":1.4142135623730951},"79":{"tf":2.449489742783178},"80":{"tf":2.23606797749979},"82":{"tf":1.0},"90":{"tf":1.4142135623730951},"91":{"tf":1.0},"92":{"tf":2.449489742783178}}}}}},"df":0,"docs":{},"r":{"df":0,"docs":{},"i":{"df":10,"docs":{"118":{"tf":1.0},"123":{"tf":1.0},"14":{"tf":1.7320508075688772},"20":{"tf":1.0},"49":{"tf":1.0},"50":{"tf":1.0},"69":{"tf":1.0},"73":{"tf":1.0},"79":{"tf":1.0},"98":{"tf":1.0}},"f":{"df":3,"docs":{"30":{"tf":1.4142135623730951},"4":{"tf":1.0},"7":{"tf":1.4142135623730951}},"i":{"c":{"a":{"df":0,"docs":{},"t":{"df":0,"docs":{},"i":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"df":0,"docs":{},"k":{"df":0,"docs":{},"e":{"df":0,"docs":{},"y":{"<":{"df":0,"docs":{},"g":{"1":{"df":0,"docs":{},"p":{"df":0,"docs":{},"o":{"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"df":0,"docs":{},"t":{"df":1,"docs":{"35":{"tf":1.0}}}}}}}},"df":0,"docs":{}}},"df":1,"docs":{"35":{"tf":1.0}}}}}}}}}},"df":0,"docs":{}},"df":59,"docs":{"102":{"tf":2.0},"104":{"tf":2.0},"109":{"tf":1.0},"110":{"tf":1.4142135623730951},"111":{"tf":1.4142135623730951},"112":{"tf":1.0},"113":{"tf":2.0},"115":{"tf":1.7320508075688772},"117":{"tf":1.4142135623730951},"118":{"tf":2.23606797749979},"119":{"tf":1.4142135623730951},"127":{"tf":1.0},"13":{"tf":1.4142135623730951},"137":{"tf":1.0},"144":{"tf":1.0},"145":{"tf":1.0},"146":{"tf":2.449489742783178},"147":{"tf":1.4142135623730951},"151":{"tf":1.0},"20":{"tf":1.0},"21":{"tf":3.3166247903554},"32":{"tf":1.0},"33":{"tf":1.7320508075688772},"34":{"tf":1.0},"35":{"tf":2.23606797749979},"4":{"tf":1.0},"44":{"tf":2.0},"48":{"tf":1.4142135623730951},"50":{"tf":1.0},"53":{"tf":1.0},"55":{"tf":1.0},"56":{"tf":1.0},"57":{"tf":1.7320508075688772},"58":{"tf":1.0},"59":{"tf":1.4142135623730951},"60":{"tf":1.4142135623730951},"61":{"tf":1.0},"62":{"tf":1.7320508075688772},"63":{"tf":1.7320508075688772},"69":{"tf":1.7320508075688772},"7":{"tf":2.0},"70":{"tf":1.4142135623730951},"71":{"tf":1.0},"72":{"tf":1.7320508075688772},"73":{"tf":1.4142135623730951},"74":{"tf":1.7320508075688772},"75":{"tf":2.449489742783178},"77":{"tf":2.23606797749979},"79":{"tf":1.4142135623730951},"8":{"tf":1.0},"80":{"tf":2.6457513110645907},"82":{"tf":1.4142135623730951},"83":{"tf":1.7320508075688772},"84":{"tf":1.4142135623730951},"85":{"tf":1.7320508075688772},"86":{"tf":1.0},"91":{"tf":1.7320508075688772},"94":{"tf":1.0},"95":{"tf":3.0}},"e":{"df":0,"docs":{},"r":{"'":{"df":2,"docs":{"35":{"tf":1.0},"95":{"tf":1.0}}},":":{":":{"df":0,"docs":{},"n":{"df":0,"docs":{},"e":{"df":0,"docs":{},"w":{"(":{"df":0,"docs":{},"k":{"df":0,"docs":{},"z":{"df":0,"docs":{},"g":{"df":2,"docs":{"35":{"tf":1.4142135623730951},"44":{"tf":1.0}}}}}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"<":{"df":0,"docs":{},"f":{"df":1,"docs":{"35":{"tf":1.0}}}},"df":0,"docs":{}}}},"y":{"(":{"(":{"df":0,"docs":{},"υ":{"df":0,"docs":{},"s":{"2":{"df":0,"docs":{},"k":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"x":{")":{",":{"df":0,"docs":{},"p":{"df":0,"docs":{},"k":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"p":{"df":0,"docs":{},"k":{"df":1,"docs":{"102":{"tf":1.0}}}}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}},"π":{"df":0,"docs":{},"k":{"df":0,"docs":{},"υ":{"df":0,"docs":{},"s":{"2":{"df":0,"docs":{},"k":{"df":0,"docs":{},"​":{"df":0,"docs":{},"​":{")":{",":{"df":0,"docs":{},"p":{"df":0,"docs":{},"k":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"p":{"df":0,"docs":{},"k":{"df":1,"docs":{"102":{"tf":1.0}}}}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}}},"−":{"df":0,"docs":{},"υ":{"df":0,"docs":{},"s":{"2":{"df":0,"docs":{},"k":{"df":0,"docs":{},"​":{"df":0,"docs":{},"​":{")":{",":{"df":0,"docs":{},"p":{"df":0,"docs":{},"k":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"p":{"df":0,"docs":{},"k":{"df":1,"docs":{"95":{"tf":1.0}}}}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}}}}}}},"df":0,"docs":{}}}},"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"η":{"1":{"df":0,"docs":{},"υ":{"df":0,"docs":{},"s":{"df":0,"docs":{},"​":{"df":0,"docs":{},"​":{")":{",":{"df":0,"docs":{},"h":{"1":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"h":{"1":{"df":1,"docs":{"95":{"tf":1.0}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}}}},"2":{"df":0,"docs":{},"υ":{"df":0,"docs":{},"s":{"df":0,"docs":{},"​":{"df":0,"docs":{},"​":{")":{",":{"df":0,"docs":{},"h":{"2":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"h":{"2":{"df":1,"docs":{"95":{"tf":1.0}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}}}},"df":0,"docs":{}},"τ":{"df":0,"docs":{},"j":{"df":0,"docs":{},"υ":{"df":0,"docs":{},"s":{"df":0,"docs":{},"​":{"df":0,"docs":{},"​":{")":{",":{"df":0,"docs":{},"t":{"df":0,"docs":{},"j":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"t":{"df":0,"docs":{},"j":{"df":1,"docs":{"95":{"tf":1.0}}}}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}}}}}},"df":0,"docs":{}}}},"−":{"df":0,"docs":{},"υ":{"df":0,"docs":{},"s":{"2":{"df":0,"docs":{},"k":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"π":{"df":0,"docs":{},"k":{"df":0,"docs":{},"−":{"df":0,"docs":{},"υ":{"df":0,"docs":{},"s":{"2":{"df":0,"docs":{},"k":{"df":0,"docs":{},"​":{"df":0,"docs":{},"​":{")":{",":{"df":0,"docs":{},"p":{"df":0,"docs":{},"k":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"p":{"df":0,"docs":{},"k":{"df":1,"docs":{"95":{"tf":1.0}}}}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}}}}}}},"df":0,"docs":{}}}},"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"η":{"1":{"df":0,"docs":{},"υ":{"df":0,"docs":{},"s":{"df":0,"docs":{},"​":{"df":0,"docs":{},"​":{")":{",":{"df":0,"docs":{},"h":{"1":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"h":{"1":{"df":1,"docs":{"95":{"tf":1.0}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}}}},"2":{"df":0,"docs":{},"υ":{"df":0,"docs":{},"s":{"df":0,"docs":{},"​":{"df":0,"docs":{},"​":{")":{",":{"df":0,"docs":{},"h":{"2":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"h":{"2":{"df":1,"docs":{"95":{"tf":1.0}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}}}},"df":0,"docs":{}},"τ":{"df":0,"docs":{},"j":{"df":0,"docs":{},"υ":{"df":0,"docs":{},"s":{"df":0,"docs":{},"​":{"df":0,"docs":{},"​":{")":{",":{"df":0,"docs":{},"t":{"df":0,"docs":{},"j":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"t":{"df":0,"docs":{},"j":{"df":1,"docs":{"95":{"tf":1.0}}}}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}}}}}},"df":0,"docs":{}}}}}},"[":{"df":0,"docs":{},"f":{"]":{"1":{"df":1,"docs":{"19":{"tf":1.0}}},"df":0,"docs":{}},"b":{"a":{"df":0,"docs":{},"t":{"c":{"df":0,"docs":{},"h":{"df":0,"docs":{},"​":{"]":{"1":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"π":{"b":{"a":{"df":0,"docs":{},"t":{"c":{"df":0,"docs":{},"h":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"ζ":{",":{"df":0,"docs":{},"f":{"b":{"a":{"df":0,"docs":{},"t":{"c":{"df":0,"docs":{},"h":{"df":1,"docs":{"33":{"tf":1.0}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"z":{"]":{"1":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"π":{"df":0,"docs":{},"s":{"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"df":0,"docs":{},"g":{"df":0,"docs":{},"l":{"df":0,"docs":{},"e":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"ζ":{"df":0,"docs":{},"ω":{",":{"df":0,"docs":{},"z":{"df":1,"docs":{"33":{"tf":1.0}}}},"df":0,"docs":{}}}},"df":0,"docs":{}}}}}}}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"a":{"b":{"df":0,"docs":{},"i":{",":{"df":0,"docs":{},"y":{",":{"df":0,"docs":{},"r":{",":{"df":1,"docs":{"92":{"tf":1.4142135623730951}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{},"i":{",":{"df":0,"docs":{},"y":{",":{"df":0,"docs":{},"r":{",":{"df":1,"docs":{"92":{"tf":1.4142135623730951}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"df":0,"docs":{},"g":{"_":{"df":0,"docs":{},"k":{"df":0,"docs":{},"e":{"df":0,"docs":{},"y":{"df":1,"docs":{"35":{"tf":2.449489742783178}}}}}},"df":0,"docs":{}}}}}}},"s":{"df":0,"docs":{},"i":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"df":1,"docs":{"13":{"tf":1.0}}}}}}}},"i":{",":{"df":0,"docs":{},"j":{"df":0,"docs":{},"​":{"=":{"df":0,"docs":{},"v":{"df":0,"docs":{},"k":{",":{"df":0,"docs":{},"l":{"df":4,"docs":{"11":{"tf":1.0},"13":{"tf":1.0},"14":{"tf":1.0},"16":{"tf":1.0}}}},"df":0,"docs":{}}}},"df":0,"docs":{}}}},"a":{"df":1,"docs":{"3":{"tf":1.0}}},"df":0,"docs":{},"e":{"df":0,"docs":{},"w":{"df":1,"docs":{"95":{"tf":1.0}}}},"o":{"df":0,"docs":{},"l":{"a":{"df":0,"docs":{},"t":{"df":1,"docs":{"20":{"tf":1.0}}}},"df":0,"docs":{}}},"r":{"df":0,"docs":{},"t":{"df":0,"docs":{},"u":{"a":{"df":0,"docs":{},"l":{"df":11,"docs":{"128":{"tf":2.8284271247461903},"130":{"tf":1.0},"131":{"tf":1.4142135623730951},"133":{"tf":2.0},"134":{"tf":1.0},"136":{"tf":1.0},"139":{"tf":1.0},"148":{"tf":1.7320508075688772},"149":{"tf":2.6457513110645907},"150":{"tf":2.449489742783178},"50":{"tf":1.4142135623730951}}}},"df":0,"docs":{}}}},"s":{"df":0,"docs":{},"u":{"a":{"df":0,"docs":{},"l":{"df":3,"docs":{"128":{"tf":1.0},"133":{"tf":1.0},"137":{"tf":1.0}}}},"df":0,"docs":{}}}},"k":{"df":1,"docs":{"44":{"tf":1.7320508075688772}}},"m":{"'":{"df":2,"docs":{"144":{"tf":1.0},"146":{"tf":1.0}}},"df":7,"docs":{"127":{"tf":1.4142135623730951},"133":{"tf":1.0},"144":{"tf":1.7320508075688772},"146":{"tf":1.0},"149":{"tf":1.0},"151":{"tf":2.8284271247461903},"50":{"tf":1.7320508075688772}}},"u":{"df":0,"docs":{},"l":{"df":0,"docs":{},"n":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":1,"docs":{"42":{"tf":1.0}}}}}}}},"w":{"2":{"=":{"df":0,"docs":{},"g":{"df":1,"docs":{"125":{"tf":1.0}}}},"df":1,"docs":{"124":{"tf":1.0}},"k":{"df":1,"docs":{"124":{"tf":1.0}}}},"=":{"8":{"df":1,"docs":{"9":{"tf":1.0}}},"df":0,"docs":{}},"_":{"df":0,"docs":{},"z":{"df":0,"docs":{},"e":{"df":0,"docs":{},"t":{"a":{"_":{"1":{"df":1,"docs":{"35":{"tf":1.0}}},"df":0,"docs":{},"o":{"df":0,"docs":{},"m":{"df":0,"docs":{},"e":{"df":0,"docs":{},"g":{"a":{"_":{"1":{"df":1,"docs":{"35":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"a":{"df":0,"docs":{},"l":{"df":0,"docs":{},"k":{"df":0,"docs":{},"t":{"df":0,"docs":{},"h":{"df":0,"docs":{},"r":{"df":0,"docs":{},"o":{"df":0,"docs":{},"u":{"df":0,"docs":{},"g":{"df":0,"docs":{},"h":{"df":2,"docs":{"51":{"tf":1.4142135623730951},"64":{"tf":1.0}}}}}}}}}}},"n":{"df":0,"docs":{},"t":{"df":10,"docs":{"103":{"tf":1.0},"123":{"tf":1.4142135623730951},"125":{"tf":1.0},"14":{"tf":1.4142135623730951},"150":{"tf":1.4142135623730951},"151":{"tf":1.0},"16":{"tf":1.0},"44":{"tf":1.0},"5":{"tf":1.0},"50":{"tf":1.0}}}},"y":{"df":22,"docs":{"10":{"tf":1.0},"101":{"tf":1.4142135623730951},"107":{"tf":1.0},"12":{"tf":1.0},"130":{"tf":1.0},"131":{"tf":1.0},"14":{"tf":1.4142135623730951},"146":{"tf":1.0},"149":{"tf":1.0},"151":{"tf":1.0},"31":{"tf":1.0},"50":{"tf":1.0},"52":{"tf":1.0},"54":{"tf":1.0},"66":{"tf":1.4142135623730951},"67":{"tf":1.0},"7":{"tf":1.0},"70":{"tf":1.0},"79":{"tf":1.0},"83":{"tf":1.0},"84":{"tf":1.0},"9":{"tf":1.0}}}},"df":3,"docs":{"11":{"tf":1.0},"124":{"tf":1.4142135623730951},"9":{"tf":1.0}},"e":{"'":{"d":{"df":1,"docs":{"125":{"tf":1.0}}},"df":0,"docs":{},"l":{"df":0,"docs":{},"l":{"df":9,"docs":{"10":{"tf":1.0},"19":{"tf":1.0},"43":{"tf":1.0},"51":{"tf":1.0},"53":{"tf":1.0},"64":{"tf":1.0},"7":{"tf":1.4142135623730951},"80":{"tf":1.0},"9":{"tf":1.7320508075688772}}}},"r":{"df":1,"docs":{"50":{"tf":1.0}}},"v":{"df":1,"docs":{"105":{"tf":1.0}}}},"a":{"df":0,"docs":{},"k":{"df":1,"docs":{"42":{"tf":1.4142135623730951}}}},"df":0,"docs":{},"l":{"df":0,"docs":{},"l":{"df":6,"docs":{"113":{"tf":1.0},"146":{"tf":1.4142135623730951},"34":{"tf":1.0},"36":{"tf":1.0},"4":{"tf":1.0},"98":{"tf":1.0}}}},"r":{"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"'":{"df":0,"docs":{},"t":{"df":2,"docs":{"151":{"tf":1.0},"3":{"tf":1.0}}}},"df":0,"docs":{}}}}},"h":{"a":{"df":0,"docs":{},"t":{"'":{"df":2,"docs":{"15":{"tf":1.0},"69":{"tf":1.0}}},"df":0,"docs":{}}},"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"df":0,"docs":{},"e":{"df":0,"docs":{},"v":{"df":2,"docs":{"118":{"tf":1.0},"123":{"tf":1.0}}}}},"t":{"df":0,"docs":{},"h":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":3,"docs":{"14":{"tf":1.7320508075688772},"84":{"tf":1.0},"92":{"tf":1.4142135623730951}}}}}}},"i":{"df":0,"docs":{},"t":{"df":0,"docs":{},"e":{"df":0,"docs":{},"p":{"a":{"df":0,"docs":{},"p":{"df":5,"docs":{"130":{"tf":1.0},"131":{"tf":1.0},"138":{"tf":1.0},"139":{"tf":1.0},"146":{"tf":1.0}}}},"df":0,"docs":{}}}}},"o":{"df":0,"docs":{},"l":{"df":0,"docs":{},"e":{"df":4,"docs":{"0":{"tf":1.0},"16":{"tf":1.4142135623730951},"21":{"tf":1.0},"80":{"tf":1.0}}}},"s":{"df":0,"docs":{},"e":{"df":5,"docs":{"101":{"tf":1.0},"114":{"tf":1.0},"123":{"tf":1.0},"149":{"tf":1.0},"54":{"tf":1.0}}}}}},"i":{"df":0,"docs":{},"k":{"df":0,"docs":{},"i":{"df":0,"docs":{},"p":{"df":0,"docs":{},"e":{"d":{"df":0,"docs":{},"i":{"a":{"df":1,"docs":{"101":{"tf":1.0}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}},"n":{"df":0,"docs":{},"t":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":0,"docs":{},"f":{"df":0,"docs":{},"e":{"df":0,"docs":{},"l":{"df":1,"docs":{"103":{"tf":1.0}}}}}}}}},"r":{"df":0,"docs":{},"e":{"df":2,"docs":{"11":{"tf":1.0},"35":{"tf":1.7320508075688772}}}},"s":{"df":0,"docs":{},"h":{"df":2,"docs":{"20":{"tf":1.0},"80":{"tf":1.0}}}},"t":{"df":2,"docs":{"35":{"tf":2.0},"44":{"tf":1.4142135623730951}},"h":{"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"df":1,"docs":{"4":{"tf":1.0}}}},"o":{"df":0,"docs":{},"u":{"df":0,"docs":{},"t":{"df":5,"docs":{"103":{"tf":1.0},"125":{"tf":1.0},"146":{"tf":1.7320508075688772},"19":{"tf":1.0},"7":{"tf":1.0}}}}}},"n":{"df":0,"docs":{},"e":{"df":0,"docs":{},"s":{"df":0,"docs":{},"s":{":":{":":{"df":0,"docs":{},"n":{"df":0,"docs":{},"e":{"df":0,"docs":{},"w":{"(":{"a":{"df":0,"docs":{},"s":{"df":0,"docs":{},"s":{"df":0,"docs":{},"i":{"df":0,"docs":{},"g":{"df":0,"docs":{},"n":{"df":1,"docs":{"44":{"tf":1.0}}}}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"<":{"df":0,"docs":{},"f":{"df":1,"docs":{"35":{"tf":1.0}}}},"df":0,"docs":{}}}}}}},"o":{"df":0,"docs":{},"n":{"'":{"df":0,"docs":{},"t":{"df":9,"docs":{"109":{"tf":1.0},"118":{"tf":1.0},"146":{"tf":1.4142135623730951},"149":{"tf":1.0},"151":{"tf":1.0},"67":{"tf":1.0},"72":{"tf":1.0},"83":{"tf":1.0},"9":{"tf":1.0}}}},"d":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":1,"docs":{"16":{"tf":1.0}}}}},"df":0,"docs":{}},"r":{"d":{"df":2,"docs":{"133":{"tf":1.4142135623730951},"146":{"tf":1.0}}},"df":0,"docs":{},"k":{"df":21,"docs":{"0":{"tf":1.0},"111":{"tf":1.4142135623730951},"119":{"tf":1.0},"125":{"tf":1.0},"14":{"tf":2.23606797749979},"146":{"tf":1.4142135623730951},"149":{"tf":1.0},"151":{"tf":1.4142135623730951},"16":{"tf":1.0},"19":{"tf":1.0},"20":{"tf":1.0},"21":{"tf":1.0},"4":{"tf":1.0},"46":{"tf":1.0},"50":{"tf":1.0},"52":{"tf":1.0},"56":{"tf":1.0},"69":{"tf":1.0},"73":{"tf":1.0},"75":{"tf":1.4142135623730951},"88":{"tf":1.4142135623730951}}},"t":{"df":0,"docs":{},"h":{"df":2,"docs":{"3":{"tf":1.0},"37":{"tf":1.0}}}}},"u":{"df":0,"docs":{},"l":{"d":{"df":0,"docs":{},"n":{"'":{"df":0,"docs":{},"t":{"df":1,"docs":{"151":{"tf":1.0}}}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"r":{"a":{"df":0,"docs":{},"p":{"df":4,"docs":{"124":{"tf":1.0},"146":{"tf":1.0},"16":{"tf":1.7320508075688772},"45":{"tf":1.0}}}},"df":0,"docs":{},"i":{"df":0,"docs":{},"t":{"df":0,"docs":{},"e":{"df":6,"docs":{"14":{"tf":1.0},"146":{"tf":1.0},"26":{"tf":1.0},"45":{"tf":1.0},"50":{"tf":1.7320508075688772},"92":{"tf":1.4142135623730951}}},"t":{"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"df":1,"docs":{"7":{"tf":1.4142135623730951}}}}}}},"o":{"df":0,"docs":{},"n":{"df":0,"docs":{},"g":{"df":2,"docs":{"124":{"tf":1.0},"42":{"tf":1.0}}}}}}},"x":{",":{"0":{"df":1,"docs":{"7":{"tf":1.0}}},"df":0,"docs":{},"i":{"df":1,"docs":{"34":{"tf":1.0}}}},"0":{"df":1,"docs":{"114":{"tf":1.0}}},"2":{"df":0,"docs":{},"n":{"df":0,"docs":{},"−":{"1":{"df":1,"docs":{"123":{"tf":1.0}},"q":{"df":0,"docs":{},"k":{"df":0,"docs":{},"t":{"df":2,"docs":{"84":{"tf":1.4142135623730951},"85":{"tf":1.0}}}}}},"df":0,"docs":{}}}},":":{"=":{"df":0,"docs":{},"g":{"+":{"df":0,"docs":{},"ζ":{"df":0,"docs":{},"k":{"df":0,"docs":{},"​":{"df":0,"docs":{},"h":{"df":1,"docs":{"102":{"tf":1.0}}}}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"=":{"3":{"df":2,"docs":{"7":{"tf":1.4142135623730951},"9":{"tf":1.0}}},"df":0,"docs":{},"x":{"0":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"x":{"1":{"df":0,"docs":{},"​":{"df":0,"docs":{},"∗":{"2":{"1":{"6":{"+":{"df":0,"docs":{},"x":{"2":{"df":0,"docs":{},"​":{"df":0,"docs":{},"∗":{"2":{"3":{"2":{"+":{"df":0,"docs":{},"x":{"3":{"df":0,"docs":{},"​":{"df":0,"docs":{},"∗":{"2":{"4":{"8":{"+":{".":{".":{".":{"+":{"df":0,"docs":{},"x":{"7":{"df":0,"docs":{},"​":{"df":0,"docs":{},"∗":{"2":{"1":{"1":{"2":{"df":1,"docs":{"151":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"_":{"0":{"df":3,"docs":{"112":{"tf":1.0},"60":{"tf":1.4142135623730951},"62":{"tf":1.0}}},"df":0,"docs":{}},"df":18,"docs":{"102":{"tf":1.0},"106":{"tf":1.0},"11":{"tf":1.0},"124":{"tf":1.0},"14":{"tf":1.0},"142":{"tf":1.4142135623730951},"151":{"tf":1.7320508075688772},"28":{"tf":1.0},"34":{"tf":1.0},"35":{"tf":1.7320508075688772},"44":{"tf":2.23606797749979},"45":{"tf":1.4142135623730951},"5":{"tf":1.7320508075688772},"52":{"tf":1.0},"7":{"tf":2.23606797749979},"88":{"tf":1.4142135623730951},"9":{"tf":2.0},"94":{"tf":1.0}},"i":{"df":2,"docs":{"123":{"tf":1.0},"151":{"tf":1.4142135623730951}}},"n":{"=":{"1":{"df":1,"docs":{"124":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{},"−":{"1":{"df":2,"docs":{"124":{"tf":1.0},"14":{"tf":1.0}}},"df":0,"docs":{}}},"x":{"df":0,"docs":{},"x":{"df":0,"docs":{},"x":{"df":0,"docs":{},"x":{"df":0,"docs":{},"x":{"df":0,"docs":{},"x":{"df":0,"docs":{},"x":{"df":0,"docs":{},"x":{"df":0,"docs":{},"x":{"df":0,"docs":{},"x":{"df":0,"docs":{},"x":{"df":0,"docs":{},"x":{"df":0,"docs":{},"x":{"df":0,"docs":{},"x":{"df":0,"docs":{},"x":{"df":0,"docs":{},"|":{"df":0,"docs":{},"x":{"df":0,"docs":{},"|":{"df":0,"docs":{},"x":{"df":0,"docs":{},"x":{"df":0,"docs":{},"|":{"df":0,"docs":{},"x":{"df":0,"docs":{},"x":{"df":0,"docs":{},"x":{"df":0,"docs":{},"x":{"df":0,"docs":{},"|":{"df":0,"docs":{},"x":{"df":0,"docs":{},"x":{"df":0,"docs":{},"x":{"df":0,"docs":{},"x":{"df":0,"docs":{},"|":{"df":0,"docs":{},"x":{"df":0,"docs":{},"x":{"df":0,"docs":{},"x":{"df":0,"docs":{},"|":{"df":0,"docs":{},"x":{"df":0,"docs":{},"x":{"df":0,"docs":{},"x":{"df":2,"docs":{"142":{"tf":1.0},"146":{"tf":1.0}},"|":{"df":0,"docs":{},"x":{"df":0,"docs":{},"x":{"df":0,"docs":{},"x":{"df":0,"docs":{},"x":{"df":0,"docs":{},"|":{"df":0,"docs":{},"x":{"df":0,"docs":{},"x":{"df":0,"docs":{},"x":{"df":0,"docs":{},"x":{"df":0,"docs":{},"|":{"df":0,"docs":{},"x":{"df":0,"docs":{},"x":{"df":0,"docs":{},"x":{"df":0,"docs":{},"x":{"df":0,"docs":{},"|":{"df":0,"docs":{},"x":{"df":0,"docs":{},"x":{"df":0,"docs":{},"x":{"df":0,"docs":{},"|":{"df":0,"docs":{},"x":{"df":0,"docs":{},"x":{"df":0,"docs":{},"x":{"df":1,"docs":{"147":{"tf":1.0}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}},"−":{"a":{"df":1,"docs":{"100":{"tf":1.0}}},"df":0,"docs":{},"z":{"2":{"df":0,"docs":{},"h":{"1":{"df":0,"docs":{},"​":{"df":0,"docs":{},"−":{"df":0,"docs":{},"h":{"1":{"df":0,"docs":{},"​":{"(":{"df":0,"docs":{},"z":{"2":{")":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"γ":{"df":0,"docs":{},"′":{"df":0,"docs":{},"x":{"df":0,"docs":{},"−":{"df":0,"docs":{},"z":{"2":{"df":0,"docs":{},"h":{"2":{"df":0,"docs":{},"​":{"df":0,"docs":{},"−":{"df":0,"docs":{},"h":{"2":{"df":0,"docs":{},"​":{"(":{"df":0,"docs":{},"z":{"2":{")":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"j":{"df":0,"docs":{},"∑":{"df":0,"docs":{},"​":{"df":0,"docs":{},"γ":{"df":0,"docs":{},"j":{"df":0,"docs":{},"​":{"df":0,"docs":{},"x":{"df":0,"docs":{},"−":{"df":0,"docs":{},"z":{"df":0,"docs":{},"t":{"df":0,"docs":{},"j":{"df":0,"docs":{},"​":{"df":0,"docs":{},"−":{"df":0,"docs":{},"t":{"df":0,"docs":{},"j":{"df":0,"docs":{},"​":{"(":{"df":0,"docs":{},"z":{")":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"γ":{"df":0,"docs":{},"j":{"df":0,"docs":{},"′":{"df":0,"docs":{},"​":{"df":0,"docs":{},"x":{"df":0,"docs":{},"−":{"df":0,"docs":{},"g":{"df":0,"docs":{},"z":{"df":0,"docs":{},"t":{"df":0,"docs":{},"j":{"df":0,"docs":{},"​":{"df":0,"docs":{},"−":{"df":0,"docs":{},"t":{"df":0,"docs":{},"j":{"df":0,"docs":{},"​":{"(":{"df":0,"docs":{},"g":{"df":0,"docs":{},"z":{"df":1,"docs":{"94":{"tf":1.0}}}}},"df":0,"docs":{}}}}}}}}}}}}}}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}}}}}}}}}}}}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"y":{",":{"df":1,"docs":{"95":{"tf":1.0}}},"0":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"…":{",":{"df":0,"docs":{},"y":{"df":0,"docs":{},"m":{"df":1,"docs":{"73":{"tf":1.0}}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"1":{"df":0,"docs":{},"​":{"+":{"d":{"1":{"df":0,"docs":{},"−":{"1":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"…":{",":{"df":0,"docs":{},"y":{"df":0,"docs":{},"m":{"df":0,"docs":{},"​":{"+":{"d":{"df":0,"docs":{},"m":{"df":0,"docs":{},"−":{"1":{"df":1,"docs":{"74":{"tf":1.0}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"=":{"(":{"1":{",":{"1":{",":{"df":0,"docs":{},"…":{",":{"1":{"df":1,"docs":{"73":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{},"p":{"(":{"d":{"1":{"df":0,"docs":{},"​":{")":{",":{"df":0,"docs":{},"…":{",":{"df":0,"docs":{},"p":{"(":{"d":{"df":0,"docs":{},"m":{"df":1,"docs":{"73":{"tf":1.4142135623730951}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"y":{"0":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"…":{",":{"df":0,"docs":{},"y":{"df":0,"docs":{},"m":{"df":1,"docs":{"72":{"tf":1.0}}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"1":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"…":{",":{"df":0,"docs":{},"y":{"df":0,"docs":{},"m":{"df":3,"docs":{"73":{"tf":1.4142135623730951},"74":{"tf":1.0},"79":{"tf":1.0}}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{},"p":{"(":{"df":0,"docs":{},"z":{"df":1,"docs":{"75":{"tf":1.0}}}},"df":0,"docs":{}},"x":{"df":0,"docs":{},"e":{"+":{"5":{"df":1,"docs":{"34":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":15,"docs":{"19":{"tf":1.4142135623730951},"35":{"tf":2.23606797749979},"44":{"tf":2.23606797749979},"45":{"tf":1.4142135623730951},"72":{"tf":2.0},"73":{"tf":2.0},"74":{"tf":1.7320508075688772},"75":{"tf":1.4142135623730951},"77":{"tf":1.4142135623730951},"78":{"tf":1.0},"79":{"tf":1.4142135623730951},"88":{"tf":1.4142135623730951},"92":{"tf":1.7320508075688772},"94":{"tf":1.4142135623730951},"95":{"tf":1.4142135623730951}},"i":{"df":2,"docs":{"72":{"tf":1.0},"80":{"tf":1.0}},"​":{",":{"df":1,"docs":{"92":{"tf":1.0}}},"=":{"df":0,"docs":{},"p":{"df":0,"docs":{},"i":{"df":0,"docs":{},"​":{"(":{"df":0,"docs":{},"z":{"df":0,"docs":{},"i":{"df":1,"docs":{"80":{"tf":1.0}}}}},"df":0,"docs":{}}}}},"df":0,"docs":{},"−":{"df":0,"docs":{},"y":{")":{"/":{"(":{"d":{"df":0,"docs":{},"i":{"df":0,"docs":{},"​":{"df":0,"docs":{},"−":{"df":0,"docs":{},"z":{"df":1,"docs":{"79":{"tf":1.0}}}}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"j":{"(":{"1":{")":{"df":0,"docs":{},"​":{"df":0,"docs":{},"∣":{"df":0,"docs":{},"∣":{"df":0,"docs":{},"…":{"df":0,"docs":{},"∣":{"df":0,"docs":{},"∣":{"df":0,"docs":{},"y":{"df":0,"docs":{},"j":{"(":{"df":0,"docs":{},"k":{"df":1,"docs":{"92":{"tf":1.0}}}},"df":0,"docs":{}}}}}}}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"o":{"df":0,"docs":{},"u":{"'":{"df":0,"docs":{},"l":{"df":0,"docs":{},"l":{"df":2,"docs":{"114":{"tf":1.0},"16":{"tf":1.0}}}},"r":{"df":1,"docs":{"111":{"tf":1.0}}}},"df":0,"docs":{},"r":{"df":0,"docs":{},"s":{"df":0,"docs":{},"e":{"df":0,"docs":{},"l":{"df":0,"docs":{},"f":{"df":2,"docs":{"118":{"tf":1.0},"42":{"tf":1.0}}}}}}}}}},"z":{"(":{"d":{")":{"df":0,"docs":{},"f":{"(":{"d":{")":{"=":{"df":0,"docs":{},"g":{"(":{"d":{")":{"df":0,"docs":{},"z":{"(":{"df":0,"docs":{},"η":{"d":{"df":1,"docs":{"14":{"tf":1.0}}},"df":0,"docs":{}},"ω":{"d":{"df":1,"docs":{"14":{"tf":1.0}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{},"h":{")":{"df":0,"docs":{},"f":{"(":{"df":0,"docs":{},"h":{")":{"=":{"df":0,"docs":{},"g":{"(":{"df":0,"docs":{},"h":{")":{"df":0,"docs":{},"z":{"(":{"df":0,"docs":{},"ω":{"df":0,"docs":{},"h":{"df":1,"docs":{"14":{"tf":1.0}}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"x":{")":{"=":{"df":0,"docs":{},"∏":{"(":{"df":0,"docs":{},"x":{"df":0,"docs":{},"−":{"df":0,"docs":{},"e":{"df":0,"docs":{},"i":{"df":0,"docs":{},"​":{")":{"df":0,"docs":{},"x":{"2":{"df":0,"docs":{},"n":{"df":0,"docs":{},"−":{"1":{"df":1,"docs":{"123":{"tf":1.0}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"x":{"df":0,"docs":{},"i":{"df":1,"docs":{"123":{"tf":1.0}}}}}}},"df":0,"docs":{}}},"df":0,"docs":{},"f":{"(":{"df":0,"docs":{},"x":{")":{"=":{"df":0,"docs":{},"g":{"(":{"df":0,"docs":{},"x":{")":{"df":0,"docs":{},"z":{"(":{"df":0,"docs":{},"ω":{"df":0,"docs":{},"x":{"df":1,"docs":{"14":{"tf":1.7320508075688772}}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":2,"docs":{"35":{"tf":1.0},"5":{"tf":1.0}}},"η":{"0":{")":{"=":{"1":{"df":1,"docs":{"14":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"ω":{"0":{")":{"=":{"1":{"df":1,"docs":{"14":{"tf":2.0}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{},"k":{"df":0,"docs":{},"−":{"1":{")":{"=":{"df":0,"docs":{},"∏":{"df":0,"docs":{},"i":{"=":{"0":{"df":0,"docs":{},"k":{"df":0,"docs":{},"−":{"2":{"df":0,"docs":{},"​":{"(":{"a":{"df":0,"docs":{},"i":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"γ":{")":{"/":{"df":0,"docs":{},"∏":{"df":0,"docs":{},"i":{"=":{"0":{"df":0,"docs":{},"k":{"df":0,"docs":{},"−":{"2":{"df":0,"docs":{},"​":{"(":{"b":{"df":0,"docs":{},"i":{"df":1,"docs":{"14":{"tf":1.0}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{},"g":{"(":{"df":0,"docs":{},"ω":{"df":0,"docs":{},"k":{"df":0,"docs":{},"−":{"1":{")":{"df":0,"docs":{},"f":{"(":{"df":0,"docs":{},"ω":{"df":0,"docs":{},"k":{"df":0,"docs":{},"−":{"1":{")":{"df":0,"docs":{},"​":{"=":{"df":0,"docs":{},"z":{"(":{"df":0,"docs":{},"ω":{"df":0,"docs":{},"k":{")":{"=":{"df":0,"docs":{},"z":{"(":{"df":0,"docs":{},"w":{"0":{")":{"=":{"1":{"df":1,"docs":{"14":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"0":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"…":{",":{"df":0,"docs":{},"z":{"df":0,"docs":{},"n":{"df":0,"docs":{},"−":{"1":{"df":1,"docs":{"25":{"tf":1.0}}},"df":0,"docs":{}}}}},"df":0,"docs":{}}},"=":{"1":{"df":1,"docs":{"25":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}}},"1":{"df":0,"docs":{},"​":{",":{"df":0,"docs":{},"…":{",":{"df":0,"docs":{},"z":{"df":0,"docs":{},"l":{"df":1,"docs":{"80":{"tf":1.0}}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"=":{"(":{"b":{"7":{"df":0,"docs":{},"​":{"df":0,"docs":{},"x":{"2":{"+":{"b":{"8":{"df":0,"docs":{},"​":{"df":0,"docs":{},"x":{"+":{"b":{"9":{"df":0,"docs":{},"​":{")":{"df":0,"docs":{},"z":{"df":0,"docs":{},"h":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"z":{"df":1,"docs":{"25":{"tf":1.0}}}},"df":0,"docs":{}}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"]":{"1":{"df":3,"docs":{"25":{"tf":1.0},"32":{"tf":1.0},"33":{"tf":1.0}}},"df":0,"docs":{}},"^":{"2":{"df":1,"docs":{"116":{"tf":1.0}}},"df":0,"docs":{}},"_":{"1":{"df":1,"docs":{"35":{"tf":1.0}}},"df":0,"docs":{},"z":{"df":0,"docs":{},"e":{"df":0,"docs":{},"t":{"a":{"_":{"df":0,"docs":{},"o":{"df":0,"docs":{},"m":{"df":0,"docs":{},"e":{"df":0,"docs":{},"g":{"a":{"df":1,"docs":{"35":{"tf":1.0}}},"df":0,"docs":{}}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"a":{"df":0,"docs":{},"v":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":0,"docs":{},"u":{"c":{"df":0,"docs":{},"h":{"a":{"df":3,"docs":{"19":{"tf":1.0},"35":{"tf":1.0},"38":{"tf":1.0}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}}},"df":22,"docs":{"112":{"tf":1.0},"14":{"tf":3.1622776601683795},"16":{"tf":1.0},"25":{"tf":1.0},"28":{"tf":1.0},"33":{"tf":1.4142135623730951},"35":{"tf":1.4142135623730951},"44":{"tf":1.4142135623730951},"45":{"tf":1.4142135623730951},"5":{"tf":1.0},"58":{"tf":1.4142135623730951},"59":{"tf":1.0},"60":{"tf":1.4142135623730951},"62":{"tf":1.0},"69":{"tf":1.4142135623730951},"75":{"tf":1.4142135623730951},"77":{"tf":1.0},"79":{"tf":1.7320508075688772},"85":{"tf":2.0},"86":{"tf":1.0},"94":{"tf":1.0},"95":{"tf":1.4142135623730951}},"e":{"df":0,"docs":{},"r":{"df":0,"docs":{},"o":{"df":14,"docs":{"10":{"tf":1.0},"107":{"tf":1.0},"121":{"tf":1.0},"125":{"tf":1.0},"13":{"tf":1.7320508075688772},"142":{"tf":1.4142135623730951},"146":{"tf":1.4142135623730951},"20":{"tf":1.0},"36":{"tf":1.0},"4":{"tf":1.0},"84":{"tf":1.0},"88":{"tf":1.0},"94":{"tf":1.0},"95":{"tf":1.0}},"f":{"df":0,"docs":{},"i":{"df":5,"docs":{"114":{"tf":1.7320508075688772},"123":{"tf":1.4142135623730951},"125":{"tf":2.0},"54":{"tf":1.0},"91":{"tf":1.4142135623730951}}}}}}},"f":{"df":0,"docs":{},"−":{"df":0,"docs":{},"g":{"df":0,"docs":{},"z":{"df":0,"docs":{},"′":{"=":{"df":0,"docs":{},"z":{"df":0,"docs":{},"h":{"df":0,"docs":{},"​":{"df":0,"docs":{},"t":{"2":{"df":1,"docs":{"16":{"tf":1.0}}},"df":0,"docs":{}}}}}},"df":0,"docs":{}}}}}},"h":{"df":2,"docs":{"14":{"tf":1.0},"20":{"tf":1.0}},"​":{"(":{"df":0,"docs":{},"ω":{"df":0,"docs":{},"i":{")":{"=":{"0":{"df":1,"docs":{"20":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}},":":{"=":{"df":0,"docs":{},"x":{"df":0,"docs":{},"n":{"df":0,"docs":{},"−":{"1":{"df":1,"docs":{"23":{"tf":1.0}}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"=":{"df":0,"docs":{},"x":{"df":0,"docs":{},"n":{"df":0,"docs":{},"−":{"1":{"df":2,"docs":{"16":{"tf":1.0},"20":{"tf":1.0}}},"df":0,"docs":{}}}}},"df":0,"docs":{},"t":{"=":{"df":0,"docs":{},"​":{"a":{"df":0,"docs":{},"q":{"df":0,"docs":{},"l":{"df":0,"docs":{},"​":{"+":{"b":{"df":0,"docs":{},"q":{"df":0,"docs":{},"r":{"df":0,"docs":{},"​":{"+":{"a":{"b":{"df":0,"docs":{},"q":{"df":0,"docs":{},"m":{"df":0,"docs":{},"​":{"+":{"c":{"df":0,"docs":{},"q":{"df":0,"docs":{},"o":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"q":{"c":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"p":{"df":0,"docs":{},"i":{"+":{"df":0,"docs":{},"α":{"(":{"df":0,"docs":{},"g":{"df":0,"docs":{},"z":{"df":0,"docs":{},"′":{"df":0,"docs":{},"−":{"df":0,"docs":{},"f":{"df":0,"docs":{},"z":{")":{"+":{"df":0,"docs":{},"α":{"2":{"(":{"df":0,"docs":{},"z":{"df":0,"docs":{},"−":{"1":{")":{"df":0,"docs":{},"l":{"1":{"df":1,"docs":{"16":{"tf":1.0}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}}}}}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"i":{"df":0,"docs":{},"p":{"df":0,"docs":{},"p":{"df":0,"docs":{},"e":{"df":0,"docs":{},"l":{"df":1,"docs":{"14":{"tf":1.4142135623730951}}}}}}},"j":{"b":{"df":1,"docs":{"91":{"tf":1.0}}},"df":0,"docs":{},"t":{"df":1,"docs":{"91":{"tf":1.0}}}},"k":{"+":{"1":{"df":0,"docs":{},"​":{"=":{"df":0,"docs":{},"z":{"df":0,"docs":{},"k":{"df":0,"docs":{},"​":{"(":{"a":{"df":0,"docs":{},"k":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"β":{"df":0,"docs":{},"s":{"df":0,"docs":{},"σ":{"1":{"df":0,"docs":{},"​":{"(":{"df":0,"docs":{},"ω":{"df":0,"docs":{},"k":{")":{"+":{"df":0,"docs":{},"γ":{")":{"(":{"b":{"df":0,"docs":{},"k":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"β":{"df":0,"docs":{},"s":{"df":0,"docs":{},"σ":{"2":{"df":0,"docs":{},"​":{"(":{"df":0,"docs":{},"ω":{"df":0,"docs":{},"k":{")":{"+":{"df":0,"docs":{},"γ":{")":{"(":{"c":{"df":0,"docs":{},"k":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"β":{"df":0,"docs":{},"s":{"df":0,"docs":{},"σ":{"3":{"df":0,"docs":{},"​":{"(":{"df":0,"docs":{},"ω":{"df":0,"docs":{},"k":{")":{"+":{"df":0,"docs":{},"γ":{")":{"(":{"a":{"df":0,"docs":{},"k":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"β":{"df":0,"docs":{},"ω":{"df":0,"docs":{},"k":{"+":{"df":0,"docs":{},"γ":{")":{"(":{"b":{"df":0,"docs":{},"k":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"β":{"df":0,"docs":{},"ω":{"df":0,"docs":{},"k":{"df":0,"docs":{},"k":{"1":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"γ":{")":{"(":{"c":{"df":0,"docs":{},"k":{"df":0,"docs":{},"​":{"+":{"df":0,"docs":{},"β":{"df":0,"docs":{},"ω":{"df":0,"docs":{},"k":{"df":0,"docs":{},"k":{"2":{"df":1,"docs":{"25":{"tf":1.0}}},"df":0,"docs":{}}}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{}}}}},"df":0,"docs":{}}}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"d":{"df":0,"docs":{},"o":{"c":{"df":1,"docs":{"42":{"tf":1.0}}},"df":0,"docs":{}}},"df":1,"docs":{"4":{"tf":1.0}}},"′":{"(":{"df":0,"docs":{},"x":{")":{"=":{"df":0,"docs":{},"z":{"(":{"df":0,"docs":{},"x":{"df":1,"docs":{"16":{"tf":1.0}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}},":":{"=":{"df":0,"docs":{},"z":{"(":{"df":0,"docs":{},"ω":{"df":0,"docs":{},"x":{"df":1,"docs":{"5":{"tf":1.0}}}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{},"∈":{"df":0,"docs":{},"f":{"df":1,"docs":{"147":{"tf":1.0}}}}},"∈":{"df":0,"docs":{},"f":{"df":2,"docs":{"147":{"tf":1.0},"85":{"tf":1.0}},"∖":{"d":{"df":0,"docs":{},"l":{"d":{"df":0,"docs":{},"e":{"df":1,"docs":{"94":{"tf":1.0}}}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"−":{"1":{")":{"df":0,"docs":{},"l":{"1":{"df":0,"docs":{},"​":{"=":{"df":0,"docs":{},"z":{"df":0,"docs":{},"h":{"df":0,"docs":{},"​":{"df":0,"docs":{},"t":{"3":{"df":1,"docs":{"16":{"tf":1.0}}},"df":0,"docs":{}}}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"title":{"root":{"0":{"df":1,"docs":{"130":{"tf":1.0}}},"1":{"df":3,"docs":{"131":{"tf":1.0},"24":{"tf":1.0},"83":{"tf":1.0}}},"2":{"df":3,"docs":{"132":{"tf":1.0},"25":{"tf":1.0},"84":{"tf":1.0}}},"3":{"df":3,"docs":{"133":{"tf":1.0},"26":{"tf":1.0},"85":{"tf":1.0}}},"4":{"df":3,"docs":{"135":{"tf":1.0},"27":{"tf":1.0},"86":{"tf":1.0}}},"5":{"df":2,"docs":{"136":{"tf":1.0},"28":{"tf":1.0}}},"6":{"df":1,"docs":{"138":{"tf":1.0}}},"7":{"df":1,"docs":{"139":{"tf":1.0}}},"a":{"df":0,"docs":{},"i":{"df":0,"docs":{},"r":{"df":2,"docs":{"105":{"tf":1.0},"109":{"tf":1.0}}}},"l":{"df":0,"docs":{},"g":{"df":0,"docs":{},"o":{"df":0,"docs":{},"r":{"df":0,"docs":{},"i":{"df":0,"docs":{},"t":{"df":0,"docs":{},"h":{"df":0,"docs":{},"m":{"df":2,"docs":{"23":{"tf":1.0},"30":{"tf":1.0}}}}}}}}}},"p":{"df":0,"docs":{},"i":{"df":2,"docs":{"104":{"tf":1.0},"43":{"tf":1.0}}}},"r":{"df":0,"docs":{},"i":{"df":0,"docs":{},"t":{"df":0,"docs":{},"h":{"df":0,"docs":{},"m":{"df":0,"docs":{},"e":{"df":0,"docs":{},"t":{"df":3,"docs":{"70":{"tf":1.0},"8":{"tf":1.0},"83":{"tf":1.0}}}}}}}}}},"b":{"a":{"df":0,"docs":{},"t":{"c":{"df":0,"docs":{},"h":{"df":3,"docs":{"80":{"tf":1.0},"86":{"tf":1.0},"98":{"tf":1.0}}}},"df":0,"docs":{}}},"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"c":{"df":0,"docs":{},"h":{"df":0,"docs":{},"m":{"a":{"df":0,"docs":{},"r":{"df":0,"docs":{},"k":{"df":1,"docs":{"2":{"tf":1.0}}}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"i":{"df":0,"docs":{},"t":{"df":1,"docs":{"101":{"tf":1.0}}}},"l":{"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"d":{"df":1,"docs":{"20":{"tf":1.0}}},"df":0,"docs":{}}}},"o":{"df":0,"docs":{},"u":{"df":0,"docs":{},"n":{"d":{"a":{"df":0,"docs":{},"r":{"df":0,"docs":{},"i":{"df":2,"docs":{"106":{"tf":1.0},"54":{"tf":1.0}}}}},"df":0,"docs":{}},"df":0,"docs":{}}}},"u":{"df":0,"docs":{},"i":{"df":0,"docs":{},"l":{"d":{"df":1,"docs":{"45":{"tf":1.0}}},"df":0,"docs":{},"t":{"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"df":1,"docs":{"151":{"tf":1.0}}}}}}}}},"c":{"a":{"df":0,"docs":{},"i":{"df":0,"docs":{},"r":{"df":0,"docs":{},"o":{"df":3,"docs":{"140":{"tf":1.0},"143":{"tf":1.0},"50":{"tf":1.0}}}}}},"df":0,"docs":{},"e":{"df":0,"docs":{},"l":{"df":0,"docs":{},"l":{"df":1,"docs":{"134":{"tf":1.0}}}}},"h":{"a":{"df":0,"docs":{},"l":{"df":0,"docs":{},"l":{"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"df":0,"docs":{},"g":{"df":1,"docs":{"97":{"tf":1.0}}}}}}}},"df":0,"docs":{},"e":{"c":{"df":0,"docs":{},"k":{"df":4,"docs":{"138":{"tf":1.0},"33":{"tf":1.0},"58":{"tf":1.0},"60":{"tf":1.0}}}},"df":0,"docs":{}}},"i":{"df":0,"docs":{},"r":{"c":{"df":0,"docs":{},"u":{"df":0,"docs":{},"i":{"df":0,"docs":{},"t":{"df":2,"docs":{"43":{"tf":1.0},"9":{"tf":1.0}}}}}},"df":0,"docs":{}}},"l":{"df":0,"docs":{},"i":{"df":1,"docs":{"152":{"tf":1.0}}}},"o":{"df":0,"docs":{},"l":{"df":0,"docs":{},"u":{"df":0,"docs":{},"m":{"df":0,"docs":{},"n":{"df":15,"docs":{"128":{"tf":1.4142135623730951},"129":{"tf":1.0},"130":{"tf":1.0},"131":{"tf":1.0},"132":{"tf":1.0},"133":{"tf":1.0},"135":{"tf":1.0},"136":{"tf":1.0},"137":{"tf":1.0},"138":{"tf":1.0},"139":{"tf":1.4142135623730951},"147":{"tf":1.0},"148":{"tf":1.0},"149":{"tf":1.0},"65":{"tf":1.0}}}}}},"m":{"df":0,"docs":{},"m":{"df":0,"docs":{},"i":{"df":0,"docs":{},"t":{"df":9,"docs":{"19":{"tf":1.0},"32":{"tf":1.0},"38":{"tf":1.0},"57":{"tf":1.0},"71":{"tf":1.0},"72":{"tf":1.0},"75":{"tf":1.0},"76":{"tf":1.0},"83":{"tf":1.0}}}},"o":{"df":0,"docs":{},"n":{"df":1,"docs":{"15":{"tf":1.0}}}}},"p":{"a":{"df":0,"docs":{},"r":{"df":0,"docs":{},"i":{"df":0,"docs":{},"s":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"df":1,"docs":{"3":{"tf":1.0}}}}}}}},"df":0,"docs":{},"l":{"df":0,"docs":{},"e":{"df":0,"docs":{},"t":{"df":1,"docs":{"78":{"tf":1.0}}},"x":{"df":1,"docs":{"45":{"tf":1.0}}}}},"o":{"df":0,"docs":{},"n":{"df":1,"docs":{"6":{"tf":1.0}}},"s":{"df":0,"docs":{},"i":{"df":0,"docs":{},"t":{"df":4,"docs":{"53":{"tf":1.0},"59":{"tf":1.0},"67":{"tf":1.0},"84":{"tf":1.0}}}}}},"u":{"df":0,"docs":{},"t":{"df":1,"docs":{"48":{"tf":1.0}}}}}},"n":{"df":0,"docs":{},"s":{"df":0,"docs":{},"i":{"d":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":1,"docs":{"120":{"tf":1.0}}}}},"df":0,"docs":{},"s":{"df":0,"docs":{},"t":{"df":2,"docs":{"58":{"tf":1.0},"60":{"tf":1.0}}}}},"t":{"df":0,"docs":{},"r":{"a":{"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"df":0,"docs":{},"t":{"df":5,"docs":{"106":{"tf":1.0},"107":{"tf":1.0},"114":{"tf":1.0},"55":{"tf":1.0},"66":{"tf":1.0}}}}}},"df":0,"docs":{},"u":{"c":{"df":0,"docs":{},"t":{"df":5,"docs":{"142":{"tf":1.0},"145":{"tf":1.0},"146":{"tf":1.0},"56":{"tf":1.0},"84":{"tf":1.0}}}},"df":0,"docs":{}}}}},"t":{"df":0,"docs":{},"e":{"df":0,"docs":{},"x":{"df":0,"docs":{},"t":{"df":1,"docs":{"109":{"tf":1.0}}}}}}},"s":{"df":0,"docs":{},"e":{"df":0,"docs":{},"t":{"df":1,"docs":{"125":{"tf":1.0}}}}}},"r":{"a":{"df":0,"docs":{},"t":{"df":0,"docs":{},"e":{"df":1,"docs":{"1":{"tf":1.0}}}}},"df":0,"docs":{}},"u":{"df":0,"docs":{},"m":{"df":1,"docs":{"139":{"tf":1.0}},"u":{"df":0,"docs":{},"l":{"df":1,"docs":{"138":{"tf":1.0}}}}},"s":{"df":0,"docs":{},"t":{"df":0,"docs":{},"o":{"df":0,"docs":{},"m":{"df":1,"docs":{"12":{"tf":1.0}}}}}}}},"d":{"df":0,"docs":{},"e":{"c":{"df":0,"docs":{},"o":{"d":{"df":0,"docs":{},"e":{"/":{"df":0,"docs":{},"o":{"df":0,"docs":{},"p":{"c":{"df":0,"docs":{},"o":{"d":{"df":1,"docs":{"131":{"tf":1.0}}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"df":0,"docs":{},"m":{"df":0,"docs":{},"p":{"df":0,"docs":{},"o":{"df":0,"docs":{},"s":{"df":0,"docs":{},"i":{"df":0,"docs":{},"t":{"df":2,"docs":{"116":{"tf":1.0},"67":{"tf":1.0}}}}}}}}}},"df":0,"docs":{},"e":{"df":0,"docs":{},"p":{"df":1,"docs":{"59":{"tf":1.0}}}},"f":{"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"df":0,"docs":{},"i":{"df":0,"docs":{},"t":{"df":1,"docs":{"91":{"tf":1.0}}}}}}},"g":{"df":0,"docs":{},"r":{"df":0,"docs":{},"e":{"df":1,"docs":{"68":{"tf":1.0}}}}},"s":{"c":{"df":0,"docs":{},"r":{"df":0,"docs":{},"i":{"df":0,"docs":{},"p":{"df":0,"docs":{},"t":{"df":1,"docs":{"82":{"tf":1.0}}}}}}},"df":0,"docs":{}},"t":{"a":{"df":0,"docs":{},"i":{"df":0,"docs":{},"l":{"df":3,"docs":{"145":{"tf":1.0},"18":{"tf":1.0},"37":{"tf":1.0}}}}},"df":0,"docs":{}}},"o":{"c":{"df":0,"docs":{},"u":{"df":0,"docs":{},"m":{"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"df":0,"docs":{},"t":{"df":1,"docs":{"126":{"tf":1.0}}}}}}}},"df":0,"docs":{},"m":{"a":{"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"df":1,"docs":{"117":{"tf":1.0}}}}},"df":0,"docs":{}}}},"df":0,"docs":{},"e":{"df":0,"docs":{},"l":{"df":0,"docs":{},"e":{"df":0,"docs":{},"m":{"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"df":0,"docs":{},"t":{"df":1,"docs":{"41":{"tf":1.0}}}}}}}},"v":{"a":{"df":0,"docs":{},"l":{"df":0,"docs":{},"u":{"df":2,"docs":{"121":{"tf":1.0},"85":{"tf":1.0}}}}},"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"/":{"df":0,"docs":{},"o":{"d":{"d":{"df":1,"docs":{"116":{"tf":1.0}}},"df":0,"docs":{}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"x":{"a":{"df":0,"docs":{},"m":{"df":0,"docs":{},"p":{"df":0,"docs":{},"l":{"df":3,"docs":{"104":{"tf":1.0},"44":{"tf":1.0},"7":{"tf":1.0}}}}}},"df":0,"docs":{},"e":{"c":{"df":0,"docs":{},"u":{"df":0,"docs":{},"t":{"df":5,"docs":{"110":{"tf":1.0},"142":{"tf":1.0},"143":{"tf":1.0},"83":{"tf":1.0},"9":{"tf":1.0}}}}},"df":0,"docs":{}},"t":{"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"d":{"df":1,"docs":{"147":{"tf":1.0}}},"df":0,"docs":{},"s":{"df":1,"docs":{"68":{"tf":1.0}}}}},"r":{"a":{"c":{"df":0,"docs":{},"t":{"df":1,"docs":{"32":{"tf":1.0}}}},"df":0,"docs":{}},"df":0,"docs":{}}}}},"f":{"df":0,"docs":{},"f":{"df":0,"docs":{},"t":{"df":3,"docs":{"121":{"tf":1.0},"2":{"tf":1.0},"99":{"tf":1.0}}}},"i":{"a":{"df":0,"docs":{},"t":{"df":2,"docs":{"39":{"tf":1.0},"42":{"tf":1.0}}}},"b":{"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"a":{"c":{"c":{"df":0,"docs":{},"i":{"df":3,"docs":{"104":{"tf":1.0},"49":{"tf":1.0},"51":{"tf":1.0}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}}}},"df":0,"docs":{},"e":{"df":0,"docs":{},"l":{"d":{"df":1,"docs":{"41":{"tf":1.0}}},"df":0,"docs":{}}}},"l":{"a":{"df":0,"docs":{},"g":{"df":1,"docs":{"131":{"tf":1.0}}}},"df":0,"docs":{}},"r":{"a":{"df":0,"docs":{},"m":{"df":0,"docs":{},"e":{"df":1,"docs":{"117":{"tf":1.0}}}}},"df":0,"docs":{},"i":{"df":2,"docs":{"68":{"tf":1.0},"73":{"tf":1.0}}}}},"g":{"a":{"df":0,"docs":{},"t":{"df":0,"docs":{},"e":{"df":1,"docs":{"12":{"tf":1.0}}}}},"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":1,"docs":{"90":{"tf":1.0}}}}}},"r":{"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"d":{"df":1,"docs":{"88":{"tf":1.0}}},"df":0,"docs":{}}}}},"h":{"df":4,"docs":{"116":{"tf":1.0},"56":{"tf":1.0},"57":{"tf":1.0},"84":{"tf":1.0}},"i":{"df":0,"docs":{},"g":{"df":0,"docs":{},"h":{"df":2,"docs":{"104":{"tf":1.0},"82":{"tf":1.0}}}}},"o":{"df":0,"docs":{},"o":{"d":{"df":1,"docs":{"111":{"tf":1.0}}},"df":0,"docs":{}}}},"i":{"d":{"df":0,"docs":{},"e":{"a":{"df":1,"docs":{"6":{"tf":1.0}}},"df":0,"docs":{}}},"df":0,"docs":{},"m":{"df":0,"docs":{},"p":{"df":0,"docs":{},"l":{"df":0,"docs":{},"e":{"df":0,"docs":{},"m":{"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"df":0,"docs":{},"t":{"df":3,"docs":{"103":{"tf":1.0},"34":{"tf":1.0},"37":{"tf":1.0}}}}}}}},"o":{"df":0,"docs":{},"r":{"df":0,"docs":{},"t":{"df":1,"docs":{"92":{"tf":1.0}}}}}}},"n":{"df":0,"docs":{},"i":{"df":0,"docs":{},"t":{"df":0,"docs":{},"i":{"df":1,"docs":{"31":{"tf":1.0}}}}},"p":{"df":0,"docs":{},"u":{"df":0,"docs":{},"t":{"df":2,"docs":{"13":{"tf":1.0},"15":{"tf":1.0}}}}},"t":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"a":{"c":{"df":0,"docs":{},"t":{"df":1,"docs":{"137":{"tf":1.0}}}},"df":0,"docs":{}},"df":0,"docs":{},"p":{"df":0,"docs":{},"o":{"df":0,"docs":{},"l":{"df":2,"docs":{"121":{"tf":1.0},"3":{"tf":1.0}}}}}}},"r":{"df":0,"docs":{},"o":{"d":{"df":0,"docs":{},"u":{"c":{"df":0,"docs":{},"t":{"df":1,"docs":{"0":{"tf":1.0}}}},"df":0,"docs":{}}},"df":0,"docs":{}}}},"v":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":0,"docs":{},"s":{"df":1,"docs":{"98":{"tf":1.0}}}}}}}},"l":{"a":{"df":0,"docs":{},"m":{"b":{"d":{"a":{"df":0,"docs":{},"w":{"df":0,"docs":{},"o":{"df":0,"docs":{},"r":{"df":0,"docs":{},"k":{"df":1,"docs":{"103":{"tf":1.0}}}}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":0,"docs":{}},"y":{"df":0,"docs":{},"o":{"df":0,"docs":{},"u":{"df":0,"docs":{},"t":{"df":1,"docs":{"127":{"tf":1.0}}}}}}},"df":0,"docs":{},"e":{"a":{"df":0,"docs":{},"v":{"df":1,"docs":{"101":{"tf":1.0}}}},"df":0,"docs":{},"v":{"df":0,"docs":{},"e":{"df":0,"docs":{},"l":{"df":2,"docs":{"104":{"tf":1.0},"82":{"tf":1.0}}}}}},"i":{"df":0,"docs":{},"n":{"df":0,"docs":{},"e":{"a":{"df":0,"docs":{},"r":{"df":1,"docs":{"21":{"tf":1.0}}}},"df":0,"docs":{}}}},"o":{"df":0,"docs":{},"w":{"df":1,"docs":{"68":{"tf":1.0}}}}},"m":{"a":{"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"df":2,"docs":{"129":{"tf":1.0},"146":{"tf":1.0}}}},"t":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":0,"docs":{},"i":{"df":1,"docs":{"144":{"tf":1.0}}}}},"r":{"df":0,"docs":{},"i":{"c":{"df":1,"docs":{"14":{"tf":1.0}}},"df":0,"docs":{},"x":{"df":2,"docs":{"10":{"tf":1.0},"11":{"tf":1.0}}}}}}},"df":0,"docs":{},"e":{"df":0,"docs":{},"m":{"df":3,"docs":{"133":{"tf":1.0},"134":{"tf":1.0},"135":{"tf":1.0}}},"r":{"df":0,"docs":{},"k":{"df":0,"docs":{},"l":{"df":1,"docs":{"101":{"tf":1.0}}}}},"t":{"df":0,"docs":{},"h":{"df":0,"docs":{},"o":{"d":{"df":1,"docs":{"3":{"tf":1.0}}},"df":0,"docs":{}}}}},"u":{"df":0,"docs":{},"l":{"df":0,"docs":{},"t":{"df":0,"docs":{},"i":{"df":1,"docs":{"139":{"tf":1.0}},"p":{"df":0,"docs":{},"l":{"df":2,"docs":{"65":{"tf":1.0},"66":{"tf":1.0}}}}}}}}},"n":{"df":0,"docs":{},"o":{"df":0,"docs":{},"t":{"a":{"df":0,"docs":{},"t":{"df":3,"docs":{"5":{"tf":1.0},"90":{"tf":1.0},"92":{"tf":1.0}}}},"df":0,"docs":{},"e":{"df":1,"docs":{"96":{"tf":1.0}}}}},"u":{"df":0,"docs":{},"m":{"b":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":1,"docs":{"49":{"tf":1.0}}}}},"df":0,"docs":{}}}},"o":{"df":0,"docs":{},"m":{"df":0,"docs":{},"i":{"df":0,"docs":{},"s":{"df":0,"docs":{},"s":{"df":1,"docs":{"64":{"tf":1.0}}}}}},"p":{"df":0,"docs":{},"e":{"df":0,"docs":{},"n":{"df":2,"docs":{"77":{"tf":1.0},"86":{"tf":1.0}}},"r":{"df":1,"docs":{"92":{"tf":1.0}}}},"t":{"df":0,"docs":{},"i":{"df":0,"docs":{},"m":{"df":1,"docs":{"96":{"tf":1.0}}}}}},"r":{"d":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":1,"docs":{"101":{"tf":1.0}}}}},"df":0,"docs":{}},"u":{"df":0,"docs":{},"t":{"df":1,"docs":{"117":{"tf":1.0}}}},"v":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":0,"docs":{},"v":{"df":0,"docs":{},"i":{"df":0,"docs":{},"e":{"df":0,"docs":{},"w":{"df":1,"docs":{"69":{"tf":1.0}}}}}}}}}},"p":{"a":{"d":{"df":1,"docs":{"36":{"tf":1.0}}},"df":0,"docs":{}},"c":{"df":1,"docs":{"136":{"tf":1.0}}},"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":0,"docs":{},"m":{"df":0,"docs":{},"u":{"df":0,"docs":{},"t":{"df":1,"docs":{"139":{"tf":1.0}}}}}}},"l":{"a":{"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"df":1,"docs":{"127":{"tf":1.0}}}}},"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"df":0,"docs":{},"k":{"df":2,"docs":{"4":{"tf":1.0},"8":{"tf":1.0}}}}}},"o":{"df":0,"docs":{},"l":{"df":0,"docs":{},"y":{"df":0,"docs":{},"n":{"df":0,"docs":{},"o":{"df":0,"docs":{},"m":{"df":0,"docs":{},"i":{"df":15,"docs":{"114":{"tf":1.0},"14":{"tf":1.0},"19":{"tf":1.0},"3":{"tf":1.0},"48":{"tf":1.0},"52":{"tf":1.0},"53":{"tf":1.0},"54":{"tf":1.0},"55":{"tf":1.0},"59":{"tf":1.0},"67":{"tf":1.0},"71":{"tf":1.0},"75":{"tf":1.0},"84":{"tf":1.0},"85":{"tf":1.0}}}}}}}},"o":{"df":0,"docs":{},"l":{"df":4,"docs":{"130":{"tf":1.0},"133":{"tf":1.0},"134":{"tf":1.0},"135":{"tf":1.0}}}}},"r":{"df":0,"docs":{},"e":{"df":0,"docs":{},"p":{"df":0,"docs":{},"r":{"df":0,"docs":{},"o":{"c":{"df":0,"docs":{},"e":{"df":0,"docs":{},"s":{"df":0,"docs":{},"s":{"df":1,"docs":{"15":{"tf":1.0}}}}}},"df":0,"docs":{}}}}},"i":{"df":0,"docs":{},"m":{"df":0,"docs":{},"i":{"df":0,"docs":{},"t":{"df":1,"docs":{"124":{"tf":1.0}}}}}},"o":{"d":{"df":0,"docs":{},"u":{"c":{"df":0,"docs":{},"t":{"df":2,"docs":{"138":{"tf":1.0},"139":{"tf":1.0}}}},"df":0,"docs":{}}},"df":0,"docs":{},"g":{"df":0,"docs":{},"r":{"a":{"df":0,"docs":{},"m":{"df":1,"docs":{"7":{"tf":1.0}}}},"df":0,"docs":{}}},"o":{"df":0,"docs":{},"f":{"df":4,"docs":{"102":{"tf":1.0},"119":{"tf":1.0},"29":{"tf":1.0},"33":{"tf":1.0}}}},"t":{"df":0,"docs":{},"o":{"c":{"df":0,"docs":{},"o":{"df":0,"docs":{},"l":{"df":6,"docs":{"17":{"tf":1.0},"69":{"tf":1.0},"82":{"tf":1.0},"86":{"tf":1.0},"87":{"tf":1.0},"93":{"tf":1.0}}}}},"df":0,"docs":{}}},"v":{"df":0,"docs":{},"e":{"df":2,"docs":{"110":{"tf":1.0},"23":{"tf":1.0}},"r":{"df":7,"docs":{"103":{"tf":1.0},"112":{"tf":1.0},"126":{"tf":1.0},"127":{"tf":1.0},"46":{"tf":1.0},"62":{"tf":1.0},"94":{"tf":1.0}}}}}}},"u":{"b":{"df":0,"docs":{},"l":{"df":0,"docs":{},"i":{"c":{"df":1,"docs":{"13":{"tf":1.0}}},"df":0,"docs":{}}}},"df":0,"docs":{}}},"q":{"df":1,"docs":{"10":{"tf":1.0}}},"r":{"a":{"df":0,"docs":{},"n":{"df":0,"docs":{},"g":{"df":1,"docs":{"138":{"tf":1.0}}}},"w":{"df":1,"docs":{"144":{"tf":1.0}}}},"c":{"1":{"6":{"/":{"df":0,"docs":{},"s":{"df":0,"docs":{},"o":{"df":0,"docs":{},"r":{"df":0,"docs":{},"t":{"df":1,"docs":{"132":{"tf":1.0}}}}}}},"df":0,"docs":{}},"df":0,"docs":{}},"df":3,"docs":{"130":{"tf":1.0},"131":{"tf":1.0},"132":{"tf":1.0}}},"df":0,"docs":{},"e":{"c":{"a":{"df":0,"docs":{},"p":{"df":1,"docs":{"47":{"tf":1.0}}}},"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"df":0,"docs":{},"s":{"df":0,"docs":{},"t":{"df":0,"docs":{},"r":{"df":0,"docs":{},"u":{"c":{"df":0,"docs":{},"t":{"df":1,"docs":{"114":{"tf":1.0}}}},"df":0,"docs":{}}}}}}}},"d":{"df":0,"docs":{},"u":{"df":0,"docs":{},"n":{"d":{"df":1,"docs":{"102":{"tf":1.0}}},"df":0,"docs":{}}}},"df":0,"docs":{},"f":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":1,"docs":{"81":{"tf":1.0}}}}},"g":{"df":0,"docs":{},"i":{"df":0,"docs":{},"s":{"df":0,"docs":{},"t":{"df":1,"docs":{"136":{"tf":1.0}}}}}},"v":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":0,"docs":{},"s":{"df":1,"docs":{"101":{"tf":1.0}}}}}}},"o":{"df":0,"docs":{},"o":{"df":0,"docs":{},"t":{"df":3,"docs":{"123":{"tf":1.0},"124":{"tf":1.0},"68":{"tf":1.0}}}},"u":{"df":0,"docs":{},"n":{"d":{"df":9,"docs":{"24":{"tf":1.0},"25":{"tf":1.0},"26":{"tf":1.0},"27":{"tf":1.0},"28":{"tf":1.0},"83":{"tf":1.0},"84":{"tf":1.0},"85":{"tf":1.0},"86":{"tf":1.0}}},"df":0,"docs":{}}}},"u":{"df":0,"docs":{},"f":{"df":0,"docs":{},"f":{"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"df":0,"docs":{},"i":{"'":{"df":1,"docs":{"100":{"tf":1.0}}},"df":0,"docs":{}}}}}},"l":{"df":0,"docs":{},"e":{"df":1,"docs":{"100":{"tf":1.0}}}},"n":{"df":1,"docs":{"86":{"tf":1.0}}}}},"s":{"a":{"df":0,"docs":{},"m":{"df":0,"docs":{},"p":{"df":0,"docs":{},"l":{"df":1,"docs":{"97":{"tf":1.0}}}}}},"c":{"df":0,"docs":{},"h":{"df":0,"docs":{},"e":{"df":0,"docs":{},"m":{"df":0,"docs":{},"e":{"df":3,"docs":{"19":{"tf":1.0},"38":{"tf":1.0},"71":{"tf":1.0}}}}}}},"df":0,"docs":{},"e":{"df":0,"docs":{},"t":{"df":0,"docs":{},"u":{"df":0,"docs":{},"p":{"df":1,"docs":{"22":{"tf":1.0}}}}}},"h":{"a":{"df":0,"docs":{},"m":{"df":0,"docs":{},"i":{"df":0,"docs":{},"r":{"df":2,"docs":{"39":{"tf":1.0},"42":{"tf":1.0}}}}}},"df":0,"docs":{}},"i":{"d":{"df":0,"docs":{},"e":{"df":4,"docs":{"112":{"tf":1.0},"113":{"tf":1.0},"62":{"tf":1.0},"63":{"tf":1.0}}}},"df":0,"docs":{},"m":{"df":0,"docs":{},"p":{"df":0,"docs":{},"l":{"df":1,"docs":{"44":{"tf":1.0}},"i":{"df":0,"docs":{},"f":{"df":1,"docs":{"64":{"tf":1.0}}}}}}}},"o":{"df":0,"docs":{},"r":{"df":0,"docs":{},"t":{"df":2,"docs":{"132":{"tf":1.0},"135":{"tf":1.0}}}},"u":{"df":0,"docs":{},"n":{"d":{"df":1,"docs":{"79":{"tf":1.0}}},"df":0,"docs":{}}}},"p":{"df":0,"docs":{},"e":{"c":{"df":0,"docs":{},"i":{"a":{"df":0,"docs":{},"l":{"df":1,"docs":{"120":{"tf":1.0}}}},"df":0,"docs":{}}},"df":0,"docs":{}}},"t":{"a":{"df":0,"docs":{},"r":{"df":0,"docs":{},"k":{"df":5,"docs":{"103":{"tf":1.0},"46":{"tf":1.0},"47":{"tf":1.0},"74":{"tf":1.0},"87":{"tf":1.0}}}}},"df":0,"docs":{},"e":{"df":0,"docs":{},"p":{"df":1,"docs":{"51":{"tf":1.4142135623730951}}}},"o":{"df":0,"docs":{},"n":{"df":0,"docs":{},"e":{"df":2,"docs":{"126":{"tf":1.0},"127":{"tf":1.0}}}}},"r":{"a":{"df":0,"docs":{},"t":{"df":0,"docs":{},"e":{"df":0,"docs":{},"g":{"df":0,"docs":{},"i":{"df":1,"docs":{"40":{"tf":1.0}}}}}}},"df":0,"docs":{},"o":{"df":0,"docs":{},"n":{"df":0,"docs":{},"g":{"df":1,"docs":{"42":{"tf":1.0}}}}}}},"u":{"b":{"c":{"df":0,"docs":{},"o":{"df":0,"docs":{},"l":{"df":0,"docs":{},"u":{"df":0,"docs":{},"m":{"df":0,"docs":{},"n":{"df":2,"docs":{"148":{"tf":1.0},"150":{"tf":1.0}}}}}}}},"df":0,"docs":{}},"df":0,"docs":{},"m":{"df":0,"docs":{},"m":{"a":{"df":0,"docs":{},"r":{"df":0,"docs":{},"i":{"df":1,"docs":{"61":{"tf":1.0}}}}},"df":0,"docs":{}}}},"y":{"df":0,"docs":{},"s":{"df":0,"docs":{},"t":{"df":0,"docs":{},"e":{"df":0,"docs":{},"m":{"df":1,"docs":{"45":{"tf":1.0}}}}}}}},"t":{"df":1,"docs":{"142":{"tf":1.0}},"h":{"df":0,"docs":{},"i":{"df":0,"docs":{},"n":{"df":0,"docs":{},"g":{"df":1,"docs":{"16":{"tf":1.0}}}}},"r":{"df":0,"docs":{},"o":{"df":0,"docs":{},"u":{"df":0,"docs":{},"g":{"df":0,"docs":{},"h":{"df":1,"docs":{"48":{"tf":1.0}}}}}}}},"o":{"df":0,"docs":{},"y":{"df":1,"docs":{"7":{"tf":1.0}}}},"r":{"a":{"c":{"df":0,"docs":{},"e":{"df":11,"docs":{"127":{"tf":1.0},"129":{"tf":1.0},"137":{"tf":1.0},"141":{"tf":1.0},"142":{"tf":1.0},"143":{"tf":1.0},"146":{"tf":1.0},"52":{"tf":1.0},"65":{"tf":1.0},"83":{"tf":1.0},"9":{"tf":1.0}},"t":{"df":1,"docs":{"108":{"tf":1.0}}}}},"df":0,"docs":{},"n":{"df":0,"docs":{},"s":{"c":{"df":0,"docs":{},"r":{"df":0,"docs":{},"i":{"df":0,"docs":{},"p":{"df":0,"docs":{},"t":{"df":4,"docs":{"118":{"tf":1.0},"31":{"tf":1.0},"40":{"tf":1.0},"89":{"tf":1.0}}}}}}},"df":0,"docs":{},"i":{"df":0,"docs":{},"t":{"df":4,"docs":{"107":{"tf":1.0},"114":{"tf":1.0},"55":{"tf":1.0},"66":{"tf":1.0}}}}}}},"df":0,"docs":{},"e":{"df":0,"docs":{},"e":{"df":1,"docs":{"101":{"tf":1.0}}}},"i":{"c":{"df":0,"docs":{},"k":{"df":2,"docs":{"18":{"tf":1.0},"21":{"tf":1.0}}}},"df":0,"docs":{}}}},"u":{"df":0,"docs":{},"n":{"d":{"df":0,"docs":{},"e":{"df":0,"docs":{},"r":{"df":1,"docs":{"111":{"tf":1.0}}}}},"df":0,"docs":{},"i":{"df":0,"docs":{},"t":{"df":0,"docs":{},"i":{"df":3,"docs":{"123":{"tf":1.0},"124":{"tf":1.0},"68":{"tf":1.0}}}}},"u":{"df":0,"docs":{},"s":{"df":1,"docs":{"134":{"tf":1.0}}}}},"p":{"d":{"a":{"df":0,"docs":{},"t":{"df":1,"docs":{"136":{"tf":1.0}}}},"df":0,"docs":{}},"df":1,"docs":{"16":{"tf":1.0}}},"s":{"a":{"df":0,"docs":{},"g":{"df":1,"docs":{"35":{"tf":1.0}}}},"df":3,"docs":{"123":{"tf":1.0},"125":{"tf":1.0},"74":{"tf":1.0}}}},"v":{"a":{"df":0,"docs":{},"l":{"df":0,"docs":{},"u":{"df":2,"docs":{"102":{"tf":1.0},"32":{"tf":1.0}}}},"r":{"df":0,"docs":{},"i":{"a":{"df":0,"docs":{},"n":{"df":0,"docs":{},"t":{"df":3,"docs":{"74":{"tf":1.0},"96":{"tf":1.0},"97":{"tf":1.0}}}}},"df":0,"docs":{}}}},"df":1,"docs":{"11":{"tf":1.0}},"e":{"c":{"df":0,"docs":{},"t":{"df":0,"docs":{},"o":{"df":0,"docs":{},"r":{"df":1,"docs":{"72":{"tf":1.0}}}}}},"df":0,"docs":{},"r":{"df":0,"docs":{},"i":{"df":0,"docs":{},"f":{"df":1,"docs":{"30":{"tf":1.0}},"i":{"df":5,"docs":{"113":{"tf":1.0},"115":{"tf":1.0},"48":{"tf":1.0},"63":{"tf":1.0},"95":{"tf":1.0}}}}}}},"i":{"df":0,"docs":{},"r":{"df":0,"docs":{},"t":{"df":0,"docs":{},"u":{"a":{"df":0,"docs":{},"l":{"df":4,"docs":{"128":{"tf":1.0},"148":{"tf":1.0},"149":{"tf":1.0},"150":{"tf":1.0}}}},"df":0,"docs":{}}}}}},"w":{"a":{"df":0,"docs":{},"l":{"df":0,"docs":{},"k":{"df":0,"docs":{},"t":{"df":0,"docs":{},"h":{"df":0,"docs":{},"r":{"df":0,"docs":{},"o":{"df":0,"docs":{},"u":{"df":0,"docs":{},"g":{"df":0,"docs":{},"h":{"df":1,"docs":{"51":{"tf":1.0}}}}}}}}}}}},"df":0,"docs":{},"h":{"df":0,"docs":{},"o":{"df":0,"docs":{},"l":{"df":0,"docs":{},"e":{"df":1,"docs":{"16":{"tf":1.0}}}}}},"o":{"df":0,"docs":{},"r":{"df":0,"docs":{},"k":{"df":1,"docs":{"111":{"tf":1.0}}}}},"r":{"a":{"df":0,"docs":{},"p":{"df":1,"docs":{"16":{"tf":1.0}}}},"df":0,"docs":{}}},"z":{"df":1,"docs":{"85":{"tf":1.0}}}}}},"lang":"English","pipeline":["trimmer","stopWordFilter","stemmer"],"ref":"id","version":"0.9.5"},"results_options":{"limit_results":30,"teaser_word_count":30},"search_options":{"bool":"OR","expand":true,"fields":{"body":{"boost":1},"breadcrumbs":{"boost":1},"title":{"boost":2}}}} \ No newline at end of file diff --git a/starks/api.html b/starks/api.html new file mode 100644 index 000000000..03c846784 --- /dev/null +++ b/starks/api.html @@ -0,0 +1,350 @@ + + + + + + High Level API - docs + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + + + +
+
+ +

High level API: Fibonacci example

+

Let's go over the main test we use for our prover, where we compute a STARK proof for a fibonacci trace with 4 rows and then verify it.

+

+#![allow(unused)]
+fn main() {
+fn test_prove_fib() {
+    let trace = simple_fibonacci::fibonacci_trace([FE::from(1), FE::from(1)], 8);
+    let proof_options = ProofOptions::default_test_options();
+
+    let pub_inputs = FibonacciPublicInputs {
+        a0: FE::one(),
+        a1: FE::one(),
+    };
+
+    let proof = prove::<F, FibonacciAIR<F>>(&trace, &pub_inputs, &proof_options).unwrap();
+    assert!(verify::<F, FibonacciAIR<F>>(&proof, &pub_inputs, &proof_options));
+}
+}
+
+

The proving system revolves around the prove function, that takes a trace, public inputs and proof options as inputs to generate a proof, and a verify function that takes the generated proof, the public inputs and the proof options as inputs, outputting true when the proof is verified correctly and false otherwise. Note that the public inputs and proof options should be the same for both. Public inputs should be shared by the Cairo runner to prover and verifier, and the proof options should have been agreed on beforehand by the two entities beforehand.

+

Below we go over the main things involved in this code.

+

AIR

+

To prove the integrity of a fibonacci trace, we first need to define what it means for a trace to be valid. As we've talked about in the recap, this involves defining an AIR for our computation where we specify both the boundary and transition constraints for a fibonacci sequence.

+

In code, this is done through the AIR trait. Implementing AIR requires defining a couple methods, but the two most important ones are boundary_constraints and compute_transition, which encode the boundary and transition constraints of our computation.

+

Boundary Constraints

+

For our Fibonacci AIR, boundary constraints look like this:

+

+#![allow(unused)]
+fn main() {
+fn boundary_constraints(
+    &self,
+    _rap_challenges: &Self::RAPChallenges,
+) -> BoundaryConstraints<Self::Field> {
+    let a0 = BoundaryConstraint::new_simple(0, self.pub_inputs.a0.clone());
+    let a1 = BoundaryConstraint::new_simple(1, self.pub_inputs.a1.clone());
+
+    BoundaryConstraints::from_constraints(vec![a0, a1])
+}
+}
+
+

The BoundaryConstraint struct represents a specific boundary constraint, meaning "column i at row j should be equal to x". In this case, because we have only one column, we are using the new_simple method to simply say

+
    +
  • Row 0 should equal the public input a0, which in the typical fibonacci is set to 1.
  • +
  • Row 1 should equal the public input a1, which in the typical fibonacci is set to 1.
  • +
+

In the case of multiple columns, the new method exists so you can also specify column number.

+

After instantiating each of these constraints, we return all of them through the struct BoundaryConstraints.

+

Transition Constraints

+

The way we specify our fibonacci transition constraint looks like this:

+

+#![allow(unused)]
+fn main() {
+fn compute_transition(
+    &self,
+    frame: &air::frame::Frame<Self::Field>,
+    _rap_challenges: &Self::RAPChallenges,
+) -> Vec<FieldElement<Self::Field>> {
+    let first_row = frame.get_row(0);
+    let second_row = frame.get_row(1);
+    let third_row = frame.get_row(2);
+
+    vec![third_row[0] - second_row[0] - first_row[0]]
+}
+}
+
+

It's not completely obvious why this is how we chose to express transition constraints, so let's talk a little about it.

+

What we need to specify in this method is the relationship that has to hold between the current step of computation and the previous ones. For this, we get a Frame as an argument. This is a struct holding the current step (i.e. the current row of the trace) and all previous ones needed to encode our constraint. In our case, this is the current row and the two previous ones. To access rows we use the get_row method. The current step is always the last row (in our case 2), with the others coming before it.

+

In our compute_transition method we get the three rows we need and return

+

+#![allow(unused)]
+fn main() {
+third_row[0] - second_row[0] - first_row[0]
+}
+
+

which is the value that needs to be zero for our constraint to hold. Because we support multiple transition constraints, we actually return a vector with one value per constraint, so the first element holds the first constraint value and so on.

+

TraceTable

+

After defining our AIR, we create our specific trace to prove against it.

+

+#![allow(unused)]
+fn main() {
+let trace = fibonacci_trace([FE17::new(1), FE17::new(1)], 4);
+
+let trace_table = TraceTable {
+    table: trace.clone(),
+    num_cols: 1,
+};
+}
+
+

TraceTable is the struct holding execution traces; the num_cols says how many columns the trace has, the table field is a vec holding the actual values of the trace in row-major form, meaning if the trace looks like this

+
| 1  | 2  |
+| 3  | 4  |
+| 5  | 6  |
+
+

then its corresponding TraceTable is

+

+#![allow(unused)]
+fn main() {
+let trace_table = TraceTable {
+    table: vec![1, 2, 3, 4, 5, 6],
+    num_cols: 2,
+};
+}
+
+

In our example, fibonacci_trace is just a helper function we use to generate the fibonacci trace with 4 rows and [1, 1] as the first two values.

+

AIR Context

+

After specifying our constraints and trace, the only thing left to do is provide a few parameters related to the STARK protocol and our AIR. These specify things such as the number of columns of the trace and proof configuration, among others. They are all encapsulated in the AirContext struct, which in our example we instantiate like this:

+

+#![allow(unused)]
+fn main() {
+let context = AirContext {
+    options: ProofOptions {
+        blowup_factor: 2,
+        fri_number_of_queries: 1,
+        coset_offset: 3,
+    },
+    trace_columns: trace_table.n_cols,
+    transition_degrees: vec![1],
+    transition_exemptions: vec![2],
+    transition_offsets: vec![0, 1, 2],
+    num_transition_constraints: 1,
+};
+}
+
+

Let's go over each of them:

+
    +
  • options requires a ProofOptions struct holding specific parameters related to the STARK protocol to be used when proving. They are: +
      +
    • The blowup_factor used for the trace LDE extension, a parameter related to the security of the protocol.
    • +
    • The number of queries performed by the verifier when doing FRI, also related to security.
    • +
    • The offset used for the LDE coset. This depends on the field being used for the STARK proof.
    • +
    +
  • +
  • trace_columns are the number of columns of the trace, respectively.
  • +
  • transition_degrees holds the degree of each transition constraint.
  • +
  • transition_exemptions is a Vec which tells us, for each column, the number of rows the transition constraints should not apply, starting from the end of the trace. In the example, the transition constraints won't apply on the last two rows of the trace.
  • +
  • transition_offsets holds the indexes that define a frame for our AIR. In our fibonacci case, these are [0, 1, 2] because we need the current row and the two previous one to define our transition constraint.
  • +
  • num_transition_constraints simply says how many transition constraints our AIR has.
  • +
+

Proving execution

+

Having defined all of the above, proving our fibonacci example amounts to instantiating the necessary structs and then calling prove passing the trace, public inputs and proof options. We use a simple implementation of a hasher called TestHasher to handle merkle proof building.

+

+#![allow(unused)]
+fn main() {
+let proof = prove(&trace_table, &pub_inputs, &proof_options);
+}
+
+

Verifying is then done by passing the proof of execution along with the same AIR to the verify function.

+

+#![allow(unused)]
+fn main() {
+assert!(verify(&proof, &pub_inputs, &proof_options));
+}
+
+ +
+ + +
+
+ + + +
+ + + + + + + + + + + + + + + + + + + + diff --git a/starks/builtins.html b/starks/builtins.html new file mode 100644 index 000000000..8169436ed --- /dev/null +++ b/starks/builtins.html @@ -0,0 +1,211 @@ + + + + + + Built-ins - docs + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + + + +
+
+ +

Builtins

+

We can understand the built-in as a small machine, that we can use to efficiently prove a subprogram. For example, it may be able to prove a hash, like Poseidon or Keccak, verify a signature, or check that some variable is in a range, and the cost would be less than what we would have if using the Cairo VM instructions.

+

For each subprogram we want to prove, we will have a machine, which will have its own set of constraints in the prover. Let's take for example the Range Check built-in. This builtin enforces that a value is between 0 and .

+

The logic behind the built-in is pretty straightforward. We split into 8 parts. So we will say that

+

Then we require that each is in the range . The idea here is to reuse the Range Check constraint that checks if the offsets are between and . If we can decompose the number in eight limbs of 16 bits, and we don't need any more limbs, it follows that the number will be less than

+

The missing ingredient is how we make sure that each value that should be constrained by the built-in is actually constrained.

+

The process starts with the VM designating special memory positions for the built-in. You can think of this as a way of communicating the VM with the specific built-in machine by sharing memory.

+

The VM won't save any instruction associated with how the built-in gets to the result and will assume the output is correct. You can think of this as an IO device in any computer, which works in a similar fashion. The VM delegates the work to an external device and takes the result from the memory.

+

Knowing which specific positions of the memory are used by the built-in, the prover can add more constraints that enforce the calculations of the built-in were done correctly. Let's see how it's done.

+

In the constraint system of the VM, we will treat every memory cell associated with the built-in as any other, treating it as a pair of addresses and values with the usual constraints. Additionally, we will add more that are specific to the builtin.

+

Let's say we have multiple values , such that each needs to be range checked by the built-in. Let each value be stored in a memory address . Let the initial expected memory position for the range check built-in be . Here is a value known and a public input.

+

We need to enforce then that , and that the built in . These constraints have to be put on top of the constraints that are used by the memory, and that's the key to all of this. If these constraints weren't in place, there wouldn't be an enforced link between the Builtin and the VM, which would lead to security issues.

+

As one last detail, since the memory cells share the same constraints, and we add more for the ones in the builtin, we can treat the builtin cells as a subcolumn. In that case, we can assign one cell for the memory every N cell, giving a ratio that will be observable in the layout.

+

This gives a better relationship between the number of cells used for the VM, and the builtin, giving an improvement in performance.

+ +
+ + +
+
+ + + +
+ + + + + + + + + + + + + + + + + + + + diff --git a/starks/cairo.html b/starks/cairo.html new file mode 100644 index 000000000..8fa564909 --- /dev/null +++ b/starks/cairo.html @@ -0,0 +1,198 @@ + + + + + + Cairo - docs + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + + + +
+
+ +

Cairo

+ +
+ + +
+
+ + + +
+ + + + + + + + + + + + + + + + + + + + diff --git a/starks/cairo_cli.html b/starks/cairo_cli.html new file mode 100644 index 000000000..5c258821c --- /dev/null +++ b/starks/cairo_cli.html @@ -0,0 +1,192 @@ + + + + + + CLI - docs + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + + + +
+
+ +

CLI

+ +
+ + +
+
+ + + +
+ + + + + + + + + + + + + + + + + + + + diff --git a/starks/cairo_rap.html b/starks/cairo_rap.html new file mode 100644 index 000000000..b39e99e56 --- /dev/null +++ b/starks/cairo_rap.html @@ -0,0 +1,237 @@ + + + + + + RAP - docs + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + + + +
+
+ +

Extended columns

+

The verifier sends challenges (or the prover samples them from the transcript). Additional columns are added to incorporate the memory constraints. To define them the prover follows these steps:

+
    +
  1. Stack the rows of the submatrix of defined by the columns pc, dst_addr, op0_addr, op1_addr into a vector a of length (this means that the first entries of a are pc[0], dst_addr[0], op0_addr[0], op1_addr[0], pc[1], dst_addr[1],...).
  2. +
  3. Stack the the rows of the submatrix defined by the columns inst, dst, op0, op1 into a vector v of length .
  4. +
  5. Define to be the matrix with columns , .
  6. +
  7. Define to be the matrix that's equal to in the first rows, and its last entries are the addresses and values of the actual public memory (program code).
  8. +
  9. Sort by the first column in increasing order. The result is a matrix of size . Denote its columns by and .
  10. +
  11. Compute the vector of size with entries +
  12. +
  13. Reshape the matrix into a in row-major. Reshape the vector into a matrix in row-major.
  14. +
  15. Concatenate these 12 rows. The result is a matrix of size
  16. +
+

The verifier sends challenge . Further columns are added to incorporate the range check constraints following these steps:

+
    +
  1. Stack the rows of the submatrix of defined by the columns in the group offsets into a vector of length .
  2. +
  3. Sort the values of in increasing order. Let be the result.
  4. +
  5. Compute the vector of size with entries +
  6. +
  7. Reshape and into matrices of size each and concatenate them into a matrix of size .
  8. +
  9. Concatenate and into a matrix of size .
  10. +
+

Using the notation described at the beginning, , and . They are respectively the columns of the first and second part of the rap, and the total number of columns.

+

Putting all together, the final layout of the trace is the following

+
 A.  flags      (16) : Decoded instruction flags
+ B.  res        (1)  : Res value
+ C.  pointers   (2)  : Temporary memory pointers (ap and fp)
+ D.  mem_a      (4)  : Memory addresses (pc, dst_addr, op0_addr, op1_addr)
+ E.  mem_v      (4)  : Memory values (inst, dst, op0, op1)
+ F.  offsets    (3)  : (off_dst, off_op0, off_op1)
+ G.  derived    (3)  : (t0, t1, mul)
+ H.  mem_a'     (4)  : Sorted memory addresses
+ I.  mem_v'     (4)  : Sorted memory values
+ J.  mem_p      (4)  : Memory permutation argument columns
+ K.  offsets_b' (3)  : Sorted offset columns
+ L.  offsets_p' (3)  : Range check permutation argument columns
+
+ A                B C  D    E    F   G   H    I    J    K   L
+|xxxxxxxxxxxxxxxx|x|xx|xxxx|xxxx|xxx|xxx|xxxx|xxxx|xxxx|xxx|xxx|
+
+ +
+ + +
+
+ + + +
+ + + + + + + + + + + + + + + + + + + + diff --git a/starks/cairo_trace_descriptive.html b/starks/cairo_trace_descriptive.html new file mode 100644 index 000000000..325d04d2b --- /dev/null +++ b/starks/cairo_trace_descriptive.html @@ -0,0 +1,322 @@ + + + + + + Trace Detailed Description - docs + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + + + +
+
+ +

Cairo execution trace

+

Raw materials

+

After the execution of a Cairo program in the Cairo VM, three files are generated that are the core components for the construction of the execution trace, needed for the proving system:

+
    +
  • trace file: Has the information on the state of the three Cairo VM registers ap, +fp, and pc at every cycle of the execution of the program. To reduce ambiguity in terms, +we should call these the register states of the Cairo VM, and leave the term trace to +the final product that is passed to the prover to generate a proof.
  • +
  • memory file: A file with the information of the VM's memory at the end of the program +run, after the memory has been relocated.
  • +
  • public inputs: A file with all the information that must be publicly available to the prover +and verifier, such as the total number of execution steps, public memory, used builtins and +their respective addresses range in memory.
  • +
+

The next section will explain in detail how these elements are used to build the final execution +trace.

+

Construction details

+

The execution trace is built in two stages. In the first one, the information on the files +described in the previous section is aggregated to build a main trace table. +In the second stage, there is an interaction with the verifier to add some extension +columns to the main trace.

+

Main trace construction

+

The layout of the main execution trace is as follows:

+
 A.  flags     (16): Decoded instruction flags
+ B.  res       (1): Res value
+ C.  pointers  (2): Temporary memory pointers (ap and fp)
+ D.  mem_a     (4): Memory addresses (pc, dst_addr, op0_addr, op1_addr)
+ E.  mem_v     (4): Memory values (inst, dst, op0, op1)
+ F.  offsets   (3)  : (off_dst, off_op0, off_op1)
+ G.  derived   (3)  : (t0, t1, mul)
+
+ A                B C  D    E    F   G
+|xxxxxxxxxxxxxxxx|x|xx|xxxx|xxxx|xxx|xxx|
+
+

Each letter from A to G represents some subsection of columns, and the number specifies how many +columns correspond to that subsection.

+

Cairo instructions

+

It is important to have in mind the information that each executed Cairo instruction holds, since it +is a key component of the construction of the execution trace. For a detailed explanation of how the +building components of the instruction interact to change the VM state, refer to the Cairo +whitepaper, sections 4.4 and 4.5.

+

Structure of the 63-bit that forms the first word of each instruction:

+
 ┌─────────────────────────────────────────────────────────────────────────┐
+ │                     off_dst (biased representation)                     │
+ ├─────────────────────────────────────────────────────────────────────────┤
+ │                     off_op0 (biased representation)                     │
+ ├─────────────────────────────────────────────────────────────────────────┤
+ │                     off_op1 (biased representation)                     │
+ ├─────┬─────┬───────┬───────┬───────────┬────────┬───────────────────┬────┤
+ │ dst │ op0 │  op1  │  res  │    pc     │   ap   │      opcode       │ 0  │
+ │ reg │ reg │  src  │ logic │  update   │ update │                   │    │
+ ├─────┼─────┼───┬───┼───┬───┼───┬───┬───┼───┬────┼────┬────┬────┬────┼────┤
+ │  0  │  1  │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │ 8 │ 9 │ 10 │ 11 │ 12 │ 13 │ 14 │ 15 │
+ └─────┴─────┴───┴───┴───┴───┴───┴───┴───┴───┴────┴────┴────┴────┴────┴────┘
+
+

Columns

+

The construction of the following columns corresponds to a colloquial explanation of what is done in +the build_cairo_execution_trace function.

+
Section A - Flags
+

The flags section A corresponds to the 16 bits that represent the configuration of the dst_reg, +op0_reg, op1_src, res_logic, pc_update, ap_update and opcode flags, as well as the zero +flag. So there is one column for each bit of the flags decomposition.

+
Section C - Temporary memory pointers
+

The two columns in this section, as well as the pc column from section D, are the most trivial. +For each step of the register states, the corresponding values are added to the columns, which are +pointers to some memory cell in the VM's memory.

+
Section D - Memory addresses
+

As already mentioned, the first column of this section, pc, is trivially obtained from the register +states for each cycle.
+Columns dst_addr, op0_addr, and op1_addr from section D are addresses constructed from pointers +stored at ap or fp, and their respective offsets off_dst, off_op0 and off_op1. The exact way these +are computed depends on the particular values of the flags for each instruction.

+
Section E - Memory values
+

The inst column, is obtained by fetching in memory the value stored at pointer pc, which +corresponds to a 63-bit Cairo instruction. +Columns dst, op0, and op1 are computed by fetching in memory by their respective addresses.

+
Section F - Offsets/Range-checked values
+

These columns represent integer values that are used to construct addresses dst_addr, op0_addr and +op1_addr and are decoded directly from the instruction. +These values have the property to be numbered in the range from 0 to 2^16.

+
Section B - Res
+

This column is computed depending on the decoded opcode and res_logic of every instruction. +In some cases, res is unused in the instruction, and the value for (dst)^(-1) is used in that +place as an optimization.

+
Section G - Derived
+

To have constraints of max degree two, some more columns are derived from the already calculated, +t0, t1, and mul:

+
    +
  • t0 is the product of the values of DST and the PC_JNZ flag for each step.
  • +
  • t1 is the product of t0 and res for each step.
  • +
  • mul is the product of op0 and op1 for each step.
  • +
+

Range check and Memory holes

+

For the values constrained between ranges and , the offsets, the prover uses a permutation argument to optimize enforcing this. In particular, it checks an ordered list with the offsets are the same as the original one, is continuous, the first value is , and the last one is less than .

+

Since not all values are used, there may be unused values, and so the ordered offset may not be continuous. These unused values are called holes, and they need to be filled with the missing values, so the checks can be done.

+

This is explained in section 9.9 of the Cairo Paper

+

In the case of memory, something similar happens, where the values should be continuous, but if there are built-ins, this may not be the case. For example, the built-in may be using addresses in ranges higher than the ones used by the program.

+

To fix this, holes in the memory cells are filled, just like the ones of the RC.

+

It's something important to note that when filling the holes, we can't use dedicated columns like op0_addr, since this would break the constraints. For this to work, we either need new columns for holes, or make use of subcolumns, which are explained in their dedicated section.

+

No matter which approach is used , either by subcolumns or columns, we will need cells where the constraints of the range check and memory are applied, but not the specific ones related to the instructions.

+

Finally, using these columns, we can fill the holes without breaking the constraint system.

+

Dummy memory accesses

+

As part of proving the execution of a Cairo program, we need to prove that memory used by the program extends the public memory. This is important since public memory contains for example the bytecode that was executed and the outputs.

+

The bytecode is something critical for the verifier to not only know that something was executed correctly but to know what was executed.

+

To do this, we the permutation check, which that proves the memory is continuous and single-valued

+

To do this, the permutation check of the memory is modified.

+

Initially, we had , and , pairs of memory addresses and values, where and are the values as used and without order, and , the pairs ordered by address.

+

For the public memory, we will add it directly to , . We also need to add dummy accesses to the pairs . These dummy accesses are just pairs of .

+

This change makes the statement that is a permutation of , which means that they are the same values in a different order, no longer true. This means that the two cumulative products used to check the statement are no longer equal, and their division

+

Luckily, we can know the new expected value on the verifier, since we have access to the public memory. Even more, it's this fact that enables the verifier to check the memory is an extension of the public memory.

+

The math is quite straightforward. When the memory was the same, we expected a final value . This came from two cumulative products that should equal, one from the unordered pairs, and one from the ordered ones.

+

Now, adding zeros to one side and the real values to the other unbalances the cumulative products, so the verifier will need to balance it by dividing by the extra factors that appeared with the addition of the public memory. Doing so will make the final value again.

+

Since they only depend on the public memory, the verifier has enough data to recalculate them and use them. Even more, if the prover lies, the equality that the verifier is expecting won't hold.

+

In reality, instead of dividing and expecting the result to equal to , we can just check the equality against the new expected value, and avoid doing that inversion.

+

All of this is explained in section 9.8 of the Cairo paper.

+

Trace extension / Padding

+

The last step is padding the trace to a power of two for efficiency. We may also need to pad the trace if for some reason some unbalance is given by the layout.

+

For this, we will copy the last executed instruction until reaching the desired length.

+

But there's a trick. If the last executed instruction is any instruction, and it's copied, the transition constraints won't be satisfied. To be able to do this, we need to use something called "proof mode". In proof mode, the main function of the program is wrapped in another one, which calls it and returns to an infinite loop. This loop is a jump relative 0.

+

Since this loop can be executed many times without changing the validity of the trace, it can be copied as many times as +needed, solving the issues mentioned before.

+

Summary

+

To construct the execution trace, we augment the RegisterStates with the information obtained from the Memory. This includes decoding instruction of each steps, and writing all the data needed to check the execution is valid.

+

Additionally, memory holes have to be filled, public memory added, and a final pad using an infinite loop is needed for everything to work properly.

+

Adding all of that, we create an execution trace that's ready for the prover to generate a proof.

+ +
+ + +
+
+ + + +
+ + + + + + + + + + + + + + + + + + + + diff --git a/starks/cairo_trace_succinct.html b/starks/cairo_trace_succinct.html new file mode 100644 index 000000000..57c7bfc52 --- /dev/null +++ b/starks/cairo_trace_succinct.html @@ -0,0 +1,229 @@ + + + + + + Trace Formal Description - docs + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + + + +
+
+ +

Trace

+

The execution of a Cairo program produces a memory vector and a matrix of size with the evolution of the three registers pc, ap, fp. All of them with entries in .

+

Construction of execution trace :

+

In this section we describe the construction of the execution trace . This is the matrix mentioned here in the description of the STARK protocol

+
    +
  1. Augment each row of with information about the pointed instruction as follows: For each entry of , unpack the -th value of . The result is a new matrix with the following layout
  2. +
+
 A.  flags     (16) : Decoded instruction flags
+ B.  res       (1)  : Res value
+ C.  pointers  (2)  : Temporary memory pointers (ap and fp)
+ D.  mem_a     (4)  : Memory addresses (pc, dst_addr, op0_addr, op1_addr)
+ E.  mem_v     (4)  : Memory values (inst, dst, op0, op1)
+ F.  offsets   (3)  : (off_dst, off_op0, off_op1)
+ G.  derived   (3)  : (t0, t1, mul)
+
+ A                B C  D    E    F   G
+|xxxxxxxxxxxxxxxx|x|xx|xxxx|xxxx|xxx|xxx|
+
+
    +
  1. +

    Let and be respectively the minimum and maximum values of the entries of the submatrix defined by the columns of the group offsets. Let be the vector of all the values between and that are not in . If the length of is not a multiple of three, extend it to the nearest multiple of three using one arbitrary value of .

    +
  2. +
  3. +

    Let be the last row of , and let be the vector that's equal to except that it has zeroes in entries corresponding to the ordered set of columns mem_a and mem_v. The set is ordered incrementally by mem_a. Let be the length of the public input (program code). Extend with additional rows to obtain a matrix by appending copies of at the bottom (the notation means the ceiling function, defined as the smallest integer that is not smaller than ).

    +
  4. +
  5. +

    Let be the vector that's equal to except that it has zeroes in entries corresponding to the set of columns mem_a and mem_v, let be the submatrix defined by the columns of the group addresses, let the submatrix that asserts , where and and . Extend with additional rows to obtain a matrix by appending copies of at the bottom.

    +
  6. +
  7. +

    Pad with copies of its last row until it has a power of two number of rows. As a result we obtain a matrix .

    +
  8. +
+ +
+ + +
+
+ + + +
+ + + + + + + + + + + + + + + + + + + + diff --git a/starks/implementation.html b/starks/implementation.html new file mode 100644 index 000000000..2164691e6 --- /dev/null +++ b/starks/implementation.html @@ -0,0 +1,200 @@ + + + + + + Implementation - docs + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + + + +
+
+ +

STARKs Prover Lambdaworks Implementation

+

The goal of this section will be to go over the details of the implementation of the proving system. To this end, we will follow the flow the example in the recap chapter, diving deeper into the code when necessary and explaining how it fits into a more general case.

+

This implementation couldn't be done without checking Facebook's Winterfell and Max Gillett's Giza. We want to thank everyone involved in them, along with Shahar Papini and Lior Goldberg from Starkware who also provided us valuable insight.

+ +
+ + +
+
+ + + +
+ + + + + + + + + + + + + + + + + + + + diff --git a/starks/protocol.html b/starks/protocol.html new file mode 100644 index 000000000..85b7f1f52 --- /dev/null +++ b/starks/protocol.html @@ -0,0 +1,439 @@ + + + + + + Protocol - docs + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + + + +
+
+ +

STARKs protocol

+

In this section we describe precisely the STARKs protocol used in Lambdaworks.

+

We begin with some additional considerations and notation for most of the relevant objects and values to refer to them later on.

+

Grinding

+

This is a technique to increase the soundness of the protocol by adding proof of work. It works as follows. At some fixed point in the protocol, the prover needs to find a string nonce such that H(H(prefix || state || grinding_factor) || nonce) has grinding_factor number of zeros to the left, where H is a hash function, prefix is the bit-string 0x0123456789abcded and state is the state of the transcript. Here x || y denotes the concatenation of the bit-strings x and y.

+

Transcript

+

The Fiat-Shamir heuristic is used to make the protocol noninteractive. We assume there is a transcript object to which values can be added and from which challenges can be sampled.

+

General notation

+
    +
  • denotes a finite field.
  • +
  • Given a vector and a function , denote by the vector . Here denotes the underlying set of .
  • +
  • A polynomial induces a function for every subset of , where .
  • +
  • Let be two polynomials. A function can be induced from them for every subset disjoint from the set of roots of , defined by . We abuse notation and denote by .
  • +
+

Definitions

+

We assume the prover has already obtained the trace of the execution of the program. This is a matrix with entries in a finite field . We assume the number of rows of is for some in .

+

Values known by the prover and verifier prior to the interactions

+

These values are determined the program, the specifications of the AIR being used and the security parameters chosen.

+
    +
  • is the number of columns of the trace matrix .
  • +
  • the number of RAP challenges.
  • +
  • is the number of extended columns of the trace matrix in the (optional) second round of RAP.
  • +
  • is the total number of columns: .
  • +
  • denote the transition constraint polynomials for . We are assuming these are of degree at most 2.
  • +
  • denote the transition constraint zerofiers for .
  • +
  • is the blowup factor.
  • +
  • is the grinding factor.
  • +
  • is number of FRI queries.
  • +
  • We assume there is a fixed hash function from to binary strings. We also assume all Merkle trees are constructed using this hash function.
  • +
+

Values computed by the prover

+

These values are computed by the prover from the execution trace and are sent to the verifier along with the proof.

+
    +
  • is the number of rows of the trace matrix after RAP.
  • +
  • a primitive -th root of unity.
  • +
  • .
  • +
  • An element . This is called the coset factor.
  • +
  • Boundary constraints polynomials for .
  • +
  • Boundary constraint zerofiers for ..
  • +
+

Derived values

+

Both prover and verifier compute the following.

+
    +
  • The interpolation domain: the vector .
  • +
  • The Low Degree Extension . Recall is the blowup factor.
  • +
+

Notation of important operations

+

Vector commitment scheme

+

Given a vector . The operation returns the root of the Merkle tree that has the hash of the elements of as leaves.

+

For , the operation returns the pair , where is the authentication path to the Merkle tree root.

+

The operation returns Accept or Reject depending on whether the -th element of is . It checks whether the authentication path is compatible with , and the Merkle tree root .

+

In our cases the sets will be of the form for some elements . It will be convenient to use the following abuse of notation. We will write to mean . Similarly, we will write instead of . Note that this is only notation and is only checking that the is the -th element of the commited vector.

+
Batch
+

As we mentioned in the protocol overview. When committing to multiple vectors , where one can build a single Merkle tree. Its -th leaf is the concatenation of all the -th coordinates of all vectors, that is, . The commitment to this batch of vectors is the root of this Merkle tree.

+

Protocol

+

Prover

+

Round 0: Transcript initialization

+
    +
  • Start a new transcript.
  • +
  • (Strong Fiat Shamir) Add to it all the public values.
  • +
+

Round 1: Arithmetization and commitment of the execution trace

+
Round 1.1: Commit main trace
+
    +
  • For each column of the execution trace matrix , interpolate its values at the domain and obtain polynomials such that .
  • +
  • Compute for all (Batch commitment optimization applies here).
  • +
  • Add to the transcript in increasing order.
  • +
+
Round 1.2: Commit extended trace
+
    +
  • Sample random values in from the transcript.
  • +
  • Use to build following the specifications of the RAP process.
  • +
  • For each column of the matrix , interpolate its values at the domain and obtain polynomials such that .
  • +
  • Compute for all (Batch commitment optimization applies here).
  • +
  • Add to the transcript in increasing order for all .
  • +
+

Round 2: Construction of composition polynomial

+
    +
  • Sample in from the transcript.
  • +
  • Sample in from the transcript.
  • +
  • Compute .
  • +
  • Compute .
  • +
  • Compute the composition polynomial +
  • +
  • Decompose as +
  • +
  • Compute commitments and (Batch commitment optimization applies here).
  • +
  • Add and to the transcript.
  • +
+

Round 3: Evaluation of polynomials at

+
    +
  • Sample from the transcript until obtaining .
  • +
  • Compute , , and and for all .
  • +
  • Add , , and and for all to the transcript.
  • +
+

Round 4: Run batch open protocol

+
    +
  • Sample , , and , in from the transcript.
  • +
  • Compute as
  • +
+
Round 4.1.k: FRI commit phase
+
    +
  • Let .
  • +
  • For do the following: +
      +
    • Sample from the transcript.
    • +
    • Decompose into even and odd parts, that is, .
    • +
    • Define .
    • +
    • If : +
        +
      • Let . Define , where .
      • +
      • Let .
      • +
      • Add to the transcript.
      • +
      +
    • +
    +
  • +
  • is a constant polynomial and therefore . Add to the transcript.
  • +
+
Round 4.2: Grinding
+
    +
  • Let be the internal state of the transcript.
  • +
  • Compute such that has leading zeroes.
  • +
  • Add to the transcript.
  • +
+
Round 4.3: FRI query phase
+
    +
  • For do the following: +
      +
    • Sample random index from the transcript and let .
    • +
    • Compute and for all .
    • +
    • Compute and .
    • +
    • Compute and .
    • +
    • Compute and for all .
    • +
    +
  • +
+

Build proof

+
    +
  • Send the proof to the verifier: +
  • +
+

Verifier

+

From the point of view of the verifier, the proof they receive is a bunch of values that may or may not be what they claim to be. To make this explicit, we avoid denoting values like as such, because that implicitly assumes that the value was obtained after evaluating a polynomial at . And that's something the verifier can't assume. We use the following convention.

+
    +
  • Bold capital letters refer to commitments. For example is the claimed commitment .
  • +
  • Greek letters with superscripts refer to claimed function evaluations. For example is the claimed evaluation and is the claimed evaluation of . Note that field elements in superscripts never indicate powers. They are just notation.
  • +
  • Gothic letters refer to authentication paths. For example is the authentication path of a opening of .
  • +
  • Recall that every opening is a pair , where is the claimed value at index and is the authentication path. So for example, is denoted as from the verifier's end.
  • +
+

Input

+

This is the proof using the notation described above. The elements appear in the same exact order as they are in the Prover section, serving also as a complete reference of the meaning of each value.

+

+

Step 1: Replay interactions and recover challenges

+
    +
  • Start a transcript
  • +
  • (Strong Fiat Shamir) Add all public values to the transcript.
  • +
  • Add to the transcript for all .
  • +
  • Sample random values from the transcript.
  • +
  • Add to the transcript for .
  • +
  • Sample and in from the transcript.
  • +
  • Sample and in from the transcript.
  • +
  • Add and to the transcript.
  • +
  • Sample from the transcript.
  • +
  • Add , , and to the transcript.
  • +
  • Sample , , and from the transcript.
  • +
  • For do the following: +
      +
    • Sample
    • +
    • If : add to the transcript
    • +
    +
  • +
  • Add to the transcript.
  • +
  • Add to the transcript.
  • +
  • For : +
      +
    • Sample random index from the transcript and let .
    • +
    +
  • +
+

Verify grinding:

+

Check that has leading zeroes.

+

Step 2: Verify claimed composition polynomial

+
    +
  • Compute
  • +
  • Compute
  • +
  • Compute
  • +
  • Verify +
  • +
+

Step 3: Verify FRI

+
    +
  • Reconstruct the deep composition polynomial values at and . That is, define +
  • +
  • For all : +
      +
    • For all : +
        +
      • Check that and are Accept.
      • +
      • Solve the following system of equations on the variables +
      • +
      • If , check that equals
      • +
      • If , check that equals .
      • +
      +
    • +
    +
  • +
+

Step 4: Verify trace and composition polynomials openings

+
    +
  • For do the following: +
      +
    • Check that the following are all Accept: +
        +
      • for all .
      • +
      • .
      • +
      • .
      • +
      • for all .
      • +
      • .
      • +
      • .
      • +
      +
    • +
    +
  • +
+

Notes on Optimizations and variants

+

Sampling of challenges variant

+

To build the composition the prover samples challenges and for and . A variant of this is sampling a single challenge and defining and as powers of . That is, define for and for .

+

The same variant applies for the challenges for used to build the deep composition polynomial. In this case the variant samples a single challenge and defines , for all , and .

+

Batch inversion

+

Inversions of finite field elements are slow. There is a very well known trick to batch invert many elements at once replacing inversions by multiplications. See here for the algorithm.

+

FFT

+

One of the most computationally intensive operations performed is polynomial division. These can be optimized by utilizing Fast Fourier Transform (FFT) to divide each field element in Lagrange form.

+

Ruffini's rule

+

In specific scenarios, such as dividing by a polynomial of the form , for example when building the deep composition polynomial, Ruffini's rule can be employed to further enhance performance.

+

Bit-reversal ordering of Merkle tree leaves

+

As one can see from inspecting the protocol, there are multiple times where, for a polynomial , the prover sends both openings and . This implies, a priori, sending two authentication paths. Domains can be indexed using bit-reverse ordering to reduce this to a single authentication path for both openings, as follows.

+

The natural way of building a Merkle tree to commit to a vector , is assigning the value to leaf . If this is the case, the value is at position and the value is at position . This is because equals for the value used in the protocol.

+

Instead of this naive approach, a better solution is to assign the value to leaf , where is the bit-reversal permutation. This is the permutation that maps to the index whose binary representation (padded to bits), is the binary representation of but in reverse order. For example, if and , then its binary representation is , which reversed is . Therefore . In the same way and . Check out the wikipedia article. With this ordering of the leaves, if is even, element is at index and is at index . Which means that a single authentication path serves to validate both points simultaneously.

+

Redundant values in the proof

+

The prover opens the polynomials of the FRI layers at and for all . Later on, the verifier uses each of those pairs to reconstruct one of the values of the next layer, namely . So there's no need to add the value to the proof, as the verifier reconstructs them. The prover only needs to send the authentication paths for them.

+

The protocol is only modified at Step 3 of the verifier as follows. Checking that is skipped. After computing , the verifier uses it to check that is Accept, which proves that is actually , and continues to the next iteration of the loop.

+ +
+ + +
+
+ + + +
+ + + + + + + + + + + + + + + + + + + + diff --git a/starks/protocol_overview.html b/starks/protocol_overview.html new file mode 100644 index 000000000..fc2973a1f --- /dev/null +++ b/starks/protocol_overview.html @@ -0,0 +1,300 @@ + + + + + + Protocol overview - docs + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + + + +
+
+ +

Protocol Overview

+

In this section, we start diving deeper before showing the formal protocol. If you haven't done so, we recommend reading the "Recap" section first.

+

At a high level, the protocol works as follows. The starting point is a matrix that encodes the trace of a valid execution of the program. This matrix needs to be in a particular format so that its correctness is equivalent to checking a finite number of polynomial equations on its rows. Transforming the execution to this matrix is what's called the arithmetization process.

+

Then a single polynomial is constructed that encodes the set of all the polynomial constraints. The satisfiability of all these constraints is equivalent to being divisible by some public polynomial . So the prover constructs as the quotient called the composition polynomial.

+

Then the verifier chooses a random point and challenges the prover to reveal the values and . Then the verifier checks that , which convinces him that the same relation holds at a level of polynomials and, in consequence, convinces the verifier that the private trace of the prover is valid.

+

In summary, at a very high level, the STARK protocol can be organized into three major parts:

+
    +
  • Arithmetization and commitment of execution trace.
  • +
  • Construction and commitment of composition polynomial .
  • +
  • Opening of polynomials at random .
  • +
+

Arithmetization

+

As the Recap mentions, the trace is a table containing the system's state at every step. In this section, we will denote the trace as . A trace can have several columns to store different aspects or features of a particular state at a specific moment. We will refer to the -th column as . You can think of a trace as a matrix where the entry is the -th element of the -th state.

+

Most proving systems' primary tool is polynomials over a finite field . Each column of the trace will be interpreted as evaluations of such a polynomial . Consequently, any information about the states must be encoded somehow as an element in .

+

To ease notation, we will assume here and in the protocol that the constraints encoding transition rules depend only on a state and the previous one. Everything can be easily generalized to transitions that depend on many preceding states. Then, constraints can be expressed as multivariate polynomials in variables + +A transition from state to state will be valid if and only if when we plug row of in the first variables and row in the second variables of , we get for all . In mathematical notation, this is +

+

These are called transition constraints and check the trace's local properties, where local means relative to specific rows. There is another type of constraint, called boundary constraint, and denoted . These enforce parts of the trace to take particular values. It is helpful, for example, to verify the initial states.

+

So far, these constraints can only express the local properties of the trace. There are situations where the global properties of the trace need to be checked for consistency. For example, a column may need to take all values in a range but not in any predefined way. Several methods exist to express these global properties as local by adding redundant columns. Usually, they need to involve randomness from the verifier to make sense, and they turn into an interactive protocol called Randomized AIR with Preprocessing.

+

Polynomial commitment scheme

+

To make interactions possible, a crucial cryptographic primitive is the Polynomial Commitment Scheme. This prevents the prover from changing the polynomials to adjust them to what the verifier expects.

+

Such a scheme consists of the commit and the open protocols. STARK uses a univariate polynomial commitment scheme that internally combines a vector commitment scheme and a protocol called FRI. Let's begin with these two components and see how they build up the polynomial commitment scheme.

+

Vector commitments

+

Given a vector , commiting to means the following. The prover builds a Merkle tree out of it and sends its root to the verifier. The verifier can then ask the prover to reveal, or open, the value of the vector at some index . The prover won't have any choice except to send the correct value. The verifier will expect the corresponding value and the authentication path to the tree's root to check its authenticity. The authentication path also encodes the vector's position and its length .

+

The root of the Merkle tree is said to be the commitment of , and we denote it here by .

+

FRI

+

In STARKs, all commited vectors are of the form for some polynomial and some fixed domain . The domain is always known to the prover and the verifier. It can be proved, as long as is less than the total number of field elements, that every vector is equal to for a unique polynomial of degree at most . This is called the Lagrange interpolation theorem. It means, there is a unique polynomial of degree at most such that for all . And is an upper bound to the degree of . It could be less. For example, the vector of all ones is the evaluation of the constant polynomial , which has degree .

+

Suppose the vector is the vector of evaluations of a polynomial of degree strictly less than . Suppose one party holds the vector and another party holds only the commitment of it. The FRI protocol is an efficient interactive protocol with which the former can convince the latter that the commitment they hold corresponds to the vector of evaluations of a polynomial of degree strictly less than .

+

More precisely, the protocol depends on the following parameters

+
    +
  • Powers of two and with .
  • +
  • A vector , with , with a nonzero value in and a primitive -root of unity
  • +
+

A prover holds a vector , and the verifier holds the commitment of it. The result of the FRI protocol will be Accept if the unique polynomial of degree less than such that has degree less than . Even more precisely, the protocol proves that is very close to a vector with of degree less than , but it may differ in negligible proportion of the coordinates.

+

The number is called the blowup factor and the security of the protocol depends in part on this parameter. The specific shape of the domain set has some symmetric properties important for the inner workings of FRI, such as for all .

+

Variant useful for STARKs

+

FRI is usually described as above. In STARK, FRI is used as a building block for the polynomial commitment scheme of the next section. For that, a small variant of FRI is needed.

+

Suppose the prover holds a vector and the verifier holds its commitment as before. Suppose further that both parties know a function that takes two field elements and outputs another field element. For example could be the function . More precisely, the kind of functions we need are .

+

The protocol can be used to prove that the transformed vector is the vector of evaluations of a polynomial of degree at most . Note that in this variant, the verifier holds originally the commitment of the vector and not the commitment of the transformed vector. In the example, the verifier holds the commitment and FRI will return Accept if is the vector of evaluations of a polynomial of degree at most .

+

Polynomial commitments

+

STARK uses a univariate polynomial commitment scheme. The following is what is expected from the commit and open protocols:

+
    +
  • Commit: given a polynomial , the prover produces a sort of hash of it. We denote it here by , called the commitment of . This hash is unique to . The prover usually sends to the verifier.
  • +
  • Open: this is an interactive protocol between the prover and the verifier. The prover holds the polynomial . The verifier only has the commitment . The verifier sends a value to the prover at which he wants to know the value . The prover sends a value to the verifier, and then they engage in the Open protocol. As a result, the verifier gets convinced that the polynomial corresponding to the hash evaluates to at .
  • +
+

Let's see how both of these protocols work in detail. The same configuration parameters of FRI are needed:

+
    +
  • Powers of two and with .
  • +
  • A vector , with , with a nonzero value in and a primitive -root of unity
  • +
+

The commitment scheme will only work for polynomials of degree at most (polynomials of degree are allowed). This means: anyone can commit to any polynomial, but the Open protocol will pass only for polynomials satisfying that degree bound.

+

Commit

+

Given a polynomial , the commitment is just the commitment of the vector . That is, is the root of the Merkle tree of the vector of evaluations of at .

+

Open

+

It is an interactive protocol. So assume there is a prover and a verifier. We describe the process considering an honest prover. In the next section, we analyze what happens for malicious provers.

+

The prover holds the polynomial , and the verifier only the commitment of it. There is also an element chosen by the verifier. The prover evaluates and sends the result back. As we mentioned, the goal is to generate proof of the validity of the evaluation. Let us denote the value received by the verifier.

+

Now they engage in the variant of the FRI protocol for the function . The verifier accepts the value if and only if the result of FRI is Accept.

+

Let's see why this makes sense.

+

Completeness

+

If the prover is honest, is of degree at most and equals . That means that + +for some polynomial . Since is of degree at most , then is of degree at most . The vector is then a vector of evaluations of a polynomial of degree at most . And it is equal to . So the FRI protocol will succeed.

+

Soundness

+

Let's sketch an idea of the soundness. Note that the value is chosen by the verifier after receiving the commitment of . So the prover does not know in advance, at the moment of sending , what will be.

+

Suppose the prover is trying to cheat and sends the commitment of a vector that's not the vector of evaluations of a polynomial of degree at most . Then the coordinates of the transformed vector are . Since was chosen by the verifier, dividing by shuffles all the elements in a very unpredictable way for the prover. So it is extremely unlikely that the cheating prover can craft an invalid vector such that the transformed vector turns out to be of degree at most . The expected degree of the polynomial associated with a random vector is .

+

Batch

+

During proof generation, polynomials are committed and opened several times. Computing these for each polynomial independently is costly. In this section, we'll see how batching polynomials can reduce the amount of computation. Let be a set of polynomials. We will commit and open as a whole. We note this batch commitment as .

+

We need the same configuration parameters as before: , with , a vector .

+

As described earlier, to commit to a single polynomial , a Merkle tree is built over the vector . When committing to a batch of polynomials , the leaves of the Merkle tree are instead the concatenation of the polynomial evaluations. That is, in the batch setting, the Merkle tree is built for the vector + +The commitment is the root of this Merkle tree. This reduces the proof size: we only need one Merkle tree for polynomials. The verifier can then only ask for values in batches. When the verifier chooses an index , the prover sends along with one authentication path. The verifier on his side computes the concatenation and validates it with the authentication path and . This also reduces the computational time. By traversing the Merkle tree one time, it can reveal several components simultaneously.

+

The batch open protocol proceeds similarly to the case of a single polynomial. The verifier sends evaluations points to the prover at which they wish to know the value of . The prover will try to convince the verifier that the committed polynomials , evaluate to some values . There is a generalization of the variant of FRI where the function takes more parameters, and in this case is +Where are challenges provided by the verifier. Then FRI return Accept if and only if the vector +is close to the vector of evaluations of a polynomial of degree at most . If this is the case, the verifier accepts the openings. In the context of STARKs, the polynomial is called the DEEP composition polynomial.

+

This is equivalent to running the open protocol times, one for each term and . Note that this optimization makes a huge difference, as we only need to run the FRI protocol once instead of running it once for each polynomial.

+

References

+ +

High-level description of the protocol

+

The protocol is split into rounds. Each round more or less represents an interaction with the verifier. Each round will generally start by getting a challenge from the verifier.

+

The prover will need to interpolate polynomials, and he will always do it over the set , where is a root of unity in . Also, the vector commitments will be performed over the set where is a root of unity and is some field element. This is the set we denoted in the commitment scheme section.

+

Round 1: Arithmetization and commitment of the execution trace

+

In round 1, the prover commits to the columns of the trace . He does so by interpolating each column and obtaining univariate polynomials . +Then the prover commits to over . In this way, we have . +From now on, the prover won't be able to change the trace values . The verifier will leverage this and send challenges to the prover. The prover cannot know in advance what these challenges will be. Thus he cannot handcraft a trace to deceive the verifier.

+

As mentioned before, if some constraints cannot be expressed locally, more columns can be added to make a constraint-friendly trace. This is done by committing to the first set of columns, then sampling challenges from the verifier and repeating round 1. The sampling of challenges serves to add new constraints. These constraints will ensure the new columns have some common structure with the original trace. In the protocol, extended columns are referred to as the RAP2 (Randomized AIR with Preprocessing). The matrix of the extended columns is denoted .

+

Round 2: Construction of composition polynomial

+

round 2 aims to build the composition polynomial . This function will have the property that it is a polynomial if and only if the trace that the prover committed to at round 1 is valid and satisfies the agreed polynomial constraints. That is, will be a polynomial if and only if is a trace that satisfies all the transition and boundary constraints.

+

Note that we can compose the polynomials , the ones that interpolate the columns of the trace , with the multivariate constraint polynomials as follows. + +These result in univariate polynomials. The same can be done for the boundary constraints. Since , these univariate polynomials vanish at every element of if and only if the trace is valid.

+

As we already mentioned, this is assuming that transitions only depend on the current and previous state. But it can be generalized to include frames with three or more rows or more context for each constraint. For example, in the Fibonacci case, the most natural way is to encode it as one transition constraint that depends on a row and the two preceding it, as we already did in the Recap section. The STARK protocol checks whether the function is a polynomial instead of checking that the polynomial is zero over the domain . The two statements are equivalent.

+

The verifier could check that all are polynomials one by one, and the same for the polynomials coming from the boundary constraints. However, this is inefficient; the same can be obtained with a single polynomial. To do this, the prover samples challenges and obtains a random linear combination of these polynomials. The result of this is denoted by and is called the composition polynomial. It integrates all the constraints by adding them up. So after computing , the prover commits to it and sends the commitment to the verifier. The rest of the protocol aims to prove that was constructed correctly and is a polynomial, which can only be true if the prover has a valid extension of the original trace.

+

Round 3: Evaluation of polynomials at

+

The verifier must check that was constructed according to the protocol rules. That is, has to be a linear combination of all the functions and similar terms for the boundary constraints. To do so, in round 3 the verifier chooses a random point and the prover computes , and for all . With all these, the verifier can check that and the expected linear combination coincide, at least when evaluated at . Since was chosen randomly, this proves with overwhelming probability that was properly constructed.

+

Round 4: Run batch open protocol

+

In this round, the prover and verifier engage in the batch open protocol of the polynomial commitment scheme described above to validate all the evaluations at from the previous round.

+ +
+ + +
+
+ + + +
+ + + + + + + + + + + + + + + + + + + + diff --git a/starks/recap.html b/starks/recap.html new file mode 100644 index 000000000..fb7209faa --- /dev/null +++ b/starks/recap.html @@ -0,0 +1,379 @@ + + + + + + Recap - docs + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + + + +
+
+ +

STARKs Recap

+

Verifying Computation through Polynomials

+

In general, we express computation in our proving system by providing an execution trace satisfying certain constraints. The execution trace is a table containing the state of the system at every step of computation. This computation needs to follow certain rules to be valid; these rules are our constraints.

+

The constraints for our computation are expressed using an Algebraic Intermediate Representation or AIR. This representation uses polynomials to encode constraints, which is why sometimes they are called polynomial constraints.

+

To make all this less abstract, let's go through two examples.

+

Fibonacci numbers

+

Throughout this section and the following we will use this example extensively to have a concrete example. Even though it's a bit contrived (no one cares about computing fibonacci numbers), it's simple enough to be useful. STARKs and proving systems in general are very abstract things; having an example in mind is essential to not get lost.

+

Let's say our computation consists of calculating the k-th number in the fibonacci sequence. This is just the sequence of numbers \(a_n\) satisfying

+

\[ +a_0 = 1 +\] +\[ +a_1 = 1 +\] +\[ +a_{n+2} = a_{n + 1} + a_n +\]

+

An execution trace for this just consists of a table with one column, where each row is the i-th number in the sequence:

+ + + + + + + + + +
a_i
1
1
2
3
5
8
13
21
+

A valid trace for this computation is a table satisfying two things:

+
    +
  • The first two rows are 1.
  • +
  • The value on any other row is the sum of the two preceding ones.
  • +
+

The first item is called a boundary constraint, it just enforces specific values on the trace at certain points. The second one is a transition constraint; it tells you how to go from one step of computation to the next.

+

Cairo

+

The example above is extremely useful to have a mental model, but it's not really useful for anything else. The problem is it just works for the very narrow example of computing fibonacci numbers. If we wanted to prove execution of something else, we would have to write an AIR for it.

+

What we're actually aiming for is an AIR for an entire general purpose Virtual Machine. This way, we can provide proofs of execution for any computation using just one AIR. This is what cairo as a programming language does. Cairo code compiles to the bytecode of a virtual machine with an already defined AIR. The general flow when using cairo is the following:

+
    +
  • User writes a cairo program.
  • +
  • The program is compiled into Cairo's VM bytecode.
  • +
  • The VM executes said code and provides an execution trace for it.
  • +
  • The trace is passed on to a STARK prover, which creates a proof of correct execution according to Cairo's AIR.
  • +
  • The proof is passed to a verifier, who checks that the proof is valid.
  • +
+

Ultimately, our goal is to give the tools to write a STARK prover for the cairo VM and do so. However, this is not a good example to start out as it's incredibly complex. The execution trace of a cairo program has around 30 columns, some for general purpose registers, some for other reasons. Cairo's AIR contains a lot of different transition constraints, encoding all the different possible instructions (arithmetic operations, jumps, etc).

+

Use the fibonacci example as your go-to for understanding all the moving parts; keep the Cairo example in mind as the thing we are actually building towards.

+

Fibonacci step by step walkthrough

+

Below we go through a step by step explanation of a STARK prover. We will assume the trace of the fibonacci sequence mentioned above; it consists of only one column of length \(2^n\). In this case, we'll take n=3. The trace looks like this

+ + + + + + + + + +
a_i
a_0
a_1
a_2
a_3
a_4
a_5
a_6
a_7
+

Trace polynomial

+

The first step is to interpolate these values to generate the trace polynomial. This will be a polynomial encoding all the information about the trace. The way we do it is the following: in the finite field we are working in, we take an 8-th primitive root of unity, let's call it g. It being a primitive root means two things:

+
    +
  • g is an 8-th root of unity, i.e., \(g^8 = 1\).
  • +
  • Every 8-th root of unity is of the form \(g^i\) for some \(0 \leq i \leq 7\).
  • +
+

With g in hand, we take the trace polynomial t to be the one satisfying

+

+

From here onwards, we will talk about the validity of the trace in terms of properties that this polynomial must satisfy. We will also implicitly identify a certain power of \(g\) with its corresponding trace element, so for example we sometimes think of \(g^5\) as \(a_5\), the fifth row in the trace, even though technically it's \(t\) evaluated in \(g^5\) that equals \(a_5\).

+

We talked about two different types of constraints the trace must satisfy to be valid. They were:

+
    +
  • The first two rows are 1.
  • +
  • The value on any other row is the sum of the two preceding ones.
  • +
+

In terms of t, this translates to

+
    +
  • \(t(g^0) = 1\) and \(t(g) = 1\).
  • +
  • \(t(x g^2) - t(xg) - t(x) = 0\) for all \(x \in {g^0, g^1, g^2, g^3, g^4, g^5}\). This is because multiplying by g is the same as advancing a row in the trace.
  • +
+

Composition Polynomial

+

To convince the verifier that the trace polynomial satisfies the relationships above, the prover will construct another polynomial that shows that both the boundary and transition constraints are satisfied and commit to it. We call this polynomial the composition polynomial, and usually denote it with \(H\). Constructing it involves a lot of different things, so we'll go step by step introducing all the moving parts required.

+

Boundary polynomial

+

To show that the boundary constraints are satisfied, we construct the boundary polynomial. Recall that our boundary constraints are \(t(g^0) = t(g) = 1\). Let's call \(P\) the polynomial that interpolates these constraints, that is, \(P\) satisfies:

+

+

The boundary polynomial \(B\) is defined as follows:

+

+

The denominator here is called the boundary zerofier, and it's the polynomial whose roots are the elements of the trace where the boundary constraints must hold.

+

How does \(B\) encode the boundary constraints? The idea is that, if the trace satisfies said constraints, then

+

+

+

so \(t(x) - P(x)\) has \(1\) and \(g\) as roots. Showing these values are roots is the same as showing that \(B(x)\) is a polynomial instead of a rational function, and that's why we construct \(B\) this way.

+

Transition constraint polynomial

+

To convince the verifier that the transition constraints are satisfied, we construct the transition constraint polynomial and call it \(C(x)\). It's defined as follows:

+

+

How does \(C\) encode the transition constraints? We mentioned above that these are satisfied if the polynomial in the numerator vanishes in the elements \({g^0, g^1, g^2, g^3, g^4, g^5}\). As with \(B\), this is the same as showing that \(C(x)\) is a polynomial instead of a rational function.

+

Constructing \(H\)

+

With the boundary and transition constraint polynomials in hand, we build the composition polynomial \(H\) as follows: The verifier will sample four numbers \(\beta_1, \beta_2\) and \(H\) will be

+

+

Why not just take \(H(x) = B(x) + C(x)\)? The reason for the betas is to make the resulting \(H\) be always different and unpredictable for the prover, so they can't precompute stuff beforehand.

+

With what we discussed above, showing that the constraints are satisfied is equivalent to saying that H is a polynomial and not a rational function (we are simplifying things a bit here, but it works for our purposes).

+

Commiting to \(H\)

+

To show \(H\) is a polynomial we are going to use the FRI protocol, which we treat as a black box. For all we care, a FRI proof will verify if what we committed to is indeed a polynomial. Thus, the prover will provide a FRI commitment to H, and if it passes, the verifier will be convinced that the constraints are satisfied.

+

There is one catch here though: how does the verifier know that FRI was applied to H and not any other polynomial? For this we need to add an additional step to the protocol.

+

Consistency check

+

After commiting to H, the prover needs to show that H was constructed correctly according to the formula above. To do this, it will ask the prover to provide an evaluation of H on some random point z and evaluations of the trace at the points \(t(z), t(zg)\) and \(t(zg^2)\).

+

Because the boundary and transition constraints are a public part of the protocol, the verifier knows them, and thus the only thing it needs to compute the evaluation \((z)\) by itself are the three trace evaluations mentioned above. Because it asked the prover for them, it can check both sides of the equation:

+

+

and be convinced that \(H\) was constructed correctly.

+

We are still not done, however, as the prover could have now cheated on the values of the trace or composition polynomial evaluations.

+

Deep Composition Polynomial

+

There are two things left the prover needs to show to complete the proof:

+
    +
  • That \(H\) effectively is a polynomial, i.e., that the constraints are satisfied.
  • +
  • That the evaluations the prover provided on the consistency check were indeed evaluations of the trace polynomial and composition polynomial on the out of domain point z.
  • +
+

Earlier we said we would use the FRI protocol to commit to H and show the first item in the list. However, we can slightly modify the polynomial we do FRI on to show both the first and second items at the same time. This new modified polynomial is called the DEEP composition polynomial. We define it as follows:

+

+

where the numbers \(\gamma_i\) are randomly sampled by the verifier.

+

The high level idea is the following: If we apply FRI to this polynomial and it verifies, we are simultaneously showing that

+
    +
  • \(H\) is a polynomial and the prover indeed provided H(z) as one of the out of domain evaluations. This is the first summand in Deep(x).
  • +
  • The trace evaluations provided by the prover were the correct ones, i.e., they were \(t(z)\), \(t(zg)\), and \(t(zg^2)\). These are the remaining summands of the Deep(x).
  • +
+

Consistency check

+

The prover needs to show that Deep was constructed correctly according to the formula above. To do this, the verifier will ask the prover to provide:

+
    +
  • An evaluation of H on z and x_0
  • +
  • Evaluations of the trace at the points \(t(z)\), \(t(zg)\), \(t(zg^2)\) and \(t(x_0)\)
  • +
+

Where z is the same random, out of domain point used in the consistency check of the composition polynomial, and x_0 is a random point that belongs to the trace domain.

+

With the values provided by the prover, the verifier can check both sides of the equation:

+

+

The prover also needs to show that the trace evaluation \(t(x_0)\) belongs to the trace. To achieve this, it needs to commit the merkle roots of t and the merkle proof of \(t(x_0)\).

+

Summary

+

We summarize below the steps required in a STARK proof for both prover and verifier.

+

Prover side

+
    +
  • Compute the trace polynomial t by interpolating the trace column over a set of \(2^n\)-th roots of unity \({g^i : 0 \leq i < 2^n}\).
  • +
  • Compute the boundary polynomial B.
  • +
  • Compute the transition constraint polynomial C.
  • +
  • Construct the composition polynomial H from B and C.
  • +
  • Sample an out of domain point z and provide the evaluations \(H(z)\), \(t(z)\), \(t(zg)\), and \(t(zg^2)\) to the verifier.
  • +
  • Sample a domain point x_0 and provide the evaluations \(H(x_0)\) and \(t(x_0)\) to the verifier.
  • +
  • Construct the deep composition polynomial Deep(x) from H, t, and the evaluations from the item above.
  • +
  • Do FRI on Deep(x) and provide the resulting FRI commitment to the verifier.
  • +
  • Provide the merkle root of t and the merkle proof of \(t(x_0)\).
  • +
+

Verifier side

+
    +
  • Take the evaluations \(H(z)\), \(H(x_0)\), \(t(z)\), \(t(zg)\), \(t(zg^2)\) and \(t(x_0)\) the prover provided.
  • +
  • Reconstruct the evaluations \(B(z)\) and \(C(z)\) from the trace evaluations we were given. Check that the claimed evaluation \(H(z)\) the prover gave us actually satisfies +
  • +
  • Check that the claimed evaluation \(Deep(x_0)\) the prover gave us actually satisfies +
  • +
  • Using the merkle root and the merkle proof the prover provided, check that \(t(x_0)\) belongs to the trace.
  • +
  • Take the provided FRI commitment and check that it verifies.
  • +
+

Simplifications and Omissions

+

The walkthrough above was for the fibonacci example which, because of its simplicity, allowed us to sweep under the rug a few more complexities that we'll have to tackle on the implementation side. They are:

+

Multiple trace columns

+

Our trace contained only one column, but in the general setting there can be multiple (the Cairo AIR has around 30). This means there isn't just one trace polynomial, but several; one for each column. This also means there are multiple boundary constraint polynomials.

+

The general idea, however, remains the same. The deep composition polynomial H is now the sum of several terms containing the boundary constraint polynomials \(B_1(x), \dots, B_k(x)\) (one per column), and each \(B_i\) is in turn constructed from the \(i\)-th trace polynomial \(t_i(x)\).

+

Multiple transition constraints

+

Much in the same way, our fibonacci AIR had only one transition constraint, but there could be several. We will therefore have multiple transition constraint polynomials \(C_1(x), \dots, C_n(x)\), each of which encodes a different relationship between rows that must be satisfied. Also, because there are multiple trace columns, a transition constraint can mix different trace polynomials. One such constraint could be

+

+

which means "The first column on the next row has to be equal to the second column in the current row".

+

Again, even though this seems way more complex, the ideas remain the same. The composition polynomial H will now include a term for every \(C_i(x)\), and for each one the prover will have to provide out of domain evaluations of the trace polynomials at the appropriate values. In our example above, to perform the consistency check on \(C_1(x)\) the prover will have to provide the evaluations \(t_1(zg)\) and \(t_2(z)\).

+

Composition polynomial decomposition

+

In the actual implementation, we won't commit to \(H\), but rather to a decomposition of \(H\) into an even term \(H_1(x)\) and an odd term \(H_2(x)\), which satisfy

+

+

This way, we don't commit to \(H\) but to \(H_1\) and \(H_2\). This is just an optimization at the code level; once again, the ideas remain exactly the same.

+

FRI, low degree extensions and roots of unity

+

We treated FRI as a black box entirely. However, there is one thing we do need to understand about it: low degree extensions.

+

When applying FRI to a polynomial of degree \(n\), we need to provide evaluations of it over a domain with more than \(n\) points. In our case, the DEEP composition polynomial's degree is around the same as the trace's, which is, at most, \(2^n - 1\) (because it interpolates the trace containing \(2^n\) points).

+

The domain we are going to choose to evaluate our DEEP polynomial on will be a set of higher roots of unity. In our fibonacci example, we will take a primitive \(16\)-th root of unity \(\omega\). As a reminder, this means:

+
    +
  • \(\omega\) is an \(16\)-th root of unity, i.e., \(\omega^{16} = 1\).
  • +
  • Every \(16\)-th root of unity is of the form \(\omega^i\) for some \(0 \leq i \leq 15\).
  • +
+

Additionally, we also take it so that \(\omega\) satisfies \(\omega^2 = g\) (\(g\) being the \(8\)-th primitive root of unity we used to construct t).

+

The evaluation of \(t\) on the set \({\omega^i : 0 \leq i \leq 15}\) is called a low degree extension (LDE) of \(t\). Notice this is not a new polynomial, they're evaluations of \(t\) on some set of points. Also note that, because \(\omega^2 = g\), the LDE contains all the evaluations of \(t\) on the set of powers of \(g\). In fact,

+

+

This will be extremely important when we get to implementation.

+

For our LDE, we chose \(16\)-th roots of unity, but we could have chosen any other power of two greater than \(8\). In general, this choice is called the blowup factor, so that if the trace has \(2^n\) elements, a blowup factor of \(b\) means our LDE evaluates over the \(2^{n} * b\) roots of unity (\(b\) needs to be a power of two). The blowup factor is a parameter of the protocol related to its security.

+ +
+ + +
+
+ + + +
+ + + + + + + + + + + + + + + + + + + + diff --git a/starks/starks.html b/starks/starks.html new file mode 100644 index 000000000..7f383748f --- /dev/null +++ b/starks/starks.html @@ -0,0 +1,199 @@ + + + + + + STARKs - docs + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + + + +
+
+ +

STARK Prover

+

The goal of this document is to give a good a understanding of our stark prover code. To this end, in the first section we go through a recap of how the proving system works at a high level mathematically; then we dive into how that's actually implemented in our code.

+ +
+ + +
+
+ + + +
+ + + + + + + + + + + + + + + + + + + + diff --git a/docs/src/starks/stone_prover/images/interaction_trace.png b/starks/stone_prover/images/interaction_trace.png similarity index 100% rename from docs/src/starks/stone_prover/images/interaction_trace.png rename to starks/stone_prover/images/interaction_trace.png diff --git a/docs/src/starks/stone_prover/images/main_trace.png b/starks/stone_prover/images/main_trace.png similarity index 100% rename from docs/src/starks/stone_prover/images/main_trace.png rename to starks/stone_prover/images/main_trace.png diff --git a/docs/src/starks/stone_prover/images/mem_pool.png b/starks/stone_prover/images/mem_pool.png similarity index 100% rename from docs/src/starks/stone_prover/images/mem_pool.png rename to starks/stone_prover/images/mem_pool.png diff --git a/starks/stone_prover/introduction.html b/starks/stone_prover/introduction.html new file mode 100644 index 000000000..1016a2ab4 --- /dev/null +++ b/starks/stone_prover/introduction.html @@ -0,0 +1,199 @@ + + + + + + Stone prover - docs + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + + + +
+
+ +

Stone prover documentation

+

This section is a reference to the information gathered regarding Starkware's Stone prover.

+ +
+ + +
+
+ + + +
+ + + + + + + + + + + + + + + + + + + + diff --git a/docs/src/starks/stone_prover/trace_csvs/fib_inter_trace_stone_plain.csv b/starks/stone_prover/trace_csvs/fib_inter_trace_stone_plain.csv similarity index 100% rename from docs/src/starks/stone_prover/trace_csvs/fib_inter_trace_stone_plain.csv rename to starks/stone_prover/trace_csvs/fib_inter_trace_stone_plain.csv diff --git a/docs/src/starks/stone_prover/trace_csvs/fib_main_trace_stone_plain.csv b/starks/stone_prover/trace_csvs/fib_main_trace_stone_plain.csv similarity index 100% rename from docs/src/starks/stone_prover/trace_csvs/fib_main_trace_stone_plain.csv rename to starks/stone_prover/trace_csvs/fib_main_trace_stone_plain.csv diff --git a/starks/stone_prover/trace_plain_layout.html b/starks/stone_prover/trace_plain_layout.html new file mode 100644 index 000000000..f96a27560 --- /dev/null +++ b/starks/stone_prover/trace_plain_layout.html @@ -0,0 +1,307 @@ + + + + + + Plain layout trace description - docs + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + + + +
+
+ +

Stone prover trace - Layout plain

+

For a Cairo program executed in N steps, the stone prover trace with the plain layout configuration is a table of 16xN rows and 8 columns.

+

From the 8 columns, 6 are built directly from the register states and memory, while the 2 other are built from the interaction phase with the verifier.

+

For every step or cycle of the Cairo VM, the trace cells representing its state are arranged along 16 rows. +This means that the subtable from row 0 to 15 has all trace cells representing the VM state at step 0, the trace cells from 16 to 31 the state at step 1, and so on.

+

The implementation details of the trace for this layout can be found in the Stone source code, cpu_air_definition10.h and cpu_air_definition10.inl.

+

Columns & virtual columns

+

In simple terms, a virtual column is just a subset of rows of a real column. +Virtual columns are broadly defined by three parameters:

+
    +
  • The real column they are a subset of.
  • +
  • The step. Given an element of the virtual column, this value specifies the number of rows you have to move to find the next element of the virtual column.
  • +
  • The row offset. Basically the number of rows you have to move in the real column to find the first element of the virtual column.
  • +
+

For a single step or cycle, the main trace can be visualized like the following. Note that it is missing the two additional interaction columns:

+

+

This representation will be useful for the explanation of each column and virtual column.

+

Main trace columns

+

Column 0 - rc pool

+

This column is made up of trace cells that need to be range checked, hence the name. While they all have to satisfy this constraint, there are three virtual columns embedded in this column, each one having its own additional constraint. The three of them are associated with the offsets found in each Cairo instruction, off_dst, off_op0 and off_op1, as they appear in the Cairo whitepaper, section 4.4.

+
    +
  • off0 (off_dst) - step 16, row offset 0.
  • +
  • off1 (off_op1) - step 16, row offset 8.
  • +
  • off2 (off_op0) - step 16, row offset 4.
  • +
+

As it can be seen, there are plenty of unused cells in this column (rows 1, 2, 3, 5, 6, 7, 8, 10, 11, 12, 13, 14 and 15). Since there is a continuity constraint in this column, the should be no holes in their values, so the unused cells are used to fill the value holes. As holes are found, they are filled in ascending order in these cells. +Since there can't be missing values in the trace, these cells have to be filled with something that doesn't break the constraints. When there are no more value holes to fill, the remaining unused cells are filled with the rc max value (although any value given by the offsets would do the trick, since we only check they are in a certain range, but this is the way it is done in Stone).

+

As an example, consider that the values in this column are 2, 4 and 5. We would insert the value 3 in the first available unused cell at row 1, and then fill all the other unused cells with the value 5 until the end of the trace.

+

For reference, check range_check_cell.h and range_check_cell.inl.

+

Column 1 - flags (decode/opcode rc)

+

This is the column in charge of holding the flags configuration of each executed Cairo instruction. As a quick summary, each Cairo instruction has 15 bit of flags. The constraint over these 15 bits is that they are in fact bits, so only 0 or 1 are allowed. Mathematically, this means that , for in , while always. +As can be seen in the Cairo whitepaper section 9.4, we can define

+

+

so that is the full 15 bit value and . This way, instead of allocating 15 virtual columns for each flag, we can allocate one of lenght 16 (for each step of the Cairo execution), with the values . These are the actual values that appear on the Stone prover trace, rather than the 0s or 1s.

+

Noting that , we get that the constraint over this virtual column becomes

+

.

+

Column 2 - sorted rc (rc16/sorted)

+

Pretty straightforward. This column has the same as the ones in column 0 but sorted in ascending order.

+

Column 3 - mem pool

+

All trace cells that reflect state of the Cairo VM memory can be found here. They are two main virtual columns:

+
    +
  • memory addresses - step 2, row offset 0.
  • +
  • memory values - step 2, row offset 1.
  • +
+

Each one has, at the same time, virtual sub-columns. In essence, this just means that in addition to the constraints that memory cells must hold, some of these memory cells have additional constraints. +To visualize them better, here is a more detailed diagram of the column:

+

+

The virtual sub-columns are

+
    +
  • instruction addr - step 16, row offset 0. The address of the instruction executed in the current step. In other words, the content of the pc register
  • +
  • instruction value - step 16, row offset 1. The actual encoded instruction pointed by the instruction address.
  • +
  • dst addr - step 16, row offset 8. The address of dst.
  • +
  • dst value - step 16, row offset 9. The actual value of dst.
  • +
  • op0 addr - step 16, row offset 4. The address of op0.
  • +
  • op0 value - step 16, row offset 5. The actual value of op0.
  • +
  • op1 addr - step 16, row offset 12. The address of op1.
  • +
  • op1 value - step 16, row offset 13. The actual value of op1.
  • +
  • pub memory addr - step 8, row offset 2. Address of a public memory cell
  • +
  • pub memory value - step 8, row offset 3. Value of a public memory cell.
  • +
+

The public memory address and value in this column are actually always . In other words, these are the dummy memory accesses that are added to the trace. +For a Cairo program with a number of instructions, there should be at least dummy memory accesses inserted. As can be seen, there are 2 dummy accesses inserted in each trace step, since the total rows in a step is 16 and the step of the public memory virtual sub-column is 8. +This means that we need at least steps to fit the necessary public memory cells. In usual Cairo programs, this is achieved easily.

+

Unused cells in mem pool

+

If we analyze the rows used by virtual sub-columns in the mem pool, we see there are 4 cells left unused in row offsets 6, 7, 14 and 15. +Since the constraints in the memory require addresses to be continuous, these unused cells are used to fill any memory holes found in the range of the accessed addresses. +As memory holes are found, they are inserted in these empty cells in ascending order, with the address of the hole and a 0 in its corresponding value. +As an example, the first memory hole address will be inserted in row 6 and the value 0 will be inserted at row 7. The next memory hole will be filled in rows 14 and 15, while the next memory hole would be filled in rows 22 and 23, etc. +An important detail is that the last accessed address + 1 will always appear as a memory hole. This means that once all gaps are filled, the (max address + 1, 0) entry will appear in all these unused cells, until the end of the trace. In particular, if the memory didn't have any hole, this will be the only value that will be appear in these unused cells. +For reference, check memory_cell.h and memory_cell.inl.

+

Column 4 - sorted mem pool

+

The same values as column 3, but the (address, value) pairs get sorted by their address, in ascending order, but there is no accesses here. +The dummy memory accesses are replaced by the real public memory values, repeating the first (pub addr, pub value) until all the dummy accesses are replaced. +For example, if the public memory is:

+
        (1, 4334524)
+        (2, 3252643)
+        (3, 3245444)
+
+

and the total dummy accesses are (in total, one more than the actual publc memory):

+
        (0, 0)
+        (0, 0)
+        (0, 0)
+        (0, 0)
+
+

the final result in the sorted column will be

+
        (1, 4334524)
+        (1, 4334524)
+        (2, 3252643)
+        (3, 3245444)
+
+

Column 5 - registers / pc update

+

This column is used to store the values of the ap and fp registers, the res value and some values used as an optimization, *t0 +*, t1 and ops_mul related to the update of the pc register. Each one has a corresponding virtual column:

+
    +
  • ap - step 16, row offset 0.
  • +
  • fp - step 16, row offset 8.
  • +
  • ops_mul - step 16, row offset 4.
  • +
  • res - step 16, row offset 12.
  • +
  • tmp0 - step 16, row offset 2.
  • +
  • tmp1 - step 16, row offset 10.
  • +
+

Interaction trace columns

+

As these columns are built from an interaction with the verifier, the values found here are not deterministic. +The interaction trace columns can be visualized with the following table:

+

+

Column 6 - range check cumulative product

+

The details about how this column is built can be found in the Cairo whitepaper, sections 9.4 and 9.9. In summary, just the cumulative product to prove the permutation between the the rc pool and the sorted rc pool.

+

Column 7 - multi column permutation cum product

+

Similarly to column 6, the details of how this column is built is referred to sections 9.7 and 9.8 of the whitepaper. +The only relevant detail is that this cumulative product is in fact a virtual column, with step 2 and row offset 0. All the other values in this column (step 2 and row offset 1) are unused and filled with 0s.

+ +
+ + +
+
+ + + +
+ + + + + + + + + + + + + + + + + + + + diff --git a/starks/under_the_hood.html b/starks/under_the_hood.html new file mode 100644 index 000000000..9999ac315 --- /dev/null +++ b/starks/under_the_hood.html @@ -0,0 +1,411 @@ + + + + + + Under the hood - docs + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + + + +
+
+ +

How this works under the hood

+

In this section we go over how a few things in the prove and verify functions are implemented. If you just need to use the prover, then you probably don't need to read this. If you're going through the code to try to understand it, read on.

+

We will once again use the fibonacci example as an ilustration. Recall from the recap that the main steps for the prover and verifier are the following:

+

Prover side

+
    +
  • Compute the trace polynomial t by interpolating the trace column over a set of -th roots of unity .
  • +
  • Compute the boundary polynomial B.
  • +
  • Compute the transition constraint polynomial C.
  • +
  • Construct the composition polynomial H from B and C.
  • +
  • Sample an out of domain point z and provide the evaluation and all the necessary trace evaluations to reconstruct it. In the fibonacci case, these are , , and .
  • +
  • Sample a domain point x_0 and provide the evaluation and .
  • +
  • Construct the deep composition polynomial Deep(x) from H, t, and the evaluations from the item above.
  • +
  • Do FRI on Deep(x) and provide the resulting FRI commitment to the verifier.
  • +
  • Provide the merkle root of t and the merkle proof of .
  • +
+

Verifier side

+
    +
  • Take the evaluation along with the trace evaluations the prover provided.
  • +
  • Reconstruct the evaluations and from the trace evaluations. Check that the claimed evaluation the prover gave us actually satisfies +
  • +
  • Take the evaluations and .
  • +
  • Check that the claimed evaluation the prover gave us actually satisfies +
  • +
  • Take the provided FRI commitment and check that it verifies.
  • +
  • Using the merkle root and the merkle proof the prover provided, check that belongs to the trace.
  • +
+

Following along the code in the prove and verify functions, most of it maps pretty well to the steps above. The main things that are not immediately clear are:

+
    +
  • How we take the constraints defined in the AIR through the compute_transition method and map them to transition constraint polynomials.
  • +
  • How we then construct H from them and the boundary constraint polynomials.
  • +
  • What the composition polynomial even/odd decomposition is.
  • +
  • What an ood frame is.
  • +
  • What the transcript is.
  • +
+

Reconstructing the transition constraint polynomials

+

This is possibly the most complex part of the code, so what follows is a long explanation for it.

+

In our fibonacci example, after obtaining the trace polynomial t by interpolating, the transition constraint polynomial is

+

+

On our prove code, if someone passes us a fibonacci AIR like the one we showed above used in one of our tests, we somehow need to construct . However, what we are given is not a polynomial, but rather this method

+

+#![allow(unused)]
+fn main() {
+fn compute_transition(
+        &self,
+        frame: &air::frame::Frame<Self::Field>,
+    ) -> Vec<FieldElement<Self::Field>> {
+    let first_row = frame.get_row(0);
+    let second_row = frame.get_row(1);
+    let third_row = frame.get_row(2);
+
+    vec![third_row[0] - second_row[0] - first_row[0]]
+}
+}
+
+

So how do we get to from this? The answer is interpolation. What the method above is doing is the following: if you pass it a frame that looks like this

+

+

for any given point , it will return the value

+

+

which is the numerator in . Using the transition_exemptions field we defined in our AIR, we can also compute evaluations in the denominator, i.e. the zerofier evaluations. This is done under the hood by the transition_divisors() method.

+

The above means that even though we don't explicitly have the polynomial , we can evaluate it on points given an appropriate frame. If we can evaluate it on enough points, we can then interpolate them to recover . This is exactly how we construct both transition constraint polynomials and subsequently the composition polynomial H.

+

The job of evaluating H on enough points so we can then interpolate it is done by the ConstraintEvaluator struct. You'll notice prove does the following

+

+#![allow(unused)]
+fn main() {
+let constraint_evaluations = evaluator.evaluate(
+    &lde_trace,
+    &lde_roots_of_unity_coset,
+    &alpha_and_beta_transition_coefficients,
+    &alpha_and_beta_boundary_coefficients,
+);
+}
+
+

This function call will return the evaluations of the boundary terms

+

+

and constraint terms

+

+

for every . The constraint_evaluations value returned is a ConstraintEvaluationTable struct, which is nothing more than a big list of evaluations of each polynomial required to construct H.

+

With this in hand, we just call

+

+#![allow(unused)]
+fn main() {
+let composition_poly =  
+    constraint_evaluations.compute_composition_poly(&   lde_roots_of_unity_coset);
+}
+
+

which simply interpolates the sum of all evaluations to obtain H.

+

Let's go into more detail on how the evaluate method reconstructs in our fibonacci example. It receives the lde_trace as an argument, which is this:

+

+

where is the primitive root of unity used for the LDE, that is, satisfies . We need to recover , a polynomial whose degree can't be more than 's. Because was built by interpolating 8 points (the trace), we know we can recover by interpolating it on 16 points. We choose these points to be the LDE roots of unity

+

+

Remember that to evaluate on these points, all we need are the evaluations of the polynomial

+

+

as the zerofier ones we can compute easily. These become:

+

+

If we remember that , this is

+

+

and we can compute each evaluation here by calling compute_transition on the appropriate frame built from the lde_trace. Specifically, for the first evaluation we can build the frame:

+

+

Calling compute_transition on this frame gives us the first evaluation. We can get the rest in a similar fashion, which is what this piece of code in the evaluate method does:

+

+#![allow(unused)]
+fn main() {
+for (i, d) in lde_domain.iter().enumerate() {
+    let frame = Frame::read_from_trace(
+        lde_trace,
+        i,
+        blowup_factor,
+        &self.air.context().transition_offsets,
+    )
+
+    let mut evaluations = self.air.compute_transition(&frame);
+
+    ...
+}
+}
+
+

Each iteration builds a frame as above and computes one of the evaluations needed. The rest of the code just adds the zerofier evaluations, along with the alphas and betas. It then also computes boundary polynomial evaluations by explicitly constructing them.

+

Verifier

+

The verifier employs the same trick to reconstruct the evaluations on the out of domain point for the consistency check.

+

Even/odd decomposition for H

+

At the end of the recap we talked about how in our code we don't actually commit to H, but rather an even/odd decomposition for it. These are two polynomials H_1 and H_2 that satisfy

+

+

This all happens on this piece of code

+

+#![allow(unused)]
+fn main() {
+let composition_poly =
+    constraint_evaluations.compute_composition_poly(&lde_roots_of_unity_coset);
+
+let (composition_poly_even, composition_poly_odd) = composition_poly.even_odd_decomposition();
+
+// Evaluate H_1 and H_2 in z^2.
+let composition_poly_evaluations = vec![
+    composition_poly_even.evaluate(&z_squared),
+    composition_poly_odd.evaluate(&z_squared),
+];
+}
+
+

After this, we don't really use H anymore, but rather H_1 and H_2. There's not that much to say other than that.

+

Out of Domain Frame

+

As part of the consistency check, the prover needs to provide evaluations of the trace polynomials in all the points needed by the verifier to check that H was constructed correctly. In the fibonacci example, these are , , and . In code, the prover passes these evaluations as a Frame, which we call the out of domain (ood) frame.

+

The reason we do this is simple: with the frame in hand, the verifier can reconstruct the evaluations of the constraint polynomials by calling the compute_transition method on the ood frame and then adding the alphas, betas, and so on, just like we explained in the section above.

+

Transcript

+

Throughout the protocol, there are a number of times where the verifier randomly samples some values that the prover needs to use (think of the alphas and betas used when constructing H). Because we don't actually have an interaction between prover and verifier, we emulate it by using a hash function, which we assume is a source of randomness the prover can't control.

+

The job of providing these samples for both prover and verifier is done by the Transcript struct, which you can think of as a stateful rng; whenever you call challenge() on a transcript you get a random value and the internal state gets mutated, so the next time you call challenge() you get a different one. You can also call append on it to mutate its internal state yourself. This is done a number of times throughout the protocol to keep the prover honest so it can't predict or manipulate the outcome of challenge().

+

Notice that to sample the same values, both prover and verifier need to call challenge and append in the same order (and with the same values in the case of append) and the same number of times.

+

The idea explained above is called the Fiat-Shamir heuristic or just Fiat-Shamir, and is more generally used throughout proving systems to remove interaction between prover and verifier. Though the concept is very simple, getting it right so the prover can't cheat is not, but we won't go into that here.

+

Proof

+

The generated proof has got all the information needed for the verifier to verify it:

+
    +
  • Trace length: The number of rows of the trace table, needed to know the max degree of the polynomials that appear in the system.
  • +
  • LDE trace commitments.
  • +
  • DEEP composition polynomial out of domain even and odd evaluations.
  • +
  • DEEP composition polynomial root.
  • +
  • FRI layers merkle roots.
  • +
  • FRI last layer value.
  • +
  • Query list.
  • +
  • DEEP composition poly openings.
  • +
  • Nonce: Proof of work setting used to generate the proof.
  • +
+

Special considerations

+

FFT evaluation and interpolation

+

When evaluating or interpolating a polynomial, if the input (be it coefficients or evaluations) size isn't a power of two then the FFT API will extend it with zero padding until this requirement is met. This is because the library currently only uses a radix-2 FFT algorithm.

+

Also, right now FFT only supports inputs with a size up to elements.

+

Other

+

Why use roots of unity?

+

Whenever we interpolate or evaluate trace, boundary and constraint polynomials, we use some -th roots of unity. There are a few reasons for this:

+
    +
  • +

    Using roots of unity means we can use the Fast Fourier Transform and its inverse to evaluate and interpolate polynomials. This method is much faster than the naive Lagrange interpolation one. Since a huge part of the STARK protocol involves both evaluating and interpolating, this is a huge performance improvement.

    +
  • +
  • +

    When computing boundary and constraint polynomials, we divide them by their zerofiers, polynomials that vanish on a few points (the trace elements where the constraints do not hold). These polynomials take the form

    +

    +

    where the are the points where we want it to vanish.

    +

    When implementing this, evaluating this polynomial can be very expensive as it involves a huge product. However, if we are using roots of unity, we can use the following trick. The vanishing polynomial for all the roots of unity is

    +

    +

    Instead of expressing the zerofier as a product of the places where it should vanish, we express it as the vanishing polynomial above divided by the exemptions polynomial; the polynomial whose roots are the places where constraints don't need to hold.

    +

    +

    where the are now the points where we don't want it to vanish. This exemptions polynomial in the denominator is usually much smaller, and because the vanishing polynomial in the numerator is only two terms, evaluating it is really fast.

    +
  • +
+

What is a primitive root of unity?

+

The -th roots of unity are the numbers that satisfy

+

+

There are such numbers, because they are the roots of the polynomial . The set of -th roots of unity always has a generator, a root that can be used to obtain every other root of unity by exponentiating. What this means is that the set of -th roots of unity is

+

+

Any such generator g is called a primitive root of unity. It's called primitive because it allows us to recover any other root.

+

Here are a few important things to keep in mind, some of which we use throughout our implementation:

+
    +
  • +

    There are always several primitive roots. If is primitive, then any power with coprime with is also primitive. As an example, if is a primitive -th root of unity, then is also primitive.

    +
  • +
  • +

    We generally will not care about which primitive root we choose; what we do care about is being consistent. We should always choose the same one throughout our code, otherwise computations will go wrong.

    +
  • +
  • +

    Because , the powers of wrap around. This means

    +

    +

    and so on.

    +
  • +
  • +

    If is a primitive -th root of unity, then is a primitive -th root of unity. In general, if is a primitive -th primitive root of unity, then is a primitive -th root of unity.

    +
  • +
+

Why use Cosets?

+

When we perform FRI on the DEEP composition polynomial, the low degree extension we use is not actually over a set of higher roots of unity than the ones used for the trace, but rather a coset of it. A coset is simply a set of numbers all multiplied by the same element. We call said element the offset. In our case, a coset of the -th roots of unity with primitive root and offset h is the set

+

+

So why not just do the LDE without the offset? The problem is in how we construct and evaluate the composition polynomial H. Let's say our trace polynomial was interpolated over the -th roots of unity with primitive root , and we are doing the LDE over the -th roots of unity with primitive root , so (i.e. the blowup factor is 2).

+

Recall that H is a sum of terms that include boundary and transition constraint polynomials, and each one of them includes a division by a zerofier; a polynomial that vanishes on some roots of unity . This is because the zerofier is what tells us which rows of the trace our constraint should apply on.

+

When doing FRI, we have to provide evaluations over the LDE domain we are using. If we don't include the offset, our domain is

+

+

Note that, because , some of the elements on this set (actually, half of them) are powers of . If while doing FRI we evaluate H on them, the zerofier could vanish and we'd be dividing by zero. We introduce the offset to make sure this can't happen.

+

NOTE: a careful reader might note that we can actually evaluate H on the elements , since on a valid trace the zerofiers will actually divide the polynomials on their numerator. The problem still remains, however, because of performance. We don't want to do polynomial division if we don't need to, it's much cheaper to just evaluate numerator and denominator and then divide. Of course, this only works if the denominator doesn't vanish; hence, cosets.

+ +
+ + +
+
+ + + +
+ + + + + + + + + + + + + + + + + + + + diff --git a/starks/virtual_cols.html b/starks/virtual_cols.html new file mode 100644 index 000000000..3cfff924e --- /dev/null +++ b/starks/virtual_cols.html @@ -0,0 +1,308 @@ + + + + + + Virtual Columns - docs + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + + + +
+
+ +

Virtual columns and Subcolumns

+

Virtual Columns

+

In previous chapters, we have seen how the registers states and the memory are augmented to generate a provable trace.

+

While we have shown a way of doing that, there isn't only one possible provable trace. In fact, there are multiple configurations possible.

+

For example, in the Cairo VM, we have 15 flags. These flags include "DstReg", "Op0Reg", "OpCode" and others. For simplification, let's imagine we have 3 flags with letters from "A" to "C", where "A" is the first flag.

+

Now, let's assume we have 4 steps in our trace. If we were to only use plain columns, the layout would look like this:

+ + + + + +
FlagAFlagBFlagB
A0B0C0
A1B1C1
A2B2C2
A3B3C3
+

But, we could also organize them like this

+ + + + + + + + + + + + + +
Flags
A0
B0
C0
A1
B1
C1
A2
B2
C2
A3
B3
C3
+

The only problem is that now the constraints for each transition of the rows are not the same. We will have to define then a concept called "Virtual Column".

+

A Virtual Column is like a traditional column, which has its own set of constraints, but it exists interleaved with another one. In the previous example, each row is associated with a column, but in practice, we could have different ratios. We could have 3 rows corresponding to one Virtual Column, and the next one corresponding to another one. For the time being, let's focus on this simpler example.

+

Each row corresponding to Flag A will have the constraints associated with its own Virtual Column, and the same will apply to Flag B and Flag C.

+

Now, to do this, we will need to evaluate the multiple rows taking into account that they are part of the same step. For a real case, we will add a dummy flag D, whose purpose is to make the evaluation move in a number that is a power of 2.

+

Let's see how it works. If we were evaluating the Frame where the constraints should give 0, the frame movement would look like this:

+
+ A0 | B0 | C0
++ A1 | B1 | C1
+  A2 | B2 | C2
+  A3 | B3 | C3
+
+
  A0 | B0 | C0
++ A1 | B1 | C1
++ A2 | B2 | C2
+  A3 | B3 | C3
+
+
  A0 | B0 | C0
+  A1 | B1 | C1
++ A2 | B2 | C2
++ A3 | B3 | C3
+
+

In the second case, the evaluation would look like this:

+
+ A0 |
++ B0 |
++ C0 |
++ D0 |
++ A1 |
++ B1 |
++ C1 |
++ D1 |
+  A2 |
+  B2 |
+  C2 |
+  D2 |
+  A3 |
+  B3 |
+  C3 |
+  D3 |
+
+
  A0 |
+  B0 |
+  C0 |
+  D0 |
++ A1 |
++ B1 |
++ C1 |
++ D1 |
++ A2 |
++ B2 |
++ C2 |
++ D2 |
+  A3 |
+  B3 |
+  C3 |
+  D3 |
+
+
  A0 |
+  B0 |
+  C0 |
+  D0 |
+  A1 |
+  B1 |
+  C1 |
+  D1 |
++ A2 |
++ B2 |
++ C2 |
++ D2 |
++ A3 |
++ B3 |
++ C3 |
++ D3 |
+
+

When evaluating the composition polynomial, we will do it over the points on the LDE, where the constraints won't evaluate to 0, but we will use the same spacing. Assume we have three constraints for each flag, , , and , and that they don't involve other trace cells. Let's call the index of the frame evaluation i, starting from 0.

+

In the first case, the constraint , and would be applied over the same rows, giving an equation that looks like this:

+

+

In the second case, the equations would look like:

+

+

+

+

Virtual Subcolumns

+

Assume now we have 3 columns that share some constraints. For example, let's have three flags that can be either 0 or 1. Each flag will also have its own set of dedicated constraints.

+

Let's denote the shared constraint , and the independent constraints .

+

What we can do is define a Column for the flags, where the binary constraint is enforced. Additionally, we will define a subcolumn for each flag, which will enforce each .

+

In summary, if we have a set of shared constraints to apply, we will be using a Column. If we want to mix or interleave Columns, we will define them as Virtual Columns. And if we want to apply more constraints to a subset of a Column of Virtual Columns, or share constraints between columns, we will define Virtual Subcolumns.

+ +
+ + +
+
+ + + +
+ + + + + + + + + + + + + + + + + + + + diff --git a/tomorrow-night.css b/tomorrow-night.css new file mode 100644 index 000000000..f71979258 --- /dev/null +++ b/tomorrow-night.css @@ -0,0 +1,104 @@ +/* Tomorrow Night Theme */ +/* http://jmblog.github.com/color-themes-for-google-code-highlightjs */ +/* Original theme - https://github.com/chriskempson/tomorrow-theme */ +/* http://jmblog.github.com/color-themes-for-google-code-highlightjs */ + +/* Tomorrow Comment */ +.hljs-comment { + color: #969896; +} + +/* Tomorrow Red */ +.hljs-variable, +.hljs-attribute, +.hljs-tag, +.hljs-regexp, +.ruby .hljs-constant, +.xml .hljs-tag .hljs-title, +.xml .hljs-pi, +.xml .hljs-doctype, +.html .hljs-doctype, +.css .hljs-id, +.css .hljs-class, +.css .hljs-pseudo { + color: #cc6666; +} + +/* Tomorrow Orange */ +.hljs-number, +.hljs-preprocessor, +.hljs-pragma, +.hljs-built_in, +.hljs-literal, +.hljs-params, +.hljs-constant { + color: #de935f; +} + +/* Tomorrow Yellow */ +.ruby .hljs-class .hljs-title, +.css .hljs-rule .hljs-attribute { + color: #f0c674; +} + +/* Tomorrow Green */ +.hljs-string, +.hljs-value, +.hljs-inheritance, +.hljs-header, +.hljs-name, +.ruby .hljs-symbol, +.xml .hljs-cdata { + color: #b5bd68; +} + +/* Tomorrow Aqua */ +.hljs-title, +.css .hljs-hexcolor { + color: #8abeb7; +} + +/* Tomorrow Blue */ +.hljs-function, +.python .hljs-decorator, +.python .hljs-title, +.ruby .hljs-function .hljs-title, +.ruby .hljs-title .hljs-keyword, +.perl .hljs-sub, +.javascript .hljs-title, +.coffeescript .hljs-title { + color: #81a2be; +} + +/* Tomorrow Purple */ +.hljs-keyword, +.javascript .hljs-function { + color: #b294bb; +} + +.hljs { + display: block; + overflow-x: auto; + background: #1d1f21; + color: #c5c8c6; + padding: 0.5em; + -webkit-text-size-adjust: none; +} + +.coffeescript .javascript, +.javascript .xml, +.tex .hljs-formula, +.xml .javascript, +.xml .vbscript, +.xml .css, +.xml .hljs-cdata { + opacity: 0.5; +} + +.hljs-addition { + color: #718c00; +} + +.hljs-deletion { + color: #c82829; +}